本文将使用Flask开发一个简单的Python web应用程序,并为“容器化”做好准备。然后创建一个Docker映像,并将其部署到测试和生产环境中。
python最流行的两个框架之一(django、flask),轻量级是最大的特点
Gunicorn:只熟悉熟悉用 java 或者 PHP 做开发的可能对 python 的部署一开始不太理解,Flask应用是一个符合WSGI规范的Python应用,不能独立运行(类似app.run的方式仅适合开发模式),需要依赖其他的组件提供服务器功能。
gunicorn 默认使用同步阻塞的网络模型(-k sync),对于大并发的访问可能表现不够好,我们很方便地顺手套一个gevent来增加并发量
Docker是目前主流IT公司广泛接受和使用的,用于构建、管理和保护它们应用程序的工具。
容器,例如Docker允许开发人员在单个操作系统上隔离和运行多个应用程序,而不是为服务器上的每个应用程序专用一个虚拟机。使用容器更轻量级,可以降低成本、更好地使用资源和发挥更高的性能。
Flask是Python的一个轻量级Web应用框架,简单易用,可以很快速地创建web应用。我们用它来创建此demo应用。
我们创建一个名为FlaskDemo的简单的web应用:
from flask import Flask
app = Flask(__name__)
@app.route("/")
def index():
return """
Python Flask in Docker!
A sample web-app for running Flask inside Docker.
"""
if __name__ == "__main__":
app.run(debug=True, host='0.0.0.0')
通过命令行 python app.py 运行这个应用:
python app.py
然后在浏览器中访问http://localhost:5000/,可以看到Dockerzing Python app using Flask这样的页面。
但是这样简单运行的话,只要按一下 ctrl + c 终止运行,或者关掉终端,网站就连接不了了,我们要寻求更长久的真正的部署。
运行以下命令即可安装这两个利器
pip install gunicorn gevent
在根目录下新建文件 /gunicorn/config.py
workers = 5 # 定义同时开启的处理请求的进程数量,根据网站流量适当调整
worker_class = "gevent" # 采用gevent库,支持异步处理请求,提高吞吐量
bind = "0.0.0.0:5000"
可以使用gunicorn命令来测试是否可以正确运行,命令如下,打开网址127.0.0.1:5000,将会打开我们的网站。
gunicorn app:app -c gunicorn/config.py
一旦报错,则根据错误提示修复即可。
注:基于 Gunicorn + Gevent 部署flask项目,可以参考之前的一篇文章:基于gunicorn部署flask项目
首先: 请确保机器上已安装Docker,如果没有可以参考文章:Ubuntu安装及使用Docker
要在Docker上运行应用程序,首先必须构建一个容器,而且必须包含使用的所有依赖项——在我们的例子中只有Flask。因此,新建一个包含所有依赖包的 requirements.txt 文件,然后创建一个Dockerfile,该文件用来描述构建映像过程。
首先我们需要为该应用创建一个 requirements.txt 文件,以便容器里面 python 环境的安装。
requirements.txt
# 可以给出具体版本
gunicorn==20.1.0
flask==1.0.2
pandas==0.23.0
numpy==1.14.3
# gevent...
requirements.txt 是做 python 项目常写的一个文件,有了这个文件,在安装 python 应用依赖的三方包时,可以直接用如下命令执行:
pip install -r requirements.txt
当然我们这里先不用执行。
接下来,需要将应用程序运行所需的所有Python文件都放在顶层文件夹apps中。
注: 建议将主入口程序命名为 app.py,将脚本中创建的Flask对象命名为 app 是一种通常的做法,这样也可以简化部署 (对应于部署命令 gunicorn -c gunicorn/config.py app:app 中的 app:app )。
FlaskApp
└── apps
└── < .py files>
└── < .py files>
└── gunicorn
└── config.py
├── app.py
├── Dockerfile
├── requirements.txt
然后我们还要创建一个 Dockerfile 文件,以便 Docker 镜像的构建。
Dockerfile
FROM ubuntu:18.04
WORKDIR /
COPY ./requirements.txt /requirements.txt
RUN apt-get update -y && \
apt-get install -y python3-pip python3-dev
COPY ./requirements.txt /requirements.txt
RUN pip3 install -r requirements.txt
# 设置时区
ENV TZ=Asia/Shanghai
RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone
COPY . /
## 使用gunicorn
CMD ["gunicorn", "app:app", "-c", "./gunicorn/config.py"]
## 若不使用gunicorn, 可将 CMD ["gunicorn"...] 替换成下面2行命令
# ENTRYPOINT [ "python3" ]
# CMD [ "app.py" ] # 不使用gunicorn, 直接python运行app.py
Dockerfile的基本指令有十三个,上面用到了部分;
注: 关于Dockerfile的相关说明可以参考博客: Dockerfile文件详解
现在 Dockerfile 已经准备好了,而且也了解了Docker的构建过程,接下来为我们的应用程序创建Docker映像.
(1) 在Dockerfile的同级目录打开终端, 输入以下命令来创建名为 testflask的映像 :
sudo docker build -t 'testflask' .
需要注意的是这个过程需要一点时间,因为它有几百兆。
注: 构建过程中可能会出现一些报错, 导致 requirements.txt 中的库无法成功安装
----------------------------------------
Command "python setup.py egg_info" failed with error code 1 in /tmp/pip-build-wu79sfu4/pandas/
You are using pip version 8.1.1, however version 21.3 is available.
You should consider upgrading via the 'pip install --upgrade pip' command.
The command '/bin/sh -c pip3 install -r requirements.txt' returned a non-zero code: 1
解决方案: 升级 pip 版本
pip install --upgrade pip
Step 6/9 : RUN pip3 install -r requirements.txt
---> Running in 01c9caacab4d
Collecting gunicorn==20.1.0 (from -r requirements.txt (line 1))
Exception:
Traceback (most recent call last):
File "/usr/share/python-wheels/urllib3-1.13.1-py2.py3-none-any.whl/urllib3/connection.py", line 140, in _new_conn
(self.host, self.port), self.timeout, **extra_kw)
File "/usr/share/python-wheels/urllib3-1.13.1-py2.py3-none-any.whl/urllib3/util/connection.py", line 91, in create_connection
raise err
File "/usr/share/python-wheels/urllib3-1.13.1-py2.py3-none-any.whl/urllib3/util/connection.py", line 81, in create_connection
sock.connect(sa)
OSError: [Errno 99] Cannot assign requested address
During handling of the above exception, another exception occurred:
...
解决方案: 可以通过先重启docker服务, 再构建docker镜像查看问题是否解决
service docker restart
(2) 构建完成之后,通过如下命令查看镜像列表,可以发现 testflask 显示在其中:
sudo docker images
在开发应用程序的过程中,快速重新构建和测试也很重要,以检查验证过程中的每个中间步骤。为此,web应用程序的开发人员需要依赖于Flask等框架提供的自动重启功能(Debug模式下,修改代码自动重启)。而这一功能也可以在容器中使用。
为了启用自动重启,在启动Docker容器时将主机中的开发目录映射到容器中的app目录。这样Flask就可以监听主机中的文件变化(通过映射)来发现代码更改,并在检测到更改时自动重启应用程序。
此外,还需要将应用程序的端口从容器转发到主机。这是为了能够让主机上的浏览器访问应用程序。
因此,启动Dokcer容器时需要使用 volume-mapping 和 port-forwarding 选项:
docker run --name flask_app(容器名) -v $PWD/app:/app -p 5000:5000 testflask(镜像名)
即:
docker run --name flask_app -v $PWD/app:/app -p 5000:5000 testflask
该命令将会执行以下操作:
看到如下页面说明Debugger模式调试已成功开启.
现在可以通过http://localhost:5000 或者 http://0.0.0.0:5000/ 访问到应用.
(1)临时运行docker镜像:
sudo docker run -it --rm -p 5000:5000 testflask
可以看到Docker镜像成功地运行起来了,并处于阻塞状态。这时,我们打开浏览器,输入服务器外网 ip,可以我们的应用已经成功部署上去。
(2)生产环境运行(以daemon方式运行)
先查看所有容器:
docker ps -a
或
docker ps -a --no-trunc #不截断,查看完整信息
可以看到我们刚构建的镜像名称 test-flask-1
sudo docker run -d -p 5000:5000 --name test-flask-1(容器名) testflask(镜像名)
即:
sudo docker run -d -p 5000:5000 --name test-flask-1 testflask
参数说明:
注意1: 记得在服务器的仪表盘(dashboard)的设置里面开启相应的外网端口(这里是 5000)
(1) 查看所有开启的端口
firewall-cmd --list-ports
(2) 开启5000端口
firewall-cmd --zone=public --add-port=5000/tcp --permanent
(3) 重启防火墙,使其生效
firewall-cmd --reload
参考博客: 【解决】:外网访问阿里云服务器80端口失败
注意2: 如果出现报错,我们可以通过 docker 的日志去查看出现问题的原因:
# 查看容器日志
docker logs (容器ID 或 容器名称)
# 查看容器日志
docker logs -f -t --tail (容器ID 或 容器名称)
如:docker logs -f -t --tail=10 f9e29e8455a5
-f : 查看实时日志
-t : 查看日志产生的日期
或者进入容器路径, 查看相关的日志文件.
# 进入容器路径
docker exec -it 容器ID /bin/bash
# 退出容器路径
exit
样例如下 (这里容器ID为 8741291a6fe0) :
xxx@xxx:~/projects/project_name$ docker exec -it 8741291a6fe0 /bin/bash
root@8741291a6fe0:/#
# 已进入容器路径, 可通过linux命令查看相关日志文件
(3) nginx + uwsgi 部署运行
虽然直接使用Flask裸跑运行应用程序对于开发来说已经足够好了,但是我们需要在生产中使用更健壮的部署方法。
目前主流的部署方案是 nginx + uwsgi,下面我们将介绍如何为生产环境部署web应用程序。Nginx是一个开源web服务器,uWSGI是一个快速、自我修复、开发人员和系统管理员友好的服务器。
这里具体可以参考博客:Docker容器化部署Python应用
【参考博客】: