Docker基础知识 (11) - 使用 Docker 部署 Nginx + Gunicorn + Flask 项目


Gunicorn 是一个 Unix 上被广泛使用的高性能的 Python WSGI UNIX HTTP Server。它和大多数的 Web 框架兼容,并具有实现简单,轻量级,高性能等特点。

Gunicorn 是一个移植自 Ruby 的 Unicorn 项目的 pre-fork worker 模型,即支持 eventlet 也支持greenlet。Gunicorn 启动项目之后一定会有一个主进程 Master 和一个或者多个工作进程。工作进程的数量可以指定,工作进程是实际处理请求的进程,主进程维护服务器的运行。

Gunicorn 默认使用 gevent 同步阻塞的网络模型(-k sync),对于大并发的访问可能表现不够好,但是可以使用 gevent 来增加并发量。

Gunicorn VS uWSGI

    (1) 两个都是实现了 WSGI协议 的Web服务器,并且都是基于 Perfork 模型。
    (2) uWSGI 是用 C 语言编写的,Gunicorn 是用 Python 语言编写的,相对于 uWSGI,Gunicorn 相对简单,启动也十分方便。

Gunicorn GitHub: https://github.com/benoitc/gunicorn


Flask 是一个轻量级的可定制框架,使用 Python 语言编写,较其他同类型框架更为灵活、轻便、安全且容易上手。它可以很好地结合 MVC 模式进行开发,开发人员分工合作,小型团队在短时间内就可以完成功能丰富的中小型网站或 Web 服务的实现。

Flask GitHub: https://github.com/pallets/flask


