Docker部署Flask项目,基于MySQL数据库及Nginx代理

1 概述

本示例详述了使用Docker部署Nginx作为反向代理,基于MySQL数据库的Flask项目的基本实现过程。实际生产环境部署,一般使用docker compose,这里演示分步实现项目部署,只是学习的一个过程,示例中的代码、配置文件,应根据具体的项目需求和环境配置,某些步骤也应该做相应的调整。

2 项目准备

2.1 基础环境和工具
  • Linux系统:虚拟机或云服务器都可以,这里使用的是基于Windows WSL的Ubuntu 24.04

    在这里插入图片描述

  • MobaXterm远程终端工具

    在这里插入图片描述

    这里使用MobaXterm连接虚拟机,便于项目部署的操作。MobaXterm极易上手,免费易用,MobaXterm 链接直达

    在这里插入图片描述

  • Docker工具

    安装及使用方法不赘述,Docker Docs

    在这里插入图片描述

2.2 Docker基础镜像

国内连接Docker镜像仓库网络的原因,我这里速度很慢且不稳定,所以这里推荐在项目部署前,把镜像先安排上,以提升项目部署速度,降低部署过程中出错中断概率。这里会用到MySQL、Python和Nginx相关基础镜像,可以从镜像仓库拉取或者本地加载。

# 仓库拉取
docker pull mysql:8.0.33
docker pull python:3.9-slim
docker pull nginx:1.27.1

# 本地加载
docker load -i ./tar/mysql-8.0.33.tar
docker load -i ./tar/python-3.9-slim.tar
docker load -i ./tar/nginx-1.27.1.tar

在这里插入图片描述

2.3 项目代码及配置文件
2.3.1 项目布局树形图
# mysql和nginx的docker容器配置文件
/nginx
   |-- Dockerfile		# 构建Nginx镜像的Dockerfile
   |-- nginx.conf		# Nginx配置文件
/mysql
   |-- conf
      |-- mysql.conf	# MySQL配置文件
   |-- data
   |-- init
      |-- init.sql		# MySQL初始化脚本
# flask项目及配置文件
/flaskDockerDemo
  |-- templates
    |-- index.html
    |-- show.html
  |-- static
    |-- styles.css
    |-- sun.png
    |-- baiduai.png
  |-- uploads
    |-- BaiduAI.jpeg
  |-- app.py			# Flask项目主程序
  |-- requirements.txt	# 示例项目比较简单,这里不要
  |-- Dockerfile		# 构建flaskdemo镜像的Dockerfile
  |-- run.sh			# 示例项目比较简单,可以不要
  |-- .dockerignore
  |-- README.md
2.3.2 flaskDockerDemo代码及其配置文件

