使用Docker-compose离线部署Django应用的方法
我们所在的内网环境需要部署一个类似CMS的应用,就是一些表格的CRUD,数据导出,人员权限管理等功能。想到Django做这方面的工作挺擅长的,而且开发量不大,于是选择Django作为开发基础。开发功能比较简单,差不多就是使用xadmin等插件实现以上功能。但有一个问题我们是不好绕过去的,那就是部署到一个内网环境,在内网pip等工具是不能使用的,但好在内网有一个yum服务器可以使用,所以我们决定在内网服务器上安装Docker,然后把开发环境的容器复制到生产环境实现部署。以下是主要的步骤:
- 安装开发环境的 Docker-ce
- 安装开发环境的 Docker-compose
- 配置开发环境
- 保存容器
- 安装生产环境的 Docker-ce 和 docker-compose
- 发送容器文件并运行
注意:我这里的开发环境是Ubuntu18.04,生产环境是Centos7.2。如果你是其他环境请自己检查差异,使用适合自己系统的命令。
安装开发环境的 Docker-ce
Docker 和 Docker-compose是我们这次部署需要重点演示的内容,Django 的应用部分我会尽量缩减的。Docker 负责容器虚拟化的底层部分,Docker-compose 是一个容器编排工具,有了它咱们就不用手写 shell 实现容器之间的连接了。我们先安装 Docker-ce,这里主要是参考 Docker 的官方文档,如果我写的不够详细或者已经过时,各位看官可到官方查看更权威更新的文档。
卸载旧版本
在安装之前需要卸载旧版本的 docker,如果你是新系统,可以忽略这一步。
$ sudo apt remove docker docker-engine docker.io containerd runc
安装用用到的 apt 仓库
更新apt包索引
$ sudo apt update
允许apt通过https访问仓库
$ sudo apt install \ apt-transport-https \ ca-certificates \ curl \ gnupg-agent \ software-properties-common
增加Docker的官方GPG key
$ curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add -
增加Docker的仓库
$ sudo add-apt-repository \ "deb [arch=amd64] https://download.docker.com/linux/ubuntu \ $(lsb_release -cs) \ stable"
安装 Docker-ce
做好以上的准备后安装Docker-ce就简单了,熟悉Ubuntu的话,很快就能装好。
$ sudo apt update $ sudo apt install -y docker-ce
安装完成后,启动 docker 服务并使其能够在每次系统引导时启动。
$ sudo systemctl start docker $ sudo systemctl enable docker
安装开发环境的Docker-compose
Docker-ce安装完成后,Docker-compose就好办了。如果你是在Linux等平台上直接下载Docker-compose的编译好的二进制文件即可使用。
代码如下:
$ sudo curl -L "https://github.com/docker/compose/releases/download/1.23.2/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
下载完成后修改权限加上可执行
$ sudo chmod +x /usr/local/bin/docker-compose
最后可执行一下查看Docker-compose的版本号验证一下是否成功安装
$ docker-compose --version docker-compose version 1.24.0-rc1, build 0f3d4dda
配置开发环境
这里的开发环境是Django的环境,演示的项目为了方便演示我尽量使用一个新建的Django项目。
新建Django项目
新建一个Django项目,先创建一个上层文件夹来把项目文件放到这个文件夹中。目录结构大致如下:
--project --Dockerfile --docker-compose.yml --mysite --manage.py --requirements.txt
先创建project文件夹
$ mkdir project
然后新建Django项目,或者你也可以把已有的项目拷贝过来。
$ django-admin.py startproject mysite
生成requirements.txt文件
上一步已经有了一个叫mysite的Django项目,假设我们把requirements.txt放到这个文件夹下,内容大致如下:
$ cat requirements.txt
defusedxml==0.5.0 diff-match-patch==20181111 Django==2.1.7 django-crispy-forms==1.7.2 django-formtools==2.1 django-import-export==1.2.0 django-reversion==3.0.3 et-xmlfile==1.0.1 future==0.15.2 httplib2==0.9.2 jdcal==1.4 odfpy==1.4.0 openpyxl==2.6.0 pytz==2018.9 PyYAML==3.13 six==1.10.0 tablib==0.12.1 unicodecsv==0.14.1 xadmin==0.6.1 xlrd==1.2.0 xlwt==1.3.0 mysqlclient==1.4.2
当然这是我的项目需要的依赖,你的依赖可能和我的不一样。
新建Dockerfile
项目有了,项目的依赖文件也有了,下一步就是创建我们的Django项目的运行环境的docker镜像了,先建一个Dockerfile来构建docker镜像。 在project文件夹新建Dockerfile,内容如下:
$ cat Dockerfile
FROM python:3.6.8 ENV PYTHONUNBUFFERED 1 RUN mkdir /config ADD /mysite/requirements.txt /config/ RUN pip install -r /config/requirements.txt RUN mkdir /src WORKDIR /src/mysite
我简单解释一下这个文件
FROM python:3.6.8
这里我使用的基础镜像是python:3.6.8,它的基础镜像是Ubuntu我比较熟悉,如果你对alpine比较熟悉的话也可以使用alpine,那个镜像要小的多。
ENV PYTHONUNBUFFERED 1
你可以使用 Env 关键字创建任意的操作系统的环境变量
ENV PYTHONUNBUFFERED 1
例如,如果你使用它来存储你的 Django 密钥,你可以这样写:
ENV DJANGO_SECRET_KEY l!fafmjcqyn+j+zz1@2@wt$o8w8k(_dhgub%41l#k3zi2m-b%m
在你的代码里这样使用:
import os SECRET_KEY = os.environ['DJANGO_SECRET_KEY']
RUN顾名思义,RUN就是在容器里面运行命令,这里RUN命令创建了两个文件夹/config和/src,以及安装Python的依赖环境。
RUN mkdir /config RUN mkdir /src RUN pip install -r /config/requirements.txt
ADD
ADD /mysite/requirements.txt /config/
增加本地的文件到容器中 WORKDIR
WORKDIR /src/mysite
是指定后面所有在容器里运行命令的默认路径,要运行的命令在稍后的docker-compose文件这种可以看到。
新建docker-compose脚本
docker-compose可以用来管理多个容器,以前手动加海量参数运行容器并连接容器的活都可以让docker-compose来做了。我的docker-compose.yml内容大致如下:
$ cat docker-compose.yml
version: '3' services: db: image: mysql:5.7 container_name: mysite_db ports: - "3306:3306" environment: MYSQL_ROOT_PASSWORD: mysite MYSQL_DATABASE: mysite LANG: C.UTF-8 web: build: . container_name: mysite_web command: bash -c "python manage.py makemigrations && python manage.py migrate && python manage.py runserver 0.0.0.0:8000" depends_on: - db volumes: - ./mysite:/src restart: always ports: - "8002:8000"
再简单解释一下这个docker-compose文件吧。
version: '3'
指的是docker-compose的版本,不同版本支持的配置项目稍有不同。 services 管理的服务,我们的例子中是两个服务:db和web。两个服务中的配置项目我分开解释一下 db:
image 直接使用docker hub或者本地的已有镜像,这个使用的是MySQL5.7 container_name 指定容器的名称 ports 指定容器对宿主机的端口映射,前面的是宿主机端口,后面的是容器端口 environment 指定当前服务运行时候的环境,环境的细节参考当前镜像的说明,那上面说支持哪些,我们就可以配置哪些。这个案例中我们指定了MySQL的root密码、默认数据库和数据库的字符集。 web: build 编译镜像,这里是使用当前文件夹下的Dockerfile command 容器启动后执行的命令 depends_on 当前容器要依赖的服务,也就是说必须依赖中的服务成功启动当前服务才能启动 volumes 当前容器要挂载的卷,前面指的是宿主机的目录,后面是容器目录 restart 指定容器的重启策略,当前案例是如果出错就一直重启。 这里把容器的8000端口映射到宿主机的8002端口,web服务就是从8002端口访问。
配置Django项目
现在针对当前的容器环境修改一下mysite项目的settings.py文件。
$ vim mysite/mysite/settings.py
找到文件中的ALLOW_HOSTS部分,添加“web”到其中,内容如下:
ALLOW_HOSTS = [ ... 'web' ]
然后修改settings.py中的DATABASES部分,把参数改为MySQL服务db的参数,内容大致如下:
DATABASES = { 'default': { 'ENGINE': 'django.db.backends.mysql', 'NAME': 'mysite', 'USER': 'root', 'PASSWORD': 'mysite', 'HOST': 'db' } }
这里的MySQL连接参数都是docker-compose.yml文件中db部分的environment中定义的。值得指出的是参数HOST值为db,docker-compose启动容器后,会连接这些容器,容器之间可以使用服务名称互相ping通,就像使用域名那样,所以这里的“HOST”可直接填写“db”。
使用Docker-compose构建项目
经过以上的努力,基本准备齐备了,我们可以构造我们的镜像了,这里有两个服务,db只需要在运行的时候下载或者使用本地镜像就行,web还需要使用Dockerfile构建一下。
$ docker-compose build
经过一阵儿下载或者构建,就能看到成功构建镜像的信息了。
运行项目并测试一下
构建完成后,就有了web服务的镜像了,我们现在使用docker-compose来启动服务。
$ docker-compose up -d
这个过程可能也需要执行一阵儿,取决于你的网速,它会下载MySQL的镜像,并且根据db和web的镜像构造容器,并运行容器。完成后可以使用docker-compose ps和docker-compose images来查看我们生成的容器和镜像
$ docker-compose ps
Name Command State Ports --------------------------------------------------------------------------------------- mysite_db docker-entrypoint.sh mysqld Up 0.0.0.0:3306->3306/tcp, 33060/tcp mysite_web bash -c python manage.py m ... Up 0.0.0.0:8002->8000/tcp
$ docker-compose images
Container Repository Tag Image Id Size -------------------------------------------------------- mysite_db mysql 5.7 e47e309f72c8 355 MB mysite_web mysite_web latest 3989acbcc3c9 938 MB
也可使用docker-compose来停止和开始服务,其他更具体的使用方法,请参考官方文档吧。
$ docker-compose start Starting db ... done Starting web ... done
$ docker-compose stop Stopping mysite_web ... done Stopping mysite_db ... done
你看这里的服务停止和启动的顺序都是有规律的启动的时候被依赖的服务先启动然后启动依赖它的服务,挺值服务的时候刚好相反。待服务正常运行后,可以访问浏览器测试一下服务是否正常启动。
保存容器
如果服务一切正常,我们就要把当前的容器保存起来,为部署到新平台上做准备。 注意: 这里要使用save保存镜像,使用save是包括容器之间的连接状态等信息的,如果用export导出镜像到生产环境是不能使用docker-compose恢复服务的。
$ docker save -o mysql.tar mysql:5.7 $ docker save -o mysite.tar mysite_web:latest
当以上命令执行成功后会在当前目录生成两个tar文件,再加上project目录的Dockerfile和docker-compose.yml文件放在一起准备迁移到生产机器上。
安装生产环境的 Docker-ce 和 docker-compose
由于生产环境是CentOS,可以直接使用yum安装
$ sudo yum install docker-ce
安装成功后,参考开发环境把docker-compose部署到生产服务器上。
发送容器文件并运行
使用scp或者其他工具把mysql.tar、mysite.tar、Docker-compose.yml以及项目文件夹发送到生产服务器,并找一个合适的文件夹存放这些文件,保持原来的目录结构。 我们先把两个镜像恢复到生产服务器上
$ docker load -i mysql.tar $ docker load -i mysite_web.tar
等待一小会儿执行完成,可以看到当前服务器已经有这两个镜像了。
REPOSITORY TAG IMAGE ID CREATED SIZE mysite_web latest 3989acbcc3c9 2 days ago 983MB mysql 5.7 e47e309f72c8 3 weeks ago 372MB
在执行构建容器以前我们还要对docker-compose.yml做个简单的修改。你也注意到,生产服务器没有互联网,所以不能再build镜像了,而且我们还把开发环境的镜像原样照搬了过来,所以这次web服务改为从镜像运行就行了,内容大致如下:
version: '3' services: db: ... web: image: mysite_web:latest ...
只要更改web中的build项删除,并加上一个image项,内容就是我们拷贝过来的那个镜像。稍后我们就可以构建容器并启动服务了。
$ docker-compose up -d
结果
Name Command State Ports ---------------------------------------------------------------------------------------- mysite_web bash -c python manage.py m ... Up 0.0.0.0:8002->8000/tcp mysite_db docker-entrypoint.sh mysqld Up 0.0.0.0:3306->3306/tcp, 33060/tcp
再打开浏览器看看,是否正常启动了。
后记
docker-compose 还有更多的用法,我会在以后的项目中做些其他方向的更深入的介绍。谢谢大家赏光看我的作品,希望你帮到你一点。