如何将FastAPI服务器连接到PostgreSQL并在GCP Cloud Run上部署
#网络开发人员 #python #fastapi #googlecloud

在Web开发领域,强大的后端对于提供高质量的用户体验至关重要。构建后端的一种流行方法是使用Restful API,这使客户能够使用HTTP请求与服务器进行通信。

当我构建Ballistic时,我选择了FastAPI作为我在Python的后端API的网络框架,因为它在很短的时间内在Python社区中广受欢迎。除了通过asyncio库的使用提供支持的性能外,我对类型注释的核心整合和完善的文档的核心集成使我深信不疑。考虑到良好的文档和广泛的社区可以在发展后端时节省大量的时间。

构建Web应用程序不仅涉及Web服务器。它需要一个可靠,有效的数据库来存储和管理应用程序的状态数据。在可用的各种数据库选项中,PostgreSQL是开发人员的流行选择。它广泛的社区和生态系统提供了宝贵的支持,从而更容易解决问题。 PostgreSQL在可靠性和稳定性方面的声誉进一步巩固了其作为可信赖解决方案的地位。此外,PostgreSQL与多个平台的兼容性确保了部署的灵活性。在此博客文章中,我们将探讨如何将我们的FastAPI服务器与PostgreSQL数据库联系起来。值得注意的是,该指南可以轻松地适用于其他数据库,例如MySQL或MongoDB,并具有最小的修改。

在部署REST服务器方面,有许多选择可供选择。一种特别有吸引力的选择是使用诸如Google Cloud Platform(GCP)之类的云提供商,该提供商提供可扩展的无状态服务(例如Cloud Run和App Engine)。借助GCP这样的云提供商,您将不必担心将服务器100%的时间保持上升,并确保它们可用,也有高额交通负荷。您的初始投资也大大降低,因为您只需要为消费的费用付费,而不是预先购买整个服务器。

在此博客文章中,我们将探讨如何使用FastAPI,PostgreSQL和GCP构建REST服务器,并将其部署到像GCP Cloud Run这样的无状态环境中。在博客文章的结尾,我还包括所有源代码,以帮助您遵循帖子本身给出的解释。

为什么无状态的REST服务器是云部署的理想选择

在研究使用GCP构建REST服务器的详细信息之前,让我们首先退后一步,考虑为什么无状态服务器是云部署的理想之选。

当服务器无状态时,这意味着它不会在请求之间存储任何会话数据或状态信息。由于服务器的任何实例都可以处理任何传入请求,因此这使扩展和部署变得更容易。这与状态服务器相反,该服务器需要跟踪会话数据并确保每个请求均由同一服务器实例处理。

无状态服务器还提供其他好处,例如提高的可靠性和容错性。由于可以通过服务器的任何实例来处理每个请求,因此可以通过旋转新实例来快速检测和处理故障或崩溃。

虽然您的应用程序必须跟踪用户数据至关重要,但重要的是要记住,这一责任属于数据库。该数据库旨在存储和维护数据完整性,而REST服务器负责处理对数据库中存储的数据运行的单个独立请求。通过分开这些职责,您的应用程序可以更有效,更易于维护。

GCP云运行与应用程序引擎

在GCP上部署REST服务器时,有两个主要选项可供选择:Cloud Run和App Engine。

Cloud Run是一个完全管理的,基于容器的平台,可让您在无服务器环境中运行无状态HTTP容器。这意味着您不必担心管理基础架构,而只需支付所使用的资源。

另一方面,

App Engine是一种平台即服务(PAAS)产品,可让您使用各种编程语言和框架来构建和部署Web应用程序和API。这是一个比云运行更完整的平台,但也需要更多的配置和管理。

Cloud Run和App Engine都是部署REST服务器的好选择,选择很大程度上取决于您的特定用例和要求。

建立后端

建立一个新项目是构建任何应用程序的第一步。虽然有多种用于管理软件包和依赖关系的工具,但我个人建议使用 poetry 使用简单性和易用性。另外,您也可以使用普通的旧虚拟环境来设置项目。

