部署概念¶
在部署 FastAPI 应用程序,或者实际上是任何类型的 Web API 时,有几个概念是您可能关心的,通过使用它们,您可以找到最适合的方式来部署您的应用程序。
一些重要的概念包括
- 安全性 - HTTPS
- 启动时运行
- 重启
- 复制(运行的进程数量)
- 内存
- 启动前的准备步骤
我们将看看它们如何影响部署。
最终目标是以安全的方式为您的 API 客户端提供服务,避免中断,并尽可能高效地使用计算资源(例如远程服务器/虚拟机)。🚀
我将在这里向您介绍更多关于这些概念的信息,希望这能为您提供所需的直觉,以决定如何在各种不同的环境中部署您的 API,甚至包括未来可能出现的、现在还不存在的环境。
通过考虑这些概念,您将能够评估并设计部署您自己的 API 的最佳方式。
在接下来的章节中,我将为您提供更多部署 FastAPI 应用程序的具体方案。
但现在,让我们先来看看这些重要的概念性想法。这些概念同样适用于任何其他类型的 Web API。💡
安全性 - HTTPS¶
在关于 HTTPS 的上一章中,我们了解了 HTTPS 如何为您的 API 提供加密。
我们还看到,HTTPS 通常由应用程序服务器外部的一个组件提供,即 TLS 终止代理。
并且必须有专门负责续订 HTTPS 证书的东西,它可以是同一个组件,也可以是不同的组件。
HTTPS 示例工具¶
您可以用作 TLS 终止代理的一些工具包括
- Traefik
- 自动处理证书续订 ✨
- Caddy
- 自动处理证书续订 ✨
- Nginx
- 配合 Certbot 等外部组件进行证书续订
- HAProxy
- 配合 Certbot 等外部组件进行证书续订
- 带有像 Nginx 这样的 Ingress 控制器的 Kubernetes
- 配合 cert-manager 等外部组件进行证书续订
- 由云服务提供商作为其服务的一部分在内部处理(见下文 👇)
另一个选择是使用云服务,它可以为您完成更多工作,包括设置 HTTPS。它可能有一些限制或收费更高,等等。但在这种情况下,您无需自己设置 TLS 终止代理。
我将在接下来的章节中向您展示一些具体的示例。
接下来要考虑的概念是关于运行您实际 API 的程序(例如 Uvicorn)。
程序与进程¶
我们将大量讨论运行中的“进程”,因此明确其含义以及与“程序”一词的区别非常有用。
什么是程序¶
程序一词通常用于描述许多事物
- 您编写的代码,即 Python 文件。
- 可以由操作系统执行的文件,例如:
python、python.exe或uvicorn。 - 当特定程序在操作系统上运行时,它会占用 CPU 并将内容存储在内存中。这也称为进程。
什么是进程¶
进程一词通常以更具体的方式使用,仅指操作系统中正在运行的事物(如上述最后一点所述)
- 特定程序在操作系统上运行时的状态。
- 这既不是指文件,也不是指代码,而是特指正在被操作系统执行和管理的事物。
- 任何程序,任何代码,只有在被执行时才能做事情。因此,这意味着有一个正在运行的进程。
- 进程可以被您或操作系统终止(或“杀死”)。此时,它停止运行/执行,无法再做任何事情。
- 您在计算机上运行的每个应用程序背后都有一个进程,每个正在运行的程序、每个窗口等。通常在计算机开启时,会有许多进程同时运行。
- 同一个程序可以有多个进程同时运行。
如果您查看操作系统中的“任务管理器”或“系统监视器”(或类似工具),您将能够看到许多正在运行的进程。
例如,您可能会看到多个进程正在运行同一个浏览器程序(Firefox、Chrome、Edge 等)。它们通常每个选项卡运行一个进程,外加一些额外的进程。