1. 部署 Gunicorn + Flask

    1) 部署环境


        IP 地址(本地测试环境):192.168.0.10
        操作系统:Linux CentOS 7.9     
        Docker 版本: 20.10.7
        gunicorn 目录:/home/docker/gunicorn    # 在 Docker 所在主机上,手动创建各级目录,下同

    2) 创建 Flask 项目

        $ cd /home/docker/gunicorn
        $ mkdir flaskapp
        $ cd flaskapp
        $ vim app.py
            from flask import Flask
 
            app = Flask(__name__)
 
            @app.route('/')
            def home():
                return 'Gunicorn/Flask - Hello World!'


    3) 创建 gunicorn.conf.py 文件

        $ cd /home/docker/gunicorn/flaskapp
        $ vim gunicorn.conf.py

            bind="0.0.0.0:8002"
            workers=5
            worker_class="gevent"

    4) 创建 Dockerfile

        $ cd /home/docker/gunicorn/flaskapp
        $ vim Dockerfile

            FROM python:3.8
            WORKDIR /home/docker/gunicorn/flaskapp
            # COPY 指令和 ADD 指令功能和使用方式类似,COPY 指令不会做自动解压文件
            #COPY . /home/docker/gunicorn/flaskapp
            RUN pip install -i https://mirrors.aliyun.com/pypi/simple gunicorn gevent
            RUN pip install -i https://mirrors.aliyun.com/pypi/simple flask==2.0.0
            EXPOSE 8002
            CMD ["gunicorn", "-c", "/home/docker/gunicorn/flaskapp/gunicorn.conf.py", "app:app"]


        注:也可以使用镜像网站 https://pypi.tuna.tsinghua.edu.cn/simple。

            本文后面运行容器时,把 /home/docker/gunicorn/flaskapp 挂载到容器内的 /home/docker/gunicorn/flaskapp 目录,所以不使用 COPY 命令。

            如果使用 Django 项目代替 Flask 项目,假设 Django 项目名称为 demo,修改 Dockerfile 文件如下:

                FROM python:3.8
                WORKDIR /home/docker/gunicorn/demo
                # COPY 指令和 ADD 指令功能和使用方式类似,COPY 指令不会做自动解压文件
                #COPY . /home/docker/gunicorn/demo
                RUN pip install -i https://mirrors.aliyun.com/pypi/simple gunicorn gevent
                RUN pip install -i https://mirrors.aliyun.com/pypi/simple django==3.0.1
                EXPOSE 8002
                CMD ["gunicorn", "-c", "/home/docker/gunicorn/demo/gunicorn.conf.py", "demo.wsgi:application"]

 

    5) 创建 python_gunicorn 镜像,并运行容器

        $ cd /home/docker/gunicorn/flaskapp

        # 创建镜像
        $ docker build -t python_gunicorn:3.8-20.1.0 .

            Sending build context to Docker daemon  4.096kB
            Step 1/6 : FROM python:3.8
            ---> a08150c12a68
            Step 2/6 : WORKDIR /home/docker/gunicorn/flaskapp
            ---> Running in b814e286f851
            Removing intermediate container b814e286f851
            ---> 4c603e0cf191
            Step 3/6 : RUN pip install -i https://mirrors.aliyun.com/pypi/simple gunicorn gevent
            ---> Running in 5dd99993625d
            Looking in indexes: https://mirrors.aliyun.com/pypi/simple
            Collecting gunicorn
            Downloading https://mirrors.aliyun.com/pypi/packages/e4/dd/5b190393e6066286773a67dfcc2f9492058e9b57c4867a95f1ba5caf0a83/gunicorn-20.1.0-py3-none-any.whl (79 kB)
                ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 79.5/79.5 KB 2.5 MB/s eta 0:00:00
            Collecting gevent
            Downloading https://mirrors.aliyun.com/pypi/packages/68/13/95b3272019d69984ba044c6783de5efea76b0d92c97d96bfe832ec7dc6fa/gevent-21.12.0-cp38-cp38-manylinux_2_12_x86_64.manylinux2010_x86_64.whl (6.5 MB)
                ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 6.5/6.5 MB 1.4 MB/s eta 0:00:00
            Requirement already satisfied: setuptools>=3.0 in /usr/local/lib/python3.8/site-packages (from gunicorn) (57.5.0)
            Collecting zope.interface
            Downloading https://mirrors.aliyun.com/pypi/packages/e9/56/945a20c9efe2a5f68cc176b525131830c4392992ceccef91902d823de7ef/zope.interface-5.4.0-cp38-cp38-manylinux2010_x86_64.whl (259 kB)
                ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 259.2/259.2 KB 1.1 MB/s eta 0:00:00
            Collecting greenlet<2.0,>=1.1.0
            Downloading https://mirrors.aliyun.com/pypi/packages/5c/93/654613aeeda6fc8f65227d6d2b9f0c469f324ec12cb1511430e77da4875b/greenlet-1.1.3-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (157 kB)
                ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 157.1/157.1 KB 2.1 MB/s eta 0:00:00
            Collecting zope.event
            Downloading https://mirrors.aliyun.com/pypi/packages/9e/85/b45408c64f3b888976f1d5b37eed8d746b8d5729a66a49ec846fda27d371/zope.event-4.5.0-py2.py3-none-any.whl (6.8 kB)
            Installing collected packages: zope.interface, zope.event, gunicorn, greenlet, gevent
            Successfully installed gevent-21.12.0 greenlet-1.1.3 gunicorn-20.1.0 zope.event-4.5.0 zope.interface-5.4.0
            WARNING: Running pip as the 'root' user can result in broken permissions and conflicting behaviour with the system package manager. It is recommended to use a virtual environment instead: https://pip.pypa.io/warnings/venv
            WARNING: You are using pip version 22.0.4; however, version 22.2.2 is available.
            You should consider upgrading via the '/usr/local/bin/python -m pip install --upgrade pip' command.
            Removing intermediate container 5dd99993625d
            ---> bab804b908e5
            Step 4/6 : RUN pip install -i https://mirrors.aliyun.com/pypi/simple flask==2.0.0
            ---> Running in a1d03ee7e1c1
            Looking in indexes: https://mirrors.aliyun.com/pypi/simple
            Collecting flask==2.0.0
            Downloading https://mirrors.aliyun.com/pypi/packages/bf/73/9180d22a40da68382e9cb6edb66a74bf09cb72ac825c130dce9c5a44198d/Flask-2.0.0-py3-none-any.whl (93 kB)
                ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 93.2/93.2 KB 2.0 MB/s eta 0:00:00
            Collecting Jinja2>=3.0
            Downloading https://mirrors.aliyun.com/pypi/packages/bc/c3/f068337a370801f372f2f8f6bad74a5c140f6fda3d9de154052708dd3c65/Jinja2-3.1.2-py3-none-any.whl (133 kB)
                ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 133.1/133.1 KB 1.7 MB/s eta 0:00:00
            Collecting click>=7.1.2
            Downloading https://mirrors.aliyun.com/pypi/packages/c2/f1/df59e28c642d583f7dacffb1e0965d0e00b218e0186d7858ac5233dce840/click-8.1.3-py3-none-any.whl (96 kB)
                ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 96.6/96.6 KB 1.3 MB/s eta 0:00:00
            Collecting itsdangerous>=2.0
            Downloading https://mirrors.aliyun.com/pypi/packages/68/5f/447e04e828f47465eeab35b5d408b7ebaaaee207f48b7136c5a7267a30ae/itsdangerous-2.1.2-py3-none-any.whl (15 kB)
            Collecting Werkzeug>=2.0
            Downloading https://mirrors.aliyun.com/pypi/packages/c8/27/be6ddbcf60115305205de79c29004a0c6bc53cec814f733467b1bb89386d/Werkzeug-2.2.2-py3-none-any.whl (232 kB)
                ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 232.7/232.7 KB 1.8 MB/s eta 0:00:00
            Collecting MarkupSafe>=2.0
            Downloading https://mirrors.aliyun.com/pypi/packages/fd/f4/524d2e8f5a3727cf309c2b7df7c732038375322df1376c9e9ef3aa92fcaf/MarkupSafe-2.1.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (25 kB)
            Installing collected packages: MarkupSafe, itsdangerous, click, Werkzeug, Jinja2, flask
            Successfully installed Jinja2-3.1.2 MarkupSafe-2.1.1 Werkzeug-2.2.2 click-8.1.3 flask-2.0.0 itsdangerous-2.1.2
            WARNING: Running pip as the 'root' user can result in broken permissions and conflicting behaviour with the system package manager. It is recommended to use a virtual environment instead: https://pip.pypa.io/warnings/venv
            WARNING: You are using pip version 22.0.4; however, version 22.2.2 is available.
            You should consider upgrading via the '/usr/local/bin/python -m pip install --upgrade pip' command.
            Removing intermediate container a1d03ee7e1c1
            ---> 477efff863c6
            Step 5/6 : EXPOSE 8002
            ---> Running in da9c964a1cf3
            Removing intermediate container da9c964a1cf3
            ---> 7b656d9d23b5
            Step 6/6 : CMD ["gunicorn", "-c", "/home/docker/gunicorn/flaskapp/gunicorn.conf.py", "app:app"]
            ---> Running in a697ed437326
            Removing intermediate container a697ed437326
            ---> d91f75cfb6ec
            Successfully built d91f75cfb6ec
            Successfully tagged python_gunicorn:3.8-20.1.0


        $ docker images

            REPOSITORY        TAG          IMAGE ID       CREATED              SIZE
            python_gunicorn   3.8-20.1.0   bb447fd877d1   About a minute ago   961MB
            python            3.8          a08150c12a68   2 weeks ago          912MB

        # 运行容器
        $ docker run --name python_gunicorn-3.8 -p 8002:8002\
                    -v /home/docker/gunicorn/flaskapp:/home/docker/gunicorn/flaskapp\
                    --privileged=true\
                    -d python_gunicorn:3.8-20.1.0

        $ docker exec -it python_gunicorn-3.8 /bin/bash  # 进入容器            

            root@af34e34693e1:/home/docker/gunicorn/flaskapp# ls
            Dockerfile  __pycache__  app.py  gunicorn.conf.py
            root@af34e34693e1:/home/docker/gunicorn/flaskapp# exit

        浏览器访问 http://192.168.0.10:8002,动态页面显示结果如下:

            Gunicorn/Flask - Hello World!


