如何构建和部署 Python 应用程序?

Back4app Python 封面

Python 是一种自由、开源的编程语言,应用广泛。它由 Guido Van Rossum 于 1991 年创建,自那时起已发展成为最流行的编程语言之一。

在本文中,我们将讨论 Python 及其优缺点、用例和部署选项。此外,我们还将演示如何构建、dockerize 和部署一个简单的 Python 应用程序到Back4app 容器

继续阅读,了解如何构建和部署 Python 项目。

Python 概述

Python 是一种高级通用编程语言。它是一种解释型动态类型语言,既可用于编写脚本,也可用于编程。由于 Python 拥有丰富的标准库,开发人员通常将 Python 描述为一种 “包含电池 “的语言。

该语言首次出现于 1991 年,由于其语法简单易读,自此成为最流行、最受欢迎的编程语言之一。蒂姆-彼得斯(Tim Peters)撰写的《Python 禅》一书中介绍了Python 的主要设计原则。

Python 拥有庞大的开发者社区和随时可用的软件包。如今,Python 被广泛应用于服务器管理、科学计算、机器学习、构建 RESTful API 等领域。Python 可以说是现代开发人员的必备技能之一。

Python 的优势

Python 的优势

易于使用

Python 是最容易使用和学习的语言之一。它的学习曲线几乎不存在,因为它的主要设计理念强调简洁性和代码的可读性。Python 在教育领域也很受欢迎,因为它是学习基本编程概念的好工具。

快速发展

Python 的另一大优点是可以快速开发。它是一种非常适合原型设计和快速完成工作的语言。使用 Python,您不必担心复杂的语法、内存分配等问题,您可以专注于您的应用程序。

多用途

Python 是最通用的编程语言之一。它被广泛应用于各个行业和领域。Python 的一些应用领域包括会计、科学研究、数据科学、网络开发、游戏开发和自动化。

便携式

Python 是一种可移植语言,因为它是解释型语言(如 Java),而不是编译型语言(如 C++)。这意味着在一个平台上编写的代码可以很容易地移植到另一个平台上。这使得 Python 成为需要支持多平台(包括 Windows、macOS、Linux 和 Android)的开发人员的热门选择。

丰富的图书馆

Python 编程语言拥有庞大的开发者社区。几乎你能想象到的任何东西都有相应的库。其中最受欢迎的库包括

这还只是皮毛。如果您想查看所有 Python 软件包,请访问GitHub 上的awesome-python

动态类型

Python 是一种动态类型语言,这意味着在创建变量时不必声明数据类型。只有在执行代码时才会分配变量类型。

动态类型语言虽然很棒,但有时也是一把双刃剑。与静态类型语言相比,动态类型语言通常更容易出错,内存控制水平也更低。

可扩展

如果您的项目要求包括繁重的计算任务,您可以用 C 或 C++ 等速度更快的语言编写这些任务,然后从 Python 代码中调用它们。

要了解从 Python 调用 C/C++ 的更多信息,请查看这篇精彩文章

Python 的局限性

Python 的缺点

糟糕的表现

与其他编程语言相比,Python 的运行速度相对较慢。最影响其性能的两个因素是 Python 是一种解释型语言和动态类型语言。如果您的项目要求包括重计算或多线程,Python 可能不是最合适的工具。您最好使用 C、C++ 或其他编译语言。

内存密集型

与使用静态类型语言编写的程序相比,Python 程序通常会消耗更多的内存,并且对内存的控制水平较低。如果您的应用程序需要提高内存效率,这可能会带来一些问题。

容易出现运行时错误

Python 作为一种解释型语言,更容易出现运行时错误。因为没有编译过程,所以无法在编译时发现错误。此外,Python 是动态类型的,这意味着开发人员可以随时更改变量的类型。这有时会导致错误,并要求开发人员注意变量类型。

全局译员锁 (GIL)

Python 中的全局解释器锁 (GIL) 是一种机制,可确保每次只有一个线程执行 Python 字节码。虽然这简化了语言的实现,并为某些类型的程序提供了一些性能优势,但也限制了在 CPU 绑定应用程序中充分利用多核处理器的能力。

