使用 Docker-compose 离线部署Django应用

我们所在的内网环境需要部署一个类似CMS的应用,就是一些表格的CRUD,数据导出,人员权限管理等功能。想到Django做这方面的工作挺擅长的,而且开发量不大,于是选择Django作为开发基础。开发功能比较简单,差不多就是使用xadmin等插件实现以上功能。但有一个问题我们是不好绕过去的,那就是部署到一个内网环境,在内网pip等工具是不能使用的,但好在内网有一个yum服务器可以使用,所以我们决定在内网服务器上安装Docker,然后把开发环境的容器复制到生产环境实现部署。以下是主要的步骤:

  1. 安装开发环境的 Docker-ce
  2. 安装开发环境的 Docker-compose
  3. 配置开发环境
  4. 保存容器
  5. 安装生产环境的 Docker-ce 和 docker-compose
  6. 发送容器文件并运行

注意:我这里的开发环境是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 仓库

  1. 更新apt包索引
$ sudo apt update
  1. 允许apt通过https访问仓库
$ sudo apt install \
    apt-transport-https \
    ca-certificates \
    curl \
    gnupg-agent \
    software-properties-common
  1. 增加Docker的官方GPG key
$ curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add -
  1. 增加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

你看这里的服务停止和启动的顺序都是有规律的启动的时候被依赖的服务先启动然后启动依赖它的服务,挺值服务的时候刚好相反。待服务正常运行后,可以访问浏览器测试一下服务是否正常启动。


2019-03-04 19-09-43 的屏幕截图.png

保存容器

如果服务一切正常,我们就要把当前的容器保存起来,为部署到新平台上做准备。注意: 这里要使用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 还有更多的用法,我会在以后的项目中做些其他方向的更深入的介绍。谢谢大家赏光看我的作品,希望你帮到你一点。

参考文档

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,185评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,445评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,684评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,564评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,681评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,874评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,025评论 3 408
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,761评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,217评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,545评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,694评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,351评论 4 332
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,988评论 3 315
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,778评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,007评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,427评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,580评论 2 349

推荐阅读更多精彩内容