使用 GitHub Actions 流水线自动化部署 FastAPI

本文详细介绍了如何为 FastAPI 应用构建一个基于 GitHub Actions 的自动化部署流水线,涵盖项目结构、环境配置、测试及部署步骤,旨在实现代码推送后自动运行测试并触发无服务器部署,提升开发效率。

使用 GitHub Actions 流水线自动化部署 FastAPI

手动部署 FastAPI 应用很快就会变得繁琐。你需要 SSH 到服务器、拉取最新代码、重启服务,并祈祷一切正常。也许你会记得先运行测试,也许不会。只要漏掉一个环境变量或跳过一项测试,你的 API 就可能宕机。用户会收到 500 错误,而你则需要手忙脚乱地重新 SSH 进去修复。

GitHub Actions 可以自动化整个部署流程。推送到主分支后,测试会自动运行,如果通过,你的应用就会部署。无需 SSH,无需手动步骤,不会遗漏检查。下面是如何进行设置。

FastAPI 应用结构

我们将使用一个简单的 FastAPI 应用来演示这个流水线。你的实际应用会更复杂,但部署过程保持不变。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
my-fastapi-app/
├── app/
│   ├── __init__.py
│   ├── main.py
│   └── routes.py
├── tests/
│   ├── __init__.py
│   └── test_api.py
├── requirements.txt
├── .github/
│   └── workflows/
│       └── deploy.yml
└── .env.example

这是一个位于 app/main.py 的基本 FastAPI 应用:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
from fastapi import FastAPI

app = FastAPI()

@app.get("/")
def read_root():
    return {"status": "healthy"}

@app.get("/api/users")
def get_users():
    return {"users": [{"id": 1, "name": "John"}]}

以及位于 tests/test_api.py 的一个简单测试:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
from fastapi.testclient import TestClient
from app.main import app

client = TestClient(app)

def test_root():
    response = client.get("/")
    assert response.status_code == 200
    assert response.json() == {"status": "healthy"}

def test_get_users():
    response = client.get("/api/users")
    assert response.status_code == 200
    assert "users" in response.json()

你的 requirements.txt 至少应包含:

1
2
3
4
fastapi==0.104.1
uvicorn==0.24.0
pytest==7.4.3
httpx==0.25.1

设置部署目标

在构建流水线之前,你需要一个部署目标。有许多选择,例如 Vercel、Netlify、Seenode、Railway 或 VPS。在本指南中,我们将使用 Seenode 作为选择的云提供商,因为它有一个简单的 API 来触发部署。

首先,在 Seenode 上创建一个新的 Web 服务并连接你的 GitHub 仓库。平台会自动检测 FastAPI 项目。配置你的构建和启动命令:

  • 构建命令: pip install -r requirements.txt
  • 启动命令: uvicorn app.main:app --host 0.0.0.0 --port 80
  • 端口: 80

手动部署服务一次以确保一切正常。如果你需要更多细节,可以遵循完整的 FastAPI 部署指南。

一旦你的服务运行起来,获取你的 API 凭证:

  • 生成 API 令牌:转到你的 Seenode 仪表板,导航到用户配置文件,选择 API 标签页。点击"创建新密钥"并为其命名,例如 “GitHub Actions Deploy”。复制该令牌,因为你只会看到一次。这是获取 API 令牌的官方指南。
  • 获取你的应用程序 ID:在 Seenode 仪表板中,打开你的 FastAPI 应用程序。应用程序 ID 会显示在应用程序设置中或 URL 中。

现在,将这些作为密钥添加到你的 GitHub 仓库:

  • 进入仓库的 Settings > Secrets and variables > Actions。
  • 点击"New repository secret"。
  • 添加 SEENODE_API_TOKEN 和你的 API 令牌。
  • 添加 SEENODE_APPLICATION_ID 和你的应用程序 ID。

这些密钥允许 GitHub Actions 在不将凭证暴露在代码中的情况下触发部署。

构建 GitHub Actions 流水线

在你的仓库中创建 .github/workflows/deploy.yml

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
name: Deploy FastAPI to Seenode

on:
  push:
    branches:
      - main

