如何在 Ubuntu 18.04 上使用 Docker 构建和部署 Flask 应用程序
作者:mmseoamin日期:2024-04-30

介绍

Docker 是一个开源应用程序,允许管理员使用容器创建、管理、部署和复制应用程序。容器可以被视为一个包,其中包含应用程序在操作系统级别运行所需的依赖关系。这意味着使用 Docker 部署的每个应用程序都存在于自己的环境中,并且其要求是分开处理的。

Flask 是一个基于 Python 构建的 Web 微框架。它被称为微框架,因为它不需要特定的工具或插件来运行。Flask 框架轻巧灵活,但高度结构化,因此比其他框架更受青睐。

使用 Docker 部署 Flask 应用程序将允许您在不同服务器上进行最小的重新配置来复制应用程序。

在本教程中,您将创建一个 Flask 应用程序并使用 Docker 进行部署。本教程还将涵盖如何在部署后更新应用程序。

先决条件

要按照本教程操作,您需要以下内容:

  • 通过遵循《在 Ubuntu 18.04 上进行初始服务器设置》指南配置的具有 sudo 特权的非根用户。
  • 安装了 Docker 的一个 Ubuntu 18.04 服务器,可以通过本教程或 DigitalOcean 一键式 Docker 镜像进行设置。
  • 通过遵循《在 Ubuntu 18.04 上安装 Nginx》教程的第一步安装了 Nginx。

    步骤 1 — 设置 Flask 应用程序

    首先,您将创建一个目录结构,用于保存您的 Flask 应用程序。本教程将在 /var/www 中创建一个名为 TestApp 的目录,但您可以修改命令以命名为您喜欢的任何名称。

    sudo mkdir /var/www/TestApp
    

    进入新创建的 TestApp 目录:

    cd /var/www/TestApp
    

    接下来,为 Flask 应用程序创建基本文件夹结构:

    sudo mkdir -p app/static app/templates 
    

    -p 标志表示 mkdir 将创建一个目录和所有不存在的父目录。在这种情况下,mkdir 将在创建 static 和 templates 目录的过程中创建 app 父目录。

    app 目录将包含与 Flask 应用程序相关的所有文件,例如其 viewsblueprints。Views 是您编写的用于响应应用程序请求的代码。Blueprints 创建应用程序组件并支持应用程序内或跨多个应用程序的常见模式。

    static 目录是存放图像、CSS 和 JavaScript 文件等资源的位置。templates 目录是您将为项目放置 HTML 模板的位置。

    现在基本文件夹结构已经完成,创建运行 Flask 应用程序所需的文件。首先,在 app 目录中创建一个 __init__.py 文件。此文件告诉 Python 解释器 app 目录是一个包,并应该被视为这样处理。

    运行以下命令创建文件:

    sudo nano app/__init__.py
    

    Python 中的包允许您将模块分组到逻辑命名空间或层次结构中。这种方法使代码能够被分解为执行特定功能的单独且可管理的块。

    接下来,您将向 __init__.py 添加代码,该代码将创建一个 Flask 实例并导入 views.py 文件中的逻辑,您将在保存此文件后创建。向新文件添加以下代码:

    from flask import Flask
    app = Flask(__name__)
    from app import views
    

    添加了该代码后,保存并关闭文件。

    有了 __init__.py 文件,您就可以在 app 目录中创建 views.py 文件。此文件将包含大部分应用程序逻辑。

    sudo nano app/views.py
    

    接下来,向 views.py 文件添加代码。此代码将向访问您的网页的用户返回 hello world! 字符串:

    from app import app
    @app.route('/')
    def home():
       return "hello world!"
    

    @app.route 函数上面的行是一个装饰器。装饰器修改其后的函数。在这种情况下,装饰器告诉 Flask 哪个 URL 将触发 home() 函数。home 函数返回的 hello world 文本将显示在用户的浏览器上。

    有了 views.py 文件,您就可以创建 uwsgi.ini 文件。此文件将包含我们应用程序的 uWSGI 配置。uWSGI 是 Nginx 的部署选项,既是协议又是应用服务器;应用服务器可以提供 uWSGI、FastCGI 和 HTTP 协议。

    要创建此文件,请运行以下命令:

    sudo nano uwsgi.ini
    

    接下来,向文件添加以下内容以配置 uWSGI 服务器:

    [uwsgi]
    module = main
    callable = app
    master = true
    

    此代码定义了 Flask 应用程序将从中提供的模块。在这种情况下,这是 main.py 文件,在此处被引用为 main。callable 选项指示 uWSGI 使用主应用程序导出的 app 实例。master 选项允许您的应用程序保持运行,因此即使重新加载整个应用程序也几乎没有停机时间。

    接下来,创建 main.py 文件,这是应用程序的入口点。入口点指示 uWSGI 如何与应用程序交互。

    sudo nano main.py
    

    接下来,将以下内容复制并粘贴到文件中。这导入了之前创建的应用程序包中名为 app 的 Flask 实例。

    from app import app
    

    最后,创建一个 requirements.txt 文件,以指定 pip 包管理器将安装到您的 Docker 部署中的依赖项:

    sudo nano requirements.txt
    

    添加以下行以将 Flask 添加为依赖项:

    Flask==1.0.2
    

    这指定要安装的 Flask 版本。在编写本教程时,1.0.2 是最新的 Flask 版本。您可以在 Flask 的官方网站上查看更新。

    保存并关闭文件。您已成功设置了 Flask 应用程序,并准备设置 Docker。

    步骤 2 —— 设置 Docker

    在这一步中,您将创建两个文件,Dockerfile 和 start.sh,以创建您的 Docker 部署。Dockerfile 是一个文本文档,其中包含用于组装镜像的命令。start.sh 文件是一个 shell 脚本,将从 Dockerfile 构建一个镜像,并创建一个容器。

    首先,创建 Dockerfile。

    sudo nano Dockerfile
    

    接下来,向 Dockerfile 添加您所需的配置。这些命令指定了镜像的构建方式以及包含的额外要求。

    FROM tiangolo/uwsgi-nginx-flask:python3.6-alpine3.7
    RUN apk --update add bash nano
    ENV STATIC_URL /static
    ENV STATIC_PATH /var/www/app/static
    COPY ./requirements.txt /var/www/requirements.txt
    RUN pip install -r /var/www/requirements.txt
    

    在这个例子中,Docker 镜像将基于现有的 tiangolo/uwsgi-nginx-flask 镜像构建,您可以在 DockerHub 上找到它。这个特定的 Docker 镜像是一个不错的选择,因为它支持多种 Python 版本和操作系统镜像。

    前两行指定了您将用于运行应用程序和安装 bash 命令处理器和 nano 文本编辑器的父镜像。它还安装了 git 客户端,用于从版本控制托管服务(如 GitHub、GitLab 和 Bitbucket)拉取和推送代码。ENV STATIC_URL /static 是特定于这个 Docker 镜像的环境变量,它定义了静态文件夹,其中包含所有的资源,如图片、CSS 文件和 JavaScript 文件。

    最后两行将 requirements.txt 文件复制到容器中,以便执行它,并解析 requirements.txt 文件以安装指定的依赖项。

    在添加配置后保存并关闭文件。

    有了您的 Dockerfile,您几乎可以准备编写 start.sh 脚本来构建 Docker 容器了。在编写 start.sh 脚本之前,首先确保您有一个可用的端口用于配置。要检查端口是否空闲,请运行以下命令:

    sudo nc localhost 56733 < /dev/null; echo $?
    

    如果上述命令的输出是 1,则该端口是空闲且可用。否则,您需要选择一个不同的端口用于 start.sh 配置文件。

    一旦找到一个可用的端口,创建 start.sh 脚本:

    sudo nano start.sh
    

    start.sh 脚本是一个 shell 脚本,将从 Dockerfile 构建一个镜像,并从生成的 Docker 镜像创建一个容器。向新文件添加您的配置:

    #!/bin/bash
    app="docker.test"
    docker build -t ${app} .
    docker run -d -p 56733:80 \
      --name=${app} \
      -v $PWD:/app ${app}
    

    第一行被称为 shebang。它指定这是一个 bash 文件,并将作为命令执行。下一行指定您要给镜像和容器的名称,并保存为名为 app 的变量。下一行指示 Docker 从当前目录中的 Dockerfile 构建一个镜像。在这个例子中,这将创建一个名为 docker.test 的镜像。

    最后三行创建一个名为 docker.test 的新容器,该容器在端口 56733 上公开。最后,它将当前目录链接到 Docker 容器上的 /var/www 目录。

    您使用 -d 标志以守护模式启动容器,或作为后台进程。您包括 -p 标志将服务器上的端口绑定到 Docker 容器上的特定端口。在这种情况下,您将端口 56733 绑定到 Docker 容器上的端口 80。-v 标志指定要挂载到容器上的 Docker 卷,在这种情况下,您将整个项目目录挂载到 Docker 容器上的 /var/www 文件夹。

    执行 start.sh 脚本以创建 Docker 镜像,并从生成的镜像构建一个容器:

    sudo bash start.sh
    

    脚本运行完成后,使用以下命令列出所有正在运行的容器:

    sudo docker ps
    

    您将收到显示容器的输出:

    CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS              PORTS                            NAMES
    58b05508f4dd        docker.test         "/entrypoint.sh /sta…"   12 seconds ago      Up 3 seconds       443/tcp, 0.0.0.0:56733->80/tcp   docker.test
    

    您将发现 docker.test 容器正在运行。现在它正在运行,访问浏览器中指定端口的 IP 地址:http://ip-address:56733

    您将看到类似以下内容的页面:

    !the home page

    在这一步中,您已成功在 Docker 上部署了您的 Flask 应用程序。接下来,您将使用模板向用户显示内容。

    步骤 3 — 提供模板文件

    模板是用于向访问应用程序的用户显示静态和动态内容的文件。在这一步中,您将创建一个 HTML 模板,为应用程序创建一个主页。

    首先,在 app/templates 目录中创建一个 home.html 文件:

    sudo nano app/templates/home.html
    

    添加模板代码。这段代码将创建一个包含标题和一些文本的 HTML5 页面。

    
       
      
        
        
        Welcome home
      
      
      
        

    Home Page

    This is the home page of our application.

    添加完模板后保存并关闭文件。

    接下来,修改 app/views.py 文件以提供新创建的文件:

    sudo nano app/views.py
    

    首先,在文件开头添加以下行以从 Flask 导入 render_template 方法。该方法解析 HTML 文件以向用户呈现网页。

    from flask import render_template
    ...
    

    在文件末尾,还需要添加一个新的路由以渲染模板文件。该代码指定当用户访问应用程序上的 /template 路由时,将为他们提供 home.html 文件的内容。

    ...
    @app.route('/template')
    def template():
        return render_template('home.html')
    

    更新后的 app/views.py 文件如下:

    from flask import render_template
    from app import app 
    @app.route('/')
    def home():
        return "Hello world!"
    @app.route('/template')
    def template():
        return render_template('home.html')
    

    完成后保存并关闭文件。

    为使这些更改生效,您需要停止并重新启动 Docker 容器。运行以下命令以重建容器:

    sudo docker stop docker.test && sudo docker start docker.test
    

    访问 http://your-ip-address:56733/template 查看新模板的提供情况。

    !homepage

    在这一步中,您创建了一个 Docker 模板文件,用于为应用程序的访问者提供服务。在下一步中,您将了解如何在无需重新启动 Docker 容器的情况下使应用程序的更改生效。

    步骤 4 — 更新应用程序

    有时您需要对应用程序进行更改,无论是安装新要求、更新 Docker 容器还是 HTML 和逻辑更改。在本节中,您将配置 touch-reload,以便无需重新启动 Docker 容器即可进行这些更改。

    Python 的 自动重新加载 监视整个文件系统的更改,并在检测到更改时刷新应用程序。自动重新加载在生产环境中是不鼓励的,因为它很快就会变得资源密集。在这一步中,您将使用 touch-reload 来监视特定文件的更改,并在更新或替换文件时重新加载。

    要实现这一点,首先打开您的 uwsgi.ini 文件:

    sudo nano uwsgi.ini
    

    然后在文件末尾添加以下突出显示的行:

    module = main
    callable = app
    master = true
    touch-reload = /app/uwsgi.ini
    

    这指定了一个文件,将修改该文件将触发整个应用程序的重新加载。完成更改后保存并关闭文件。

    为了演示这一点,对应用程序进行小的更改。首先打开您的 app/views.py 文件:

    sudo nano app/views.py
    

    替换 home 函数返回的字符串:

    from flask import render_template
    from app import app
    @app.route('/')
    def home():
        return "There has been a change"
    @app.route('/template')
    def template():
        return render_template('home.html')
    

    在进行更改后保存并关闭文件。

    接下来,如果您在浏览器中打开应用程序的主页 http://ip-address:56733,您会注意到更改没有反映出来。这是因为重新加载的条件是对 uwsgi.ini 文件的更改。要重新加载应用程序,使用 touch 激活条件:

    sudo touch uwsgi.ini
    

    再次在浏览器中重新加载应用程序主页。您会发现应用程序已经包含了更改:

    !Homepage Updated

    在这一步中,您设置了一个 touch-reload 条件,以在进行更改后更新您的应用程序。

    结论

    在本教程中,您创建并部署了一个 Flask 应用程序到一个 Docker 容器中。您还配置了 touch-reload,以在无需重新启动容器的情况下刷新应用程序。

    有了您在 Docker 上的新应用程序,现在可以轻松扩展。要了解更多关于使用 Docker 的信息,请查看它们的官方文档。