使用Docker部署有诸多好处,flask程序也通常需要搭配一个高性能的wsgi容器,今天就记录一下在使用docker+gunicorn+flask过程中的一些坑,错误之处欢迎指正。
一个简单的demo(宿主机为ubuntu18.04),先来看目录结构:
即 myweb/src/为flask工程路径,与src目录同级的Dockerfile、run.sh文件分别用于构建docker镜像和docker-entrypoint脚本,requirement.txt为flask所需依赖
再来看一下Dockerfile:
FROM centos
WORKDIR /var/jenkins_home/workspace
# 添加文件
ADD src /var/jenkins_home/workspace/src
ADD run.sh /var/jenkins_home/workspace/
ADD Dockerfile /var/jenkins_home/workspace/
ADD requirements.txt /var/jenkins_home/workspace/
# 时区设置
RUN /bin/cp /usr/share/zoneinfo/Asia/Shanghai /etc/localtime \
&& echo 'Asia/Shanghai' >/etc/timezone
# 安装pip及flask依赖
RUN yum -y install epel-release
RUN yum -y install python-pip
RUN pip install -r requirements.txt -i https://pypi.tuna.tsinghua.edu.cn/simple
# 安装gunicorn
RUN pip install gunicorn -i https://pypi.tuna.tsinghua.edu.cn/simple
# 建日志目录
RUN mkdir -p /var/jenkins_home/workspace/log/
RUN chmod +x /var/jenkins_home/workspace/run.sh
# 声明数据卷挂载点
VOLUME /var/jenkins_home/workspace
# 启动命令
ENTRYPOINT ["./run.sh"]
# 容器暴露端口
EXPOSE 8000
Dockerfile基本命令的解释文档有很多,可自行搜索,本人参考的文章:Dockerfile基本命令解释,上面的Dockerfile大意是把当前路径下的文件及文件夹添加到容器的"/var/jenkins_home/workspace/"下,安装依赖,声明挂载点:
再来看一下requirements.txt
Flask==0.12.1
接着看一下 容器运行时的自动运行的脚本 run.sh
#!/bin/bash
set -e
pwd
# 日志文件
touch /var/jenkins_home/workspace/log/access_print.log
touch /var/jenkins_home/workspace/log/error_print.log
touch /var/jenkins_home/workspace/log/output_print.log
pwd
ls -l
echo makedir ok
chmod 777 src
# gunicorn启动命令
exec gunicorn src.manage:app \
--bind 0.0.0.0:8000 \
--workers 4 \
--log-level debug \
--access-logfile=/var/jenkins_home/workspace/log/access_print.log \
--error-logfile=/var/jenkins_home/workspace/log/error_print.log
exec "$@"
同样的,gunicorn命令解释文档也有很多,不一一说了,我参考的是:gunicorn配置文件解释,有两个需要注意的地方:
一个是:当run.sh和flask启动文件manage.py不在同一级目录时,使用 gunicorn src.manage:app ,
而非:gunicorn /src/manage:app,或者指定gunicorn的pathonpath参数,--pythonpath /var/jenkins_home/workspace/src
另一个注意点:若启动容器时报 "docker standard_init_linux.go:195: exec user process caused no such file or directory",
有可能是run.sh文本格式问题,可以如下解决:
sudo apt install dos2unix
dos2unix Dockerfile
dos2unix run.sh
再看一下flask的主文件manage.py:
# coding:utf-8
from flask import Flask
import logging
import sys
app = Flask(__name__)
# 日志输出到终端
app.debug = True
handler = logging.StreamHandler(sys.stdout)
logging_format = logging.Formatter('%(asctime)s - %(levelname)s - %(filename)s - %(lineno)s - %(message)s')
handler.setFormatter(logging_format)
# 日志输出到文本
file_handler = logging.handlers.RotatingFileHandler('/var/jenkins_home/workspace/log/output_print.log', maxBytes=10*1024*1024, backupCount=9)
file_handler.setLevel(logging.DEBUG)
file_handler.setFormatter(logging_format)
app.logger.addHandler(file_handler)
app.logger.addHandler(handler)
app.logger.debug('app is ready')
@app.route('/')
def hello_world():
app.logger.debug('debug level log')
app.logger.info('info level log')
return "hello world"
if __name__ == '__main__':
from werkzeug.contrib.fixers import ProxyFix
app.wsgi_app = ProxyFix(app.wsgi_app)
app.run(host='0.0.0.0', port=8000)
日志输出到文本便于使用docker挂载查看日志,不然容器停止运行就不方便查看日志了
构造镜像:
docker build -t myflask .
查看构建好的镜像: docker images
指定镜像启动容器,并指定挂载路径:
docker run -p 8888:8000 -v /var/log/supp:/var/jenkins_home/workspace/log --name ademo 5551f90f20a4
上面启动容器参数含义: -p 8888:8000 指定宿主机端口到容器端口映射
-v /var/log/supp:/var/jenkins_home/workspace/log 指定宿主机"/var/log/supp"文件映射容器的"/var/jenkins_home/workspace/log"文件夹,容器停止运行后日志文件依然可以被查看(需要先在宿主机自定义建文件夹/var/log/supp)。
--name ademo 容器命名为 ademo
这里还有一个坑,第一次写helloworld的时候,这个flask工程中我没有新建 __init__.py文件,导致"包"无法被导入,找不到manane.py文件,启动容器时gunirocn一直报错:
"no model named manage" 和 "gunicorn.errors.HaltServer:
通过查看我们自定义的错误信息日志文件/var/log/supp/error_pring.log,找到是importError,
启动成功:
验证,先查看挂载卷文件夹下新建的日志文件:请求日志记录在access.log,错误日志在error.log,自定义输出日志在output.log
访问宿主机8888端口(即容器对应的8000端口),helloworld ojbk了:
一个简单的实例就OK了