现如今,通过 Docker 将服务封装成镜像来部署变得越来越流行。通过这种方式可以极大的节省发布时间,也可以方便的测试人员对服务进行测试,同时还可以避免运行环境不同导致的各种服务发布问题。
本文将介绍通过 Docker 封装 Django2.2 的服务,通过 uWSGI 和 Nginx 来实现高并发。通过 Supervisor 来启动服务和 Celery 任务。本文使用的 Python 版本为 3.7,假设服务部署在 /opt 目录下面,以 app 命名项目。
首先确保服务能够通过 runserver 启动,通常启动方式如下:
python3 /opt/app/manage.py runserver 127.0.0.1:8080
此时服务会以 8080 端口启动,可以通过访问 8080 端口查看服务是否启动正常。这也是开发过程中常用的调试方式。
如果服务启动正常,就可以测试通过 uWSGI 启动服务。
uWSGI 是一个 Web 服务器,它实现了 WSGI 协议、uwsgi、http 等协议。
在安装uWSGI之前,需要安装软件所依赖的 Python 开发文件:
sudo apt-get install python3-dev
然后通过 pip 安装 uWSGI 服务
sudo pip3 install uwsgi
高版本的 pip 不再使用 pip3,因此上面的命令也可能是下面这样的
sudo pip install uwsgi
通过命令行将站点的基本信息传递给uWSGI服务进行测试。
uwsgi --http :8080 --home /opt/app/venv --chdir /opt/app -w firstsite.wsgi
–home 告诉 uWSGI 使用 venv 目录中的虚拟环境
–chdir 切换到项目根目录,并使用存储在 firstsite 项目中的 wsgi.py 文件来提供相关服务(该文件自动生成)
此时服务还是以8080端口启动,你将再次看到你的站点。但是静态文件, 在 static 中的文件也许无法加载,这个会在后面 Nginx 服务启动后解决。
你可以在项目的目录下面直接创建 uwsgi.ini 文件,具体内容可以参照下面。同时也需要创建一个目录 uwsgi 用于存储 uWSGI 服务的运行状态文件。
# uwsgi.ini
[uwsgi]
chdir = /opt/app
module = firstsite.wsgi
master = true
processes = 10
enable-threads = true
socket = 127.0.0.1:8080
listen = 100
vacuum = true
stats = %(chdir)/uwsgi/uwsgi.status
pidfile = %(chdir)/uwsgi/uwsgi.pid
log-maxsize = 500000000
req-logger = file:/tmp/reqlog
logger = file:/tmp/errlog
此时服务可以通过 uwsgi --ini /opt/app/uwsgi.ini 进行启动。
安装Nginx作为反向代理
sudo apt-get install nginx
在项目目录下面添加 uwsgi_param 文件,内容如下:
uwsgi_param QUERY_STRING $query_string;
uwsgi_param REQUEST_METHOD $request_method;
uwsgi_param CONTENT_TYPE $content_type;
uwsgi_param CONTENT_LENGTH $content_length;
uwsgi_param REQUEST_URI $request_uri;
uwsgi_param PATH_INFO $document_uri;
uwsgi_param DOCUMENT_ROOT $document_root;
uwsgi_param SERVER_PROTOCOL $server_protocol;
uwsgi_param REQUEST_SCHEME $scheme;
uwsgi_param HTTPS $https if_not_empty;
uwsgi_param REMOTE_ADDR $remote_addr;
uwsgi_param REMOTE_PORT $remote_port;
uwsgi_param SERVER_PORT $server_port;
uwsgi_param SERVER_NAME $server_name;
创建服务器块配置文件
sudo vi /etc/nginx/sites-available/firstsite
放入以下内容:
server {
listen 80;
server_name your.site.com 127.0.0.1;
charset utf-8;
client_max_body_size 75M;
location /static {
alias /opt/app/static;
}
location / {
uwsgi_pass 127.0.0.1:8080;
include /opt/app/uwsgi_params;
}
}
将新配置文件链接到 Nginx 的启用站点目录:
sudo ln -s /etc/nginx/sites-available/firstsite /etc/nginx/sites-enabled
测试配置文件
sudo nginx -t
如果未检测到语法错误,则可以重新启动Nginx服务以加载新配置:
sudo systemctl restart nginx
目前服务以 80 端口进行启动,如果访问正常,说明服务已经可以通过Nginx 进行访问。
supervisor 可以帮助我们启动和管理 uWSGI 和 Celery 服务。
[unix_http_server]
file=/var/run/supervisor.sock
[supervisord]
nodaemon=true
logfile=/var/log/supervisor/supervisord.log
pidfile=/var/run/supervisord.pid
[supervisorctl]
serverurl=unix:///var/run/supervisor.sock
[rpcinterface:supervisor]
supervisor.rpcinterface_factory = supervisor.rpcinterface:make_main_rpcinterface
[program:uwsgi]
autorestart=True
autostart=True
redirect_stderr=True
startsecs=10
satrtretries=3
command=uwsgi --ini /opt/app/uwsgi.ini
directory=/opt/app
stdout_logfile_maxbytes = 50MB
stdout_logfile_backups = 10
stdout_logfile = /opt/app/logs/uwsgi_stdout.log
stopasgroup=true
killasgroup=true
[program:celery_worker]
autorestart=True
autostart=True
redirect_stderr=True
startsecs=10
satrtretries=3
command=celery -A firstsite worker -l info
directory=/opt/app
stdout_logfile_maxbytes = 50MB
stdout_logfile_backups = 10
stdout_logfile = /opt/app/logs/celery_worker_stdout.log
stopasgroup=true
killasgroup=true
[program:celery_beat]
autorestart=True
autostart=True
redirect_stderr=True
startsecs=10
satrtretries=3
command=celery -A firstsite beat -l info
directory=/opt/app
stdout_logfile_maxbytes = 50MB
stdout_logfile_backups = 10
stdout_logfile = /opt/app/upload/logs/celery_beat_stdout.log
stopasgroup=true
killasgroup=true
镜像通过基础镜像加发布镜像分层构建。
基础镜像包含应用启动所需的依赖软件包,可以帮助我们在更新发布镜像时节省构建时间。
dockerfile_base 的内容如下:
FROM ubuntu:18.04
RUN sed -i s@/archive.ubuntu.com/@/mirrors.aliyun.com/@g /etc/apt/sources.list && \
apt-get clean && \
apt-get update -y && \
apt-get install -y \
locales\
git \
python3 \
python3-dev \
python3-setuptools \
python3-pip \
nginx && \
localedef -i en_US -c -f UTF-8 -A /usr/share/locale/locale.alias en_US.UTF-8 && \
pip3 install --upgrade setuptools pip -i https://pypi.tuna.tsinghua.edu.cn/simple && \
pip install -i https://pypi.tuna.tsinghua.edu.cn/simple uwsgi==2.0.18 supervisor==4.1.0 && \
mkdir /opt/app && \
mkdir /etc/supervisor && \
echo_supervisord_conf > /etc/supervisor/supervisord.conf && \
mkdir -p /opt/app/upload
ENV TZ=Asia/Shanghai
RUN set -eux; \
ln -snf /usr/share/zoneinfo/$TZ /etc/localtime; \
echo $TZ > /etc/timezone
ENV LANG en_US.utf8
ENV PYTHONUNBUFFERED 1
ENV PYTHONIOENCODING UTF-8
WORKDIR /opt/app
COPY requirements.txt /opt/app/
RUN pip install -i https://pypi.tuna.tsinghua.edu.cn/simple -r requirements.txt
新建一个目录,放置以下文件:
requirements.txt
dockerfile_base
假设 dockerfile 的文件名为 dockerfile_base,通过运行以下命令进行基础镜像构建。
sudo docker build --rm -t app:v0 -f dockerfile_base .
在刚才的目录创建另一个 dockerfile 文件,命名为 dockerfile_publish,
内容如下:
FROM app:v0
WORKDIR /opt/app
COPY app/. /opt/app/
COPY app/nginx.conf /etc/nginx/sites-available/default
COPY app/supervisord.conf /etc/supervisor/
RUN pip install -i https://pypi.tuna.tsinghua.edu.cn/simple -r requirements.txt && \
mkdir /var/log/supervisor && \
touch /var/log/supervisor/supervisord.log && \
touch /var/run/supervisor.sock && \
chmod 777 /var/log/supervisor/supervisord.log && \
chmod 777 /var/run/supervisor.sock && \
chmod +x /etc/supervisor/supervisord.conf && \
chmod +x /opt/app/start.sh
EXPOSE 80
VOLUME ["/opt/app/upload"]
ENTRYPOINT ["/opt/app/start.sh"]
然后将项目文件放置在 app 目录下面。目录下面现在有以下目录和文件:
app
requirements.txt
dockerfile_base
dockerfile_publish
然后运行发布镜像的构建命令:
sudo docker build --rm -t app:v1 -f dockerfile_publish .
此时一个名为 app,标签为 v1 的镜像会构建出来。
服务的启动路径为 start.sh,包含服务启动所需的命令。
#!/bin/sh
/etc/init.d/nginx start
supervisord -c /etc/supervisor/supervisord.conf