未优化数据库访问

由于缺乏像 Java Database Connectivity(JDBC)这样功能强大且用户友好的接口,在 Python 应用程序中使用数据库可能更具挑战性。虽然 Python 仍可用于涉及简单读写任务的数据库操作,但对于需要处理大型复杂数据库的应用程序来说,它可能不是最合适的选择。

Python 部署选项

Python 应用程序可以部署到多个云平台上。一般来说,我们可以将其分为以下三类:

  • 基础设施即服务(IaaS)
  • 平台即服务(PaaS)
  • 容器即服务(CaaS)

IaaS 的抽象程度最低,CaaS 的抽象程度最高。此外还有传统的托管服务,但我相信大家对此已经很熟悉了。

基础设施即服务(IaaS)

基础设施即服务或 IaaS 是一种云计算模式,由第三方供应商通过互联网提供虚拟化计算资源,如服务器、存储、操作系统和网络。所提供的资源可通过高级仪表板或高级应用程序接口进行管理,使客户能够完全控制其整个基础设施。

IaaS 的主要优点是可扩展性、节约成本、增强支持、性能和安全性。大多数 IaaS 提供商采用的支付结构都是现收现付系统,只对使用的资源收费。

IaaS 是最灵活的云计算模式,自 2010 年代初发布以来一直是最受欢迎的选择。它最大的缺点是客户要对自己的应用程序、操作系统和数据负全责。

平台即服务(PaaS)

平台即服务(PaaS)是一种云计算服务,为用户提供一个云环境来创建、管理和交付应用程序。PaaS 为应用程序的开发、定制和测试提供预建工具。有了 PaaS,用户可以专注于自己的应用程序,因为服务提供商会负责底层基础设施,包括服务器、操作系统、软件、备份等。

PaaS 的优势包括更快的上市速度、更高的安全性、成本效益、可扩展性、高可用性,而且通常需要的代码更少。不过,也有一些缺点。主要的三个缺点是缺乏灵活性、缺乏控制和被供应商锁定的风险。尽管如此,PaaS 仍能让用户以更少的管理开销更快地构建应用程序。

容器即服务(CaaS)

容器即服务(CaaS)是一种云计算模式,允许企业通过使用容器虚拟化技术上传、运行、扩展和管理容器。CaaS 供应商抽象出了许多必要的工作,如基础设施、操作系统、软件、容器化引擎等。

容器的好处在于,一旦您的应用程序被容器化,您就可以将其部署到几乎任何地方,并保证以相同的方式运行。如果需要,您可以从一家 CaaS 供应商转移到另一家。CaaS 客户通常按容器计费(基于容器规格)。

与 IaaS 和 PaaS 相比,CaaS 的成本通常较高,灵活性和控制能力较低,而且需要进行一些初始工作,如应用程序的容器化等。不过,一旦你的应用程序被容器化,它也是比较容易使用的云计算模式之一。

要了解有关容器的更多信息,请查看《云计算中的容器是什么?

Python 部署流程

在本节教程中,我们将逐步演示如何构建和部署一个简单的 Python 应用程序。网络应用程序将使用Flask 框架实现,并部署到Back4app 容器中。

先决条件

  • 具有使用 Python 和 Flask 的经验
  • 对 Docker 有基本了解
  • 已安装Python3.8 或更高版本和Docker Desktop

什么是 Back4app Containers?

Back4app Containers是一个免费的开源平台,可让您使用全球分布式容器部署和扩展应用程序。

有了 Back4app Containers,您就可以专注于构建软件并快速交付,而无需担心 DevOps。

该平台与 GitHub 紧密集成,并配备了内置的 CI/CD 系统。使用 Back4app Containers,您可以在几分钟内将应用程序上线。

为什么使用 Back4app 容器?

  • 与 GitHub 完美集成
  • 扩展和零停机部署
  • 使用极为方便
  • 卓越的客户支持

项目介绍

在本文中,我们将构建并部署一个简单的 RESTful API,作为 TODO 列表。Web API 将允许用户执行基本的 CRUD 操作,如添加任务、删除任务、标记任务为已完成等。

