# FastAPI 项目 - 部署(Deployment) 你可以使用 Docker Compose 将本项目部署到远程服务器。 本项目假定你已经有一个 Traefik 代理,用于处理对外的 HTTP/HTTPS 流量以及证书管理。 你可以使用 CI/CD(持续集成与持续部署)系统自动部署,本项目已经提供了 GitHub Actions 的示例配置。 在此之前,你需要完成一些基础配置。🤓 ## 部署前准备 * 准备一台可访问的远程服务器。 * 将你的域名 DNS 记录解析到该服务器的 IP。 * 为该域名配置通配符子域名(Wildcard),例如 `*.fastapi-project.example.com`,以便为不同服务使用不同子域名,如 `dashboard.fastapi-project.example.com`、`api.fastapi-project.example.com`、`traefik.fastapi-project.example.com`、`adminer.fastapi-project.example.com` 等;同时也便于配置 `staging` 环境,比如 `dashboard.staging.fastapi-project.example.com`、`adminer.staging.fastapi-project.example.com` 等。 * 在远程服务器上安装并配置 [Docker](https://docs.docker.com/engine/install/)(Docker Engine,而非 Docker Desktop)。 ## 公共 Traefik 我们需要一个 Traefik 代理来处理外部请求以及 HTTPS 证书。 下面的步骤只需执行一次。 ### Traefik 的 Docker Compose * 在远程服务器上创建一个目录,用来存放 Traefik 的 Docker Compose 文件: ```bash mkdir -p /root/code/traefik-public/ ``` 在本地终端中使用 `rsync` 将 Traefik 的 Docker Compose 文件拷贝到服务器: ```bash rsync -a docker-compose.traefik.yml root@your-server.example.com:/root/code/traefik-public/ ``` ### Traefik 公共网络 Traefik 会期望有一个名为 `traefik-public` 的 Docker “公共网络”,用来与各个应用 stack 通信。 通过这种方式,可以用一个公共 Traefik 代理对外处理 HTTP/HTTPS 流量,其后可以挂载一个或多个不同域名的应用 stack,即便它们都在同一台物理服务器上。 在远程服务器中创建该网络: ```bash docker network create traefik-public ``` ### Traefik 环境变量 Traefik 的 Docker Compose 文件在启动前需要若干环境变量,你可以在远程服务器终端中通过以下命令设置: * 为 HTTP Basic Auth 创建用户名,例如: ```bash export USERNAME=admin ``` * 创建 Basic Auth 密码对应的环境变量,例如: ```bash export PASSWORD=changethis ``` * 使用 openssl 生成该密码的哈希值并保存到环境变量中: ```bash export HASHED_PASSWORD=$(openssl passwd -apr1 $PASSWORD) ``` 可以通过打印检查哈希是否已生成: ```bash echo $HASHED_PASSWORD ``` * 创建保存服务器主域名的环境变量,例如: ```bash export DOMAIN=fastapi-project.example.com ``` * 创建保存 Let's Encrypt 邮箱的环境变量,例如: ```bash export EMAIL=admin@example.com ``` **注意**:邮箱不能是 `@example.com` 这样的占位域名,需要使用真实邮箱。 ### 启动 Traefik Docker Compose 在远程服务器上进入存放 Traefik Docker Compose 文件的目录: ```bash cd /root/code/traefik-public/ ``` 在环境变量已经设置、`docker-compose.traefik.yml` 已就位的情况下,启动 Traefik: ```bash docker compose -f docker-compose.traefik.yml up -d ``` ## 部署 FastAPI 项目 在 Traefik 准备好之后,就可以使用 Docker Compose 部署 FastAPI 项目。 **提示**:你也可以直接跳到后面的「使用 GitHub Actions 持续部署」部分。 ## 环境变量 在部署前,需要设置若干环境变量。 设置 `ENVIRONMENT`,默认是 `local`(开发环境),部署到服务器时可以设置为 `staging` 或 `production`,例如: ```bash export ENVIRONMENT=production ``` 设置 `DOMAIN`,本地默认是 `localhost`,在部署时应改为你的实际域名,例如: ```bash export DOMAIN=fastapi-project.example.com ``` 你还可以设置以下变量: * `PROJECT_NAME`:项目名称,用于 API 文档及邮件内容中展示。 * `STACK_NAME`:在 Docker Compose 中使用的 stack 名称,也用于标签与项目名。该值在 `staging`、`production` 等环境中应各不相同。一个常见做法是将域名中的点替换为短横线,例如 `fastapi-project-example-com` 与 `staging-fastapi-project-example-com`。 * `BACKEND_CORS_ORIGINS`:允许的 CORS 源列表,使用逗号分隔。 * `SECRET_KEY`:FastAPI 项目的密钥,用于签发 token 等安全相关操作。 * `FIRST_SUPERUSER`:首个超级用户的邮箱地址,该用户可以创建其他用户。 * `FIRST_SUPERUSER_PASSWORD`:首个超级用户的密码。 * `SMTP_HOST`:SMTP 服务器地址,用于发邮件(如 Mailgun、Sparkpost、Sendgrid 等提供的地址)。 * `SMTP_USER`:SMTP 用户名。 * `SMTP_PASSWORD`:SMTP 密码。 * `EMAILS_FROM_EMAIL`:发件邮箱地址。 * `POSTGRES_SERVER`:PostgreSQL 服务器主机名。使用 Docker Compose 时可以保持默认值 `db`,除非你改为使用第三方数据库服务。 * `POSTGRES_PORT`:PostgreSQL 端口,通常保持默认即可。 * `POSTGRES_PASSWORD`:PostgreSQL 密码。 * `POSTGRES_USER`:PostgreSQL 用户名,一般使用默认值即可。 * `POSTGRES_DB`:本应用使用的数据库名称,默认 `app`。 * `SENTRY_DSN`:如果接入了 Sentry,这里填入 DSN。 ## GitHub Actions 环境变量 有一些环境变量只在 GitHub Actions 中使用,可以按需配置: * `LATEST_CHANGES`:用于 [latest-changes](https://github.com/tiangolo/latest-changes) GitHub Action,根据合并的 PR 自动生成发布日志。它需要一个个人访问令牌(PAT),具体可见其文档。 * `SMOKESHOW_AUTH_KEY`:用于 [Smokeshow](https://github.com/samuelcolvin/smokeshow) 发布测试覆盖率报告,同样需要按其说明生成一个(免费的)密钥。 ### 生成密钥 `.env` 文件中的一些环境变量默认为 `changethis`,需要改为真正的随机密钥。 可以使用下面的命令生成密钥: ```bash python -c "import secrets; print(secrets.token_urlsafe(32))" ``` 复制输出内容并用作密码 / 密钥;需要多个密钥时多执行几次即可。 ### 使用 Docker Compose 部署 环境变量准备就绪后,可以使用 Docker Compose 部署: ```bash docker compose -f docker-compose.yml up -d ``` 在生产环境中,一般不希望加载 `docker-compose.override.yml` 中的开发用覆盖配置,因此这里显式指定只使用 `docker-compose.yml`。 ## 持续部署(CD) 你可以使用 GitHub Actions 自动部署你的项目。😎 可以为多个环境配置不同的部署流程。 本项目中已经预置了 `staging` 与 `production` 两个环境。🚀 ### 安装 GitHub Actions Runner * 在远程服务器上为 GitHub Actions 创建一个用户,例如: ```bash sudo adduser github ``` * 为 `github` 用户添加 Docker 权限: ```bash sudo usermod -aG docker github ``` * 临时切换为 `github` 用户: ```bash sudo su - github ``` * 进入 `github` 用户的 home 目录: ```bash cd ``` * 按照官方文档安装 GitHub Action 自托管 Runner: * 在安装过程中,当被询问标签(labels)时,为 Runner 添加对应环境的标签,例如 `production`;也可以在后续再补充标签。 安装完成后,官方文档会让你手动运行一个命令以启动 Runner。但一旦你断开终端连接或进程退出,Runner 就会停止。 为了确保服务器重启后 Runner 仍能自动运行,建议将其安装为服务。具体步骤如下: * 从 `github` 用户退出,回到之前的用户: ```bash exit ``` 此时你会回到之前的用户及其当前目录。 * 在能访问 `github` 用户 home 目录之前,你需要切换成 `root` 用户(如果已经是则可略过): ```bash sudo su ``` * 在 `root` 用户下,进入 `github` 用户 home 下的 `actions-runner` 目录: ```bash cd /home/github/actions-runner ``` * 安装 Runner 为服务,并指定服务用户为 `github`: ```bash ./svc.sh install github ``` * 启动该服务: ```bash ./svc.sh start ``` * 查看服务状态: ```bash ./svc.sh status ``` 更多细节可参考官方文档: 「Configuring the self-hosted runner application as a service」 ### 配置 Secrets 在 GitHub 仓库中,为部署需要的环境变量配置 Secrets,包括前面提到的 `SECRET_KEY` 等。可参考官方文档: 「Using secrets in GitHub Actions」 当前的 GitHub Actions 工作流期望存在以下 Secrets: * `DOMAIN_PRODUCTION` * `DOMAIN_STAGING` * `STACK_NAME_PRODUCTION` * `STACK_NAME_STAGING` * `EMAILS_FROM_EMAIL` * `FIRST_SUPERUSER` * `FIRST_SUPERUSER_PASSWORD` * `POSTGRES_PASSWORD` * `SECRET_KEY` * `LATEST_CHANGES` * `SMOKESHOW_AUTH_KEY` ## GitHub Actions 部署工作流 在 `.github/workflows` 目录中已经包含了一些用于部署到不同环境(基于 Runner 标签)的 GitHub Actions 工作流: * `staging`:在推送(或合并)到 `master` 分支后触发。 * `production`:在发布 Release 后触发。 如果你需要更多环境,可以以上述工作流为模板进行扩展。 ## URLs 下面示例中出现的 `fastapi-project.example.com` 请替换为你自己的域名。 ### 主 Traefik 控制台 Traefik UI:`https://traefik.fastapi-project.example.com` ### 生产环境(Production) 前端:`https://dashboard.fastapi-project.example.com` 后端 API 文档:`https://api.fastapi-project.example.com/docs` 后端 API 基础地址:`https://api.fastapi-project.example.com` Adminer:`https://adminer.fastapi-project.example.com` ### 预发布环境(Staging) 前端:`https://dashboard.staging.fastapi-project.example.com` 后端 API 文档:`https://api.staging.fastapi-project.example.com/docs` 后端 API 基础地址:`https://api.staging.fastapi-project.example.com` Adminer:`https://adminer.staging.fastapi-project.example.com`