假设您已经在系统中安装了 poetry ,则可以使用以下命令创建一个新项目:

poetry new fastapi-gcp

这将创建一个名为fastapi-gcp的新目录,其中有一些默认文件和一个基本的项目结构。接下来,使用 cd fastapi-gcp 导航到新创建的目录,然后使用 fastapi 使用 poetry ,如下所示:

poetry add fastapi asyncpg

这将安装 fastapi 包装及其依赖项,使其可用于您的项目。

构建FastApi服务器

现在,让我们研究代码并开始构建我们的FastAPI服务器。只需几行代码,我们就可以创建一个简单的服务器来处理请求。创建一个[main.py](http://main.py)文件并添加以下代码:

from fastapi import FastAPI
from fastapi.responses import HTMLResponse

app = FastAPI(title="fastapi-gcp")

@app.get("/", response_class=HTMLResponse)
def healthcheck():
    return "<h1>All good!</h2>"

在这里,我们只是使用最小设置来创建一台服务器,该服务器在某人在浏览器中打开服务器的根URL时返回HTML就绪响应。要在 localhost:8080 上运行此服务器,请执行以下命令:

uvicorn main:app --host 0.0.0.0 --port 8080

现在您的服务器正在运行,请确保在http://localhost:8080上打开浏览器,以查看所有内容!

由于FastApi基于OpenAPI,此时您也可以使用自动生成的文档。有多个选项,默认情况下包括两个。通过访问以下URL来尝试一下:

设置PostgreSQL DB

现在,我们的服务器正在运行并运行,让我们旋转我们的PostgreSQL服务器。实现此目的的最简单方法是利用Docker和the official PostgreSQL image。如果您安装了Docker,则可以通过运行简单的单线快速实现这一目标:

docker run --name postgres-fastapi -e POSTGRES_PASSWORD=postgres-fastapi --rm -p 5432:5432 postgres

在这里,我们启动一个使用密码“ Postgres-fastapi”的容器,“ Postgres-fastapi”,“公开默认的PostgreSQL端口5432.本文: How to Use the PostgreSQL Docker Official Image

设置管理

在将我们的FastAPI服务器连接到PostgreSQL数据库之前,重要的是要考虑如何管理设置。在我们的Python存储库中以纯文本为数据库存储访问凭证会带来重大的安全风险,因为黑客或具有恶意意图的个人很容易发现。为了解决这个问题,最好的做法是将所有敏感凭据提取到外部环境文件(例如.env文件),然后将这些值加载到我们的FastAPI服务器中。这种方法允许灵活性,就像通过简单地使用不同的.env文件,我们可以连接到其他数据库。在运行不同版本的应用程序时,这是特别有用的,例如在登台环境中,另一个在生产环境中。

由于Fastapi是建立在Pydantic上的,因此我们可以利用底座对象来促进我们的设置管理。首先,我们需要通过执行以下命令来安装其他依赖关系:

poetry add pydantic[dotenv]

让我们创建一个名为 settings.py 的新文件,并添加以下代码:

from pathlib import Path

from pydantic import BaseSettings

class Settings(BaseSettings):
    DB_NAME: str
    DB_HOST: str
    DB_USER: str
    DB_PASS: str

settings = Settings(_env_file=Path(__file__).parent/ ".env", _env_file_encoding="utf-8")

此代码假设您在此 .env 文件中,在此 settings.py10 模块中包含您的数据库名称( DB_NAME ),主机( DB_HOST ),用户( DB_USER )和密码( DB_PASS ),例如:

DB_NAME=postgres
DB_HOST=localhost
DB_USER=postgres
DB_PASS=postgres-fastapi

当然,该.env文件应包含在您的.gitignore文件中,以将其排除到存储库中。此时,您可以在Python代码中的任何地方轻松使用数据库设置,例如:

from settings import settings

print(f"My database name is {settings.DB_NAME}")

将FastApi连接到PostgreSQL DB

使用我们的FastApi和PostgreSQL服务器启动和运行,以及我们的设置管理,我们现在可以继续建立我们的FastApi Server和PostgreSQL数据库之间的连接。

为此,我们可以使用Tortoise-ORM。首先安装软件包:

poetry add tortoise-orm

接下来,创建一个名为 database.py 的新文件,我们将在其中定义数据库连接的所有详细信息。这是一个例子:

from fastapi import FastAPI
from tortoise import Tortoise
from tortoise.contrib.fastapi import register_tortoise

from settings import settings

Tortoise.init_models(["models"], "models")

TORTOISE_ORM = {
    "connections": {
        "default": f"postgres://{settings.DB_USER}:{settings.DB_PASS}@{settings.DB_HOST}:5432/{settings.DB_NAME}",
    },
    "apps": {
        "models": {
            "models": ["models", "aerich.models"],
            "default_connection": "default",
        },
    },
    "use_tz": False,
}

def init_db(app: FastAPI) -> None:
    register_tortoise(
        app,
        config=TORTOISE_ORM,
        modules={"models": ["models", "aerich.models"]},
        generate_schemas=True,
        add_exception_handlers=True,
    )

在此[database.py](http://database.py)模块中,我们定义了TORTOISE_ORM对象中ORM的核心配置,然后我们定义了一个函数init_db,该函数实际上将乌龟ORM注册到现有的FastApi应用程序中。

还请注意,我们正在使用 tortoise/aerich 工具来执行数据库迁移。随着时间的流逝,数据库迁移是管理数据库架构演变的关键方面。随着应用程序的发展,通常需要修改数据库架构以适应新功能,修复错误或优化性能。像Aerich这样的数据库迁移工具提供了一种系统的方法来应用这些更改,同时保留现有数据。确保通过运行安装AERICH依赖性:

poetry add aerich

现在,让我们在[main.py](http://main.py)模块中使用init_db函数,我们在其中定义了FastApi应用程序:

from fastapi import FastAPI
from fastapi.responses import HTMLResponse

app = FastAPI(title="fastapi-gcp")
init_db(app)

@app.get("/", response_class=HTMLResponse)
def healthcheck():
    return "<h1>All good!</h2>"

实现 init_db 函数后,您的FastAPI服务器具有连接到数据库的功能。但是,在进行继续之前,我们需要创建第一个数据库模型,并首次运行Aerich迁移来初始化乌龟ORM集成。让我们创建我们的初始 Note 模型,它将对应于PostgreSQL数据库中的 notes 表。该表将存储带有文件名,标题和内容等属性的注释集合。

from tortoise import fields
from tortoise.models import Model

class Note(Model):
    id = fields.IntField(pk=True)
    created_at = fields.DatetimeField(auto_now_add=True)
    updated_at = fields.DatetimeField(auto_now=True)
    filename = fields.CharField(max_length=256)
    title = fields.CharField(max_length=1000)
    content = fields.TextField(default="")

现在,让我们通过运行来初始化Aerich迁移:

aerich init -t database.TORTOISE_ORM
aerich init-db
aerich migrate
aerich upgrade

接下来,我们将使用两个端点扩展我们的[main.py](http://main.py)服务器模块:一个在数据库中创建一个新注释,一个通过提供其标题来过滤现有注释:

from fastapi import FastAPI, HTTPException
from fastapi.responses import HTMLResponse
from pydantic import BaseModel

from database import init_db
from models import Note

app = FastAPI(title="fastapi-gcp")
init_db(app)

@app.get("/", response_class=HTMLResponse)
def healthcheck():
    return "<h1>All good!</h2>"

class CreateNotePayload(BaseModel):
    filename: str
    title: str
    content: str

@app.post("/notes")
async def create_note(payload: CreateNotePayload):
    note = await Note.create(**payload.dict())
    return {"message": f"Note created successfully with id {note.id}"}

@app.get("/notes/{title}")
async def get_note_by_title(title: str):
    if not (note := await Note.get_or_none(title=title)):
        raise HTTPException(status_code=404, detail="Note not found")

    return note.id

您可以利用Swagger或ReToc自动文档工具方便地测试端点和数据库连接。首先使用Post Endpoint创建一个新注释,然后使用GET端点来检索最近创建的注释的ID。如果这个过程成功,恭喜!您已经成功建立了FastAPI服务器和PostgreSQL数据库之间的连接。

部署到GCP

现在,我们已经开发了后端并建立了我们的FastAPI服务器和数据库之间的连接,让我们深入研究将应用程序部署到GCP(Google Cloud Platform)之类的云提供商的过程中。

将PostgreSQL DB部署到GCP

到目前为止,我们一直在与本地PostgreSQL Server合作测试我们的应用程序。但是,将我们的应用程序部署到云提供商时,必须使用在云中运行的PostgreSQL Server。幸运的是,您有几种实现此目标的选择。

最直接的选择是利用GCP提供的 Cloud SQL 服务。 Cloud SQL是一款完全管理的数据库服务,可为您处理PostgreSQL实例。另一个选项是使用我们以前用于本地实例使用的相同的PostgreSQL Docker映像在计算引擎实例上部署PostgreSQL数据库。

在计算引擎实例上部署时,Cloud SQL作为一个完全管理的解决方案具有多个优点:

  • 托管服务:使用Cloud SQL,Google负责基础架构,维护和更新,从而减轻您的这些责任。
  • 可伸缩性:Cloud SQL提供了自动可伸缩性来处理不同的工作负载。它允许您根据应用程序的要求轻松调整CPU,内存和存储等资源。可以使用最小的停机时间进行缩放,而无需提供其他虚拟机。
  • 备份和恢复:Cloud SQL提供数据库的自动备份,以确保数据的安全性。您可以毫不费力地恢复备份或执行时间恢复到过去的特定时刻。可以启用自动备份,并且可以根据您的需求设置保留期。

重要的是要注意,这些优势带有更高的成本。云SQL的定价取决于各种因素,包括负载,CPU使用时间,磁盘使用情况和内存使用情况。对于较小的数据库,使用云SQL的成本可能比在计算引擎实例上部署相同的数据库高10倍。为了估算不同选择的价格,您可以使用 GCP Pricing Calculator

无论您选择哪种部署选项,请确保在您之前创建的 .env 文件中更新数据库凭据,以匹配已部署的postgresql数据库的正确凭据。

容器化FastApi服务器

为了促进您在GCP上的应用程序的部署,建议使用Docker对其进行集装。这涉及创建一个安装诗歌的Dockerfile,将FastAPI服务器代码复制到图像中,并将Uvicorn Server配置为入口点。以下是完成这些任务的Dockerfile的一个示例:

FROM python:3.11

WORKDIR /app
ENV PYTHONFAULTHANDLER 1
ENV PYTHONUNBUFFERED 1
ENV PIP_NO_CACHE_DIR off
ENV PIP_DISABLE_PIP_VERSION_CHECK on

RUN curl -sSL https://install.python-poetry.org | python3 -
ENV PATH="/root/.local/bin:${PATH}"

COPY poetry.lock .
COPY pyproject.toml .

RUN POETRY_VIRTUALENVS_CREATE=false poetry install --only main --no-interaction --no-ansi

COPY . /app

EXPOSE 8080

CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8080"]

在此Dockerfile中,我们从官方的Python基本图像开始,将工作目录指定为 /app ,然后安装以管理项目依赖性的诗歌。接下来,我们将 poetry.lock pyproject.toml 文件复制到容器中,并使用诗歌安装项目依赖项。然后,我们将FastAPI服务器代码从主机上的根目录复制到容器内的 /app 目录。

假设FastApi服务器将在该端口上侦听,我们将公开端口8080。如果您的服务器使用其他端口,请确保相应地更新 EXPOSE 语句。

最后,我们设置了输入点命令以使用适当的参数运行UVICORN服务器。在此示例中,它运行 app 模块的 main fastapi实例,将其绑定到所有网络接口( 0.0.0.0 ),并在端口8080上liskens。

可以根据您的特定要求随意修改此Dockerfile,例如添加其他依赖项或环境变量。

创建了DockerFile后,您可以使用以下命令来构建图像:

docker build -t fastapi-gcp-backend:1.0 .

如果此构建过程中的诗歌安装步骤失败,请确保删除

packages = [{include = "fastapi_gcp"}]

如果有的话,请从您的pyproject.toml文件中行。这应该解决现有项目中诗歌安装的问题。

将图像推向GCP工件注册表

构建成功完成后,您可以将图像推到GCP人工制品注册表中,并在创建GCP云运行配置时选择它,以轻松部署您的应用程序。

要将图像推到GCP伪像注册表中,您应该使用适当的存储库名称标记它,并在身份验证到存储库后将图像推开。您可以了解有关authenticating to a GCP repository here的更多信息。然后,您应该这样标记您的图像:

LOCATION-docker.pkg.dev/PROJECT-ID/REPOSITORY/IMAGE

在我们的情况下,这将是:

us-east1-docker.pkg.dev/<your gcp project>/<your repo>/fastapi-gcp-backend

您应该在哪里填写GCP项目和项目存储库。我们可以标记并推动这样的图像,例如:

docker tag fastapi-gcp-backend:1.0 us-east1-docker.pkg.dev/<your gcp project>/<your repo>/fastapi-gcp-backend:1.0
docker push us-east1-docker.pkg.dev/<your gcp project>/<your repo>/fastapi-gcp-backend:1.0

将图像部署到GCP云运行

既然您的图像已成功地推到GCP文物注册表中,则可以在云运行配置中轻松地使用此图像。要配置新的云运行服务,您可以使用 https://console.cloud.google.com 的GCP Web控制台遵循以下步骤:

  1. 通过单击适当的菜单选项导航到云运行产品。
  2. 单击“创建服务”以开始配置过程。
  3. 在“创建服务”页面中,您将找到一个部分来指定容器映像。选择允许您从工件注册表中选择图像的选项。
  4. 从可用选项中选择刚刚推到文物注册表的图像。
  5. 根据您的要求配置其他设置,例如服务名称,区域,身份验证和缩放选项。
  6. 完成必要的配置后,单击“创建”以创建云运行服务。

Image description

在“身份验证”部分中,请确保选择“如果要创建公共API:

Image description

通过遵循以下步骤,您将能够配置使用伪影注册表中的容器映像的云运行服务。这使您可以轻松地将FastAPI应用程序部署到由GCP提供的可扩展和托管的环境中。

请注意,这些步骤可能会根据GCP Web控制台接口的任何更新而略有不同。始终以最新的说明参考GCP文档。

结论

总而言之,为您的应用程序建立强大的后端对于提供高质量的用户体验至关重要。 RESTFUL API提供了一种有效的方法,可以在Google Cloud Platform(GCP)等云平台上构建后端并部署它们,可以为服务器提供可扩展性和无状态性。在此博客文章中,我们探讨了在云部署上使用无状态REST服务器的好处,讨论了GCP上的云运行和App Engine之间的差异,并学习了如何使用FastPapi和PostgreSQL构建REST服务器,并将其部署到GCP Cloud Run。我们还看到了如何使用诸如Tortoise ORM(ORM)的对象关系映射(ORM)将数据库表映射到Python对象,并为与数据库进行交互提供了易于使用的API。通过遵循本文中概述的步骤,您可以构建并部署满足您应用程序要求的可扩展的RESTFUL API。

可能的改进

有几种优化此工作流程的方法,如果收益大于所需的额外工作,建议您探索这些选项。一些潜在的改进包括:

  • 使用Terraform以声明的方式定义GCP资源,例如云运行,计算引擎和CloudSQL。这允许自动部署和标准化的基础设施提供,从而减少了人为错误的可能性并提供了提高的可见性。
  • 考虑探索乌龟虫的替代方案。虽然乌龟的文档是全面的,但更大的异步ORMS(例如SQLModel)可以更好地提供更具体的查询。

源代码

您可以在此处找到所有源代码:https://github.com/GlennViroux/fastapi-postgres-gcp-blog

确保让我知道您是否有任何评论,问题或建议!

愉快的编码! :)