示例代码及配置文件就不推到GitHub了,完整贴出来如下,需要自行复制

  • templates

    • index.html 代码:
    <!DOCTYPE html>
    <html lang="zh-CN">
    <head>
        <meta charset="utf-8">
        <title>FlaskDemo</title>
        <link rel="icon" type="image/png" href="{{ url_for('static', filename='sun.png') }}">
        <link rel="stylesheet" href="{{ url_for('static', filename='styles.css') }}">
    </head>
    <body>
    <nav class="navbar">
        <ul>
            <li><a href="{{ url_for('hello_world') }}">Home</a></li>
        </ul>
    </nav>
    <main class="">
        <h1>{{ greetings[0][1] }} × {{ greetings|length }}</h1>
        <img src="{{ url_for('static', filename='baiduai.png') }}" height="600">
    </main>
    <footer class="footer">
        <hr>
        <small> &copy; 2024 <a href="{{ url_for('hello_world') }}">HelloFlask</a></small>
    </footer>
    </body>
    </html>
    
    • show.html 代码:
    <!DOCTYPE html>
    <html lang="zh-CN">
    <head>
        <meta charset="utf-8">
        <title>FlaskDemo</title>
        <link rel="icon" type="image/png" href="{{ url_for('static', filename='sun.png') }}">
        <link rel="stylesheet" href="{{ url_for('static', filename='styles.css') }}">
    </head>
    <body>
    <nav class="navbar">
        <ul>
            <li><a href="{{ url_for('hello_world') }}">Home</a></li>
        </ul>
    </nav>
    <main class="">
        <h1>你像花一样灿烂</h1>
        <img src="{{ url_for('bloom', filename='BaiduAI.jpeg') }}" height="600">
    </main>
    <footer class="footer">
        <hr>
        <small> &copy; 2024 <a href="{{ url_for('hello_world') }}">HelloFlask</a></small>
    </footer>
    </body>
    </html>
    
  • static

    • styles.css 代码:
    /* 导航条样式 */
    .navbar {
        background-color: #333; /* 导航条背景颜色 */
        overflow: hidden; /* 清除浮动 */
        position: sticky; /* 固定定位,根据需要可改为absolute、fixed或static */
        top: 0; /* 底部对齐 */
        width: 100%; /* 宽度100% */
    }
    
    .navbar ul {
        list-style-type: none; /* 移除列表前的标记 */
        margin: 0;
        padding: 0;
        display: flex; /* 使用Flexbox使导航项水平排列 */
        justify-content: left; /* 均匀分布导航项space-around */
    }
    
    .navbar li {
        float: left; /* 浮动列表项,但对于Flexbox不是必需的 */
    }
    
    .navbar li a {
        display: block; /* 将链接变成块级元素,便于调整大小 */
        color: white; /* 链接文字颜色 */
        text-align: center; /* 文本居中 */
        padding: 14px 16px; /* 内边距 */
        text-decoration: none; /* 移除下划线 */
    }
    
    .navbar li a:hover {
        background-color: #ddd; /* 鼠标悬停时的背景颜色 */
        color: black; /* 鼠标悬停时的文字颜色 */
    }
    
    /* 页脚样式 */
    .footer {
        background-color: #333; /* 页脚背景颜色 */
        color: white; /* 页脚文字颜色 */
        text-align: center; /* 文本居中 */
        padding: 10px 0; /* 上下内边距,左右为0 */
        position: fixed; /* 固定定位,根据需要可改为absolute、fixed或static */
        bottom: 0; /* 底部对齐 */
        width: 100%; /* 宽度100% */
    }
    
    /* 内容样式 */
    main {
        text-align: center; /* 文本居中 */
    }
    
    • sun.png 图片:

    在这里插入图片描述

    • baiduai.png 图片:

    在这里插入图片描述

  • uploads

    • BaiduAI.jpeg

    在这里插入图片描述

  • app.py

    import os
    import pymysql
    from flask import Flask, render_template, send_from_directory
    
    app = Flask(__name__)
    
    # 配置上传文件的目录
    UPLOAD_FOLDER = os.path.join(app.root_path, 'uploads')
    app.config['UPLOAD_FOLDER'] = UPLOAD_FOLDER
    
    db_config = {
        'host': 'mysql',
        'port': 3306,
        'user': 'root',
        'password': '123456',
        'database': 'demo',
    }
    
    
    @app.route('/')
    def hello_world():  # put application's code here
        conn = pymysql.connect(**db_config)
        cursor = conn.cursor()
        cursor.execute('''CREATE TABLE IF NOT EXISTS hello(
                        id INT AUTO_INCREMENT PRIMARY KEY,
                        greeting VARCHAR(16) NOT NULL
                        ) DEFAULT CHARSET=utf8;''')
        cursor.execute('''INSERT INTO hello(greeting) VALUES ('Hello, Flask!')''')
        conn.commit()
        cursor.execute("SELECT * FROM hello")
        greetings = cursor.fetchall()
        cursor.close()
        conn.close()
        return render_template('index.html', greetings=greetings)
    
    
    @app.route('/show')
    def show():
        return render_template('show.html')
    
    
    @app.route('/flower/<path:filename>')
    def bloom(filename):
        return send_from_directory(app.config['UPLOAD_FOLDER'], filename)
    
  • Dockerfile

    # 使用Python 3.9的slim版本作为基础镜像,slim版本较为轻量,适合生产环境
    FROM python:3.9-slim
    
    # 将当前目录下的所有文件复制到容器内的/app/目录
    COPY . /app/
    # 将当前目录下的run.sh文件复制到容器内的/run.sh目录
    COPY ./run.sh /run.sh
    
    # 设置容器内的工作目录为/app/
    WORKDIR /app/
    
    # 设置环境变量TIME_ZONE为Asia/Shanghai,用于设置容器内的时区
    ENV TIME_ZONE=Asia/Shanghai
    # 设置环境变量PIPURL,这里设置为清华大学的pypi镜像
    ENV PIP_INDEX_URL=https://pypi.tuna.tsinghua.edu.cn/simple/
    #或 RUN pip config set global.index-url https://pypi.tuna.tsinghua.edu.cn/simple/
    
    # 更新系统时区设置,确保容器内的时间与上海时区一致
    RUN echo "${TIME_ZONE}" > /etc/timezone && \
        ln -sf /usr/share/zoneinfo/${TIME_ZONE} /etc/localtime
        
    # 更新pip版本
    RUN /usr/local/bin/python -m pip install --upgrade pip
    #或 RUN python -m pip install --upgrade pip
    
    # 安装依赖包
    RUN pip install python-dotenv flask flask-wtf flask-sqlalchemy flask-migrate flask-ckeditor pymysql bleach gunicorn cryptography
    #或 RUN pip install -r requirements.txt
    
    # 给予run.sh脚本执行权限
    RUN chmod +x /run.sh
    
    # 暴露端口,可以不写
    EXPOSE 5000
    
    # 设置容器启动时执行的命令
    CMD ["/run.sh"]
    
  • run.sh

    #!/bin/sh
    
    # 这里可以写一些项目初始化的脚本,比如数据库初始化
    #echo "Initializing Flask database..."
    #flask db init
    #flask db migrate
    #flask db upgrade
    
    echo "Starting Gunicorn."
    exec gunicorn -b :'5000' app:app
    
