zoukankan      html  css  js  c++  java
  • Flask-爱家租房项目ihome-11-Docker-compose部署flask+celery

    使用gunicorn+gevent作为web服务器启动flask应用

    首先需要明白一个概念, FlaskDjango等web框架严格意义上来说只是应用程序的框架, 主要是用来处理业务逻辑的.

    而前端浏览器与后端web框架之间是通过请求来沟通的, 这个专门解析和发送请求的中间桥梁叫做web服务器, 在python中也叫作WSGI(Web Server Getway Interface) server.

    WSGI是一种在python中规定的web服务器web应用框架之间通信的协议, 能够支持这种协议的web服务器就叫做WSGI server, Flask和Django都自带了WSGI(Web Server Getway Interface) server, 其自带的启动命令flask runpython manager.py runserver就是使用了WSGI server来启动项目的.

    但是这种web 框架自带的WSGI server只能用来开发测试, 因为性能等以下问题, 不适合在最终的生产环境中使用, 所以一般我们会使用gunicorn或者uWSGI作为线上的WSGI server. 两者都可以使用, 具体区别可以自行百度, 这里我们使用gunicorn, 当然前面也可以再加一台nginx, 或者再加上supervisor对后台程序进行监控, 这里就不再扩展了.

    下载gunicorn和gevent

    pip install gunicorn gevent
    

    在虚拟环境中下载gunicorn, 由于gunicorn默认使用的是同步阻塞的网络模型, 因此为了提高并发能力, 可以将其设置为gevent模式. 通过协程提高并发量, 因此也需要下载gevent库

    配置gunicorn

    可以直接在gunicorn的运行命令中添加参数实现设置, 但是一般建议通过一个单独的配置文件来配置.

    我们在ihome项目的根目录下创建配置文件gunicorn.conf.py

    image-20200913212745292

    # gunicorn.conf.py
    # 定义同时开启的处理请求的进程数量,根据网站流量适当调整
    workers = 5
    # 采用gevent库,支持异步处理请求,提高吞吐量
    worker_class = "gevent"
    # 绑定IP和端口
    bind = "0.0.0.0:5000"
    # 使用后台守护进程的模式启动,若开启则docker启动时容器会直接退出
    # daemon = "true"
    
    # 日志设置
    # 设置gunicorn访问日志格式,错误日志无法设置
    access_log_format = '%(h)s %(l)s %(u)s %(t)s "%(r)s" %(s)s %(b)s "%(f)s" "%(a)s"'
    # 访问日志的存储文件, 当前路径的logs目录下
    accesslog = "logs/gunicorn_access.log"
    # 错误日志的存储文件, 当前路径的logs目录下
    errorlog = "logs/gunicorn_error.log"
    # 日志记录等级
    loglevel = 'info'
    

    注:

    1. 后台守护模式可以在手动使用gunicorn启动命令的情况下开启, 如果是使用docker启动的话, 开启之后会直接退出容器, 所以这种情况下不要开启后台守护模式

    gunicorn记录flask的日志

    上面的配置文件中介绍了日志的配置

    1. 日志信息设置存放在了当路径的logs目录下

    2. 此时只会记录gunicorn的日志, 而flask程序中手动写的记录日志current_app.logger.error(e)并不会存入到gunicorn设置的日志文件中, 所以需要将flask程序中的日志也记录到gunicorn日志文件中. 在flask的启动文件manager.py中, 添加gunicorn的日志记录器

      from flask_migrate import Migrate
      from ihome import create_app
      from ihome import db
      import logging
      
      app = create_app('dev')
      # 配置日志信息
      if __name__ != '__main__':
          # 使用gunicorn启动时, 将flask应用中的日志绑定到gunicorn的日志配置中
          gunicorn_logger = logging.getLogger('gunicorn.error')
          app.logger.handlers = gunicorn_logger.handlers
          app.logger.setLevel(gunicorn_logger.level)
      # 创建迁移对象
      migrate = Migrate(app, db)
      

      当使用gunicorn启动时, manager.py会被当做一个模块被导入, 所以__name__ != '__main__'是成立的. 创建一个gunicorn的日志器, 将flask app的日志器与gunicorn绑定, 这样flask程序中的日志也会记录到gunicorn日志文件中.

    3. 之前我们在ihom/__init__.py中设置的flask日志管理, 也同样会生效, 即也会把日志记录在了logs/log.log文件中, 所以两个日志关系器都会有效, 如果不需要的话可以把ihom/__init__.py中的日志关系器注释掉

    启动gunicorn

    (flask) alex@alex:~/python/FlaskIhome$ gunicorn manager:app -c gunicorn.conf.py 
    

    在项目根目录运行启动命令, manager为项目入口启动文件, app为启动文件中创建的app名字, -c 为设置配置文件的参数名. 如果运行命令后没有任何报错, 则说明运行成功了, linux中没有消息就是最好的消息

    查看gunicorn进程运行情况

    运行ps -ef | grep gunicorn命令查看进程:

    (flask) alex@alex:~/python/FlaskIhome$ ps -ef | grep gunicorn
    alex      42217      1  0 21:41 ?        00:00:00 /home/alex/.virtualenvs/flask/bin/python /home/alex/.virtualenvs/flask/bin/gunicorn manager:app -c gunicorn.conf.py                                
    alex      42220  42217  0 21:41 ?        00:00:00 /home/alex/.virtualenvs/flask/bin/python /home/alex/.virtualenvs/flask/bin/gunicorn manager:app -c gunicorn.conf.py                                
    alex      42221  42217  0 21:41 ?        00:00:00 /home/alex/.virtualenvs/flask/bin/python /home/alex/.virtualenvs/flask/bin/gunicorn manager:app -c gunicorn.conf.py                                
    alex      42222  42217  0 21:41 ?        00:00:00 /home/alex/.virtualenvs/flask/bin/python /home/alex/.virtualenvs/flask/bin/gunicorn manager:app -c gunicorn.conf.py                                
    alex      42223  42217  0 21:41 ?        00:00:00 /home/alex/.virtualenvs/flask/bin/python /home/alex/.virtualenvs/flask/bin/gunicorn manager:app -c gunicorn.conf.py                                
    alex      42224  42217  0 21:41 ?        00:00:00 /home/alex/.virtualenvs/flask/bin/python /home/alex/.virtualenvs/flask/bin/gunicorn manager:app -c gunicorn.conf.py 
    

    或者pstree -ap | grep gunicorn 命令查看进程树, 这样进程间的父子关系展示的更直观

    (flask) alex@alex:~/python/FlaskIhome$ pstree -ap | grep gunicorn                                                                                                                                    
      |-gunicorn,42217 /home/alex/.virtualenvs/flask/bin/gunicorn manager:app -c gunicorn.conf.py
      |   |-gunicorn,42220 /home/alex/.virtualenvs/flask/bin/gunicorn manager:app -c gunicorn.conf.py
      |   |   `-{gunicorn},42225
      |   |-gunicorn,42221 /home/alex/.virtualenvs/flask/bin/gunicorn manager:app -c gunicorn.conf.py
      |   |   `-{gunicorn},42226
      |   |-gunicorn,42222 /home/alex/.virtualenvs/flask/bin/gunicorn manager:app -c gunicorn.conf.py
      |   |   `-{gunicorn},42227
      |   |-gunicorn,42223 /home/alex/.virtualenvs/flask/bin/gunicorn manager:app -c gunicorn.conf.py
      |   |   `-{gunicorn},42229
      |   `-gunicorn,42224 /home/alex/.virtualenvs/flask/bin/gunicorn manager:app -c gunicorn.conf.py
      |       `-{gunicorn},42228
      |   |   |-grep,48392 gunicorn
    

    可以看到进程号42217是gunicorn的主进程, 下面的5个子进程就是根据gunicorn的配置文件中的worker参数配置的. gunicorn的工作模式就是主进程主要管理worker子进程, 子进程才是真正解析和处理请求的worker

    停止gunicorn

    直接运行kill 主进程号就可以停止gunicorn了, 子进程会跟着自动停止. 也可以通过额外配置supervisor来启停gunicorn. 这里就不介绍了.

    使用 Docker 封装 Flask 应用

    首先需要下载安装docker, 这里就不赘述了, 可以查看官网: https://docs.docker.com/engine/install/

    生成requirements.txt

    在项目的根目录生成该项目需要安装的python包和环境信息文件.

    (flask) alex@alex:~/python/FlaskIhome$ pip freeze >> requirements.txt
    

    这一步在python项目迁移时必须要做, 通常需要在新的机器上执行如下命令将该项目需要安装的环境信息都安装好

    pip install -r requirements.txt
    

    但是这里我们由于是封装在docker中的, 所以不需要手动执行pip安装命令

    创建Dockerfile

    在项目的根目录创建文件Dockerfile, 一般命名规定就为这个, 因为在docker build时其会自动去寻找运行build命令的当前路径下的Dockerfile文件

    FROM python:3.7
    MAINTAINER alex<g1242556827@163.com>
    WORKDIR /Project/FlaskIhome
    
    COPY requirements.txt ./
    RUN pip install -r requirements.txt -i https://mirrors.aliyun.com/pypi/simple/
    RUN ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime
    RUN echo 'Asia/Shanghai' > /etc/timezone
    EXPOSE 5000
    
    COPY . .
    
    CMD ["gunicorn", "manager:app", "-c", "./gunicorn.conf.py"]
    

    注:

    1. FROM python:3.7是基础镜像, 我们的项目是在python3.7下开发的, 所以基于该镜像

    2. MAINTAINER是只作者或者维护者的信息

    3. WORKDIR是指进入容器后的工作目录, 如果容器中没有该目录会自动创建. 配合后面的COPY . .意思是把当前Dockerfile所在的目录下的所有文件复制到WORKDIR的路径下, 也就是把WORKDIR作为了容器中的项目目录. 下面的例子就是后续docker运行起来后, 进入docker终端的示例, 默认就是进入了WORKDIR路径

      (root@Aliyun-Alex:/home/alex/python/docker_ihome)# docker ps 
      CONTAINER ID        IMAGE                                                       COMMAND                  CREATED             STATUS              PORTS                    NAMES
      f2f48daf07ea        registry.cn-shanghai.aliyuncs.com/alexgong/flaskihome:3.0   "gunicorn manager:ap…"   13 hours ago        Up 13 hours         0.0.0.0:5000->5000/tcp   docker_ihome_web_1
      d1a43dbca2c4        registry.cn-shanghai.aliyuncs.com/alexgong/flaskihome:3.0   "celery -A ihome.cel…"   13 hours ago        Up 13 hours         5000/tcp                 docker_ihome_worker_1
      (root@Aliyun-Alex:/home/alex/python/docker_ihome)# docker exec -it f2f48daf07ea /bin/bash
      root@f2f48daf07ea:/Project/FlaskIhome# ls
      Dockerfile  README.md  __pycache__  config.py  docker-compose.yml  gunicorn.conf.py  ihome  logs  manager.py  migrations  requeirements.py  requirements.txt  single_manager.py
      root@f2f48daf07ea:/Project/FlaskIhome# pwd
      /Project/FlaskIhome
      

      image-20200913223033035

    4. COPY requirements.txt ./是指将当前目录下的requirements.txt复制到docker容器的WORKDIR

    5. RUN pip install -r requirements.txt是指docker在启动后, 会自动运行pip命令把所需的环境安装好

    6. Docker运行起了项目后,发现获取到的时间是美国时间,所以需要将时区改为中国上海

      RUN ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime
      RUN echo 'Asia/Shanghai' > /etc/timezone
      
    7. EXPOSE 5000是只docker容器向外暴露的5000端口号

    8. CMD ["gunicorn", "manager:app", "-c", "./gunicorn.conf.py"]是指docker启动后, 自动运行gunicorn的启动命令, 即启动了容器就会启动其中的flask项目

    9. Dockerfile的其他编写关键字可以查看官网

    build构建镜像

    Dockerfile的路径中(项目根目录)运行命令

    sudo docker build -t 'flaskihome' .
    

    注:

    1. 该命令会默认寻找当前目录的名为Dockerfile的文件, 也可以使用-f指定具体的Dockerfile

    2. -t是指--tag list , 给镜像命名name:tag, 未指定:后面的tag则默认为latest

    3. 最后的.不要忘记了

    4. 这一步构建会花一点时间, 因为要下载python基础镜像和pip包等, 构建完成之后截图如下

      image-20200913225918227

    查看构造的镜像

    (flask) alex@alex:~/python/FlaskIhome$ sudo docker images
    REPOSITORY                                              TAG                 IMAGE ID            CREATED             SIZE                                                                             
    flaskihome                                              latest              ee00fc32da96        6 minutes ago       1.06GB 
    

    上传镜像

    可以将镜像上传至Docker Hub官网, 由于官网上传下载比较慢, 所以这里选择上传到阿里云的容器镜像服务中. 具体步骤可以查看我的另一篇博客总结 Docker-基础005-发布自己的镜像, 简单来说就是进入容器镜像服务->创建命名空间->创建镜像仓库, 创建好镜像仓库后, 可以在基本信息页签中查看操作指南, 告诉你如何上传拉取镜像

    1. 登录阿里云Docker Registry
    $ sudo docker login --username=alex马上读初一 registry.cn-shanghai.aliyuncs.com
    用于登录的用户名为阿里云账号全名,密码为开通服务时设置的密码。
    
    2. 从Registry中拉取镜像
    $ sudo docker pull registry.cn-shanghai.aliyuncs.com/alexgong/flaskihome:[镜像版本号]
    
    3. 将镜像推送到Registry
    $ sudo docker login --username=alex马上读初一 registry.cn-shanghai.aliyuncs.com
    $ sudo docker tag [ImageId] registry.cn-shanghai.aliyuncs.com/alexgong/flaskihome:[镜像版本号]
    $ sudo docker push registry.cn-shanghai.aliyuncs.com/alexgong/flaskihome:[镜像版本号]
    

    image-20200913230814479

    在另一台机器中拉取并运行镜像

    首先同样需要安装docker, 根据上面的指示拉取镜像, 然后运行命令即可启动容器同时启动flask项目, -d为后台守护进程运行

    # 启动容器
    sudo docker run -d -p 5000:5000 镜像ID
    # 停止容器
    sudo docker stop 容器ID
    

    在容器中启动celery

    由于容器启动后, 只启动了flask应用, 本项目中还需要启动celery的worker, 因此需要进入容器终端启动celery

    # 运行命令进入容器终端
    sudo docker exec -it 2bc65dc10f2 /bin/bash
    # 启动celery命令
    celery -A ihome.celery_tasks.main worker -l=info
    

    image-20200913233448803

    使用 Docker-compose 封装 flask + celery

    编写容器的docker-compose file

    由于上面还需要手动进入容器启动celery, 我们可以使用docker-compose来编排docker, 启动多个命令

    回到之前编写的flask项目目录, 创建docker-compose的配置文件docker-compose.yml

    version: '3.8'
    services:
      web:
        build: .
        ports:
          - "5000:5000"
      worker:
        build: .
        command: "celery -A ihome.celery_tasks.main worker -l=info"
    

    注:

    1. docker-compose的配置文件编写语法可以查看官网: https://docs.docker.com/compose/compose-file/

    2. version参数需要根据docker的版本填写, 对应关系可以查看上面的官网, 这里的docker engine版本为19.03.12, 所以选择3.8

    3. 这里定义了两个services, web为flask应用, worker为celery的worker, 需要添加celery的运行命令celery -A ihome.celery_tasks.main worker -l=info

    build构建镜像

    docker-compose.yml的路径中(项目根目录)运行命令, 注意后面没有.

    sudo docker-compose build
    

    构建完成后, 查看镜像, 发现其构建了两个镜像flaskihome_webflaskihome_worker, 但是这两个的镜像ID是一样的, 说明用的是同一份代码

    (flask) alex@alex:~/python/FlaskIhome$ sudo docker images
    REPOSITORY                                              TAG                 IMAGE ID            CREATED              SIZE                                                                            
    flaskihome_web                                          latest              6fa5f85e885a        About a minute ago   1.06GB                                                                          
    flaskihome_worker                                       latest              6fa5f85e885a        About a minute ago   1.06GB 
    

    上传镜像

    根据上面的阿里云容器镜像服务的指南, 上传镜像

    # 打tag
    sudo docker tag 6fa5f85e885a registry.cn-shanghai.aliyuncs.com/alexgong/flaskihome:1.0
    # 上传
    sudo docker push registry.cn-shanghai.aliyuncs.com/alexgong/flaskihome:1.0
    

    在另一台机器中编写docker-compose file启动容器

    在合适的位置创建自定义项目目录docker_ihome, 并在目录下创建配置文件docker-compose.yml

    version: '3.8'
    services:
      web:
        image: registry.cn-shanghai.aliyuncs.com/alexgong/flaskihome:1.0
        ports:
          - "5000:5000"
      worker:
        image: registry.cn-shanghai.aliyuncs.com/alexgong/flaskihome:1.0
        command: "celery -A ihome.celery_tasks.main worker -l=info"
    

    注:

    1. 和前面的docker-compose file内容类似, 只是把build参数改成了image参数

    启动docker-compose

    执行命令, 运行docker-compose, -d是后台守护进程运行

    $ sudo docker-compose up -d
    Starting docker_ihome_web_1    ... done
    Starting docker_ihome_worker_1 ... done
    

    可以发现两个服务已经启起来了, 使用sudo docker ps可以查看正在启动的docker

    (root@Aliyun-Alex:/home/alex/python/docker_ihome)# docker ps
    CONTAINER ID        IMAGE                                                       COMMAND                  CREATED             STATUS              PORTS                    NAMES
    66af64b043ee        registry.cn-shanghai.aliyuncs.com/alexgong/flaskihome:1.0   "celery -A ihome.cel…"   6 minutes ago       Up About a minute   5000/tcp                 docker_ihome_worker_1
    5d8773442c3d        registry.cn-shanghai.aliyuncs.com/alexgong/flaskihome:1.0   "gunicorn manager:ap…"   6 minutes ago       Up About a minute   0.0.0.0:5000->5000/tcp   docker_ihome_web_1
    

    浏览flask网站

    访问该机器的5000端口, 发现可以正常访问项目网站

    image-20200914092057771

  • 相关阅读:
    iOS 6编程UIScrollView滚动视图结合UIImageView图像视图实现图像缩放效果
    iOS 6编程UIScrollView滚动视图和UIPageControl分页控件实现图像分页显示(2)
    iOS 6编程基于AV Foundation框架开发简单音乐播放器
    iOS 6 的5个新特性创建杀手级应用
    mysql数据库备份和还原
    SEO实战:解决百度收录问题
    nginx的80端口配置两个Web服务
    DedeCMS, Discuz, Phpwind, PhpCMS
    nginx下安装wordpress
    larbin编译和配置
  • 原文地址:https://www.cnblogs.com/gcxblogs/p/13664977.html
Copyright © 2011-2022 走看看