如何在Ubuntu20.04上使用Docker构建和部署Flask应用程序

来自菜鸟教程
跳转至:导航、​搜索

作为 Write for DOnations 计划的一部分,作者选择了 Tech Education Fund 来接受捐赠。

介绍

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

Flask 是一个基于 Python 构建的 Web 微框架。 它被称为微框架,因为它不需要特定的工具或插件即可运行。 Flask 框架轻量级且灵活,但高度结构化,使其特别适用于用 Python 编写的小型 Web 应用程序。

使用 Docker 部署 Flask 应用程序将允许您以最少的重新配置跨不同服务器复制应用程序。

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

先决条件

要学习本教程,您将需要以下内容:

第 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会在制作statictemplates目录的过程中创建app父目录。

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

static 目录是图片、CSS 和 JavaScript 文件等资产所在的位置。 templates 目录用于放置项目的 HTML 模板。

现在基本文件夹结构已经完成,您需要创建运行 Flask 应用程序所需的文件。 首先,使用 nano 或您选择的文本编辑器在 app 目录中创建一个 __init__.py 文件。 该文件告诉 Python 解释器 app 目录是一个包,应该这样对待。

运行以下命令来创建文件:

sudo nano app/__init__.py

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

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

/var/www/TestApp/app/__init__.py

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

添加该代码后,保存并关闭文件。 您可以按 Ctrl+X, 保存并关闭文件,然后在出现提示时按 YEnter

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

sudo nano app/views.py

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

/var/www/TestApp/app/views.py

from app import app

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

函数上方的 @app.route 行称为 decorator。 装饰器是 Flask 广泛使用的 Python 语言约定; 它们的目的是修改紧随其后的功能。 在这种情况下,装饰器告诉 Flask 哪个 URL 将触发 home() 函数。 home函数返回的hello world文本将在浏览器上显示给用户。

准备好 views.py 文件后,您就可以创建 uwsgi.ini 文件了。 该文件将包含我们应用程序的 uWSGI 配置。 uWSGI 是 Nginx 的一个部署选项,它既是协议又是应用服务器; 应用服务器可以服务于 uWSGI、FastCGI 和 HTTP 协议。

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

sudo nano uwsgi.ini

接下来,将以下内容添加到您的文件中以配置 uWSGI 服务器:

/var/www/TestApp/uwsgi.ini

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

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

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

sudo nano main.py

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

/var/www/TestApp/main.py

from app import app

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

sudo nano requirements.txt

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

/var/www/TestApp/requirements.txt

Flask>=2.0.2

这指定要安装的 Flask 版本。 在编写本教程时,2.0.2 是最新的 Flask 版本,指定 >=2.0.2 将确保您获得 2.0.2 或更高版本。 因为您在本教程中制作了一个基本的测试应用程序,所以语法不太可能由于 Flask 的未来更新而过时,但如果您希望安全并且仍然接收次要更新,您可以指定您不要想要通过指定类似 Flask>=2.0.2,<3.0 来安装未来的主要版本。 您可以在 Flask 的官方网站上查看更新,或者在 Flask 库 的 Python 包索引的登录页面上查看更新。

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

第 2 步 — 设置 Docker

在此步骤中,您将创建两个文件,Dockerfilestart.sh,以创建 Docker 部署。 Dockerfile 是一个文本文档,其中包含用于组合图像的命令。 start.sh 文件是一个 shell 脚本,它将构建映像并从 Dockerfile 创建一个容器。

首先,创建 Dockerfile

sudo nano Dockerfile

接下来,将所需的配置添加到 Dockerfile。 这些命令指定如何构建映像,以及将包含哪些额外要求。

/var/www/TestApp/Dockerfile

FROM tiangolo/uwsgi-nginx-flask:python3.8-alpine
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 后,您几乎可以编写构建 Docker 容器的 start.sh 脚本了。 在编写 start.sh 脚本之前,首先确保您有一个开放的端口可以在配置中使用。 要检查端口是否空闲,请运行以下命令:

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

如果上述命令的输出是 1,那么端口是空闲且可用的。 否则,您将需要在 start.sh 配置文件中选择要使用的不同端口。

找到要使用的开放端口后,创建 start.sh 脚本:

sudo nano start.sh

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

/var/www/TestApp/start.sh

#!/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 处公开。 最后,它将当前目录链接到容器的 /var/www 目录。

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

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

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

sudo bash start.sh

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

sudo docker ps

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

OutputCONTAINER 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

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

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

第 3 步 — 提供模板文件

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

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

sudo nano app/templates/home.html

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

/var/www/TestApp/app/templates/home.html

<!doctype html>

<html lang="en-us">   
  <head>
    <meta charset="utf-8">
    <meta http-equiv="x-ua-compatible" content="ie=edge">
    <title>Welcome home</title>
  </head>
  
  <body>
    <h1>Home Page</h1>
    <p>This is the home page of our application.</p>
  </body> 
</html>

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

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

sudo nano app/views.py

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

/var/www/TestApp/app/views.py

from flask import render_template
...

在文件末尾,您还将添加一个新路由来呈现模板文件。 此代码指定只要用户访问应用程序上的 /template 路由,就会为用户提供 home.html 文件的内容。

/var/www/TestApp/app/views.py

...

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

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

/var/www/TestApp/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 访问您的应用程序以查看正在提供的新模板。

在此,您创建了一个 Docker 模板文件来为您的应用程序上的访问者提供服务。 在下一步中,您将看到您对应用程序所做的更改如何在无需重新启动 Docker 容器的情况下生效。

第 4 步 — 更新应用程序

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

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

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

sudo nano uwsgi.ini

接下来,将突出显示的行添加到文件末尾:

/var/www/TestApp/uwsgi.ini

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

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

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

sudo nano app/views.py

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

/var/www/TestApp/app/views.py

from flask import render_template
from app import app

@app.route('/')
def home():
    return "<b>There has been a change</b>"

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

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

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

sudo touch uwsgi.ini

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

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

结论

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

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