本文适合使用 django
进行 web 开发,并有对 docker
有一定了解的同学。阅读完能帮助你:
- 在开发环境中使用 docker 快速搭建和开发 django 项目
- 在生产环境中使用 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.py
里 DEBUG
已设为 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
来部署。