jobs:
  test:
    runs-on: ubuntu-latest

    steps:
      - name: Checkout code
        uses: actions/checkout@v4

      - name: Set up Python
        uses: actions/setup-python@v4
        with:
          python-version: '3.11'

      - name: Install dependencies
        run: |
          python -m pip install --upgrade pip
          pip install -r requirements.txt

      - name: Run tests
        run: pytest tests/ -v

  deploy:
    needs: test
    runs-on: ubuntu-latest

    steps:
      - name: Trigger Seenode Deployment
        run: |
          curl --fail-with-body -X POST \
            -H "Authorization: Bearer ${{ secrets.SEENODE_API_TOKEN }}" \
            -H "Content-Type: application/json" \
            -d '{"gitCommitSha": "${{ github.sha }}"}' \
            "https://api.seenode.com/v1/applications/${{ secrets.SEENODE_APPLICATION_ID }}/deployments"

盲目运行脚本是极其糟糕的做法,所以让我们分解一下这段代码的作用。

  • 触发器:on: push: branches: - main 意味着每次你推送到主分支时,工作流都会运行。你可以将其更改为 develop,或者如果你想的话,可以添加多个分支。
  • 测试作业:首先运行。它检出你的代码,设置 Python 3.11,安装你的依赖项,并运行 pytest。如果任何测试失败,整个工作流就会停止。部署永远不会发生。
  • 部署作业:needs: test 意味着该作业只有在测试作业通过后才会运行。它会向 Seenode 发起一个 API 调用,并附带你的提交 SHA。Seenode 拉取该特定提交,构建你的 FastAPI 应用,并部署它。
  • ${{ github.sha }} 变量是触发工作流的提交哈希值。Seenode 用它来确切地知道要部署哪个版本的代码。

为什么这种结构有效:测试充当了一个关卡。坏代码永远不会到达生产环境,因为流水线在测试阶段就停止了。你可以放心地推送,知道错误的更改不会被部署。

流水线在实际中如何工作

当你推送代码时会发生以下情况:

  • 你对 FastAPI 应用进行更改并推送到主分支。
  • GitHub Actions 自动启动工作流。
  • 测试作业运行(安装依赖项并执行你的测试套件)。
  • 如果测试通过,则触发部署作业。
  • GitHub Actions 向 Seenode 的 API 发送一个包含你提交 SHA 的 POST 请求。
  • Seenode 接收触发信号,从 GitHub 拉取你的最新代码,并开始部署。
  • Seenode 构建你的应用程序,处理数据库连接,并在零停机的情况下推出新版本。

你可以从 GitHub 仓库的 Actions 标签页实时查看这一切的发生。每个步骤都显示日志,因此如果出现故障,你可以准确看到位置和原因。

在 Seenode 这边,应用程序仪表板的 Deployments 标签页显示部署进度。你会看到构建日志、部署状态以及新版本上线的时间。

整个过程需要 2-5 分钟,具体取决于你的应用大小和测试套件。但关键是你无需观看。推送你的代码然后继续做其他事情。流水线会处理剩下的事情。

常见问题及解决方法

  • 测试失败并阻止部署:这是预期行为。检查 Actions 日志以查看哪个测试失败。在本地修复问题,再次推送,流水线会重新运行。不要为了更快部署而跳过测试。那样会让错误进入生产环境。
  • 部署步骤中的"身份验证错误":你的 SEENODE_API_TOKENSEENODE_APPLICATION_ID 有误。仔细检查添加密钥时是否复制正确。密钥不能有额外的空格或换行。如果需要,重新生成 API 令牌。
  • 工作流未触发:确保你推送到工作流文件中指定的分支。如果你的工作流写的是 branches: - main 但你推送到的是 masterdevelop,则什么都不会发生。要么推送到 main,要么更新工作流以匹配你的分支名称。
  • 依赖项安装缓慢:GitHub Actions 每次运行都会重新安装你的依赖项。添加缓存以加快速度:
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
- name: Cache pip packages
  uses: actions/cache@v3
  with:
    path: ~/.cache/pip
    key: ${{ runner.os }}-pip-${{ hashFiles('requirements.txt') }}

- name: Install dependencies
  run: |
    python -m pip install --upgrade pip
    pip install -r requirements.txt

这会在运行之间缓存你的 pip 包。首次运行较慢,后续运行会快得多。

总结

你现在拥有一个在每次推送到主分支时运行的部署流水线。测试保护生产环境。部署自动进行。你再也不需要 SSH 到服务器了。

这里的基本模式,即先测试再部署,也可以扩展到更复杂的场景。通过从 develop 分支部署来创建第二个工作流,从而添加一个暂存环境。通过在部署前在一个单独的作业中运行数据库迁移来添加它们。添加 Slack 通知,以便在部署成功或失败时收到通知。

一旦你掌握了自动化部署的理念,无论你的设置有多大,都可以让它工作起来。

干杯!

comments powered by Disqus
使用 Hugo 构建
主题 StackJimmy 设计