django + docker 从开发到生产

本文适合使用 django 进行 web 开发,并有对 docker 有一定了解的同学。阅读完能帮助你:

  1. 在开发环境中使用 docker 快速搭建和开发 django 项目
  2. 在生产环境中使用 docker 方便快捷地部署个人 django 项目

对于 docker,你至少需要理解 镜像容器宿主机 这几个概念。
如果你还不知道 docker 是什么,你可以通过这篇 《Docker 傻瓜入门》 文章
大致了解一下。

开始构建开发环境

假设你的系统非常纯洁,没有 python 更没有 django,只有 docker。我们要做的第一步就是利用 docker 来创建一个安装有 django 的环境。
创建一个名叫 Dockerfile 的文件,内容如下:

FROM python:3.8
RUN pip install django
CMD bash

这个文件的内容很简单,就是构建一个基于 python3.8 的镜像,然后额外的安装一下 django,至于 CMD bash 可以暂时不予理会。
然后用 build 命令构建出一个镜像:

docker build -t dj .

从以上命令可以看出,我们给构建的镜像取名叫 dj,构建完成后可以通过 docker images 命令看到这个镜像

创建 django 项目

好了我们已经有了一个 python3.8 + django 的开发环境了,接下来要做的是进入这个开发环境:

docker run --name dev -it -p 8000:8000 -v `pwd`/workspace:/workspace dj

这时你就进入了一个容器中,我们给他取名叫 dev,并且将他和宿主机之间建立了端口和目录的映射关系,即宿主机上当前目录下的 workspace 和容器里面的 /workspace 是等价的。
我们进入容器里的 /workspace 目录创建个 django 项目

cd /workspace
django-admin.py startproject myproj
cd myproj
python manage.py startapp myapp

接下来,我们新开一个终端,回到宿主机上的 workspace 目录,可以看到刚在容器里创建的 myproj 项目也出现在了这里

.
├── manage.py
├── myapp
│   ├── __init__.py
│   ├── admin.py
│   ├── apps.py
│   ├── migrations
│   │   └── __init__.py
│   ├── models.py
│   ├── tests.py
│   └── views.py
└── myproj
    ├── __init__.py
    ├── asgi.py
    ├── settings.py
    ├── urls.py
    └── wsgi.py

现在你可以使用任何喜欢的编辑器在宿主机上进行代码编写和开发了。

运行项目

项目开发到一定程度后我们就需要运行我们的 web 服务了,由于我们的宿主机并没有 python 和 django 环境,运行不了代码,所以我们必须在 dev 容器中运行。
还能找到 dev 容器的那个终端吗?如果不小心关了也没关系,只要运行以下命令,就可以重新进入 dev 了:

docker start dev
docker exec -it dev bash

dev 容器中我们使用 runserver 启动 django 的 web 服务:

cd /workspace/myproj
python manage.py runserver 0.0.0.0:8000

现在在宿主机上打开浏览器,访问 http://127.0.0.1:8000 可以看到你的项目运行起来啦,恭喜!

以上就是在开发环境实践 django + docker 的步骤了,不过我个人其实不太推荐这种做法,因为给你的电脑安装一个 python 然后在 pip install 一下 django 并不算太费事,反而是使用 docker 需要不断的在容器和宿主机之间来回切换,有点伤神。

但是如果你安装 python 和 django 或其他依赖时总是莫名地出现问题,为了避免 “安装环境花了一周时间” 的悲剧出现在你的身上(虽然这通常是 Java 程序员才会遇到的 ),你就应该及时使用 docker 了。

构建生产镜像

对于生产环境部署来说, django + docker 才算是真的派上大用场了
在部署之前我们需要稍微修改一下我们的项目代码,首先我们需要一个 requirements.txt 文件记录下项目所需的所有依赖库,它看上去应该像这样:

# python3.8
django==3.0.4
gunicorn==20.0.4
gevent==1.4.0

然后在 myproj 目录中新建一个 Dockerfile 文件,其内容如下:

FROM python:3.8
WORKDIR /workspace
COPY requirements.txt /workspace/requirements.txt
RUN pip install -r requirements.txt 
ADD . /workspace
EXPOSE 8000
CMD gunicorn -k gevent -w 5 -b 0.0.0.0:8000 myproj.wsgi

接着本地构建和运行一下我们的项目看看

docker build -t prod .
docker run --name prod -d -p 8001:8000 prod

在浏览器打开 http://127.0.0.1:8001 如果看的网站运行正常,就可以进行正式的生产部署了

生产部署

在上生产前确认下 settings.pyDEBUG 已设为 False,以及 STATIC_ROOT 配置的路径。
下面假设我们的 STATIC_ROOT/workspace/static
将整个项目代码包括 Dockerfile 文件推送到生产服务器,在服务器上运行如下命令:

docker build -t prod .
docker run --rm -it prod python manage.py migrate
docker run --rm -it -v /var/www/html/static:/workspace/static prod python manage.py collectstatic --noinput
docker run --name prod -d -p 8000:8000 prod

接着配置一下 nginx 反向代理到 8000 端口,静态文件路由指向 /var/www/html/static 即可。

进阶

如果你的项目有用到 celery 的话,可能还需要这些命令来启动异步任务和监控:

docker run --name worker -d prod worker -A myproj
docker run --name beat -d prod beat -A myproj
docker run --name flower -d -p 5555:5555 prod flower -A myproj --address=0.0.0.0

如果每次启动都要敲这么多命令,那也太闹心了,让我们可以利用 docker 的 entrypoint 功能来优化一下
我们在 myproj 目录下创建一个 entrypoint.sh 文件,内容如下:

#!/bin/bash

appname=myproj;
echo $appname;

if [[ -z "$1" ]]; then
    echo "run server";
    python manage.py migrate;
    python manage.py collectstatic --noinput;
    gunicorn -k gevent -w 5 -b 0.0.0.0:8000 $appname.wsgi;
elif [[ "$1" = "worker" ]]; then
    echo "run celery worker";
    celery worker -A $appname -l info;
elif [[ "$1" = "beat" ]]; then
    echo "run celery beat";
    celery beat -A $appname -l info;
elif [[ "$1" = "flower" ]]; then
    echo "run celery flower";
    flower -A $appname --address=0.0.0.0;
else
    echo "$@"
    exec "$@"
fi

再修改一下我们原来的 Dockerfile

FROM python:3.8
WORKDIR /workspace
COPY requirements.txt /workspace/requirements.txt
RUN pip install -r requirements.txt 
ADD . /workspace
EXPOSE 8000
ENTRYPOINT ["entrypoint.sh"]

重新 build 镜像:

docker build -t prod .

启动时只需要这样就行了:

docker run --name prod -d -p 8000:8000 -v /var/www/html/static:/workspace/static prod
docker run --name worker -d prod worker
docker run --name beat -d prod beat
docker run --name flower -d -p 5555:5555 prod flower

后话

当然真正的企业级生产部署不可能这么简单,他们一般会在代码提交后通过 CI 自动化地测试和构建镜像,生产服务器上则直接拉取稳定版本的镜像运行。

企业级的生产也不会使用质朴的 docker run 来运行镜像,更多的会在 k8s 集群里调度运行,或至少会使用 docker-compose 来部署。

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

推荐阅读更多精彩内容