使用docker-compose 编排用nginx做代理中间件的Django+Vue前后端分离项目

目录

前期准备

开发环境

软件安装

创建Django+Vue前后端分离项目

后端工程结构

前端工程结构

docker化工程结构

基于Docker和Docker-compose构建项目

初识Docker

几个常用docker指令

Dockerfile

初识Docker-compose

docker-compose常用命令

docker-compose.yml

其他配置

结语

彩蛋


本文将致力于实现搭建一个容器化的使用nginx做代理中间件的 Django+Vue前后端分离的项目。

前期准备

开发环境

整个项目的开发环境是在windows下,但是docker化发行部署是在linux里完成的。

软件安装

  • Docker:学习 Docker 当然要安装 Docker 软件了(免费的社区版),安装方法见官方文档

  • Docker-compose:这是 Docker 官方推出的用于编排、运行多个容器的工具,安装方法见官方文档。本教程大部分内容都与它有关。

  • Python3:教程部署的是 Django 项目,那 Python3 是当然要有的了(包括 python 的包管理工具 pip)。

准备就绪后就继续下一步吧。

创建Django+Vue前后端分离项目

本文重点是docker化该项目,整个试验项目预先已基本完成。接下来是对项目结构做简要的介绍。

后端工程结构

后端工程结构
图1 后端工程结构

图1中,主要配置信息都在./backEnd/settings.py文件中编写好,其中后端使用的监听端口是8000

轻省起见,数据库直接使用了默认的sqllite3。

前端工程结构

前端工程结构
图2 前端工程结构

 图2中,dist文件夹是执行下行代码,

npm run build

然后发行的前端文件。在docker化构建中,将直接就该文件夹做处理。

事实上,vue的项目docker化,应该是从源码执行打包命令开始的,此处跳过了那一步,降低了整体难度,有时间的话我会重新完整走一遍的。

 前端的主要配置信息是在./cms/config/index.js中:

前端配置信息
图3 前端部分配置信息

图3所展示的部分中,"port:8080"指定的是前端的监听端口是8080。因为是前后端分离的项目,会涉及到跨域,proxyTable处理的便是跨域问题,当遇到请求中带有"/api"字样的url,便会将其及之前内容替换成后端的ip。

docker化工程结构

上文说到,在构建前端镜像时,直接使用发行后的dist文件。所以,在docker化构建的过程中,工程目录如图4所示。这样放在一起,而不分开构建镜像的目的是为了使用docker-compose对两个镜像统一编排。

docker化工程结构
图4 docker化工程结构

基于Docker和Docker-compose构建项目

初识Docker

Docker 的整个生命周期由三部分组成:镜像(image)+ 容器(container)+ 仓库(repository)。

容器是由镜像实例化而来,这有点像面向对象的概念:镜像就是类,容器是类实例化之后的对象。

镜像是一个只读的模板,它包括了运行容器所需的数据。镜像可以包含一个完整的 Linux 操作环境,里面仅安装了 Python 或者其他用户需要的程序。

容器是由镜像创建出来的实例,类似虚拟机,里面可以运行特定的应用,并且容器与容器是相互隔离的。

仓库概念与 Git 和 Github 类似,如果你用过它们就非常容易理解。Docker 使用的默认仓库是由官方维护的 Docker hub 公共仓库,从中上传、拉取的操作类似 Git。

几个常用docker指令

我们可以用 docker images 查看本地已有的镜像:

$ docker images

REPOSITORY      TAG        IMAGE ID           CREATED           SIZE
hello-world     latest     fce289e99eb9       9 months ago      1.84kB

 表列分别为镜像名、版本、ID 号、创建时间、大小。

 还可以查看本地已有的容器:

$ docker ps -a

CONTAINER ID   IMAGE        ..   CREATED         ..
38cb03a96dca   hello-world  ..   2 minutes ago   ..

除此之外还有一些非常有用的基础指令:

docker rmi [images ID]  # 删除此 ID 的镜像
docker container stop [container ID]  # 停止此 ID 的容器
docker container start [container ID]  # 启动此 ID 的容器
docker container rm [container ID]  # 删除此 ID 的容器

Dockerfile

Docker 允许通过文本格式的配置文件来构建镜像,默认名称为 Dockerfile。因此在项目根目录新建文件 Dockerfile,写入:

1.对dockerfile与Dockerfile,docker-compose不区分大小写;

2.此处的项目跟目录是指后端的根目录,具体位置可参考图4.

FROM python:3.7

ENV PYTHONUNBUFFERED 1

RUN echo \
deb https://mirrors.tuna.tsinghua.edu.cn/debian/ buster main contrib non-free\
deb https://mirrors.tuna.tsinghua.edu.cn/debian/ buster-updates main contrib non-free\
deb https://mirrors.tuna.tsinghua.edu.cn/debian/ buster-backports main contrib non-free\
deb https://mirrors.tuna.tsinghua.edu.cn/debian-security buster/updates main contrib non-free\
    > /etc/apt/sources.list

RUN mkdir /code
WORKDIR /code
RUN pip install pip -U -i https://pypi.tuna.tsinghua.edu.cn/simple
ADD ./requirements.txt /code/
RUN pip install -r requirements.txt -i https://pypi.tuna.tsinghua.edu.cn/simple
ADD . /code/

理解这些 Docker 指令的关键在于,一定要牢记容器里的环境和外界(宿主机)是隔离的,它两是完全不一样的。换句话说,要搞清楚哪些操作是针对宿主机、哪些操作是针对容器

FROM python:3.7 指令从仓库拉取一个包含 python 3.7 的 Linux 操作系统环境(Linux 版本为 Debian)。

RUN 和 WORKDIR 指令都是针对容器的,功能是在容器里创建目录、并将其设置为工作目录。注意宿主机是没有这个目录的。

ADD 指令出现了两次。ADD requirements.txt /code/ 意思是将宿主机当前目录(即 Dockerfile 所在目录)的 requirements.txt 文件复制到容器的 /code 目录中。ADD . /code/ 意思是把当前目录所有内容复制到容器 /code/ 目录,注意中间那个

初识Docker-compose

在线上环境中,通常不会将项目的所有组件放到同一个容器中;更好的做法是把每个独立的功能装进单独的容器,这样方便复用。比如将 Django 代码放到容器A,将 Mysql 数据库放到容器B,将vue代码放到容器C,以此类推。

因此同一个服务器上有可能会运行着多个容器,如果每次都靠一条条指令去启动,未免也太繁琐了。 Docker-compose 就是解决这个问题的,它用来编排多个容器,将启动容器的命令统一写到 docker-compose.yml 文件中,以后每次启动这一组容器时,只需要 docker-compose up 就可以了。因此教程也会用 docker-compose 来管理容器。

  • Docker Compose 将所管理的容器分为三层,分别是工程(project)、服务(service)、容器(container)

  • Docker Compose 运行目录下的所有文件(docker-compose.yml)组成一个工程,一个工程包含多个服务,每个服务中定义了容器运行的镜像、参数、依赖,一个服务可包括多个容器实例。

docker-compose常用命令

docker-compose up     # 重新构建并启动项目中所有容器
docker-compose up -d  # 在后台重新构建并启动项目中所有容器
docker-compose ps     # 列出项目中目前的所有容器
docker-compose stop   # 停止正在运行的容器
docker-compose start  # 再次启动已停止的容器
docker-compose logs   # 查看服务容器的输出

docker-compose.yml

在项目根目录下创建 docker-compose.yml 并写入:

此处的根目录,我是用./devops当的。常理来说,一般是放在有dockerfile的那个根目录,但是我想同时编排和组织前、后端两个不同的镜像,因为它们本身是前后端分离的,所以抽了出来,放在同时包含backEnd(后端)与cms(前端)文件夹的父目录里。执行时,只需定位到./devops,然后输入docker-compose up即可。至于docker-compose怎么去寻找相应的dockerfile,详见docker-compose.yml代码,可以指明每个镜像的构建路径,docker-compose就可以去相应的路径里寻找相应的dockerfile。