我们将首先处理源代码,然后将其部署到Back4app Flask 容器中。网络应用程序将使用Flask 网络框架实现。

您可以跳过 “代码应用程序 “部分,使用自己的 Python 项目进行学习。

代码应用程序

初始项目

首先,为 Flask 应用程序创建一个专用目录并导航至该目录:

$ mkdir flask-todo
$ cd flask-todo

创建虚拟环境并激活:

$ python3 -m venv venv && source venv/bin/activate

接下来,通过 pip 安装Flask

$ (venv) pip install Flask==2.2.2

为了确保一切正常,让我们用下面的内容替换app.py的内容:

# app.py

from flask import Flask

app = Flask(__name__)


@app.route('/')
def index_view():
    return {
        'detail': 'Hello world'
    }

这段代码初始化了 Flask,并创建了一个返回消息的简单端点。

用以下命令启动服务器

$ (venv) flask run

最后,在您喜欢的浏览器中导航到http://localhost:5000/。您应该会看到一条 “世界你好“的信息。

数据库

接下来,我们来处理数据库。

我们将使用Flask-SQLAlchemy— 一个简单的 FlaskORM 来代替执行原始SQL。要安装它,请运行

$ (venv) pip install Flask-SQLAlchemy

接下来,用以下内容替换app.py的内容:

# app.py

from datetime import datetime

from flask import Flask, jsonify, request
from flask_sqlalchemy import SQLAlchemy

db = SQLAlchemy()
app = Flask(__name__)
app.config['SECRET_KEY'] = '5b3ef5s80gl3b217c20fb37044fe4k33'
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
app.config["SQLALCHEMY_DATABASE_URI"] = "sqlite:///default.db"
db.init_app(app)


class Task(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(128), nullable=False)
    description = db.Column(db.String(256), nullable=False)

    is_done = db.Column(db.Boolean, default=False)
    created_at = db.Column(db.DateTime, default=datetime.now(), nullable=True)
    updated_at = db.Column(db.DateTime, default=None, nullable=True)

    def as_dict(self):
        return {c.name: getattr(self, c.name) for c in self.__table__.columns}

    def __repr__(self):
        return f'<Task {self.title}>'


@app.route('/')
def index_view():
    return {
        'name': 'flask-todo',
        'description': 'a simple todo app written in flask',
        'version': 1,
    }

这段代码配置了 Flask 并定义了一个名为Task 的新模型。任务模型有一个名称描述is_done 和一些其他变量,如created_atupdated_at,我们将动态更新这些变量。

接下来,创建一个名为init_db.py的 Python 脚本,用于初始化和填充SQLite 数据库

# init_db.py

from app import db, app
from app import Task

with app.app_context():
    db.create_all()

    if Task.query.count() == 0:
        tasks = [
            Task(name='Backup the database', description='Make sure to backup the database with all the tables.'),
            Task(name='Setup 2FA', description='Setup the two factor authentication to secure your account.'),
            Task(name='Malware scan', description='Perform a malware scan.'),
        ]

        for task in tasks:
            db.session.add(task)

        db.session.commit()

从命令行运行脚本:

$ (venv) python init_db.py

你会发现一个名为instance的新目录已经创建。在该目录中,将出现default.db,这是默认数据库。

应用程序接口

Flask 应用程序将包含以下 URL:

  1. /api/返回所有任务的列表
  2. /api// 显示或删除特定任务
  3. /api/create/ 创建新任务
  4. /api/toggle// 切换特定任务的is_done属性

要实现这些功能,请在app.py 的末尾添加以下内容:

# app.py

@app.route('/api/')
def list_view():
    json = [task.as_dict() for task in Task.query.all()]
    return jsonify(json)


@app.route('/api/<int:task_id>/', methods=['GET', 'DELETE'])
def detail_view(task_id):
    task = db.get_or_404(Task, task_id)

    if request.method == 'DELETE':
        db.session.delete(task)
        db.session.commit()

        return {
            'detail': 'Task has been successfully deleted.'
        }
    else:
        return task.as_dict()


