作者:HelloGitHub-追梦人物
文中涉及的示例代码,已同步更新到 HelloGitHub-Team 仓库
之前一系列繁琐的部署步骤让我们感到痛苦。这些痛苦包括:
要去服务器上执行 n 条命令
本地环境和服务器环境不一致,明明本地运行没问题,一部署服务器上就挂挂,死活启动不起来
如果上面的情况发生了,又要去服务器上执行 n 条命令以解决问题
本地更新了代码,部署上线后,上述历史又重演一遍,想死的心都有了
那么我们有没有办法,让本地开发环境和线上环境保持一致?这样我们在部署上线前,就可以在本地进行验证,只要验证没问题,我们就有 99% 的把握保证部署上线后也没有问题(1%保留给程序玄学)。
Docker 是一种容器技术,可以为我们提供一个隔离的运行环境。要使用 Docker,首先我们需要编排一个镜像,镜像就是用来描述这个隔离环境应该是什么样子的,它需要安装哪些依赖,需要运行什么应用等,可以把它类比成一搜货轮的制造图。
有了镜像,就可以在系统中构建出一个实际隔离的环境,这个环境被称为容器,就好比根据设计图,工厂制造了一条船。工厂也可以制造无数条这样的船。
容器造好了,只要启动它,隔离环境便运行了起来。由于事先编排好了镜像,因此无论是在本地还是线上,运行的容器内部环境都一样,所以保证了本地和线上环境的一致性,大大减少了因为环境差异导致的各种问题。
所以,我们首先来编排 Docker 镜像。
类似于分离 settings.py 文件为 local.py 和 production.py,我们首先建立如下的目录结构,分别用于存放开发环境的镜像和线上环境的镜像:
HelloDjango-blog-tutorial\ blog\ ... compose\ local\ production\ django\ nginx\ ...local 目录下存放开发环境的 Docker 镜像文件,production\ 下的 django 文件夹存放基于本项目编排的镜像,由于线上环境还要用到 Nginx,所以 nginx 目录下存放 Nginx 的镜像。
线上环境 镜像文件我们先来在 production\django 目录下编排博客项目线上环境的镜像文件,镜像文件以 Dockerfile 命名:
FROM python:3.6-alpine ENV PYTHONUNBUFFERED 1 RUN apk update \ # Pillow dependencies && apk add jpeg-dev zlib-dev freetype-dev lcms2-dev openjpeg-dev tiff-dev tk-dev tcl-dev WORKDIR /app RUN pip install pipenv -i https://pypi.douban.com/simple COPY Pipfile /app/Pipfile COPY Pipfile.lock /app/Pipfile.lock RUN pipenv install --system --deploy --ignore-pipfile COPY . /app COPY ./compose/production/django/start.sh /start.sh RUN sed -i \'s/\r//\' /start.sh RUN chmod +x /start.sh首先我们在镜像文件开头使用 FROM python:3.6-alpine 声明此镜像基于 python:3.6-alpine 基础镜像构建。alpine 是一个 Linux 系统发行版,主打小巧、轻量、安全。我们程序运行需要 Python 环境,因此使用这个小巧但包含完整 Python 环境的基础镜像来构建我们的应用镜像。
ENV PYTHONUNBUFFERED 1 设置环境变量 PYTHONUNBUFFERED=1
接下来的一条 RUN 命令安装图像处理包 Pilliow 的依赖,因为如果使用 django 处理图片时,会使用到 Pillow 这个Python 库。
接着使用 WORKDIR /app 设置工作目录,以后在基于此镜像启动的 Docker 容器中执行的命令,都会以这个目录为当前工作目录。
然后我们使用命令 RUN pip install pipenv 安装 pipenv,-i 参数指定 pypi 源,国内一般指定为豆瓣源,这样下载 pipenv 安装包时更快,国外网络可以省略 -i 参数,使用官方的 pypi 源即可。
然后我们将项目依赖文件 Pipfile 和 Pipfile.lock copy 到容器里,运行 pipenv install 安装依赖。指定 --system 参数后 pipenv 不会创建虚拟环境,而是将依赖安装到容器的 Python 环境里。因为容器本身就是个虚拟环境了,所以没必要再创建虚拟环境。
接着将这个项目的文件 copy 到容器的 /app 目录下(当然有些文件对于程序运行是不必要的,所以一会儿我们会设置一个 dockerignore 文件,里面指定的文件不会被 copy 到容器里)。
然后我们还将 start.sh 文件复制到容器的 / 目录下,去掉回车符(windows 专用,容器中是 linux 系统),并赋予了可执行权限。
start.sh 中就是启动 Gunicorn 服务的命令:
#!/bin/sh python manage.py migrate python manage.py collectstatic --noinput gunicorn blogproject.wsgi:application -w 4 -k gthread -b 0.0.0.0:8000 --chdir=http://www.likecs.com/app我们会让容器启动时去执行此命令,这样就启动了我们的 django 应用。--chdir=http://www.likecs.com/app 表明以 /app 为根目录,这样才能找到 blogproject.wsgi:application。
在项目根目录下建立 .dockerignore 文件,指定不 copy 到容器的文件:
.* _credentials.py fabfile.py *.sqlite3