0
点赞
收藏
分享

微信扫一扫

docker 容器化部署 Flask 项目_MySQL+Gunicorn+Nginx

非衣所思 2022-02-11 阅读 88
flask容器

本文环境:

  • Linux 发行版:Ubuntu Focal 20.04.3 (LTS)
  • Linux 内核:Linux 5.4.0-90-generic
  • Docker 版本:20.10.11, build dea9396
  • MYSQL:5.7.36
  • Python:3.7.9
  • Flask:2.0.2
  • Gunicorn:20.1.0
  • Nginx:1.21.4

部署前提

已启动数据库。本文数据库使用 mysql 5.7,数据库 IP 地址为 192.168.1.11 。

登录 mysql 创建测试数据库及用户:

-- 创建应用数据库
mysql> CREATE DATABASE demo;

-- 创建数据库用户
mysql> CREATE USER 'demouser'@'%' IDENTIFIED BY 'demo123';

-- 授权
mysql> GRANT ALL PRIVILEGES ON demo.* TO 'demouser'@'%';

创建测试项目

目录结构

└── flask_project
    ├── docker-compose.yml
    ├── flaskapp
    │   ├── app.py
    │   ├── config.py
    │   ├── Dockerfile
    │   ├── gunicorn.config.py
    │   └── requirements.txt
    ├── mysql
    │   └── test.env
    └── nginx
        ├── Dockerfile
        └── nginx.conf

./flaskapp/app.py

import os
from datetime import datetime

from flask import Flask, jsonify
from flask_sqlalchemy import SQLAlchemy

import config

# 实例化一个web应用
app = Flask(__name__)
app.config.from_object(config)
# 初始化一个数据库连接对象
db = SQLAlchemy(app)


# 测试数据库连接
# 建表
class User(db.Model):
    __tablename__ = 'user'
    uid = db.Column(db.Integer, primary_key=True, autoincrement=True)
    username = db.Column(db.String(50), nullable=False)
    email = db.Column(db.String(50), nullable=False, unique=True)
    update_time = db.Column(db.DateTime, default=datetime.now)

    def to_json(self):
        return {
            'uid': self.uid,
            'username': self.username,
            'email': self.email,
            'update_time': self.update_time,
        }


db.create_all()


@app.route("/")
def index():
    app_name = os.getenv("APP_NAME")

    return f"Hello from {app_name} running Flask inside Docker."


@app.route("/query")
def query():
    result = User.query.all()
    return jsonify(list(map(User.to_json, result)))


@app.route("/add/<username>")
def add(username):
    result = User.query.filter_by(username=username).first()
    if result:
        return f"user {username} already exists."
    user1 = User(username=username, email=username + '@456.com')
    db.session.add(user1)
    db.session.commit()
    return f"add {username}."


if __name__ == "__main__":
    app.run(debug=True, host='0.0.0.0')

数据库配置文件 ./flaskapp/config.py

DB_USERNAME = 'demouser'
DB_PASSWORD = 'demo123'
DB_HOST = 'mysql57'
DB_PORT = '3306'
DB_NAME = 'demo'
DB_URI = 'mysql+pymysql://%s:%s@%s:%s/%s?charset=utf8mb4' % \
         (DB_USERNAME, DB_PASSWORD, DB_HOST, DB_PORT, DB_NAME)
SQLALCHEMY_DATABASE_URI = DB_URI
SQLALCHEMY_TRACK_MODIFICATIONS = False

项目依赖文件:./flaskapp/requirements.txt

PyMySQL==1.0.2
Flask==2.0.2
Flask-SQLAlchemy==2.5.1
gunicorn==20.1.0
gunicorn[gevent]

gunicorn 配置文件:./flaskapp/gunicorn.config.py

import multiprocessing

# 默认'127.0.0.1:8000',防火墙需开启对应端口
bind = '0.0.0.0:8000'

# 并发进程数,默认1
workers = multiprocessing.cpu_count() * 2 + 1

# 允许挂起的连接数最大值,默认2048
backlog = 2048

# 进程的工作方式,默认'sync'
worker_class = 'gevent'

# 最大客户客户端并发数量,对使用线程和协程的worker的工作有影响
#worker_connections = 1200

# 调试模式,默认False
#Debugging = True

# 进程名
proc_name = 'gunicorn.proc'


# pid文件,如果不设置将不会创建
pidfile = '/tmp/gunicorn.pid'

# 访问记录
accesslog = '-'
# 访问记录格式
# access_log_format = '%(h)s %(t)s %(U)s %(q)s'

# 日志文件
errorlog = '-'

# 错误日志输出等级,默认'info'
#loglevel = 'debug'

注:容器写入 stdoutstderr 的任何内容都将被捕获并存储为容器的日志。 出于这个原因,accesslogerrorlog 都配置为 -,容器部署时把日志发送到标准输出,以便由 Docker 作为日志存储。

容器部署

创建容器网络

创建 flask 和 mysql,nginx 通信使用的网络

// 创建网桥,连接方式是 bridge
$ docker network create -d bridge flask-nginx

// 显示所有 bridge
$ docker network ls
# NETWORK ID     NAME      DRIVER    SCOPE
# ddeb87894904   flask-nginx   bridge    local

创建 flask 镜像+容器

以 python:3.7.9-slim 为基础镜像,Dockerfile 如下:

FROM python:3.7.9-slim
WORKDIR /www/flaskapp
COPY ./requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt -i https://pypi.tuna.tsinghua.edu.cn/simple

