DaemonDaemonAI LAB
DAEMON / ARTICLE

ECS部署改ACR

2026.09.16 · 12 MIN READ

1. 整体流程

旧方式是在 ECS 上下载源码并构建镜像:

text
提交代码 → ECS 下载源码 → ECS 构建镜像 → 启动容器

这种方式构建慢,还会占用生产服务器资源。

现在改为:

text
提交代码
  → 云效构建业务镜像
  → 推送到 ACR
  → ECS 拉取镜像
  → 启动容器

各组件的作用:

组件作用
代码仓库保存代码和版本记录
云效流水线自动构建、推送和部署
ACR保存 Docker 镜像
ECS拉取并运行镜像
Docker Compose管理多个容器

核心原则:云效负责构建,ACR 负责保存,ECS 只负责运行。

部署脑图

text
ECS 部署 ACR
├─ 代码仓库
│  └─ 保存业务代码和 Git 版本
├─ 基础镜像
│  ├─ 本地脚本构建
│  ├─ 包含 Python、uv 和后端依赖
│  └─ 推送到 ACR
├─ 云效流水线
│  ├─ 克隆当前提交
│  ├─ 从 ACR 拉取 Server 基础镜像
│  ├─ 构建 Server、Web 业务镜像
│  ├─ 使用 Commit SHA 标记版本
│  ├─ 推送业务镜像到 ACR
│  └─ 上传部署制品
├─ ACR
│  ├─ 保存基础镜像
│  └─ 保存每次提交的业务镜像
└─ ECS
   ├─ 下载并解压部署制品
   ├─ 从 ACR 拉取指定版本
   ├─ Docker Compose 启动容器
   └─ Caddy → Web → Server → PostgreSQL

2. 一次性准备

首次搭建时需要准备:

这些内容只需配置一次,日常部署不需要重复操作。

3. 基础镜像和业务镜像

基础镜像

基础镜像保存不经常变化、下载比较慢的运行环境和依赖,例如:

text
Python + uv + 后端依赖

普通业务代码变化时,不需要重新制作基础镜像。

业务镜像

业务镜像包含当前提交的业务代码,由云效流水线构建并推送到 ACR。

Server 业务镜像的实际构建过程:

text
云效从 ACR 拉取 Server 基础镜像
  → 复制当前提交的后端业务代码
  → 构建 Server 业务镜像
  → 将业务镜像推送回 ACR

因此,基础镜像只负责提供 Python、uv 和后端依赖;每次提交的业务代码仍然由云效加入并生成新的业务镜像。

Web 当前直接使用 Node 基础镜像构建业务代码,然后将生成的 Web 业务镜像推送到 ACR。

每个业务镜像使用 Git Commit SHA 作为版本标签:

text
server:<commit-sha>
web:<commit-sha>

这样可以知道 ECS 运行的是哪次提交,也方便回滚。

Server 和 Web 必须使用同一个 ${CI_COMMIT_SHA}。部署时 ECS 也使用这个值拉取两个镜像,避免前后端版本不一致。

PostgreSQL、Caddy 等不包含业务代码的服务直接使用官方镜像,不需要为它们构建业务镜像。

4. 本地发布后端基础镜像

项目提供了本地发布脚本:

text
scripts/publish-server-base.ps1

在项目根目录执行:

powershell
pnpm docker:publish-server-base

脚本会自动:

  1. 根据后端依赖锁文件生成镜像版本。
  2. 登录 ACR。
  3. 构建基础镜像。
  4. 推送基础镜像到 ACR。
  5. 更新后端 Dockerfile 使用的基础镜像版本。

完成后,把依赖文件、基础镜像配置和脚本更新的 Dockerfile 一起提交:

powershell
git add server/pyproject.toml server/uv.lock server/Dockerfile.base server/Dockerfile
git commit -m "build(server): 更新后端依赖基础镜像"
git push

正确顺序是:先把基础镜像推送成功,再提交引用这个镜像版本的 Dockerfile。否则云效可能拉不到对应的基础镜像。

以下情况需要重新发布基础镜像:

只修改业务代码时,直接运行普通流水线即可。

Web 当前使用公共 Node 镜像和云效构建缓存,还没有独立的 Web 基础镜像发布脚本。如果以后依赖下载仍然经常很慢,可以按相同思路增加 Web 基础镜像。

5. 云效流水线