version: "3"
services:
  app:
    restart: always
    build: ./backEnd  # '点'代表当前目录; ./backEnd将构建路径指向后端
    command: bash -c "python3 manage.py migrate && python3 manage.py makemigrations &&python3 manage.py runserver 0.0.0.0:8000"  
    # 使用'bash -c'以允许command里同时输入并次第执行多个指令。这批指令作用于后端容器。
    volumes:
      - ./backEnd:/code
    expose:
      - "8000"
    networks:
      - web_network

  vue_nginx:
    restart: always
    image: nginx:latest
    ports:
      - "80:8080"  # 将宿主机的80端口与该容器内的8080端口进行映射
    volumes:
      - ./cms/dist/:/usr/share/nginx/html/
      - ./config/nginx:/etc/nginx/conf.d
    depends_on:
      - app
    networks:
      - web_network

networks:
  web_network:
    driver: bridge

volumes:
  static-volume:

让我们来分解一下其中的各项含义。

version 代表 docker-compose.yml 的版本,目前最新版为 3,不需要改动它。

接着定义了一个名叫 app 的容器。下面的内容是 app 容器的相关配置:

  • restart :除正常工作外,容器会在任何时候重启,比如遭遇 bug、进程崩溃、docker 重启等情况。

  • build :指定一个包含 Dockerfile 的路径,并通过此 Dockerfile 来构建容器镜像。"./backEnd" 指向后端所在位置。

  • command :容器运行时需要执行的命令。这里就是我们很熟悉的运行开发服务器了,我让其在运行前先执行数据迁移;。

  • volumes :卷,这是个很重要的概念。前面说过容器是和宿主机完全隔离的,但是有些时候又需要将其连通;比如我们开发的 Django 项目代码常常会更新,并且更新时还依赖如 Git 之类的程序,在容器里操作就显得不太方便。所以就有,它定义了宿主机和容器之间的映射:"./backEnd" 表示宿主机的相对工作目录,":" 为分隔符,"/code" 表示容器中的目录。即宿主机的后端工作目录和容器的 /code 目录是连通的,宿主机后端工作目录的 Django 代码更新时,容器中的 /code 目录中的代码也相应的更新了。这有点儿像是在容器上打了一个洞,某种程度上也是实用性隔离性的一种妥协。可以看到,在使用sqllite3的情况下,随着后端容器内数据库数据量的增加,实际上宿主机的后端工作目录里的sqlite3也是同步增长的。

严格意义上讲,这里用到的 ./backEnd:/code 并不是,而是叫挂载,它两是有区别的,只不过 docker-compose 允许将挂载写到卷的配置中。后面会讲到。

  • expose:将这个容器的8000端口暴露给其他容器,但不暴露给宿主机。

然后定义了一个名为vue_nginx的容器。下面的内容是 vue_nginx 容器的相关配置:

  • image:如何构建 nginx 镜像?这么常用的镜像官方已经帮我们构建好了,只需要把它从仓库拉取到本地就可以了。我所拉取的是最新版本。

  • ports :定义了宿主机和容器的端口映射。容器的隔离不止环境,甚至连端口都隔离起来了。但 web 应用不通过端口跟外界通信当然不行,因此这里定义将宿主机的 80 端口映射到容器的 8080 端口,即访问宿主机的 80 端口就是访问到了这个容器的 8080 端口,但要确保端口没有被其他程序占用。

  • volumes:定义卷(这里实际是挂载),上面已经讲过了,它实现了宿主机和容器目录的映射。这里,"./cms/dist/:/usr/share/nginx/html/",使nginx托管了前端的发行文件,属于前端范畴,故给这个容器命名vue_nginx。
    但是因为这是前后端分离项目,而且Django的开发服务器太过脆弱,所以采用Nginx做为代理。"./config/nginx:/etc/nginx/conf.d"将宿主机相应目录里关于前后端使用nginx的配置文件放入nginx容器里的对应位置。关于nginx配置文件,会在篇尾呈现。

  • epends_on ,意思是此容器需要等待 app容器启动完毕才能够启动

