部署概念¶
在部署 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 等外部组件进行证书更新
- 带有 Ingress Controller(如 Nginx)的 Kubernetes
- 使用诸如 cert-manager 之类的外部组件来进行证书更新
- 由云服务商内部处理,作为其服务的一部分(请阅读下文👇)
另一种选择是你可以使用云服务来完成更多工作,包括设置 HTTPS。它可能有一些限制或向你收取更多费用等。但在这种情况下,你不必自己设置 TLS 终止代理。
我将在接下来的章节中向你展示一些具体示例。
接下来要考虑的概念都是关于运行实际 API 的程序(例如 Uvicorn)。
程序和进程¶
我们将讨论很多关于正在运行的“进程”的内容,因此弄清楚它的含义以及与“程序”这个词有什么区别是很有用的。
什么是程序¶
程序这个词通常用来描述很多东西:
- 你编写的 代码,Python 文件。
- 操作系统可以执行的文件,例如:
python、python.exe或uvicorn。 - 在操作系统上运行、使用 CPU 并将内容存储在内存上的特定程序。这也被称为进程。
什么是进程¶
进程这个词通常以更具体的方式使用,仅指在操作系统中运行的东西(如上面的最后一点):
- 在操作系统上运行的特定程序。
- 这不是指文件,也不是指代码,它具体指的是操作系统正在执行和管理的东西。
- 任何程序,任何代码,只有在执行时才能做事。因此,是当有进程正在运行时。
- 该进程可以由你或操作系统终止(或“杀死”)。那时,它停止运行/被执行,并且它不再能做事情。
- 你计算机上运行的每个应用程序背后都有一些进程,每个正在运行的程序,每个窗口等。并且通常在计算机打开时同时运行许多进程。
- 同一程序可以有多个进程同时运行。
如果你检查操作系统中的“任务管理器”或“系统监视器”(或类似工具),你将能够看到许多正在运行的进程。
例如,你可能会看到有多个进程运行同一个浏览器程序(Firefox、Chrome、Edge 等)。它们通常每个 tab 运行一个进程,再加上一些其他额外的进程。