流水线主要步骤:

text
克隆代码
  → 打包部署文件
  → 从 ACR 拉取 Server 基础镜像
  → 加入后端代码,构建并推送 Server 业务镜像
  → 构建并推送 Web 镜像
  → 上传制品
  → 部署到 ECS

镜像流转过程:

text
ACR 基础镜像 → 云效构建业务镜像 → ACR 业务镜像 → ECS 拉取运行

镜像构建任务

两个业务镜像构建任务都使用项目根目录作为构建上下文:

任务Dockerfile推送版本
Serverserver/Dockerfile${CI_COMMIT_SHA}
Webweb/Dockerfile${CI_COMMIT_SHA}

Server 构建任务会通过 FROM 自动从 ACR 拉取基础镜像,再复制当前提交的后端代码。Web 构建任务会安装前端依赖并构建当前提交的页面。两个任务完成后,都会把业务镜像推送到 ACR。

云效构建缓存只用于加速构建,不是 ECS 部署的镜像版本。

打包部署文件

使用当前 Git 提交生成部署包,避免旧文件和缓存被重复打包:

bash
set -euo pipefail

ROOT_DIR="$(git rev-parse --show-toplevel)"
OUTPUT_FILE="$(pwd)/hahame.tgz"

rm -f "${OUTPUT_FILE}"

git -C "${ROOT_DIR}" archive --format=tar HEAD |
  gzip > "${OUTPUT_FILE}"

echo "打包提交:$(git -C "${ROOT_DIR}" rev-parse HEAD)"
tar -xOzf "${OUTPUT_FILE}" scripts/deploy-production.sh |
  grep "pull postgres server web caddy"

ls -lh "${OUTPUT_FILE}"

ECS 部署

云效先把 hahame.tgz 上传为制品,再由主机部署任务下载到 ECS。主机部署脚本需要:

  1. 清空上次解压目录,避免新旧文件混用。
  2. 解压本次制品。
  3. 确认其中包含当前版本的 compose.yml 和部署脚本。
  4. 进入解压后的项目目录。
  5. 执行仓库中的部署脚本:
bash
scripts/deploy-production.sh

如果制品平台在外层再次包装了 hahame.tgz,需要先解压外层,再解压里面的 hahame.tgz。不能直接复用 ECS 上一次遗留的源码目录。

部署脚本主要执行:

bash
docker compose pull postgres server web caddy
docker compose up -d --remove-orphans
docker compose ps

也就是先从镜像仓库拉取指定版本,再重建并启动容器。

部署脚本会使用云效传入的 CI_COMMIT_SHA 作为镜像版本,并在拉取私有镜像前登录 ACR。生产密码和密钥由云效变量传入,不保存在代码仓库中。

正常日志会出现:

text
Login Succeeded
server Pulled
web Pulled
server Healthy
web Healthy
caddy Started

如果日志中再次出现 [web build ...][server build ...],说明 ECS 仍在使用旧的现场构建方式。

6. 生产服务结构

text
浏览器
  → Caddy
  → Web
  → Server
  → PostgreSQL

生产环境的密码和密钥保存在云效加密变量中,不要写进代码或提交 .env

7. 日常操作

只修改业务代码

text
提交代码 → 推送 → 手动运行一条新流水线

当前关闭了代码提交自动触发,因此 git push 不会自动部署。需要在云效页面手动运行新流水线,不要重试旧提交的历史任务。

修改后端依赖

text
更新依赖
  → pnpm docker:publish-server-base
  → 提交 Dockerfile
  → 推送代码
  → 运行云效流水线

部署回滚

确认 ACR 中仍保留目标 Commit SHA 对应的 Server 和 Web 镜像,然后使用该 SHA 重新部署。不要把构建缓存标签当成业务版本。

8. 故障判断

现象通常表示
ECS 日志出现 Pulled正在使用 ACR 镜像部署
ECS 日志出现 [web build ...]仍在执行旧版 ECS 现场构建
提示找不到 compose.yml制品打包或解压路径错误
容器显示 unhealthy查看对应容器日志和健康检查
容器健康但域名无法访问检查安全组、DNS、HTTPS 和备案状态

9. 总结

text
基础镜像:解决依赖下载慢
业务镜像:保存每次提交的代码
云效:构建并推送镜像
ACR:保存镜像
ECS:拉取并运行镜像