F

Flask 和 Docker 入门 🐳🚀 flask_starter_app

2025-06-09

Flask 和 Docker 入门

flask_starter_app

过去几周,我开发了几个 Flask 应用,涵盖了各种用例。目的是复习 Flask 的知识,并学习生产应用的正确结构。后来,我接到一个挑战,要在一个入门项目中使用 Docker 和 Flask 应用,并撰写相关文章。这对我来说是一个巩固知识的绝佳机会,同时也提供了我自己版本的快速入门指南。

受众和目标

本文面向寻求 Docker 和 Flask 使用指南的初级开发者。不过,中级开发者也可以从中学习一些知识。我也会尽力指出我在这个项目中遇到的问题。

本文旨在开发一个简单的 Flask 应用程序,并将该应用程序 docker 化并将代码推送到 GitHub。

入门先决条件

为了有效地遵循这篇文章和后续代码,您需要满足以下先决条件。

  • Python 和 pip(我目前使用的是 3.9.7)任何高于 3.7 的版本都应该可以工作。
  • 您的系统中已安装 Git。请查看适合您系统的说明。
  • 系统上的 Docker。安装说明
  • 终端。

初始设置

这些说明已验证可在大多数 Unix 系统上运行。注意: Windows 系统的具体实现可能有所不同。

  • 创建一个新目录并进入该目录。
mkdir flask_starter_app && cd flask_starter_app
Enter fullscreen mode Exit fullscreen mode
  • 为项目创建一个新的虚拟环境。或者,激活您首选的虚拟环境。
  • 继续使用 pip 安装我们所需的模块。我们将使用 flask、flask-bootstrap 和 jikanpy
  • 将已安装的包保存在需求文件中。
  python3 -m venv venv
  source venv/bin/activate
  pip install flask flask-bootstrap jikanpy 
  pip freeze > requirements.txt
Enter fullscreen mode Exit fullscreen mode

我们正在为项目安装主要的 Flask 模块。Flask-Bootstrap 将帮助我们将 Bootstrap 集成到应用中进行样式设置。我们还安装了 Jikanpy,它是Jikan Api
的 Python 包装器,Jikan Api 是非官方的MyAnimeList Api。

希望一切都安装成功。或者检查代码

GitHub 徽标 KenMwaura1 / flask_starter_app

利用docker的简单Flask启动应用程序

flask_starter_app

简单的 Flask 启动应用程序利用 docker 使用jikanpy(myanimelist 非官方 api)展示季节性动漫。

文章

此处链接至文章

Docker 快速入门

建议使用 Docker,因为它可以保证应用程序使用兼容版本的 Python 和 Node 运行

应用程序内部有一个 Dockerfile 可以帮助您入门。

构建应用程序的开发版本

docker build -t flask-starter-app .   
Enter fullscreen mode Exit fullscreen mode

运行应用程序

 docker run --name=flask-app -p 5001:5000 -t -i flask-starter-app  
Enter fullscreen mode Exit fullscreen mode

如果一切顺利,应用程序应该在localhost:5001上运行




都是容器

Docker 指的是开源容器化平台。
容器是标准化的可执行组件,它将应用程序源代码与操作系统级依赖项和库结合在一起。我们可以不使用 Docker 来创建容器,但它提供了一种一致、更简单、更安全的容器构建方式。从软件开发到软件交付,甚至测试,容器的使用量飞速增长的主要原因之一是其易用性和整个工作流程的可重复性。

以前,开发人员使用云端虚拟机或自托管服务器来运行应用程序和工作负载。
然而,由于操作系统差异或依赖项的差异,从开发到生产有时会出现故障。容器使我们能够将代码、文件结构、依赖项等打包部署到服务器上,并以最少的更改使其按预期运行。

术语

这里我们将介绍一些与 Docker 相关的工具和术语:

DockerFile

Docker容器最初是一个文本文件,其中包含有关如何构建镜像的所有相关说明。Dockerfile自动执行镜像创建过程,并包含一系列供 Docker 引擎组装镜像的 CLI 指令。

Docker 镜像