@app.route('/api/create/', methods=['POST'])
def create_view():
    name = request.form.get('name')
    description = request.form.get('name')

    if name is None or description is None:
        return {
            'detail': 'Please provide the name and the description.'
        }, 400

    task = Task(name=name, description=description)
    db.session.add(task)
    db.session.commit()

    return task.as_dict()


@app.route('/api/toggle/<int:task_id>/')
def toggle_view(task_id):
    task = db.get_or_404(Task, task_id)

    if task.is_done:
        task.is_done = False
    else:
        task.is_done = True

    task.updated_at = datetime.now()
    db.session.commit()

    return task.as_dict()

这段代码不言自明。我们定义了所需的路由并实现了所需的逻辑。每次要对数据库进行更改时,我们都必须提交()这些更改。

这就是 Flask 应用程序的全部内容。在接下来的章节中,我们将为项目部署做好准备。

独角兽

Flask 的网络服务器并不推荐用于生产,因为它的设计是一次处理一个请求,可能无法处理大量的流量。因此,我们将其与 Gunicorn 互换–Gunicorn 是一个可用于生产的 Python WSGI 服务器。

首先,通过 pip 安装:

$ (venv) pip install gunicorn==20.1.0

安装成功后,您可以像这样启动服务器:

$ (venv) gunicorn -w 2 -b 0.0.0.0:5000 app:app

[INFO] Starting gunicorn 20.1.0
[INFO] Listening at: http://0.0.0.0:5000 (1)
[INFO] Using worker: sync
[INFO] Booting worker with pid: 3
[INFO] Booting worker with pid: 4

请注意,该命令仅适用于 UNIX 操作系统。

这将启动两个 Gunicorn Worker,并通过端口5000 公开您的应用程序。要访问您的应用程序,请打开您最喜欢的网络浏览器并导航至http://localhost:5000。

requirements.txt

在 Docker 化应用程序之前,我们需要做的最后一件事是创建requirements.txt文件。requirements.txt文件用于指定项目的依赖关系。

运行以下命令生成它:

$ (venv) pip freeze > requirements.txt

然后,其他人(包括 Docker 容器)可以像这样利用requirements.txt文件:

$ (venv) pip install -r requirements.txt

Dockerize 应用程序

我们将使用Dockerfile 对应用程序进行对接。Dockerfile 用于定义构建 Docker 镜像的指令。它们允许你设置基础镜像、工作目录、环境变量、执行命令等。

.dockerignore

在创建 Dockerfile 之前,我们先创建一个.dockerignore文件。.dockerignore文件用于指定映像中应省略哪些文件夹和文件。

.git/
.idea/
instance/
__pycache__/

请确保根据自己的需要修改.dockerignore文件。

Dockerfile

在项目根目录下新建一个名为Dockerfile的文件,内容如下:

# syntax=docker/dockerfile:1.4
FROM --platform=$BUILDPLATFORM python:3.10-alpine

WORKDIR /app

# set environment variables
ENV PYTHONDONTWRITEBYTECODE 1
ENV PYTHONUNBUFFERED 1

# install the requirements
COPY requirements.txt /app
RUN --mount=type=cache,target=/root/.cache/pip \
    pip3 install -r requirements.txt

COPY . .

# initialize the database (create DB, tables, populate)
RUN python init_db.py

EXPOSE 5000/tcp

CMD ["gunicorn", "-w", "2", "-b", "0.0.0.0:5000", "app:app"]

该 Dockerfile 使用python:3.10-alpine作为基础镜像。然后,它会设置工作目录、安装需求、复制项目、初始化数据库,最后在5000 端口启动 Gunicorn 服务器。

要了解有关Dockerfile 的更多信息,请查阅Dockerfile 参考资料

测试

为了确保 Dockerfile 能正常运行,我们可以在本地构建并运行它。要构建镜像,请运行

$ docker build -t flask-todo:1.0 .

如果您列出图像,就会看到一个新图像:

$ docker images

REPOSITORY        TAG       IMAGE ID       CREATED       SIZE
flask-todo        1.0       7ege66240eb1   3 hours ago   109MB

最后,使用镜像启动一个新的 Docker 容器:

