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