今天,我想尝试一些新的东西,开始探索 Python 的世界。在本文中,我将介绍如何使用 Python、Fast API、Hydra 和 Mamba实现简单的 REST API的分步教程。此外,我将简要介绍如何将所有这些蛇打包到一个 Docker 映像中并使它们协同工作。下面提供的整个代码可在我的GitHub 上找到。
让我们从一个简短的段落开始,说明我为什么决定选择这个主题。
为什么还要关心?首先,我想分享我的知识,因为最近我不得不实现一个基于 Python 的 REST API。框架的选择很简单——FAST API。我们还需要依赖管理工具,所以我们选择了 Mamba。我们还决定选择 Hydra 进行配置管理和加载。所有这些工具似乎都运行良好,并提供了我们需要的所有功能,因此整个组合听起来相当不错。不幸的是,当我们开始集成这些工具并试图让它们一起工作时,我们发现事情并不是那么简单。更重要的是,资源和示例的数量相当有限。这就是我最终产生写这篇文章的想法的方式。
什么是mamba?它是一种蛇,实际上是一种非常致命的蛇。开个玩笑,它是一个用于管理我们项目的依赖关系和构建 python 虚拟环境的工具。它是在Anaconda之上构建的,但应该更快,而且根据我对 Mamba 的短暂经验,我可以确认它很快。由于它是在 Conda 之上构建的,我们可以访问来自 Conda 存储库的所有现有包。更重要的是,整个 Mamba API 与 Conda 非常相似,这使得 Conda 前用户更容易切换到 Mamba。
什么是fast API?这是一个我可能不需要向 python 社区的任何人介绍的工具:异步,一种用于构建 REST API 的快速且轻量级的工具。就目前而言,它可能是推荐给任何想要开始使用 Python 和 REST 之旅的人的工具。它包含构建工作 API 所需的所有功能以及WebSockets和流支持。此外,FAST API 使用 python 类型提示,因此代码完成在 IDE 中工作得非常好(至少在 PyCharm 中)。内置 swagger 支持是另一个在我看来非常有用的功能。事实上,当我发现它的存在时,我感到非常惊讶。
什么是Hydra?九头蛇是古希腊神话中的一个怪物,以拥有众多头颅而闻名。但说真的,Hydra 是一个开源工具,用于管理和运行基于 Python 的应用程序的配置。它基于 Omega-Conf 库并引用其主页:“关键特性是能够通过组合动态创建分层配置并通过配置文件和命令行覆盖它。” 对我来说非常有用的是上面引用中描述的能力——分层配置。就我而言,它工作得很好,并且允许更清晰地分离配置文件。
执行1. 让我们通过创建environment.yaml环境配置来启动项目。
YAML
name: greeter-service
channels:
- conda-forge
dependencies:
- python=3.8.13
- pip
- fastapi
- uvicorn[standard]
- hydra-core
- pytest
它包含我们的新虚拟环境(greeter-service)的名称以及应从中下载依赖项的源(conda-forge)以及应用程序正常工作所需的依赖项的完整列表。感谢 Mamba,我可以通过一个简单的命令在几分钟内设置整个环境:
mamba env create -n greeter-service --file environment.yaml
为了安装 Mamba,我建议遵循Mamba 作者自己创建的教程。
2. 在这一步中,我将定义一个config.yaml文件,其中包含应用程序所需的所有配置。
YAML
app:
port: ${oc.env:GREETER_API_PORT,8070}
version: ${oc.env:GREETER_API_VERSION,v1}
greet_message: "Hello, "
这里没什么特别的。只有一个非常简单的.yaml文件,其中包含一些与读取环境变量相关的魔法。这是我将在本教程中使用的整个配置。这是一个相当标准的设置:
- 我们的 API 将在其上运行的端口和
- 将在端点中使用的 API 版本
唯一非标准的东西是greet_message包含将返回给用户的消息的基础的参数。
3. 我正在添加一个名为config.py负责读取 Hydra 配置的文件。
Python
import os
import hydra
from hydra import compose
hydra.initialize_config_dir(config_dir=os.getenv('GREETER_CONFIG_DIR'))
api_config = compose(config_name='config')
首先,我config根据 path 下的目录初始化 Hydra 上下文./ 。Hydra 将使用环境变量或获取项目根目录。然后我使用 Hydra 的 compose 方法来读取上一步中定义的配置。
4. 在这里,我正在实现 API 的第一个端点。我将在一个名为的文件中定义它,health_check.py因为它将负责处理健康检查请求。
from fastapi import APIRouter
health_router = APIRouter(prefix='/health', tags=['health_checks'])
@health_router.get('', status_code=200)
def is_ok():
return 'Ok'
代码很简单且具有自我描述性。它只是一个 FAST API 路由器,只有一个方法,Ok在调用时返回和 200 个 HTTP 代码。
5. 在这一步中,我将创建greeter.py负责处理传入请求的文件。
Python
from fastapi import APIRouter
from api.config import api_config
greeting_router = APIRouter(tags=['greet'])
@greeting_router.get('/greet/{name}', status_code=200)
def say_hello(name: str):
return api_config.app.greet_message name
另一个简单的 FAST API 基础端点,它以用户名作为输入。然后它将提供的名称与从 config 读取的消息格式混合,并向用户返回最终的 hello 消息。
6. 现在我正在实现一个main.py文件,它将前面步骤中的两个路由器捆绑在一起。
Python
import uvicorn
from fastapi import FastAPI, APIRouter
from api.config import api_config
from api.greeter_api import greeting_router
from api.health_check import health_router
main_api = FastAPI()
main_router = APIRouter(prefix=f'/{api_config.app.version}')
main_router.include_router(health_router)
main_router.include_router(greeting_router)
main_api.include_router(main_router)
def start():
uvicorn.run(main_api, host='0.0.0.0', port=api_config.app.port)
它只是普通的 FAST API 代码。这里值得注意的是,我将版本添加为所有端点的基本前缀。这里最重要的方法是start我手动启动uvicorn的方法(它不是拼写错误,它实际上是服务器名称)服务器上从配置读取的端口。
我的简单服务已准备好接受测试,但不要害怕,因为这不是我们今天旅程的终点。现在我将描述如何使它作为 Docker 镜像工作。
7. 我将从定义setup.py文件开始这部分。
Python
import uvicorn
from fastapi import FastAPI, APIRouter
from api.config import api_config
from api.greeter_api import greeting_router
from api.health_check import health_router
main_api = FastAPI()
main_router = APIRouter(prefix=f'/{api_config.app.version}')
main_router.include_router(health_router)
main_router.include_router(greeting_router)
main_api.include_router(main_router)
def start():
uvicorn.run(main_api, host='0.0.0.0', port=api_config.app.port)
这个脚本中最重要的参数是entry_points参数,它有效地定义了哪个python方法负责应用程序。在这种情况下,它是start来自 的方法main.py。它还定义了可用于从命令行运行应用程序的 python 服务的名称。
8. 现在是时候准备了Dockerfile。
Dockerfile
import uvicorn
from fastapi import FastAPI, APIRouter
from api.config import api_config
from api.greeter_api import greeting_router
from api.health_check import health_router
main_api = FastAPI()
main_router = APIRouter(prefix=f'/{api_config.app.version}')
main_router.include_router(health_router)
main_router.include_router(greeting_router)
main_api.include_router(main_router)
def start():
uvicorn.run(main_api, host='0.0.0.0', port=api_config.app.port)
这里到底发生了什么?
- 首先,我使用的是官方的 Mamba 映像,因为我不想从头开始在 Docker 上安装 Mamba 做额外的工作。
- 然后我将 greeter-service 设置为工作目录并添加CONFID_DIR为新的环境变量。
- Hydra 将使用此变量作为应用程序配置文件的路径。在下一步中,我复制了环境文件并使用它在Docker上创建了 Mamba 虚拟环境。
- 接下来的几行只是包含应用程序代码和配置的目录的临时副本。
- 最后两行是围绕 Conda 的一种 hack,与 docker 并在一定程度上与 shell 本身不完全兼容。
如果没有这种快速解决方法,您将看到如下异常消息:Your shell has not been properly configured to use 'conda activate并且您将无法执行conda activate greeter-service. 不幸的是,至少在我的情况下,似乎没有其他修复方法可以解决这个问题。可以在此处、 此处和此处找到与该主题相关的一些链接,因为它吸引了很多观众。
9. 最重要的是 docker compose 文件,它将使 docker 映像的设置变得更加容易。
YAML
version: "3.9"
services:
greeter-api:
build: .
command: greeter-service
ports:
- "8070:8070"
environment:
- GREETER_CONFIG_DIR=/greeter-service/config
- GREETER_API_PORT=8070
就目前而言,它只是一个使用两个环境变量的 docker 服务——带有配置和端口的目录路径。Compose 将基于Dockerfile本地目录构建一个 docker 镜像。接下来,它将greeter-service用作容器的启动命令。它还将公开本地端口 8070 并将其绑定到容器上的端口 8070。
瞧,实施完成。现在是时候做一些测试了。
测试就像在我的其他文章中一样,我将使用Postman来执行启动和运行 API 的测试。让我们通过启动 docker 映像开始测试。设置整个测试环境所需的一切都很简单docker compose build && docker compose up,至少在一切都按设计工作的情况下。
泊坞窗已启动,因此我可以测试 API。简单的一两个请求,我可以确认一切正常。让我们从greet端点开始。
现在health端点。
来自 docker 容器的一些日志——以证明我不只是绘制这些图像。
编码已完成,服务已测试,现在是快速总结的时候了。
概括该集成与我决定使用的鲜为人知的工具一起实施、测试和描述。该示例非常简单,但它包含了在您想要的任何地方运行它可能需要的所有内容。此外,它可以轻松扩展并用作更复杂项目的基础。我希望它对你有用。感谢您的时间。