2. 部署 Nginx + Gunicorn + Flask

    1) 部署环境

        IP 地址(本地测试环境):192.168.0.10
        操作系统:Linux CentOS 7.9     
        Docker 版本: 20.10.7

    2) 拉取 nginx 镜像

        $ docker pull nginx     # 相当于 docker pull nginx:latest
        $ docker images         # 查看镜像列表

            REPOSITORY                TAG        IMAGE ID       CREATED         SIZE
            nginx                     latest     2b7d6430f78d   10 days ago     142MB
            ...

        注:可以访问 Dockers Hub(https://hub.docker.com/_/nginx?tab=tags)查询 Nginx 镜像。

    3) 配置 nginx

        由于 Gunicorn 和 Flask 处理静态文件的能力较弱,所以本文把 nginx 配置为动态文件的代理服务器,nginx 自己处理静态文件。

        (1) 创建 nginx.conf 文件
            
            在 Docker 所在主机上,创建 /home/docker/nginx/conf.d 目录,在该目录下创建 nginx.conf 文件。

                server {
                    listen 80 default_server;
                    server_name localhost;
                
                    location / {
                        proxy_pass http://python_gunicorn-3.8:8002;
                    }

                    # nginx 自己处理 /static 对应的静态文件
                    root /usr/share/nginx/html;

                    location /static {
                        alias /usr/share/nginx/html;
                    }

                }


            参数说明:

                (1) server.location.proxy_pass: http://python_gunicorn-3.8:8002 表示 “容器名称:端口”,也可以写成 “http://ip:端口”,获取 ip 的方法,运行如下命令:
                
                    $ docker inspect --format='{{.NetworkSettings.IPAddress}}' python_gunicorn-3.8

                        172.17.0.2

                (2) server.root: /usr/share/nginx/html 是 nginx 容器内的路径。静态文件 (HTML/CSS/JS/Images 等文件)对应的映射目录是 /usr/share/nginx/html;

                    location /static: nginx 自己处理 /static 对应的静态文件
      
        (2) 创建 hello.html 文件

            $ cd /home/docker/nginx/html
            $ vim hello.html

                <h3>Nginx/Gunicorn/Flask - hello.html</h3>

            注:这种模式下,静态文件由 nginx 处理,nginx 容器内的静态文件目录是 /usr/share/nginx/html,运行 nginx 容器会把 Docker 所在主机上的 /home/docker/nginx/html 挂载到 /usr/share/nginx/html。

        (3) 运行 nginx 容器

            $ docker run --name nginx-gunicorn-3.8 -p 8889:80\
                        -v /home/docker/nginx/html:/usr/share/nginx/html\
                        -v /home/docker/nginx/conf.d:/etc/nginx/conf.d\
                        --privileged=true\
                        --link python_gunicorn-3.8\
                        -d nginx

            参数说明:

                --name nginx-gunicorn-3.8:将容器命名为 nginx-gunicorn-3.8。
                -p 8889:80:端口进行映射,将本地 8889 端口映射到容器内部的 80 端口。
                -v:将本地文件夹与容器文件夹挂载。
                --privileged=true:使用该参数,容器内的 root 拥有真正的 root 权限。否则,容器内的 root 只是外部的一个普通用户权限。
                -d:设置容器在后台一直运行。
                nginx: 这里表示 nginx:latest,镜像:tag。

            $ docker ps  # 查看运行的容器列表


            浏览器访问 http://192.168.0.10:8889/,动态页面显示如下:

                 Gunicorn/Flask - Hello World!

            浏览器访问 http://192.168.0.10:8889/static/hello.html,静态页面显示如下:

                Nginx/Gunicorn/Flask - hello.html


posted @ 2022-09-14 19:01  垄山小站  阅读(1633)  评论(0)    收藏  举报