现在我们知道了术语 进程 和 程序 之间的区别,让我们继续讨论部署。
启动时运行¶
在大多数情况下,当你创建 Web API 时,你希望它始终运行、不间断,以便你的客户端始终可以访问它。当然,除非你有特定原因希望它仅在某些情况下运行,但大多数时候你希望它不断运行并且可用。
在远程服务器中¶
当你设置远程服务器(云服务器、虚拟机等)时,你可以做的最简单的事情就是使用 fastapi run(它使用 Uvicorn)或类似方式,手动运行,就像本地开发时一样。
它将会在开发过程中发挥作用并且很有用。
但是,如果你与服务器的连接丢失,正在运行的进程可能会终止。
如果服务器重新启动(例如更新后或从云提供商迁移后),你可能不会注意到它。因此,你甚至不知道必须手动重新启动该进程。所以,你的 API 将一直处于挂掉的状态。 😱
启动时自动运行¶
一般来说,你可能希望服务器程序(例如 Uvicorn)在服务器启动时自动启动,并且不需要任何人为干预,让进程始终与你的 API 一起运行(例如 Uvicorn 运行你的 FastAPI 应用程序)。
单独的程序¶
为了实现这一点,你通常会有一个单独的程序来确保你的应用程序在启动时运行。在许多情况下,它还可以确保其他组件或应用程序也运行,例如数据库。
启动时运行的示例工具¶
可以完成这项工作的工具的一些示例是:
- Docker
- Kubernetes
- Docker Compose
- Docker in Swarm Mode
- Systemd
- Supervisor
- 作为其服务的一部分由云提供商内部处理
- 其他的...
我将在接下来的章节中为你提供更具体的示例。
重新启动¶
与确保应用程序在启动时运行类似,你可能还想确保它在失败后重新启动。
我们会犯错误¶
作为人类,我们总是会犯错误。软件几乎总是在不同的地方隐藏着 bug。 🐛
作为开发人员,当我们发现这些 bug 并实现新功能(也可能添加新 bug 😅)时,我们会不断改进代码。
自动处理小错误¶
使用 FastAPI 构建 Web API 时,如果我们的代码中存在错误,FastAPI 通常会将其限制在触发错误的单个请求中。 🛡
对于该请求,客户端将收到 500 内部服务器错误,但应用程序将继续处理下一个请求,而不是完全崩溃。
更大的错误 - 崩溃¶
尽管如此,在某些情况下,我们编写的一些代码可能会导致整个应用程序崩溃,从而导致 Uvicorn 和 Python 崩溃。 💥
尽管如此,你可能不希望应用程序因为某个地方出现错误而保持死机状态,你可能希望它继续运行,至少对于未损坏的路径操作。
崩溃后重新启动¶
但在那些严重错误导致正在运行的进程崩溃的情况下,你需要一个外部组件来负责重新启动进程,至少尝试几次...
提示
...尽管如果整个应用程序只是立即崩溃,那么永远重新启动它可能没有意义。但在这些情况下,你可能会在开发过程中注意到它,或者至少在部署后立即注意到它。
因此,让我们关注主要情况,在未来的某些特定情况下,它可能会完全崩溃,但重新启动它仍然有意义。
你可能希望让这个负责重新启动你的应用程序的东西作为一个外部组件,因为到那时,使用 Uvicorn 和 Python 的同一应用程序已经崩溃了,因此同一应用程序的相同代码中没有任何东西可以对此做什么。
自动重新启动的示例工具¶
在大多数情况下,用于启动时运行程序的同一工具也用于处理自动重新启动。
例如,可以通过以下方式处理:
- Docker
- Kubernetes
- Docker Compose
- Docker in Swarm Mode
- Systemd
- Supervisor
- 作为其服务的一部分由云提供商内部处理
- 其他的...
复制 - 进程和内存¶
对于 FastAPI 应用程序,使用像运行 Uvicorn 的 fastapi 命令这样的服务器程序,在一个进程中运行一次就可以同时为多个客户端提供服务。
但在许多情况下,你会希望同时运行多个工作进程。
多进程 - Workers¶
如果你的客户端数量多于单个进程可以处理的数量(例如,如果虚拟机不是太大),并且服务器的 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 RAM 将导致问题。 🚨
多进程 - 一个例子¶
在此示例中,有一个 Manager Process 启动并控制两个 Worker Processes。
该管理器进程可能是监听 IP 中的端口的进程。它将所有通信传输到工作进程。
这些工作进程将是运行你的应用程序的进程,它们将执行主要计算以接收请求并返回响应,并且它们将加载你放入 RAM 中的变量中的任何内容。
当然,除了你的应用程序之外,同一台机器可能还运行其他进程。
一个有趣的细节是,随着时间的推移,每个进程使用的 CPU 百分比可能会发生很大变化,但内存 (RAM) 通常会或多或少保持稳定。
如果你有一个每次执行相当数量的计算的 API,并且你有很多客户端,那么 CPU 利用率 可能也会保持稳定(而不是不断快速上升和下降)。
复制工具和策略示例¶
可以通过多种方法来实现这一目标,我将在接下来的章节中向你详细介绍具体策略,例如在谈论 Docker 和容器时。
要考虑的主要限制是必须有一个单个组件来处理公共 IP 中的端口。然后它必须有一种方法将通信传输到复制的进程/worker。
以下是一些可能的组合和策略:
- 带有
--workers的 Uvicorn- 一个 Uvicorn 进程管理器将监听 IP 和端口,并且它将启动多个 Uvicorn 工作进程。
- Kubernetes 和其他分布式容器系统
- Kubernetes 层中的某些东西将侦听 IP 和端口。复制将通过拥有多个容器来完成,每个容器运行一个 Uvicorn 进程。
- 云服务 为你处理此问题
- 云服务可能为你处理复制。它可能会让你定义要运行的进程,或要使用的容器镜像,在任何情况下,它很可能是单个 Uvicorn 进程,并且云服务将负责复制它。
提示
如果这些关于容器、Docker 或 Kubernetes 的内容还没有多大意义,请不要担心。
我将在以后的章节中向你详细介绍容器镜像、Docker、Kubernetes 等:容器中的 FastAPI - Docker。
启动之前的步骤¶
在很多情况下,你希望在启动应用程序之前执行一些步骤。
例如,你可能想要运行数据库迁移。
但在大多数情况下,你只想执行这些步骤一次。
因此,在启动应用程序之前,你将需要一个单个进程来执行这些前面的步骤。
而且你必须确保它是运行前面步骤的单个进程,即使之后你为应用程序本身启动多个进程(多个 worker)。如果这些步骤由多个进程运行,它们会通过并行运行来重复工作,并且如果这些步骤像数据库迁移一样需要小心处理,它们可能会导致彼此之间发生冲突。
当然,也有一些情况,多次运行前面的步骤也没有问题,这样的话就好办多了。
提示
另外,请记住,根据你的设置,在某些情况下,你在启动应用程序之前可能甚至不需要任何先前的步骤。
在这种情况下,你就不必担心这些。 🤷
前面步骤策略的示例¶
这将在很大程度上取决于你部署系统的方式,并且可能与你启动程序、处理重启等的方式有关。
以下是一些可能的想法:
- Kubernetes 中的“Init Container”在应用程序容器之前运行
- 一个 bash 脚本,运行前面的步骤,然后启动你的应用程序
- 你仍然需要一种方法来启动/重新启动那个 bash 脚本、检测错误等。
提示
我将在以后的章节中为你提供使用容器执行此操作的更具体示例:容器中的 FastAPI - Docker。
资源利用率¶
你的服务器是一个资源,你可以通过你的程序消耗或利用 CPU 上的计算时间以及可用的 RAM 内存。
你想要消耗/利用多少系统资源?你可能很容易认为“不多”,但实际上,你可能希望在不崩溃的情况下尽可能多地消耗。
如果你支付了 3 台服务器的费用,但只使用了它们的一点点 RAM 和 CPU,那么你可能浪费金钱 💸,并且可能浪费服务器电力 🌎,等等。
在这种情况下,最好只拥有 2 台服务器并使用更高比例的资源(CPU、内存、磁盘、网络带宽等)。
另一方面,如果你有 2 台服务器,并且正在使用它们 100% 的 CPU 和 RAM,则在某些时候,一个进程会要求更多内存,并且服务器将不得不使用磁盘作为“内存”(这可能会慢数千倍),甚至崩溃。或者一个进程可能需要执行一些计算,并且必须等到 CPU 再次空闲。
在这种情况下,最好购买一台额外的服务器并在其上运行一些进程,以便它们都有足够的 RAM 和 CPU 时间。
由于某种原因,你的 API 的使用量也有可能出现激增。也许它像病毒一样传播开来,或者也许其他一些服务或机器人开始使用它。在这些情况下,你可能需要额外的资源来保证安全。
你可以将一个任意数字设置为目标,例如,资源利用率在 50% 到 90% 之间。重点是,这些可能是你想要衡量和用来调整部署的主要内容。
你可以使用 htop 等简单工具来查看服务器中使用的 CPU 和 RAM 或每个进程使用的数量。或者你可以使用更复杂的监控工具,这些工具可能分布在服务器等上。
回顾¶
你在这里阅读了一些在决定如何部署应用程序时可能需要牢记的主要概念:
- 安全性 - HTTPS
- 启动时运行
- 重新启动
- 复制(运行的进程数)
- 内存
- 启动前的先前步骤
了解这些想法以及如何应用它们应该会给你足够的直觉,以便在配置和调整部署时做出任何决定。 🤓
在接下来的部分中,我将为你提供更具体的示例,说明你可以遵循的可能策略。 🚀