Docker 容器化部署 Python 应用
点击上方“Python数据科学”,星标公众号
作者:jerry,爬虫/数据分析/Web。Blog: http://www.spiderpy.cnGithub: https://github.com/jhao104
https://docs.docker.com/install/
)。Docker Engine — 应用打包工具,用于封装应用程序。
Docker Hub — 用于管理云上容器应用程序的工具。
FlaskDemo
。并在该目录下创建应用代码文件app.py
。app.py
中,首先引入Flask
模块,然后创建一个web应用:app = Flask(__name__)
def index():
return """
<h1>Python Flask in Docker!</h1>
<p>A sample web-app for running Flask inside Docker.</p>
"""
app.run(debug=True, host='0.0.0.0')
$ python app.py
http://localhost:5000/
,可以看到Dockerzing Python app using Flask
这样的页面。requirements.txt
文件,然后创建一个Dockerfile,该文件用来描述构建映像过程。requirements.txt
文件非常简单,只需要填入项目的依赖包和其对应版本即可:app
的目录。app.py
,将脚本中创建的Flask对象命名为 app
是一种通常的做法,这样也可以简化部署。├── requirements.txt
├── Dockerfile
└── app
└── app.py
└── <other .py files>
MAINTAINER jhao104 "j_hao104@163.com"
RUN apt-get update -y && \
apt-get install -y python3-pip python3-dev
COPY ./requirements.txt /requirements.txt
WORKDIR /
RUN pip3 install -r requirements.txt
COPY . /
ENTRYPOINT [ "python3" ]
CMD [ "app/app.py" ]
FROM - 所有Dockerfile的第一个指令都必须是 FROM ,用于指定一个构建镜像的基础源镜像,如果本地没有就会从公共库中拉取,没有指定镜像的标签会使用默认的latest标签,如果需要在一个Dockerfile中构建多个镜像,可以使用多次。
MAINTAINER - 描述镜像的创建者,名称和邮箱。
RUN - RUN命令是一个常用的命令,执行完成之后会成为一个新的镜像,通常用于运行安装任务从而向映像中添加额外的内容。在这里,我们需更新包,安装 python3 和 pip 。在第二个 RUN 命令中使用 pip 来安装 requirements.txt 文件中的所有包。
COPY - 复制本机文件或目录,添加到指定的容器目录, 本例中将 requirements.txt 复制到镜像中。
WORKDIR - 为RUN、CMD、ENTRYPOINT指令配置工作目录。可以使用多个WORKDIR指令,后续参数如果是相对路径,则会基于之前命令指定的路径。
ENTRYPOINT - 在启动容器的时候提供一个默认的命令项。
RUN - 运行
app
目录中的app.py
。
Docker build
命令构建的。在构建镜像时,Docker创建了所谓的“层(layers)”。每一层都记录了Dockerfile中的命令所导致的更改,以及运行命令后镜像的状态。ubuntu:16.04
的基础镜像,相同容器的所有后续构建都可以重用它,因为它不会改变。但是,因为项目修改,在下次重新构建过程中 app 目录的内容可能会有所不同,因此只会重新构建这一层。Dockerfile
中紧随其后的所有层也都需要重新构建。例如,我们首先复制 requirements.txt
文件,然后再复制应用程序的其余部分。这样之前安装的依赖项只要没有新的依赖关系,即使应用程序中的其他文件发生了更改,也不需要重新构建这一层。这一点在创建 Dockerfiles
时一定要注意。pip
安装与应用程序其余部分的部署分离,可以优化容器的构建过程。Dockerfile
已经准备好了,而且也了解了Docker的构建过程,接下来为我们的应用程序创建Docker映像:volume-mapping
和 port-forwarding
选项:基于之前构建的 docker-flask 镜像启动一个容器;
这个容器的名称被设置为 flask_app 。如果没有 ——name 选项,Docker将为容器生成一个名称。显式指定名称可以帮助我们定位容器(用来停止等操作);
-v 选项将主机的app目录挂载到容器;
-p 选项将容器的端口映射到主机。
http://localhost:5000
或者 http://0.0.0.0:5000/
访问到应用:Ctrl + C
, 并运行 docker rm flask_app
移除容器。nginx + uwsgi
,下面我们将介绍如何为生产环境部署web应用程序。Nginx是一个开源web服务器,uWSGI是一个快速、自我修复、开发人员和系统管理员友好的服务器。entry-point.sh
:if [ ! -f /debug0 ]; then
touch /debug0
while getopts 'hd:' flag; do
case "${flag}" in
h)
echo "options:"
echo "-h show brief help"
echo "-d debug mode, no nginx or uwsgi, direct start with 'python3 app/app.py'"
exit 0
;;
d)
touch /debug1
;;
*)
break
;;
esac
done
fi
if [ -e /debug1 ]; then
echo "Running app in debug mode!"
python3 app/app.py
else
echo "Running app in production mode!"
nginx && uwsgi --ini /app.ini
fi
app.ini
:plugins = /usr/lib/uwsgi/plugins/python3
chdir = /app
module = app:app
uid = nginx
gid = nginx
socket = /run/uwsgiApp.sock
pidfile = /run/.pid
processes = 4
threads = 2
nginx.conf
:worker_processes 4;
pid /run/nginx.pid;
events {
worker_connections 20000;
}
http {
include mime.types;
sendfile on;
keepalive_timeout 65;
gzip off;
server {
listen 80;
access_log off;
error_log off;
location / { try_files $uri @flaskApp; }
location @flaskApp {
include uwsgi_params;
uwsgi_pass unix:/run/uwsgiApp.sock;
}
}
}
Dockerfile
将nginx
和uWSGI
安装到镜像,将配置文件复制到镜像中,并设置运行nginx所需的用户权限:MAINTAINER jhao104 "j_hao104@163.com"
RUN apt-get update -y && \
apt-get install -y python3-pip python3-dev && \
apt-get install -y nginx uwsgi uwsgi-plugin-python3
COPY ./requirements.txt /requirements.txt
COPY ./nginx.conf /etc/nginx/nginx.conf
WORKDIR /
RUN pip3 install -r requirements.txt
COPY . /
RUN adduser --disabled-password --gecos '' nginx\
&& chown -R nginx:nginx /app \
&& chmod 777 /run/ -R \
&& chmod 777 /root/ -R
ENTRYPOINT [ "/bin/bash", "/entry-point.sh"]
requirements.txt
指定依赖项和对应版本,然后通过 pip 安装。requirements.txt
文件,都需要重新构建Docker镜像。entry-point.sh
:if [ ! -f debug0 ]; then
touch debug0
if [ -e requirements_os.txt ]; then
apt-get install -y $(cat requirements_os.txt)
fi
if [-e requirements.txt ]; then
pip3 install -r requirements.txt
fi
while getopts 'hd:' flag; do
case "${flag}" in
h)
echo "options:"
echo "-h show brief help"
echo "-d debug mode, no nginx or uwsgi, direct start with 'python3 app/app.py'"
exit 0
;;
d)
touch debug1
;;
*)
break
;;
esac
done
fi
if [ -e debug1 ]; then
echo "Running app in debug mode!"
python3 app/app.py
else
echo "Running app in production mode!"
nginx && uwsgi --ini /app.ini
fi
requirements_os.txt
中指定将要安装的系统软件包名称,这些包名以空格分隔放在同一行。他们将和 requirements.txt
中的Python依赖库一样在应用程序启动之前安装。它破坏了容器化的目标之一,即修复和测试由于部署环境的变化而不会改变的依赖关系;
增加了应用程序启动的额外开销,这将增加容器的启动时间;
每次启动应用程序时需要安装依赖项,这样对网络资源有要求。
推荐阅读