既然我们知道了进程和程序这两个术语之间的区别,让我们继续讨论部署。
启动时运行¶
在大多数情况下,当您创建 Web API 时,您希望它始终运行,不间断,以便您的客户端可以随时访问它。除非您有特定的理由希望它仅在某些情况下运行,但大多数时候,您都希望它持续运行并可用。
在远程服务器中¶
当您设置远程服务器(云服务器、虚拟机等)时,您可以做的最简单的事情就是手动使用 fastapi run(使用 Uvicorn)或类似工具,就像您在本地开发时所做的那样。
这在开发期间是有效且有用的。
但如果与服务器的连接断开,运行中的进程很可能会死亡。
而且如果服务器重启(例如在更新后,或云提供商进行迁移后),您可能根本察觉不到。正因如此,您甚至不知道需要手动重启进程。所以,您的 API 将保持离线状态。😱
启动时自动运行¶
通常,您可能希望服务器程序(例如 Uvicorn)在服务器启动时自动启动,无需任何人工干预,从而使进程始终运行您的 API(例如 Uvicorn 运行您的 FastAPI 应用)。
独立程序¶
为了实现这一点,您通常需要一个独立的程序来确保您的应用程序在启动时运行。在许多情况下,它还会确保其他组件或应用程序也随之运行,例如数据库。
启动时运行的示例工具¶
可以完成这项工作的一些工具示例包括
- Docker
- Kubernetes
- Docker Compose
- Docker Swarm 模式
- Systemd
- Supervisor
- 由云服务提供商作为其服务的一部分在内部处理
- 其他...
我将在接下来的章节中为您提供更具体的示例。
重启¶
类似于确保应用程序在启动时运行,您可能还希望确保它在故障后能够重启。
我们会犯错¶
作为人类,我们一直在犯错误。软件几乎总是在不同地方隐藏着缺陷(bug)。🐛
作为开发者,当我们发现这些缺陷并实现新功能时(可能还会引入新的缺陷 😅),我们会不断改进代码。
自动处理的小错误¶
使用 FastAPI 构建 Web API 时,如果代码中出现错误,FastAPI 通常会将错误限制在触发该错误的单个请求中。🛡
客户端会收到 500 Internal Server Error,但应用程序将继续处理后续请求,而不是直接完全崩溃。
更大的错误 - 崩溃¶
然而,在某些情况下,我们编写的代码可能会导致整个应用程序崩溃,使 Uvicorn 和 Python 同时宕机。💥
即便如此,您通常也不希望应用程序因为一个地方的错误而彻底瘫痪,您可能希望它至少对于未损坏的路径操作继续运行。
崩溃后重启¶
但在那些导致运行中的进程崩溃的严重错误情况下,您需要一个负责重启进程的外部组件,至少重启几次...
提示
...尽管如果整个应用程序只是立即崩溃,不断地重启可能没有意义。但在这些情况下,您通常会在开发过程中,或者至少在部署后立即注意到它。
因此,让我们专注于主要情况,即未来在某些特定情况下可能会完全崩溃,但重启仍然是有意义的。
您可能希望让负责重启应用程序的组件成为一个外部组件,因为到了那个阶段,带有 Uvicorn 和 Python 的应用程序已经崩溃了,所以同一个应用程序代码中没有任何东西可以对此采取行动。
自动重启的示例工具¶
在大多数情况下,用于在启动时运行程序的同一个工具也会被用来处理自动重启。
例如,这可以由以下工具处理
- Docker
- Kubernetes
- Docker Compose
- Docker Swarm 模式
- Systemd
- Supervisor
- 由云服务提供商作为其服务的一部分在内部处理
- 其他...
复制 - 进程与内存¶
对于 FastAPI 应用程序,使用像运行 Uvicorn 的 fastapi 命令这样的服务器程序时,在一个进程中运行它就可以同时为多个客户端提供服务。
但在许多情况下,您会希望同时运行多个工作进程。
多进程 - 工作进程¶
如果您拥有的客户端数量超过了单个进程的处理能力(例如,如果虚拟机不够大),且服务器的 CPU 拥有多个核心,那么您可以同时运行多个进程来执行同一个应用程序,并将所有请求分发给它们。
当您运行同一个 API 程序的多个进程时,它们通常被称为工作进程(Workers)。
工作进程与端口¶
还记得关于 HTTPS 的文档中提到的,在服务器上只有一个进程可以监听同一个端口和 IP 地址组合吗?
这一点依然成立。
因此,为了能够同时拥有多个进程,必须有一个单一的进程监听端口,然后以某种方式将通信传输到每个工作进程。
每个进程的内存¶
当程序将事物加载到内存中(例如变量中的机器学习模型,或变量中的大文件内容)时,所有这些都会消耗一点服务器的内存 (RAM)。
并且多个进程通常不共享任何内存。这意味着每个运行中的进程都有自己的事物、变量和内存。如果您在代码中消耗了大量的内存,每个进程都将消耗等量的内存。
服务器内存¶
例如,如果您的代码加载了一个 1 GB 大小的机器学习模型,当您运行一个 API 进程时,它将至少消耗 1 GB 的 RAM。如果您启动 4 个进程(4 个工作进程),每个都将消耗 1 GB 的 RAM。所以总共,您的 API 将消耗 4 GB 的 RAM。
如果您的远程服务器或虚拟机只有 3 GB 的 RAM,尝试加载超过 4 GB 的内存将会导致问题。🚨
多进程 - 示例¶
在此示例中,有一个管理进程来启动和控制两个工作进程。
这个管理进程很可能是监听 IP 和端口的进程。它会将所有通信传输给工作进程。
那些工作进程是运行您的应用程序的进程,它们执行主要的计算以接收请求并返回响应,并将您放入内存变量中的任何内容加载到 RAM 中。
当然,同一台机器上除了您的应用程序之外,可能还会有其他进程在运行。
一个有趣的细节是,每个进程使用的 CPU 百分比随时间变化很大,但内存 (RAM) 通常保持相对稳定。
如果您的 API 每次执行的计算量大致相当且拥有大量客户端,那么CPU 利用率通常也会很稳定(而不是不断快速地上升和下降)。
复制工具与策略示例¶
有多种方法可以实现这一点,我将在接下来的章节中告诉您更多关于特定策略的信息,例如在讨论 Docker 和容器时。
需要考虑的主要约束是,必须有一个单一组件来处理公共 IP 上的端口。然后,它必须有一种方法将通信传输到复制的进程/工作进程。
以下是一些可能的组合和策略
- Uvicorn 使用
--workers- 一个 Uvicorn 进程管理器将监听 IP 和 端口,并启动多个 Uvicorn 工作进程。
- Kubernetes 和其他分布式容器系统
- Kubernetes 层中的某个东西将监听 IP 和 端口。复制方式是通过拥有多个容器,每个容器运行一个 Uvicorn 进程。
- 为您处理此事的云服务
- 云服务很可能会为您处理复制。它可能会让您定义要运行的进程或要使用的容器镜像,无论如何,这很可能是一个单一的 Uvicorn 进程,而云服务将负责复制它。
提示
如果其中一些关于容器、Docker 或 Kubernetes 的条目目前还不太清楚,请不必担心。
我将在未来的章节中向您介绍更多关于容器镜像、Docker、Kubernetes 等内容:容器中的 FastAPI - Docker。
启动前的准备步骤¶
在许多情况下,您需要在启动应用程序之前执行一些步骤。
例如,您可能需要运行数据库迁移。
但在大多数情况下,您只会希望这些步骤执行一次。
因此,您需要有一个单一进程来在启动应用程序之前执行这些准备步骤。
即使随后您为应用程序本身启动了多个进程(多个工作进程),您也必须确保只有一个进程在运行这些准备步骤。如果这些步骤由多个进程运行,它们将通过并行运行而重复工作,如果这些步骤是像数据库迁移这样敏感的操作,它们可能会相互冲突。
当然,在某些情况下多次运行准备步骤没有问题,那样的话处理起来会容易得多。
提示
另外请记住,根据您的设置,在某些情况下,您甚至可能根本不需要在启动应用程序之前执行任何准备步骤。
那种情况下,您无需担心这些。🤷
准备步骤的策略示例¶
这在很大程度上取决于您部署系统的方式,并且很可能与您启动程序、处理重启等方式相关联。
这里有一些可能的想法
- Kubernetes 中在应用容器之前运行的“Init Container”
- 一个运行准备步骤然后启动应用程序的 bash 脚本
- 您仍然需要一种方法来启动/重启那个 bash 脚本,检测错误等。
提示
我将在未来的章节中为您提供使用容器执行此操作的更具体示例:容器中的 FastAPI - Docker。
资源利用率¶
您的服务器是您可以利用的资源,通过您的程序,您可以消耗 CPU 计算时间和可用的 RAM 内存。
您希望消耗/利用多少系统资源?认为“不多”可能很容易,但实际上,您可能希望在不崩溃的前提下尽可能多地利用资源。
如果您支付了 3 台服务器的费用,但只使用了它们很少一部分的 RAM 和 CPU,您可能是在浪费金钱 💸,也可能是在浪费服务器电力 🌎,等等。
在这种情况下,拥有两台服务器并使用它们更高比例的资源(CPU、内存、磁盘、网络带宽等)可能会更好。
另一方面,如果您有 2 台服务器并且正在使用其 100% 的 CPU 和 RAM,那么在某个时刻,一个进程将请求更多内存,服务器将不得不使用磁盘作为“内存”(这可能比内存慢数千倍),甚至崩溃。或者某个进程可能需要进行计算,却必须等待 CPU 空闲。
在这种情况下,获得一台额外的服务器并在其上运行一些进程会更好,这样它们都有足够的 RAM 和 CPU 时间。
还有一种可能性是,由于某种原因,您的 API 使用量会出现激增。也许它火了,或者其他服务或机器人开始使用它。在这些情况下,您可能希望拥有额外的资源以确保安全。
您可以设定一个任意的数字作为目标,例如资源利用率在 50% 到 90% 之间。重点是,这些可能是您想要衡量并用于微调部署的主要指标。
您可以使用像 htop 这样简单的工具来查看服务器上使用的 CPU 和 RAM,或者每个进程使用的数量。您也可以使用更复杂的监控工具,它们可以跨服务器分布等。
总结¶
您刚才阅读了一些在决定如何部署应用程序时可能需要牢记的主要概念
- 安全性 - HTTPS
- 启动时运行
- 重启
- 复制(运行的进程数量)
- 内存
- 启动前的准备步骤
理解这些想法以及如何应用它们,应该能为您在配置和微调部署时做出任何决策提供必要的直觉。🤓
在接下来的部分中,我将为您提供更多您可以遵循的策略的具体示例。🚀