$ docker run -it -p 5000:5000 flask-todo:1.0

[2023-02-02 20:08:57 +0000] [1] [INFO] Starting gunicorn 20.1.0
[2023-02-02 20:08:57 +0000] [1] [INFO] Listening at: http://0.0.0.0:5000 (1)
[2023-02-02 20:08:57 +0000] [1] [INFO] Using worker: sync
[2023-02-02 20:08:57 +0000] [7] [INFO] Booting worker with pid: 3
[2023-02-02 20:08:57 +0000] [8] [INFO] Booting worker with pid: 4

你可以使用-d以分离模式启动 Docker 容器。这意味着容器在终端后台运行,不接收输入也不显示输出。

很好,您的应用程序现在已在容器中运行。在您喜欢的网络浏览器中导航至http://localhost:5000,查看网络应用程序的运行情况。

{
    "name": "flask-todo",
    "description": "a simple todo app written in flask",
    "version": 1
}

推送到 GitHub

以下步骤需要您拥有一个 GitHub 账户。如果还没有,请先注册,然后登录。此外,请确保已安装和配置Git

登录 GitHub 后,使用屏幕右上方的 “加号 “按钮打开下拉菜单。然后选择 “新建仓库”:

GitHub 索引页

为你的版本库取一个自定义名称。我选择 “flask-todo”,然后点击 “创建版本库”:

GitHub 创建仓库

创建版本库后,请注意远程 URL:

GitHub SSH 远程地址

现在,让我们回到本地项目并推送代码。

既然有些文件我们不想推送到 Git,那就在项目根目录下创建一个.gitignore文件。我将在其中添加以下内容,但你也可以根据自己的需要随意修改:

instance/*
!instance/.gitignore
.webassets-cache
.env

__pycache__/
*.py[cod]
*$py.class

接下来,打开命令行并运行以下命令:

$ git init
$ git remote add origin <your_remote_url>
$ git add .
$ git commit -m "init"
$ git push origin master

这将初始化一个新的 Git 仓库,添加远程起源,VCS 所有文件,并创建初始提交。最后,将源代码推送到 GitHub 仓库。

很好,应该就是这样了。现在在浏览器中导航到版本库,就能看到所有文件都已提交。

部署应用程序

以下步骤需要您有一个 Back4app 帐户。如果您已经有一个账户,请登录,否则请注册免费账户

登录后,您将被重定向到应用程序的控制面板。点击 “创建新应用 “按钮,开始应用创建过程。

Back4app 创建应用程序

Back4app 允许你构建和部署两种类型的应用程序:后端即服务(BaaS)和容器即服务(CaaS)。由于我们要部署一个 docker 化的应用程序,因此我们将使用 “容器即服务”。

Back4app 容器即服务

接下来,将 GitHub 与 Back4app 账户连接起来。确保赋予 Back4app 上一步创建的仓库权限。然后点击绿色的 “选择 “按钮将其选中。

Back4app 选择存储库

Back4app Containers 允许您配置部署流程。你可以设置默认分支、根目录、启用/禁用自动部署以及设置环境变量。我们不需要这些,所以只需命名我们的应用程序,然后点击 “创建应用程序 “即可。

Back4app 配置应用程序

Back4app 会花一些时间构建容器,将其上传到容器注册表并运行容器。容器准备就绪后,状态将变为 “Ready”(准备就绪),您可以在屏幕左侧看到一个绿色的 URL。

成功部署 Back4app

点击 URL 将在浏览器中打开网络应用程序。您会发现 Back4app 自动为您的应用程序颁发了 SSL 证书,并允许您免费托管 Python 应用程序。

结论

在本文中,您已经了解了 Python 及其优缺点和部署选项。您还成功部署了一个简单的 Python RESTful API。现在,您应该能够构建简单的 API 并将其部署到 Back4app 容器。

最终源代码可在GitHub 上找到。

今后的步骤

  • 目前,每次重新部署都会清除所有数据。这是因为数据库包含在 Docker 映像中。可以考虑改用托管数据库实例。
  • 研究多阶段构建,加快部署过程。

Leave a reply

Your email address will not be published.