COPY . .
CMD ["gunicorn", "-c", "./gunicorn.config.py", "app:app"]

构建镜像并启动

# 当前工作目录:flaskapp
# 构建镜像
$ docker build -t="my_flaskapp:0.1" .

# 后台启动flask容器,加入网桥front
$ docker run -itd \
--name flask \
--network flask-nginx \
--add-host=mysql57:192.168.1.11 \
-p 8000:8000 \
-e APP_NAME=flask_0.1 \
my_flaskapp:0.1 

# 查看容器ip
$ docker inspect --format='{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' flask
# 172.18.0.3
  • --add-host:添加自定义hosts,这里是 mysql 数据库的 IP 地址

创建 nginx 镜像+容器

nginx 配置文件 nginx.conf

user nginx;
worker_processes auto;
error_log /var/log/nginx/error.log;
pid /run/nginx.pid;

include /usr/share/nginx/modules/*.conf;

events {
    worker_connections 1024;
}

http {
    log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
                      '$status $body_bytes_sent "$http_referer" '
                      '"$http_user_agent" "$http_x_forwarded_for"';

    access_log  /var/log/nginx/access.log  main;

    sendfile            on;
    tcp_nopush          on;
    tcp_nodelay         on;
    keepalive_timeout   65;
    types_hash_max_size 2048;

    include             /etc/nginx/mime.types;
    default_type        application/octet-stream;

    include /etc/nginx/conf.d/*.conf;

    server {
        listen       80 default_server;
        listen       [::]:80 default_server;
        server_name  _;
        access_log   /var/log/nginx/flaskapp_access.log;
        error_log    /var/log/nginx/flaskapp_error.log;
        root         /usr/share/nginx/html;

        include /etc/nginx/default.d/*.conf;

        location / {                                                                
            proxy_pass http://flask:8000;                                   
            proxy_redirect     off;                                             
            proxy_set_header   Host                 $http_host;                 
            proxy_set_header   X-Real-IP            $remote_addr;               
            proxy_set_header   X-Forwarded-For      $proxy_add_x_forwarded_for; 
            proxy_set_header   X-Forwarded-Proto    $scheme;                    
        }                                                                           
                                                                                
        location ~ .*\.(gif|jpg|jpeg|png|bmp|swf|ico)$ {                            
             proxy_pass  http://flask:8000;                                   
             expires 30d;                                                       
        }
                                                                           
        location ~ .*\.(js|css)?$ {                                                 
            proxy_pass http://flask:8000;                                     
            expires 15d;                                                        
        }

        error_page 404 /404.html;
            location = /40x.html {
        }

        error_page 500 502 503 504 /50x.html;
            location = /50x.html {
        }
    }
}

nginx 容器通过 --network 连接 flask 容器,因此配置文件中 location 的代理地址写 flask:8000
注:docker 官方已不推荐使用 docker run --link 来链接 2 个容器互相通信,随后的版本中会删除 --link

以 nginx:1.21.4 为基础镜像,Dockerfile 如下:

FROM nginx:1.21.4
RUN rm /etc/nginx/conf.d/default.conf
COPY ./nginx.conf /etc/nginx

构建镜像并启动

# 当前工作目录:nginx
$ docker build -t "my_nginx:1.0" . 

# 后台启动nginx容器,加入网桥front
$ docker run -itd --name nginx -p 80:80 --network flask-nginx my_nginx:1.0

# 查看容器ip
$ docker inspect --format='{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' nginx
# 172.18.0.4

现在浏览器可以通过 http://主机地址 访问 flask 项目。

测试

# 访问项目地址
$ curl http://127.0.0.1
Hello from flask_0.1 running Flask inside Docker

# 添加记录到数据库
$ curl http://127.0.0.1/add/lisi
add lisi.

# 查询添加结果
$ curl http://127.0.0.1/query
{"email":"lisi@456.com","uid":1,"update_time":"Wed, 15 Dec 2021 16:46:10 GMT","username":"lisi"}]

容器编排

前提:安装 Docker Compose。

docker-compose.yml

version: '3'
networks:
  flask-nginx:
services:
  flaskapp:
    image: my_flaskapp:0.1
    build: ./flaskapp
    container_name: flask
    volumes:
      - "/etc/timezone:/etc/timezone:ro"
      - "/etc/localtime:/etc/localtime:ro"
    networks:
      - flask-nginx
    ports:
      - "8000"
    environment:
      - APP_NAME=flask_0.2
    extra_hosts:
      - "mysql57:192.168.1.11"
  nginx:
    image: my_nginx:1.0
    build: ./nginx
    container_name: nginx
    volumes:
      - "/etc/timezone:/etc/timezone:ro"
      - "/etc/localtime:/etc/localtime:ro"	
    networks:
      - flask-nginx
    ports:
      - "80:80"
    depends_on:
      - flaskapp

上面配置中,连接数据库使用了外部 hosts,也可使用外部容器网络:

networks:
  mysql57:
    external: true
services:
  flaskapp:
    networks:
      - mysql57

运行项目

# 后台运行
docker-compose up -d

# 查看运行状态
docker-compose ps

– END –

参考

  • 使用docker部署nginx+flask+gunicorn+mysql项目 - 简书 (jianshu.com) 2020-06-17

  • docker 的 link 和 network 网络互连问题 - 小寒的故事 - 博客园 (cnblogs.com) 2018-12-18

  • docker link 过时不再用了?那容器互联、服务发现怎么办? - YatHo - 博客园 (cnblogs.com) 2017-11-20

举报

相关推荐

0 条评论