2.3.3 mysql
  • mysql.conf

    [client]
    default_character_set=utf8mb4
    [mysql]
    default_character_set=utf8mb4
    [mysqld]
    character_set_server=utf8mb4
    collation_server=utf8mb4_unicode_ci
    init_connect='SET NAMES utf8mb4'
    
  • init.sql

    DROP DATABASE IF EXISTS `demo`;
    CREATE DATABASE IF NOT EXISTS `demo` DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci;
    
2.3.4 nginx
  • Dockerfile

    FROM nginx:1.27.1
    COPY nginx.conf /etc/nginx/nginx.conf
    
  • nginx.conf

    user  nginx;  
    worker_processes  auto;
      
    error_log  /var/log/nginx/error.log warn;  
    pid        /var/run/nginx.pid;
    
    include /usr/share/nginx/modules/*.conf;
      
    events {  
        worker_connections  1024;  
    }  
      
    http {  
        include       /etc/nginx/mime.types;  
        default_type  application/octet-stream;  
      
        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;
        keepalive_timeout  65;
      
        # 代理设置  
        upstream flask_app {  
            server flaskdemo:5000; # Flask容器的名称或IP
        }  
      
        server {  
            listen       80;  
            server_name  _;
    
            location /static {
                alias /app/static;	# 静态资源路径
            }
    
            location /uploads {
                alias /app/uploads; # 用户上传文件路径
            }
      
            # 转发请求到Flask应用  
            location / {  
                proxy_pass http://flaskdemo:5000;
                proxy_set_header Host $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;
    
                proxy_redirect off;  
            }  
      
            # 其他可能的配置...
        }  
      
        # 其他server配置...  
    }
    
2.4 将2.3中创建的相关文件丢进虚拟机

在这里插入图片描述

这里是以conderchi用户登录的虚拟机,将文件丢进用户根目录即可,如果是以root权限登录,需要将文件拖到root根目录下。

3 项目分步部署

3.1 构建Docker网络

为了实现后续要部署的MySQL、Flask 项目和Nginx等各容器通过容器名无缝直连,首先构建一个Docker网络flasknet,代码如下:

# 创建flasknet网络
docker network create flasknet

# 查看已存在的网络
docker network ls

代码执行后查看创建的网络如下图:

在这里插入图片描述

flasknet网络构建完成,还可以通过以下指令查看网络具体信息:

# 通过网络名查看
docker network inspect flasknet
# 或通过网络id查看
docker network inspect 8ab
3.2 MySQL容器部署

部署MySQL容器配置项目相对来说比较简单,基于MySQL官方基础镜像直接运行MySQL容器就足够了,这里没有必要再单独创建自己的MySQL镜像。

部署运行指令如下,注意需要在根目录下运行该指令

docker run -d \
  --name mysql \
  -p 3306:3306 \
  -e TZ=Asia/Shanghai \
  -e MYSQL_ROOT_PASSWORD=123456 \
  -v ./mysql/data:/var/lib/mysql \
  -v ./mysql/conf:/etc/mysql/conf.d \
  -v ./mysql/init:/docker-entrypoint-initdb.d \
  --network flasknet \
  mysql:8.0.33

代码执行后mysql容器即部署完成,可以看到mysql的挂载目录data已有数据被创建出来,如下图:

在这里插入图片描述

也可以在其他目录下去构建运行容器,但挂载数据卷时路径要做相应调整,比如在mysql目录下,挂载数据卷路径应修改如下。

  -v ./data:/var/lib/mysql \
  -v ./conf:/etc/mysql/conf.d \
  -v ./init:/docker-entrypoint-initdb.d \

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

执行以下命令,查看mysql容器是否已运行:

docker ps

在这里插入图片描述

进入容器内部,查看数据库demo是否已创建:

docker exec -it mysql mysql -uroot -p123456

show databases;
exit;	# 或Ctrl+D,退出mysql容器

在这里插入图片描述

3.3 Flask项目部署
3.3.1 构建flaskdemo镜像

注意:在构建flaskdemo镜像时,应根据Dockerfile路径及配置信息,选择在哪个目录下执行镜像构建命令,这里可以先进入flaskDockerDemo目录下,然后执行构建镜像,或者在构建镜像时明确指定Dockerfile路径。

# 方式一:进入flaskDockerDemo目录
cd ./flaskDockerDemo
docker build -t flaskdemo .

# 方式二:在根目录下
docker build -t flaskdemo ./flaskDockerDemo

在这里插入图片描述

查看刚构建的flaskdemo镜像

docker images

在这里插入图片描述

3.3.2 部署运行flaskdemo容器
  • 不使用Nginx反向代理静态文件,可以使用以下命令直接部署
docker run -d --name flask -p 5000:5000 -v ./flaskDockerDemo:/app --network flasknet flaskdemo
# 使用下面命令查看正在运行的容器
docker ps

在这里插入图片描述

以下命令可以查看容器运行日志

docker logs flask	# 或docker logs 7fa

在这里插入图片描述

查看虚拟机ip

ip addr

在这里插入图片描述

浏览器打开网页

在这里插入图片描述

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

  • 使用Nginx反向代理静态文件,使用以下命令部署
docker run -d --name flaskdemo -v ./flaskDockerDemo:/app --network flasknet flaskdemo

在这里插入图片描述

3.4 Nginx容器部署
3.4.1 构建nginx镜像

方法和3.3.1构建flaskdemo镜像相同。

# 方式一:进入nginx目录
cd ./nginx
docker build -t nginx .

# 方式二:在根目录下
docker build -t nginx ./nginx

在这里插入图片描述

3.4.2 部署运行nginx容器

执行以下命令部署运行nginx容器

docker run --name nginx -d \
    --network flasknet \
    -p 80:80 \
    -v ./nginx/nginx.conf:/etc/nginx/nginx.conf \
    -v ./flaskDockerDemo/static:/app/static \
    -v ./flaskDockerDemo/uploads:/app/uploads \
    nginx

在这里插入图片描述

打开网页

在这里插入图片描述

在这里插入图片描述

4 设置开机自动启动

常用有以下3中方法,对于分步部署,个人更推荐前两种方式

4.1 容器已经部署

当容器已经部署到系统,可以通过以下命令,设置容器开机自动启动

docker update --restart=always mysql flaskdemo nginx
4.2 部署容器时设置

也可以通过以下命令,在容器部署时设置容器开机自动启动

# 在执行docker run时,增加 --restart always参数
# mysql
docker run -d \
  --restart always \
  --name mysql \
  -p 3306:3306 \
  -e TZ=Asia/Shanghai \
  -e MYSQL_ROOT_PASSWORD=123456 \
  -v ./mysql/data:/var/lib/mysql \
  -v ./mysql/conf:/etc/mysql/conf.d \
  -v ./mysql/init:/docker-entrypoint-initdb.d \
  --network flasknet \
  mysql:8.0.33
  
# flaskdemo
docker run -d \
  --restart always \
  --name flaskdemo \
  -v ./flaskDockerDemo:/app \
  --network flasknet \
  flaskdemo
  
# nginx
docker run -d \
	--name nginx
    --restart always \
    --network flasknet \
    -p 80:80 \
    -v ./nginx/nginx.conf:/etc/nginx/nginx.conf \
    -v ./flaskDockerDemo/static:/app/static \
    -v ./flaskDockerDemo/uploads:/app/uploads \
    nginx
4.3 使用systemd服务

systemd是现代Linux系统的标准初始化系统,通过创建一个服务单元文件(.service),可以定义容器启动的参数和重启策略,并将其设置为开机自启动。步骤如下:

  • 创建一个名为 .service 的文件

    • mysql.service
    [Unit]
    Description=mysql container service
    Requires=docker.service
    After=docker.service
    
    [Service]
    RemainAfterExit=yes
    ExecStart=/usr/bin/docker start mysql
    Restart=always
    
    [Install]
    WantedBy=multi-user.target
    
    • flaskdemo.service
    [Unit]
    Description=flaskdemo container service
    Requires=docker.service
    After=docker.service
    
    [Service]
    RemainAfterExit=yes
    ExecStart=/usr/bin/docker start flaskdemo
    Restart=always
    
    [Install]
    WantedBy=multi-user.target
    
    • nginx.service
    [Unit]
    Description=nginx container service
    Requires=docker.service
    After=docker.service
    
    [Service]
    RemainAfterExit=yes
    ExecStart=/usr/bin/docker start nginx
    Restart=always
    
    [Install]
    WantedBy=multi-user.target
    

    注意:在 ExecStart 中,因前面已经存在,所以直接使用 docker start 命令;如果容器不存在,需要使用 docker run 命令,这里不赘述。

    另外,在 ExecStart 中,要指定 docker 路径,你的可能和示例 /usr/bin/docker 不同,可以使用以下命令查看 docker 路径

    which docker							# 查看docker路径
    
  • .service 文件放到虚拟机 /etc/systemd/system/ 目录下

    因这里使用的是user账户连接的虚拟机系统,可以采取以下方式将 .service 文件放到 /etc/systemd/system/ 目录下,以 flaskdemo.service 文件为例如下:

    • 将文件拖到用户根目录下

    • 执行以下指令,将文件复制到 /etc/systemd/system/ 目录

      sudo cp flaskdemo.service /etc/systemd/system/	# 第一次sudo需要输入系统密码
      

      在这里插入图片描述

    • 进入 /etc/systemd/system/ 目录查看文件是否到位

      cd /etc/systemd/system/
      

      在这里插入图片描述

    如果是root权限登录的系统,直接进入 /etc/systemd/system/ 目录,将文件拖进去就好了

    cd /etc/systemd/system/
    
  • 启用并启动服务

    sudo systemctl daemon-reload
    sudo systemctl enable flaskdemo.service
    sudo systemctl start flaskdemo.service
    # 可以重启系统使用 docker ps 看服务是否正常激活
    

    如果重启系统后,容器没有正常启动,大概率是 .service 文件配置有问题。可以使用以下指令查看服务日志

    systemctl list-units --type=service		# 查看已启动的服务
    sudo journalctl -u flaskdemo.service	# 查看服务日志
    

5 总结

  • 项目配置文件路径及构建和部署时路径应特别注意,避免部署时出现意外
  • 项目部署存在依赖关系,需要按照网络→MySQL→Flask→Nginx顺序

Docker Compose部署Flask项目,基于MySQL数据库及Nginx代理

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值