部署到 AWS
自包含的 AWS、Cloudflare 与 Neon 生产部署说明。
Open Connector 的生产拓扑是一台运行 Docker Compose 的 AWS EC2 主机。Cloudflare Tunnel 为 API 与两个 SSR 站点提供公网入口;控制台 SPA 存在 S3 中并由 CloudFront 提供服务;Postgres 使用 Neon。机密存放在 AWS Secrets Manager,并由客户托管 KMS 密钥加密;非机密运行配置存放在 SSM Parameter Store。
Fly.io 不再是受支持的部署路径。该拓扑刻意不在 EC2 安全组开放应用端口或 SSH 端口。
准备条件
- 生产 AWS 账户的 AWS CLI 凭证、带 buildx 的 Docker,以及 OpenTofu 1.6+(也可使用兼容 Terraform 的工具)。
- 管理域名的 Cloudflare 账户,以及 Neon 生产数据库 URL。
- API、控制台、营销站和文档站各自的公开域名。
DATABASE_URL、BETTER_AUTH_SECRET、CONNECTOR_ENCRYPTION_KEY、TUNNEL_TOKEN和各公开 origin 的值。两个加密密钥使用openssl rand -base64 48生成。
生产拓扑
| 公网域名 | 入口 | 目标 | 用途 |
|---|---|---|---|
api.example.com | Cloudflare Tunnel | server:3000 | API、认证、OAuth 回调与 Broker 路由 |
www.example.com | Cloudflare Tunnel | web:3000 | 营销站 |
docs.example.com | Cloudflare Tunnel | fumadocs:3000 | 文档站 |
app.example.com | Cloudflare → CloudFront | 私有 S3 Bucket | 控制台 SPA |
EC2 主机运行 server、web、fumadocs、cloudflared,以及按需运行的 worker。Cloudflare Tunnel 和 AWS SSM 都是从主机向外建立连接,因此无需入站安全组规则。紧急维护通过 SSM Session Manager,而不是 SSH。
1. 创建 AWS 资源
从示例创建本地 terraform.tfvars,配置可管理 KMS 密钥的 ARN,然后创建资源:
cd deploy/terraform
cp terraform.tfvars.example terraform.tfvars
tofu init
tofu apply记录以下输出;首次部署和 GitHub Actions 都需要它们:
tofu output instance_id
tofu output ecr_registry
tofu output cloudfront_distribution_id
tofu output cloudfront_domain
tofu output github_actions_deploy_role_arn该步骤会创建 KMS 密钥、ECR 仓库、控制台所需的私有 S3 Bucket 与 CloudFront 分发、版本化 Blob Bucket,以及一台 arm64 EC2 主机和最小权限实例角色。主机首次启动时已安装 Docker、Compose、AWS CLI、生产 Compose 文件和拉取机密的脚本。
2. 写入运行配置与机密
创建被忽略的明文输入文件,填入必填值,再写入 AWS:
cd ../..
cp deploy/secrets.plain.env.example deploy/secrets.plain.env
AWS_REGION=us-east-1 deploy/scripts/seed-secrets.sh这个部署输入文件绝不能提交。脚本将敏感值写入 Secrets Manager 的 open-connector/prod/env/<NAME>,将托管 OAuth 凭证按 { "client_id": "…", "client_secret": "…" } 写入 open-connector/prod/oauth/<provider>,将公开 origin 和功能配置写入 SSM 的 /open-connector/prod-config/<NAME>。
主机启动时会把这些值写入 tmpfs 上的 /run/open-connector/secrets.env(权限 0600),再由 Compose 注入容器。长期保存的机密唯一事实来源是 Secrets Manager,而不是仓库、镜像或主机磁盘。
3. 配置公网入口
在 Cloudflare Zero Trust 创建 remotely managed Cloudflared Tunnel。把 token 写入 TUNNEL_TOKEN,重新运行机密写入命令,然后为 Tunnel 添加以下 public hostname:
| 域名 | 服务 URL |
|---|---|
| API 域名 | http://server:3000 |
| 营销站域名 | http://web:3000 |
| 文档站域名 | http://fumadocs:3000 |
控制台则创建一个指向 OpenTofu 输出 CloudFront 域名的代理 CNAME。CloudFront 必须将该域名配置为 Alternate Domain Name,并拥有在 us-east-1 验证过的 ACM 证书;否则 Cloudflare 转发的 Host header 会导致 403。
4. 部署
在仓库根目录导出实例和分发标识后运行:
INSTANCE_ID=i-... \
CF_DISTRIBUTION_ID=E... \
deploy/scripts/deploy.sh默认部署构建 arm64 镜像并推送到 ECR;在滚动 server 前迁移 Neon;发布控制台到 S3 并刷新 CloudFront;最后经由 SSM 拉取并重启所选服务。镜像以 Git SHA 标记,因此部分部署不会改变未选中的服务。
请使用包含改动的最小服务集合:
SERVICES=server deploy/scripts/deploy.sh # API + 数据库迁移
SERVICES=fumadocs deploy/scripts/deploy.sh # 仅文档 SSR
SERVICES=web deploy/scripts/deploy.sh # 仅营销 SSR
SERVICES=app deploy/scripts/deploy.sh # 仅 S3/CloudFront 控制台
SERVICES=server,worker deploy/scripts/deploy.sh手动触发的 Deploy (AWS) GitHub Actions 工作流也支持同样的服务名。该工作流需要 AWS_DEPLOY_ROLE_ARN、AWS_REGION、ECR_REGISTRY、INSTANCE_ID 和 CF_DISTRIBUTION_ID。
验证与日常运维
完整部署后检查 API 根路径、文档、控制台 shell 和营销站 sitemap。部署脚本会对每个所选服务做对应 smoke check。
curl --fail https://api.example.com/
# OK
curl --fail https://docs.example.com/
curl --fail https://www.example.com/sitemap.xml使用 SSM 进入主机或查看简要运行状态:
aws ssm start-session --target "$INSTANCE_ID"
aws ssm send-command --region us-east-1 --instance-ids "$INSTANCE_ID" \
--document-name AWS-RunShellScript \
--parameters 'commands=["cd /opt/open-connector && docker compose -f docker-compose.prod.yml ps"]'轮换机密时,更新被忽略的明文输入文件,再运行 seed-secrets.sh,最后重建受影响容器让它拉取新的 env-file。回滚时部署先前已发布的 Git SHA;ECR 会保留带标签镜像,只清理无标签镜像。
排错
| 现象 | 检查与处理 |
|---|---|
| 部署提示 SSM 超时 | 先检查 command invocation 与公网端点,再判定失败;主机滚动可能在 waiter 超时后完成。 |
SSR 启动时报 Invalid environment variables | VITE_* 是构建期输入。重新构建对应的 web/docs/app 镜像;只修改运行机密不会改变它们。 |
| 认证返回数据库列错误 | 生产 schema 落后于代码。部署 server 切片,让迁移阶段执行。 |
| 新增 Cloudflare 域名没有证书 | 边缘证书可能需要 30–40 分钟;这是 Cloudflare 传播,不是源站 TLS 错误。 |
| 控制台域名从 CloudFront 返回 403 | 检查 CloudFront Alternate Domain Name 与已验证的 us-east-1 ACM 证书。 |