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