Docker 镜像包含运行应用程序所需的所有源代码、库和依赖项。从零开始构建 Docker 镜像是完全可行的,但开发人员可以利用通用存储库来下载常用软件和工具的预构建镜像。Docker
镜像由层组成,每次从镜像构建容器时,都会添加一个新层,成为镜像的最新版本。您可以使用单个镜像运行多个实时容器。

Docker 中心

这是一个 Docker 镜像的公共仓库,包含超过 10 万个容器镜像。它包含来自商业供应商、开源项目甚至个人开发者的软件容器。

Docker 守护进程

指在您的系统中运行的用于创建 Docker 镜像和容器的服务。守护进程从客户端接收命令并执行它们。

Docker 注册表

这是一个开源的可扩展 Docker 镜像存储和分发系统。使用 git(一个版本控制系统),镜像仓库中的镜像版本可以通过标签进行识别,从而追踪镜像的版本。

让我们开始建造吧!

烧瓶

Flask以微框架而自豪,因此它仅提供简单的开箱即用配置。然而,它允许广泛的自定义配置选项。这让你可以
自由地从简单的框架入手,并随着开发规模的扩大添加各种扩展程序。

我们正在构建什么

今天我们将构建一个简单的 Web 应用,用于显示 MyAnimeList 上当前季度的动漫。如果你关注我的Twitter,你就会知道我是个超级动漫迷。MyAnimeList 是一个提供信息、评论和排名的平台,因此它是最佳选择。然而,它没有 API 或 SDK 来访问其内容。通常情况下,我们必须抓取网站数据,幸运的是,优秀的社区创建了Jikan Api 以及jikanpy,它是该 API 的 Python 包装器。

代码在哪儿?

现在希望您已完成“先决条件”部分中的上述步骤。确保您的虚拟环境已激活。在我们的flask-starter-app目录中创建run.py文件。

# run.py
from app import app


if __name__ == '__main__':
    app.run()

Enter fullscreen mode Exit fullscreen mode

此文件将用于我们的应用。首先,我们从 app 目录导入应用实例。该目录目前尚不存在。让我们创建它。
创建 app 目录,并在其中创建:

  • 模板文件夹
  • __init__.py文件
  • models.py文件
  • views.py文件
  • anime_requests.py文件

您的文件夹结构现在应如下所示:

python-projects $ tree flask_starter_app
flask_starter_app
├── app
│   ├── __init__.py
│   ├── anime_request.py
│   ├── models.py
│   ├── views.py
│   └── templates
│       └── index.html
└── requirements.txt
└── run.py
└── venv


2 directories, 7 files
Enter fullscreen mode Exit fullscreen mode

在文件内__init__.py添加以下代码:

# app/__init__.py

from flask import Flask
from flask_bootstrap import Bootstrap

bootstrap = Bootstrap()
app = Flask(__name__)
bootstrap.init_app(app)
from . import views

Enter fullscreen mode Exit fullscreen mode

前两行分别从各自的模块导入 Flask 和 Bootstrap 类。然后,我们实例化 Bootstrap 类并将其赋值给 bootstrap 变量。app 变量包含 Flask 类的一个实例,并将名称作为第一个参数传递,以引用我们的应用。然后,我们通过调用该init_app()方法并将我们的应用作为参数传递,来初始化 bootstrap。
最后,我们从当前目录导入 views 文件。

课程正在进行中

文件内models.py添加以下代码:

# app/models.py
from dataclasses import dataclass

@dataclass
class Anime:
    """
    class to model anime data
    """
    mal_id: int
    url: str
    title: str
    image_url: str
    synopsis: str
    type: str
    airing_start: str
    episodes: int
    members: int
Enter fullscreen mode Exit fullscreen mode

该文件将保存我们所有的模型,这里我们创建一个 Anime 类来保存来自 Api 的数据。我们从dataclasses模块导入了 dataclass 装饰器。这将使我们能够访问各种特殊方法,从而使我们的代码简洁明了。我们将装饰器附加到类上,然后继续定义来自 Api 的数据结构。查看文档了解更多信息。

请求,请求...

将以下内容添加到anime_request.py文件:

# app/anime_request.py
from jikanpy import Jikan
from .models import Anime

jikan = Jikan()
# function to get seasonal anime
def get_season_anime():
    """
    function to get the top anime from My anime list
    :return: list of anime
    """
    season_anime_request = jikan.season()
    season_anime = []
    if season_anime_request['anime']:
        response = season_anime_request['anime']

        for anime in response:
            mal_id = anime.get('mal_id')
            url = anime.get('url')
            title = anime.get('title')
            image_url = anime.get('image_url')
            synopsis = anime.get('synopsis')
            type = anime.get('type')
            airing_start = anime.get('airing_start')
            episodes = anime.get('episodes')
            members = anime.get('members')

            new_anime = Anime(mal_id, url, title, image_url, synopsis, type, airing_start, episodes, members)
            season_anime.append(new_anime)

    return season_anime
Enter fullscreen mode Exit fullscreen mode

在上面的代码中,我们从 jikanpy 模块导入了 Jikan 类,这将使我们能够访问各种方法向 Jikan Api 发出请求。我们还从models文件中导入了 Anime 类。我们创建一个变量 jikan 并为其分配一个 Jikan 类的实例。
现在,我们定义get_season_anime函数来向 Jikan Api 发出请求并将其附加到列表中。我们创建一个从 Jikan 类season_anime_request调用season方法的变量。它接受两个参数:年份和季节,当您想要从年份甚至季节中检索特定数据时,这非常方便。在我们的例子中,我们没有指定以获取当前季节的动漫。然后,我们定义一个空列表来保存我们的数据。

season 方法返回一个包含各种键值对的字典。我们需要的数据是anime键对应的值,它是一个字典列表。我们添加一个 if 语句来检查所需的键是否存在,然后循环遍历所有值。我们创建适当的变量来引用响应中的数据。我们
创建一个new_animeAnime 类的实例变量。我们将类添加到空列表中,最后返回类列表。

每日浏览量

在您的文件中添加以下代码views.py

from flask import render_template
from .anime_request import get_season_anime

from . import app


@app.route('/', methods=['GET', 'POST'])
def index():
    """
    root page view that returns the index page and its data
    :return: index template
    """
    season_anime = get_season_anime()
    return render_template('index.html', season_anime=season_anime)

Enter fullscreen mode Exit fullscreen mode

此文件保存了我们 Flask 应用程序的路由。目前我们只有一个,欢迎添加更多。我们首先导入render_template它,它将在浏览器中渲染我们的 HTML 页面并传递任何所需的参数。我们还get_season_anime从 anime_request 文件中导入了函数。我们还从__init__.py文件中导入了我们的应用程序,这样就可以使用@app暴露路由方法的装饰器了。这会注册作为参数传递的路由以及该路由允许使用的方法。

我们定义了一个index函数,该函数将在用户打开根路由时调用。在函数内部,我们定义了 season_anime 变量,用于保存 Anime 类的实例列表。最后,我们调用该render_template函数,并将 templates 文件夹中的 index.html 文件以及 season_anime 变量传递给模板。

将以下内容添加到 templates 文件夹内的 index.html 文件中:

<!--app/templates/index.html -->
{% extends 'bootstrap/base.html' %}

{% block navbar %}
    <div class="navbar navbar-inverse" role="navigation">
        <div class="container-fluid">
            <div class="navbar-header">
                <button type="button" class="navbar-toggle" data-toggle="collapse" data-target=".navbar-collapse">
                    <span class="sr-only">Toggle navigation</span>
                    <span class="icon-bar"></span>
                    <span class="icon-bar"></span>
                    <span class="icon-bar"></span>
                </button>
                <a class="navbar-brand" href="/"> Anime Watchlist </a>
            </div>
            <div class="navbar-collapse collapse">
                <ul class="nav navbar-nav">
                    <li><a href="/">Home</a></li>
                </ul>
            </div>
        </div>
    </div>
{% endblock %}