网络 network

Docker 允许用户给每个容器定义其工作的网络,只有在相同的网络之中才能进行通讯。你可以看到 app 和 vue_nginx  容器都处于 采用桥接模式的 web_network 网中,所以二者可以直接通信。

定义网络可以隔离容器的网络环境,也方便运维人员一眼看出网络的逻辑关系。

数据卷

之前我们见识过的用于映射宿主机和容器目录的卷了,实际上称为挂载;现在新出现的 static-volume 才叫。它的使用方式像这样:static-volume:/code/collected_static ,冒号后面还是容器内的目录,但冒号前的却不是宿主机目录、仅仅是卷的名称而已。从本质上讲,数据卷也是实现了宿主机和容器的目录映射,但是数据卷是由 Docker 进行管理的,你甚至都不需要知道数据卷保存在宿主机的具体位置。

相比挂载,数据卷的优点是由于是 Docker 统一管理的,不存在由于权限不够引发的挂载问题,也不需要在不同服务器指定不同的路径;缺点是它不太适合单配置文件的映射。

和挂载一样,数据卷的生命周期脱离了容器,删除容器之后卷还是存在的。下次构建镜像时,指定卷的名称就可以继续使用了。

其实在此次试验中并没有用到static-volume。

其他配置

最后就是关于前后端对nginx的配置文件了。

django_app.conf

# django_app.conf

upstream app {
  ip_hash;
  server app:8000;
}

server {
  listen 8000;
  server_name localhost;

#  location /static/ {
#   autoindex on;
#    alias /code/collected_static/;
#  }
  
  location / {
    proxy_pass http://app/;
  }
}

vue_app.conf

# vue_app.conf
upstream vue_nginx {
  ip_hash;
  server vue_nginx:8080;
}


server {
  listen 8080;
  server_name localhost;
  
#  location /static/ {
#   autoindex on;
#    alias /code/collected_static/;
#  }
  
    location / {
        # root 根目录,默认nginx下的html文件夹,可以指定其他
        root   /usr/share/nginx/html;
        index  index.html;
        # vue的路由并不是真实物理路由,所以用try_files,路径都指向根目录下的index.html
        try_files $uri $uri/ /index.html;
    }


    # 代理配置,解决跨域问题  
    location /api {
        proxy_pass http://127.0.0.1:8000/;
    }

    error_page   500 502 503 504  /50x.html;
    location = /50x.html {
      root   /usr/share/nginx/html;
    }
}




在vue_app.conf中,通过设置location /api{…}完成跨域。nginx在匹配到前端发出的携带"http://127.0.0.1:8080/api/……"的请求,会将之重写成"http://127.0.0.1:8080/……",以使nginx代理的8000端口能够收到,并转发给app容器。(请结合本文开头的项目工程结构来读)。

结语

至此就完成了整个项目的构建了。只需回到./devops目录下,输入docker-compose up -d ,待启动成功后,即可使用http://localhost。或者服务器的话,从公网ip访问,记得在安全组中开放80端口。

图5 效果展示

经过这次实验,整个知识体系就已经走通了。

感谢你坚持读到这里( •̀ ω •́ )✧


彩蛋

其实还有一些升级打怪的剩余情节。比如:

  • 将sqllite3替换成mysql,增加一个mysql的镜像,要重新组织网络的
  • 前端不从发行文件开始构建,而是类似于后端的构建过程,从源码文件开始
  • 对nginx配置文件进行深入研究
  • ……
  • 还有很多有意思的地方,最重要的是“一键三连”还没给呐,亲!
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值