{% block content %}
    {% for anime in season_anime %}
        <div class="card-group col-xs-12 col-sm-4 col-md-2 col-lg-2">
            <div class="card">
                <img src="{{ anime.image_url }}" alt="{{ anime.title }} poster" class="img-responsive"
                     style="max-height: 30rem">
                <li class="text-left ">
                    <a href="/anime/{{anime.mal_id}}">
                        {{ anime.title|truncate(30)}}</a>
                    <p> Episodes : {{ anime.episodes }}</p>
                    <p> date started airing: {{ anime.airing_start | truncate(13) }}</p>
                </li>
            </div>

        </div>
    {% endfor %}
{% endblock %}
Enter fullscreen mode Exit fullscreen mode

Flask 使用jinja模板引擎。这使我们能够使用一系列高级功能。在本例中,我们扩展了包含所有 Bootstrap 样式的基础 html 文件,这使得我们能够拥有一个适用于所有页面的基本结构。我们还使用 special 来{% %}定义一个特殊的导航栏块。
通常,它会在其自己的文件中设置并导入,但在这里我们直接在这里使用。我们在其中定义一个内容块,并循环遍历视图文件中传递的 season_anime 参数。对于每个值,我们渲染一张卡片,其中包含标题、图片、剧集数量以及开始播出的日期。

打开终端并运行python run.py您的应用程序应如下所示:

将一切 Docker 化

现在我们有了一个功能齐全的 Flask 应用,让我们将它 docker 化。在应用(flask_starter_app)的根目录下,创建一个Dockerfile
添加以下配置:

#Dockerfile 

FROM python:3.9.7

MAINTAINER Ken Mwaura "kemwaura@gmail.com"

COPY ./requirements.txt /app/requirements.txt

RUN pip install -r requirements.txt

COPY . /app

WORKDIR app

ENV FLASK_APP=run.py

ENV FLASK_ENV=development

EXPOSE 5001:5000

CMD ["flask", "run", "--host", "0.0.0.0"]
Enter fullscreen mode Exit fullscreen mode

第一行设置了要构建的基础镜像,在本例中,我们使用 Python 3.9.7 镜像来镜像开发
环境。让我们回顾一下以下 Docker 指令:

  1. MAINTAINER 设置镜像的 Author 字段(推送到 Docker Hub 时很有用)。
  2. COPY 将文件从第一个参数(源。)复制到目标参数(在本例中为 /app)。
  3. RUN 使用 pip 从需求文件中安装所需的依赖项。
  4. WORKDIR 设置工作目录(以下所有指令均在此目录内操作);您可以根据需要随时使用 WORKDIR。
  5. ENV 将环境变量设置FLASK_APP为 run.py 文件。此 flask cli 会运行该文件。
  6. ENV 将环境变量设置FLASK_ENV为 development。这告诉 Flask 以开发模式运行应用程序。
  7. EXPOSE 告诉 docker 将端口 5001 映射到我们的应用程序正在运行的端口 5000。
  8. CMD 告诉 Docker 应该执行什么来运行我们的应用程序。在我们的例子中,它是 flask run 命令和指定的主机。

构建图像

现在我们给出了 Dockerfile,让我们检查它是否正确构建

docker build -t flask-starter-app .    
Enter fullscreen mode Exit fullscreen mode

docker-build

运行容器

构建完成后,运行容器

 docker run --name=flask-app -p 5001:5000 -t -i flask-starter-app  
Enter fullscreen mode Exit fullscreen mode

转到localhost:5001,您应该会看到您的应用程序正在运行,如下所示:

docker 截图

更多信息

确保你使用的端口正确。Flask 默认运行在 5000 端口(而不是像 Django 那样的 8000 端口,也不是像 Apache 那样的 80 端口)。更多信息,
请参阅“绑定 Docker 端口” 。

希望你喜欢这篇文章,并受到启发,进一步扩展它。继续编码!欢迎在下方留言或
在 Twitter 上联系:Ken_Mwaura1

请我喝杯咖啡

鏂囩珷鏉ユ簮锛�https://dev.to/ken_mwaura1/getting-started-with-flask-and-docker-3ie8
PREV
JavaScript 的弱者:第一部分 - WeakMap
NEXT
对于你来说,React Hooks 有什么困难?