返回文章

从0开始手把手教你《阿里云arc自动部署go+nuxt全栈项目》

本文教会新手小白从购买ECS服务器开始,搭建流水线自动部署Go+Gin+MySQL+Redis+nux3全栈项目。

收录于专栏 · Go 学习笔记

从零开通阿里云 ECS,用 Gitee Go 自动部署 Skeryb

适用对象:第一次接手本项目、准备从空环境上线的开发者。基线:Alibaba Cloud Linux 3、x86_64、2 核 2G、Docker Compose。核对日期:2026-11-12。

完成后,推送版本 tag 即可触发:Gitee Go 构建 API/Web → 推送阿里云 ACR → ECS Agent 更新运维脚本 → 拉取镜像 → 备份、迁移、启动 → 健康检查通过后发布。Nginx、MySQL、Redis、Go API、Nuxt Web 全部运行在 Docker 中。

本文从购机开始,依次说明首次上线、日常发布、备份恢复和常见故障,所需命令均在正文给出。已有生产配置和数据库时沿用,不重复生成密钥。ACR(容器镜像服务)用于保存构建后的镜像。

阅读方式与准备清单

  • 按第 1~13 节顺序完成首次上线;第 14~16 节用于维护和排错。
  • 标有“本地”的命令在你自己的电脑执行;标有“ECS root”的命令在服务器执行。换终端后变量不会自动保留,按第 7 节重新加载。
  • 每段命令出现错误就先停下来,按对应成功标志检查,不要把后续命令一起粘贴运行。
  • YOUR_...、www.example.com、you@example.com 都是需要替换的示例,不是本项目提供的账号或域名。
  • 历史截图来自项目部署时的真实操作,已裁切或脱敏;标有“历史反例”的图不能原样照填。官方截图注明来源,配置示意图不冒充控制台截图。

需要准备:阿里云个人账号及实名认证、Gitee 账号、项目完整源码、自己可管理的域名、本地 Git、可登录 ECS 的终端。服务器费用、域名费用、流量和可选 ACR 企业版费用以控制台下单页为准。

阶段 做什么 完成标志
1~3 购买 ECS、开放端口、安装 Docker、解析域名 能 SSH 登录,Docker 可用,两个域名指向 ECS
4~6 建自己的 Gitee 仓库、开通 ACR、配置部署公钥 两个镜像仓库存在,ECS 能读取 Git HEAD
7~8 生成生产配置、保存 ACR 凭据、申请证书 MySQL/Redis 健康,证书申请成功
9~11 安装 Agent、配置变量与流水线 主机在线,引用变量正确,拉取代码开启
12~13 推送 tag、检查构建和线上结果 Deployment healthy,健康接口 SHA 一致

图 1:首次部署顺序。首次初始化完成后,日常发布只走 tag 流程。

1. 开通阿里云账号与 ECS

1.1 注册、实名认证和地域

打开阿里云控制台,注册账号并完成实名认证。在控制台搜索“云服务器 ECS”,进入实例列表,选择“创建实例 / 自定义购买”。不要把“轻量应用服务器”当作 ECS,本指南按 ECS 控制台编写。

本文示例选中国香港。需要中国内地节点时,先处理网站域名 ICP 备案要求;ECS 和 ACR 尽量选同一地域。域名购买地不决定服务器地域。

1.2 购买页逐项选择

图 2:购机字段示意,非控制台截图。官方当前购买说明见自定义购买 ECS。

购买项 本文选择 说明
计费方式 按预算选择包年包月或按量付费 检查总费用、续费和公网费用,不照搬活动价格
地域 中国香港,示例 创建后不能直接把现有实例改成其他地域
架构 / 规格 x86_64,2 vCPU / 2 GiB 不选 ARM;镜像发布时会检查 amd64
镜像 官方基础镜像 Alibaba Cloud Linux 3 使用基础镜像,不预装其他应用环境
系统盘 建议 60 GiB 留出容器、版本与成对备份空间;至少保持 20 GiB 可用
公网访问 分配公网 IPv4,有出网能力 私网 IP 不能用作公网 DNS 记录
带宽 / 流量 按预算设置非零公网能力 访问速度、镜像下载和费用都受它影响
网络 同一地域 VPC、交换机 首次可按控制台提示创建默认网络
登录凭证 设置密码或绑定自己的登录密钥 这是登录 ECS 的凭证,与 ACR/Gitee 密码不同
实例名称 例如 skeryb-production 方便区分用途,不是 Gitee 主机 UUID

确认配置和费用后下单。在实例列表等待状态变为“运行中”,记下公网 IPv4、地域和实例 ID。只开通一台即可完成本教程,不需要同时购买 RDS、Redis 云数据库或负载均衡。

**2 核 2G 的边界:**本项目让构建在 Gitee 执行,ECS 运行五个常驻容器。Compose 配置的容器内存上限合计约 1 GiB,操作系统、Docker、Agent 和短期任务仍需额外内存。它适合作为学习或低访问量站点起步,容量以实际监控为准;不要再在这台 ECS 上编译 Nuxt。

1.3 安全组开放哪些端口

在 ECS 实例详情找到“网络与安全组 → 安全组”,进入该安全组的“入方向 → 添加规则”。

协议 / 端口 来源 用途
TCP 22 你当前的公网 IP /32 SSH 管理服务器;换网络后需更新来源
TCP 80 0.0.0.0/0 HTTP 与证书 HTTP-01 验证
TCP 443 0.0.0.0/0 HTTPS 网站

3000、8080、13306 不对公网开放。MySQL 绑定 127.0.0.1:13306,Redis 不映射主机端口;需要查库时走第 14 节 SSH 隧道。Agent、Git、Docker 还需能访问外网 HTTPS;Git SSH 使用出方向 22,保留相应出方向访问能力。

**成功标志:**安全组已绑定目标 ECS,22 的来源是你的管理 IP,80/443 已允许。安全组和服务器内部防火墙是两个位置;如果 systemctl is-active firewalld 显示 active,还要在主机防火墙开放网站服务。

2. 登录 ECS,安装 Docker

2.1 从本地连接

【本地】把下面的 IP 换成第 1 节的公网 IP:

ssh root@YOUR_ECS_PUBLIC_IP

首次连接核对主机指纹,使用购买时设置的登录凭证。部分镜像默认登录用户不是 root,可先用该用户登录,再执行 sudo -i。也可使用 ECS 控制台“远程连接 → Workbench”进入终端。

【ECS root】确认系统和架构:

whoami
cat /etc/os-release
uname -m
command -v dnf
free -h
df -h /

应看到 root、Alibaba Cloud Linux 3、x86_64 和可用的 dnf。**历史踩坑:这套系统不是 Ubuntu,直接执行 apt-get 会报命令不存在。**如果装了其他系统,应使用该系统的安装步骤,不要混用软件源。

2.2 安装 Docker 与 Compose

【ECS root】逐条执行:

dnf install -y dnf-plugins-core git python3 rsync util-linux logrotate openssh-clients curl bind-utils
dnf config-manager --add-repo https://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo
sed -i 's/\$releasever/8/g' /etc/yum.repos.d/docker-ce.repo
dnf install -y docker-ce docker-ce-cli containerd.io docker-compose-plugin
systemctl enable --now docker
docker version
docker compose version
git --version
python3 --version

sed 只调整新加入的 Docker CE 软件源,不修改系统其他仓库。安装命令仅适用于前面确认的 Alibaba Cloud Linux 3。主机无需安装 Go、Node、npm 或 Docker Buildx。

若 firewalld 处于 active,再执行:

firewall-cmd --permanent --add-service=http
firewall-cmd --permanent --add-service=https
firewall-cmd --reload

成功标志:docker version 同时显示 Client 和 Server,docker compose version 能输出版本。后续脚本会检查 up --wait --wait-timeout、up --pull never 等必要参数。

2.3 可选:给 2G 内存增加 2G swap

【ECS root】先查看,不重复创建:

swapon --show
ls -l /swapfile-skeryb

第二条若提示文件不存在,且第一条没有已有 swap,可执行下面这一个完整代码块:

if [ ! -e /swapfile-skeryb ]; then
  fallocate -l 2G /swapfile-skeryb &&
  chmod 600 /swapfile-skeryb &&
  mkswap /swapfile-skeryb &&
  swapon /swapfile-skeryb &&
  printf '%s\n' '/swapfile-skeryb none swap sw 0 0' >> /etc/fstab
fi
free -h
swapon --show

swap 是运行余量,不是重新回到 ECS 在线构建的理由。若创建失败先看磁盘空间,不要反复追加 /etc/fstab。

3. 准备域名与 DNS

本项目证书脚本按 www 站点和裸域名同时申请证书,例如 www.example.com 与 example.com。域名可以在阿里云或其他注册商购买,DNS 控制台不必和服务器是同一家。

没有域名时,在阿里云搜索“域名注册”,查询名称、选后缀、核对首年与续费价格后购买,按注册商要求完成信息验证。购买完成后进入“域名列表”,确认当前 DNS 服务商;在它的解析页面操作。仅有 ECS 公网 IP 不能完成本文的双域名证书步骤。

在域名当前使用的 DNS 服务商添加两条 A 记录:

记录类型 主机记录 记录值
A @ ECS 公网 IPv4
A www 同一个 ECS 公网 IPv4

不要填 https://、路径或服务器私网 IP。若有指向旧服务器的 A/AAAA 记录,应先调整;不要保留错误的 IPv6 解析让证书验证走到别处。首次可用普通 DNS 解析,先排除 CDN 或代理回源配置的干扰。

【ECS root】把示例域名换成自己的,等待查询结果均为本机公网 IP:

dig +short A example.com
dig +short A www.example.com
dig +short AAAA example.com
dig +short AAAA www.example.com

**成功标志:**两个 A 记录正确,没有错误 AAAA;80/443 入方向允许。此时还没启动网站,浏览器打不开首页不代表 DNS 配错。

4. 把项目放进自己的 Gitee 仓库

4.1 创建仓库

先取得本项目完整源码及使用权限;作者的私有仓库地址不等于读者自动拥有访问权。可以导入已有仓库,也可以把完整源码上传到自己新建的 Gitee 私有仓库。

在 Gitee 创建仓库,名称可用 skeryb-blog,分支为 main。上传已有项目时,新仓库可不勾选初始化 README,避免产生两份互不相同的初始历史。确认以下内容都在仓库中:

.workflow/ReleaseImages.yml
deploy/release-from-agent.sh
deploy/deploy-acr.sh
deploy/compose.production.yaml
deploy/ci/backend.Dockerfile
deploy/ci/web.Dockerfile
scripts/prepare-production.sh
backend/
web/

4.2 给本地 Git 配置推送权限

已有本地 SSH 公钥且能推送该仓库时,直接进入下一小节。否则先在【本地】终端执行下面的完整代码块;已有密钥不覆盖:

mkdir -p ~/.ssh
chmod 700 ~/.ssh
if [ ! -f ~/.ssh/id_ed25519 ]; then
  ssh-keygen -t ed25519 -C 'skeryb-local-git' -f ~/.ssh/id_ed25519
fi
cat ~/.ssh/id_ed25519.pub

生成新密钥时按提示选择是否设置口令。把公钥整行加到 Gitee 个人设置 → SSH 公钥,再运行 ssh -T git@gitee.com 验证账号认证。首次连接核对 Gitee 官方 SSH 指纹。本地个人公钥用于推送;第 6 节 ECS 的仓库部署公钥只读,两者不要混用。

4.3 推送源码

【本地】已使用 Git 管理且已有 origin 的项目,进入实际源码目录后执行:

cd ~/workspace/skeryb-blog
git status --short
git remote -v
git remote set-url origin git@gitee.com:YOUR_GITEE_NAME/skeryb-blog.git
git push -u origin main

目录、用户名和仓库名换成自己的。需要提交本地改动时,先检查暂存内容再提交。若拿到的是不带 .git 的源码包,用下面这一组初始化;已有 Git 历史不要重复执行本组:

cd ~/workspace/skeryb-blog
git init -b main
git config user.name 'YOUR_DISPLAY_NAME'
git config user.email 'you@example.com'
git add .
git diff --cached --name-only
git diff --cached --check
git commit -m 'chore: initialize Skeryb project'
git remote add origin git@gitee.com:YOUR_GITEE_NAME/skeryb-blog.git
git push -u origin main

本地 Git 推送权限与下一节 ECS 的只读权限分开配置。本地用自己的账号或 SSH 密钥推送;ECS 用部署公钥读取。不要上传 .env、var/、数据库、私钥或实际密码。源码包里也要保留隐藏的 .workflow 和 .gitignore。

**成功标志:**Gitee 网页的 main 分支能看到上面的文件。此时先不推生产 tag,等 ECS 初始化与 Agent 配好后再触发发布。

5. 开通阿里云 ACR,创建两张镜像的仓库

5.1 进入正确产品

在阿里云控制台搜索“容器镜像服务 ACR”,打开ACR 控制台,选择与 ECS 相同的地域。

图 3:历史实操中的实例列表,个人版和企业版入口不同。页面文案可能随账号和版本变化。

本案例按个人学习环境使用“个人版”。官方当前限制:个人实名认证账号使用,单账号 ACR 个人版实例数量限额为一个,地域以控制台为准;已有实例时先检查它,不能假设能在每个地域再创建一个。个人版官方定位为开发测试、无 SLA;正式生产保障需求应评估企业版及其费用。企业实名认证或资源目录账号不符合个人版条件时,不要为了继续教程随意购买付费规格。

选择个人版入口,按提示创建实例。若页面只有“设置 Registry 登录密码”,先完成它,再进入命名空间和仓库页面。

图 4:Registry 密码设置入口。它不是阿里云网站登录密码,也不是 ECS root 密码。

5.2 创建命名空间与 API/Web 仓库

在实例内进入“仓库管理 → 命名空间”,创建自己的命名空间,例如 skeryb。再进入“镜像仓库 → 创建镜像仓库”,创建两次:

项目 API Web
命名空间 skeryb 同左
仓库名称 skeryb-api skeryb-web
仓库类型 私有 私有
代码源 本地仓库 本地仓库
ACR 自动构建 不启用 不启用

**历史踩坑:ACR 代码源里找不到 Gitee。**本方案由 Gitee Go 构建并推送,ACR 只存成品镜像,所以选“本地仓库”即可,不需要绑定 Gitee 代码源。“本地仓库”是推送来源类型,并不是把线上构建放回你自己的电脑。

5.3 记录四个值

在实例“访问凭证”取得用户名并设置 Registry 密码;在镜像仓库“基本信息 / 操作指南”复制公网登录地址。

后面使用的变量 从哪里取得 格式
ACR_PUBLIC_REGISTRY 仓库操作指南中的公网登录地址 只有域名,不带 https:// 或命名空间
ACR_NAMESPACE 刚创建的命名空间 例如 skeryb
ACR_USERNAME ACR 访问凭证页 按控制台显示的完整用户名填写
ACR_PASSWORD 自己设置的 Registry 密码 后面存入 Gitee 敏感变量与 ECS 凭据文件

新个人版域名可能是 crpi-...cn-hongkong.personal.cr.aliyuncs.com,老实例也可能是其他格式。**以自己控制台显示的地址为准,不复制历史截图里作者的专属域名。**若使用 VPC 拉取地址,必须来自同一个实例、同一地域,并确认 ECS 网络可达;首次用同一实例的公网地址即可跑通。

**成功标志:**同一命名空间下有 API、Web 两个私有仓库,已取得四个值。空仓库还没有镜像版本是正常的。

6. 给 ECS 配置 Gitee 只读部署公钥

6.1 先分清这几种身份

图 5:ECS 登录、Git 读取、镜像仓库登录、Agent 注册各管一件事。Agent 在线不会自动赋予私有仓库读取权限;docker login 也不负责 Git 认证。

6.2 生成或复用 root 的密钥

【ECS root】执行以下完整代码块。已有默认密钥时保留,不覆盖:

install -d -m 700 /root/.ssh
if [ ! -f /root/.ssh/id_ed25519 ]; then
  ssh-keygen -t ed25519 -C 'skeryb-ecs-deploy' -f /root/.ssh/id_ed25519 -N ''
fi
cat /root/.ssh/id_ed25519.pub

复制输出中以 ssh-ed25519 开头的一整行。打开自己的 Gitee 项目“管理 → 部署公钥 / 部署公钥管理 → 添加公钥”,标题填 skeryb-ecs,粘贴整行公钥,使用只读权限并保存。只添加 .pub 的内容,私钥留在 ECS。

图 6:Gitee 官方部署公钥管理配图。这里添加的是 ECS 公钥,默认只读,适合自动拉取代码。

本项目 Agent 按 root 运行,因此配置的是 /root/.ssh;给普通用户或本地电脑加过公钥,不代表 root 已配置。若复用的密钥带口令,需要让 Agent 的 SSH 环境能非交互使用它,或配置单独的部署密钥。

6.3 验证访问的是自己的仓库

【ECS root】替换 Gitee 用户名与仓库名:

git ls-remote git@gitee.com:YOUR_GITEE_NAME/skeryb-blog.git HEAD

首次连接核对 Gitee 官方 SSH 指纹,确认后按提示接受。成功时显示“40 位提交 SHA + HEAD”,这比只执行 ssh -T 更能证明具体仓库权限已配好。

图 7:真实踩坑。Permanently added ... known hosts 只是记录 Gitee 服务器身份,后面的 Permission denied (publickey) 才是失败点。此时应补公钥授权,不是重新输入 ACR 密码。

如果仍失败,检查公钥是否加到了当前仓库、Agent 是否以 root 运行、/root/.ssh/config 是否指定了其他密钥。可用下面的命令只指定本次密钥验证,再根据结果调整 SSH 配置:

GIT_SSH_COMMAND='ssh -i /root/.ssh/id_ed25519 -o IdentitiesOnly=yes' \
  git ls-remote git@gitee.com:YOUR_GITEE_NAME/skeryb-blog.git HEAD

这条只影响本次诊断;最终还要让前面的普通 git ls-remote 也成功,Agent 才能沿用同一默认配置。

7. 首次生成并安装生产配置

7.1 填写自己的安装参数

【ECS root】将以下六项改成自己的。只保存公开的部署参数,不把 Registry 密码写进这里:

export SITE_DOMAIN='www.example.com'
export SOURCE_URL='git@gitee.com:YOUR_GITEE_NAME/skeryb-blog.git'
export ACR_ECS_REGISTRY='YOUR_ACR_PUBLIC_REGISTRY'
export ACR_NAMESPACE='skeryb'
export CERT_EMAIL='you@example.com'
export SITE_ORIGIN="https://$SITE_DOMAIN"

umask 077
declare -p SITE_DOMAIN SOURCE_URL ACR_ECS_REGISTRY ACR_NAMESPACE CERT_EMAIL SITE_ORIGIN \
  > /root/skeryb-install.env

SITE_DOMAIN 必须用自己已解析的 www 域名,CERT_EMAIL 是自己能收邮件的邮箱,用于证书联系信息,不是到阿里云某处领取的邮箱。ACR 地址仅填域名,不能包含协议、命名空间或镜像名。

后面新开 ECS 终端时先运行:

source /root/skeryb-install.env

7.2 拉取源码,生成独立生产凭据

【ECS root】这是首次从 main 获取安装脚本,不是在 ECS 编译:

git clone --branch main --depth 1 "$SOURCE_URL" /opt/skeryb-blog
cd /opt/skeryb-blog
test -f deploy/release-from-agent.sh
test -f deploy/compose.production.yaml
bash scripts/prepare-production.sh \
  "$SITE_DOMAIN" \
  "$SOURCE_URL" \
  "$ACR_ECS_REGISTRY" \
  "$ACR_NAMESPACE"
bash deploy/install.sh var/production-config

/opt/skeryb-blog 已存在时先确认是不是同一个项目,不删除目录强行重来。配置脚本只在首次使用:生成数据库、Redis、JWT、后台管理员等独立生产口令;已有输出会拒绝覆盖,避免重置密钥。

httpd:2.4-alpine 在此仅用于生成 bcrypt,不是网站服务。首次 Docker 拉取它超时,要处理镜像网络问题,不需要在 ECS 安装 Go 来替代。运行中的五个常驻服务不包含这个工具容器。

7.3 安装完成后目录是什么

路径 内容 后续发布是否重置
/opt/skeryb-blog 首次安装源码;Agent 可能使用自己的 workspace 不依赖在此手工拉取最新代码
/www/skeryb/ops 运维脚本、生产 Compose Agent 随本次 SHA 更新
/www/skeryb/shared env、证书、凭据、运行状态 保留
/www/skeryb/data/mysql MySQL 物理数据 保留
/www/skeryb/data/media 上传图片 保留
/www/skeryb/releases 每次发布的 Compose 与镜像引用 按版本保存
/www/skeryb-backups SQL、图片与应用的成对备份 独立保存和轮换

生产后台用户名是 admin,初始密码在 /opt/skeryb-blog/var/production-config/credentials.txt。部署者私下查看并保管;不要放进文档截图、Git 或流水线日志。它与本地 var/admin-credentials.txt 不是同一份口令。

7.4 保存 ECS 自动登录 ACR 的凭据

【ECS root】执行一次,按提示输入第 5 节记录的用户名和 Registry 密码:

bash /www/skeryb/ops/configure-acr.sh

密码输入不显示字符是正常的。脚本保存到 root 所有、权限 600 的 /www/skeryb/shared/acr.env;后续发布经标准输入自动登录,无需每次手工 docker login。此命令只保存凭据,不代表两张镜像已经存在或已经拉取成功。

**历史踩坑:**改过 Registry 密码后,Gitee 的 ACR_PASSWORD 和 ECS 的 acr.env 都需要同步。只在自己终端登录过 Docker,也不能保证另一用户运行的 Agent 使用了同一份凭据。本项目统一由 root Agent 读取明确的凭据文件。

8. 初始化 MySQL、Redis 和 HTTPS

8.1 检查端口,再启动依赖

【ECS root】先检查是否有旧服务占用:

ss -lntp | grep -E ':(80|443|13306)\b' || true
docker ps --format 'table {{.Names}}\t{{.Image}}\t{{.Ports}}'

新机器通常为空。若看到已有容器 0.0.0.0:13306->3306,会与本项目 MySQL 冲突,即便本项目只绑定 127.0.0.1。先确认占用服务和数据归属;已有主机 Nginx 也可能占用 80/443。端口冲突解决后再继续,不删除数据库或数据卷来强行启动。

端口就绪后执行:

/www/skeryb/ops/compose.sh up -d --wait mysql redis
/www/skeryb/ops/compose.sh ps

**成功标志:**MySQL、Redis 为 healthy。MySQL 只在空数据目录执行初始化 SQL;修改 env 不会自动修改已有数据库账号密码。

8.2 申请证书

【ECS root】确认第 3 节两条 DNS 和 80/443 均已就绪:

source /root/skeryb-install.env
/www/skeryb/ops/init-https.sh "$CERT_EMAIL"

脚本先启动 HTTP 挑战入口,再申请 www 和裸域名证书,最后切到 HTTPS 配置。成功日志包含 HTTPS certificate installed。若验证失败,先看错误中具体哪个域名失败,核对 A/AAAA、80 端口及旧 Nginx,不反复重新生成生产配置。

此时尚未发布 API/Web,首页返回 503 可以是正常的初始化状态;完成第 12 节后才按整站健康结果验收。

9. 开通 Gitee Go,添加 ECS 主机

9.1 进入流水线服务

打开自己的 Gitee 项目,在“服务 → Gitee Go”进入流水线。首次按账号页面开通,构建额度和功能可用性以账号套餐显示为准。找不到入口时,先确认自己拥有仓库管理权限,查看项目或组织的流水线设置,不要转到 ACR 代码源里找 Gitee。

主机管理通常在 Gitee Go 的“主机管理 / 资源管理”;组织版也可能在“管理 → 功能设置 → 主机管理”。这是 Gitee 的主机组,与 ECS 控制台的“运维与监控”不是同一个入口。

9.2 新建主机组并关联当前仓库

本指南使用“自主导入”,直接安装 Gitee Agent。主机注册使用平台生成的命令,无需填写阿里云 AccessKey。

图 8:历史实操的新建主机组页面,选择“自主导入”。

图 9:填写组名、标识、操作系统和仓库作用域。

字段 示例 / 选择
主机组名称 skeryb-production
主机组标识 skerybproduction,记录下来,后面填 hostGroupID.ID
操作系统 Linux
仓库作用域 勾选你自己的 skeryb-blog;有项目作用域时同步关联

9.3 安装 Agent

图 10:建组后主机列表为空,点击“添加 Linux 主机 / 添加主机”。

平台会生成包含当前主机组注册信息的 Linux 安装命令。复制自己页面生成的完整命令,到 ECS root 终端执行。注册令牌随组生成,不能从他人的截图复制,也不存在本文可以通用于所有账号的固定安装命令。

安装目录可选择 /opt/gitee-agent,或沿用生成命令指定的目录。按页面命令完成安装、启动,等待 Gitee 主机列表显示“在线”,并启用“任务开关”。不要在文档、提交记录或公开截图中保存注册令牌。

**成功标志:**组中能看到目标 ECS 的主机名,状态在线,任务允许执行。此时只能证明 Agent 能接收任务,Git 权限还以第 6 节验证为准。安装命令中若只做首次后台启动,还应按平台提供的方式配置开机启动;服务器重启后再次确认在线,不能把 Docker 的自启等同于 Agent 自启。

10. 配置 Gitee Go 通用变量

10.1 创建变量,不在运行弹窗里临时填

在当前项目或组织的 Gitee Go“通用变量”页面添加以下四项,值来自第 5 节:

名称,必须完全一致 值 类型
ACR_PUBLIC_REGISTRY ACR 公网登录域名 文本
ACR_NAMESPACE 自己的命名空间 文本
ACR_USERNAME ACR 访问凭证中的用户名 密码 / 加密等敏感类型
ACR_PASSWORD Registry 登录密码 密码 / 加密等敏感类型

如果页面以“文本 + 加密开关”的方式提供,开启敏感值保护;保存后不应在列表里明文展示密码。不要把 ${ACR_PASSWORD} 这个引用文本当作默认值,默认值应是实际凭据。

图 11:Gitee 官方配图,裁切自通用变量文档。界面版本可能不同,重点是选择正确变量类型。

图 12:历史踩坑截图,值已脱敏。四个变量“已经存在”不代表流水线已经引用;图中敏感项使用文本类型也需要修正。

10.2 让流水线真正引用变量

顶层 YAML 必须包含:

ref_variable:
  - ACR_PUBLIC_REGISTRY
  - ACR_NAMESPACE
  - ACR_USERNAME
  - ACR_PASSWORD

图 13:Gitee 官方“引用通用变量”配图。若使用 YAML,按上面的 ref_variable 引用即可。

历史踩坑:“运行流水线 → 变量设置”是本次运行输入,不能代替通用变量引用,也不能修复构建任务的认证模式。

11. 配置完整流水线与主机绑定

11.1 新建或导入流水线

Gitee Go → 新建流水线。若支持从仓库导入,选择 .workflow/ReleaseImages.yml;若只能选择模板,可先选“镜像构建”,创建后切换到代码视图,用本节完整 YAML 替换默认内容。

图 14:选择镜像构建模板,只是进入编排的入口,后面仍需配置 API、Web 和部署三个任务。

图 15:历史表单。流水线文件使用 ReleaseImages.yml;图中自动生成的默认分支触发规则需要替换。确保只启用一份生产流水线,避免一个 tag 触发多次发布。

11.2 在图形编排里取得自己的主机 ID

在 API、Web 构建阶段后增加“自动部署 ECS”阶段,在该阶段添加 Agent 分类下的“Shell 脚本执行”,对应官方 shell@agent。

图 16:添加任务入口,使用在目标主机运行的 Shell 任务。

任务中选择第 9 节创建的主机组,只勾选这一台 ECS。脚本类型选择 Shell,超时设为 30 分钟,重试次数设为 0;开启“是否克隆代码 / 拉取代码”。切到代码视图,复制平台生成的:

hostGroupID:
  ID: 你的主机组标识
  hostID:
    - 平台生成的主机UUID

hostID 不是 ECS 的 i-... 实例 ID,不是公网 IP,也不是主机名。其他读者必须换成自己的值,不能直接使用本项目当前绑定的作者机器。

图 17:历史反例。图中旧脚本只调用 /www/skeryb/ops/deploy-acr.sh,且“拉取代码”关闭。当前必须改用 release-from-agent.sh 并开启拉取。

11.3 完整 YAML

下面是完整配置,可以直接复制到 .workflow/ReleaseImages.yml。把 YOUR_HOST_GROUP_ID、YOUR_GITEE_AGENT_HOST_UUID 换成上一步从页面取得的值。四个 ACR 值通过通用变量注入,不要把实际密码写进 YAML。

# 示例文件:替换主机组标识和 Agent UUID;ACR 凭据在通用变量中配置。
version: "1.0"
name: release-images
displayName: 生产镜像
stages:
- name: 构建并推送镜像
  displayName: 构建并推送镜像
  strategy: naturally
  trigger: auto
  executor: []
  steps:
  - step: build@docker
    name: build_api_image
    displayName: 构建 API 镜像
    type: account
    repository: "${ACR_PUBLIC_REGISTRY}"
    username: "${ACR_USERNAME}"
    password: "${ACR_PASSWORD}"
    tag: "${ACR_NAMESPACE}/skeryb-api:${GITEE_COMMIT}"
    dockerfile: ./deploy/ci/backend.Dockerfile
    artifacts: []
    isCache: false
  - step: build@docker
    name: build_web_image
    displayName: 构建 Web 镜像
    type: account
    repository: "${ACR_PUBLIC_REGISTRY}"
    username: "${ACR_USERNAME}"
    password: "${ACR_PASSWORD}"
    tag: "${ACR_NAMESPACE}/skeryb-web:${GITEE_COMMIT}"
    dockerfile: ./deploy/ci/web.Dockerfile
    artifacts: []
    isCache: false
- name: 自动部署ECS
  displayName: 自动部署ECS
  strategy: naturally
  trigger: auto
  steps:
  - step: shell@agent
    name: deploy_ecs
    displayName: Shell 脚本执行
    schedulerType: DISTRIBUTED
    scriptType: SH
    script: |-
      #!/bin/bash
      set -eu

      # Agent 检出本次提交;先同步运维文件,再运行 ACR 中的同版本镜像。
      bash deploy/release-from-agent.sh '${GITEE_COMMIT}'
    gitClone: "true"
    hostGroupID:
      ID: YOUR_HOST_GROUP_ID
      hostID:
      - YOUR_GITEE_AGENT_HOST_UUID
    strategy:
      retry: "0"
      timeout: 30
triggers:
  trigger: auto
  push:
    tags:
      prefix: []
      precise: []
      include:
      - "^v[0-9]+\\.[0-9]+\\.[0-9]+$"
notify: []
ref_variable:
- ACR_PUBLIC_REGISTRY
- ACR_NAMESPACE
- ACR_USERNAME
- ACR_PASSWORD

保存网页编排,同时把最终配置同步回仓库 .workflow/ReleaseImages.yml 并提交。新手最容易出现的情况是“网页编辑了一份、仓库保留另一份”:后续运行前核对构建记录实际使用的文件和提交。

11.4 保存前核对这六项

  1. 两个 build@docker 都有 type: account。缺省会走仓库凭证 cert 模式;页面没“认证方式”选项时直接改 YAML,不需要盲找凭证下拉框。
  2. repository 只有 ${ACR_PUBLIC_REGISTRY};tag 是 ${ACR_NAMESPACE}/skeryb-api:${GITEE_COMMIT} 或 Web 对应项,不重复拼域名。
  3. Dockerfile 是 deploy/ci/backend.Dockerfile、deploy/ci/web.Dockerfile,构建上下文为仓库根目录。
  4. 四个 ACR_* 已在顶层 ref_variable 引用;语法为 ${变量名},不是 ${{变量名}}。
  5. shell@agent 绑定自己的主机,gitClone: "true",运行 bash deploy/release-from-agent.sh '${GITEE_COMMIT}'。
  6. 触发器为 v数字.数字.数字 tag,不是默认的“所有分支 push”。正则若放在 YAML 单引号里,点写 \.;放在双引号里需按 YAML 规则转义,不能只看界面里的反斜线数量判断。

${GITEE_COMMIT} 是 Gitee 系统参数,替换为本次完整提交 SHA,不需要自己创建同名通用变量。v1.0.0 是触发版本;两张镜像使用它指向的提交 SHA 作为镜像 tag。

12. 推送第一个版本,观察自动部署

12.1 最后一次检查初始化是否就绪

此时应同时满足:ECS Docker 可用;两条 DNS 正确且证书已申请;MySQL/Redis healthy;生产 env 已安装;ACR 凭据保存;SSH 读取自己的仓库成功;Agent 在线;流水线引用变量、绑定主机和拉取代码均正确。

【本地】代码有业务改动时,先准备 Go 1.27.3+、Node 22.22+、Python 3、Git、Make 和已启动的 Docker。macOS 的 Go race 测试还需要 Xcode Command Line Tools;Windows 建议使用 WSL2,并开启 Docker 的 WSL 集成。

在自己的源码根目录执行;首次初始化后,再运行后两条检查:

cd ~/workspace/skeryb-blog
make init
make check
make check-mysql

make init 依次生成本地配置、启动 MySQL/Redis、执行迁移和 seed、安装前端依赖;已有配置不会覆盖。make check 包含格式、静态检查、Go race、前端类型及单元测试;make check-mysql 使用并清理独立的 skeryb_test 测试库。它们都在开发机器完成,已有生产数据不能拿来做测试,不在 2G ECS 上编译。

12.2 提交并推送 tag

【本地】进入自己的源码目录。下面 v1.0.0 必须换成未使用过的版本:

cd ~/workspace/skeryb-blog
git status --short
git diff --check
git tag --list

若修改了源码或流水线,先检查、提交并推送;已有干净提交时跳过这组提交命令:

git add .
git diff --cached --name-only
git diff --cached --check
git diff --cached --stat
git commit -m 'chore: configure automated deployment'
git push -u origin main

确认后创建版本:

git tag -a v1.0.0 -m 'Release v1.0.0'
git push origin v1.0.0
git rev-list -n 1 v1.0.0

记下最后输出的完整 SHA。不要移动、覆盖已发布 tag;后续修复创建新版本。仅推 main 不会触发本文的生产流水线。

12.3 在 Gitee 看构建和部署日志

图 18:构建、脚本同步、镜像拉取、备份和健康切换的实际顺序。

在本次构建记录确认:触发源是新 tag;API、Web 均成功;两张镜像写入同一实例和命名空间,tag 都是第 12.2 节 SHA。

图 19:历史 API 仓库的镜像版本。

图 20:Web 仓库也要有对应版本。不要因为其中一张镜像成功就认为整站可部署。

自动部署任务应依次出现:

Agent checkout verified: <本次完整SHA>
Updating operations for commit: <同一SHA>
Operations updated for commit: <同一SHA>
Logging in to ACR automatically
Pull succeeded: .../skeryb-api:<同一SHA>
Pull succeeded: .../skeryb-web:<同一SHA>
Checking Docker Compose command support
Paired backup verified: predeploy/...
Applying database migrations
Seeding initial records
Starting application services
Checking release health
Deployment healthy: <同一SHA>

第一次无旧版本时,备份内容与后续版本可能略有不同。Offsite destination is not configured 表示当前只有本地恢复点,不是这次失败原因;要做异地备份需另配目标。真正的成功标志是最后的 Deployment healthy,不是“镜像已拉取”或“备份完成”。

13. 验收网站、数据库和后台

13.1 运行结构

图 21:常驻服务和端口关系。公开访问走 Nginx,服务间走 Docker 网络。

【ECS root】执行:

source /root/skeryb-install.env
/www/skeryb/ops/compose.sh ps
curl -fsS "$SITE_ORIGIN/api/health"
curl -fsS "$SITE_ORIGIN/healthz"
curl -s -o /dev/null -w '%{http_code}\n' "$SITE_ORIGIN/api/v1/admin/posts"
readlink -f /www/skeryb/current
docker stats --no-stream
free -h
df -h / /www

应看到五个常驻容器 healthy;两个健康接口返回成功且 release 等于本次完整 SHA;匿名后台接口返回 401。健康接口不是浏览器功能验收,还需打开网站测试首页、后台登录、保存草稿、发布、图片和一篇文章的公开访问。

后台地址为 https://自己的www域名/admin。用户名 admin,初始密码由第 7 节生成。首次登录后修改密码;修改后的密码以后台设置为准,旧 credentials.txt 不会自动更新。

13.2 MySQL 怎么建库建表

  1. 空数据目录首次启动 MySQL:创建 skeryb 数据库及应用/备份账号。
  2. 发布脚本用已拉取的 API 镜像执行 migrate:按版本执行程序内嵌的 SQL,记录校验和并加迁移锁;不会使用 GORM AutoMigrate 随启动改生产表。
  3. 再执行 seed:补齐首个管理员、分类、专栏、工具和设置;不会清空已有文章或重置已有管理员。

【ECS root】只读查看表与迁移记录:

/www/skeryb/ops/compose.sh exec -T mysql mysql \
  --defaults-extra-file=/etc/skeryb/mysql-backup.cnf \
  --database=skeryb \
  -e 'SHOW TABLES; SELECT * FROM schema_migrations;'

当前源码基线为 19 张表、001/002/003 三条迁移;未来新增迁移后数量会变化。历史 SQL 不修改,升级结构新增 migration。MySQL DDL 不能整批事务回滚,迁移状态不明时按第 16 节处理。

14. 备份、续证和 DBeaver 查库

14.1 首次成功后启用定时任务

【ECS root】

systemctl enable --now skeryb-backup.timer skeryb-cert-renew.timer
systemctl list-timers --all 'skeryb-*'

手工验证一次备份:

/www/skeryb/ops/backup.sh daily
cat /www/skeryb/shared/runtime/backup-status.txt
ls -lt /www/skeryb-backups/daily

应用发布前也会备份。正常上线后的副本包括 SQL、同一时点的图片、应用 Compose、镜像引用和 API/Web 镜像归档;默认保留 7 个日备份、4 个周备份和 4 个发版前备份。生产 env 和 TLS 私钥不在副本中,需要单独加密保管。

14.2 DBeaver 通过 SSH 隧道连接生产库

【本地】把 IP 换成 ECS 公网 IP,终端保持运行:

ssh -N -L 13307:127.0.0.1:13306 root@YOUR_ECS_PUBLIC_IP

在 DBeaver 新建 MySQL 连接:主机 127.0.0.1、端口 13307、数据库 skeryb、用户名 skeryb。数据库应用密码在 ECS 的 /www/skeryb/shared/backend.env 中;查看 MYSQL_DSN 的 skeryb:密码@tcp(...),取冒号与 @tcp 之间的部分,与初始化 SQL 同步生成。由部署者私下取得,不是 ECS root 密码、ACR 密码或网站 admin 密码。

本地转发用 13307,是为了不与开发环境的 13306 冲突。测试连接成功后点击“完成 / Finish”,不是等待灰色的 Next 变亮;打开 Database Navigator,展开数据库和 Tables 查看数据。终端停止隧道后连接会断开。

图 22:历史开发环境截图,密码区域已处理;生产连接按本节使用 13307,图中的旧端口不能照抄。

14.3 校验备份与恢复数据

先在独立环境演练。恢复会覆盖 SQL 和图片,备份时点之后的数据会丢失,并撤销已有登录。以下命令适用于已有有效 current 版本、且没有其他发布或恢复任务的环境;已有维护标记时先核查来源,不直接删除。

【ECS root】先制作当前恢复点:

/www/skeryb/ops/backup.sh daily
ls -lt /www/skeryb-backups/daily

从列表选择要恢复的历史目录,把下面的 YYYYMMDDTHHMMSSZ 换成该目录名。校验通过只说明文件完整,仍需确认它是需要的数据时点:

cd /www/skeryb-backups/daily/YYYYMMDDTHHMMSSZ
sha256sum -c SHA256SUMS

确认恢复目标后再执行;--confirm-restore 表示明确接受数据覆盖:

/www/skeryb/ops/restore.sh \
  /www/skeryb-backups/daily/YYYYMMDDTHHMMSSZ \
  --confirm-restore

脚本会校验副本、进入维护、停止 API/Web、导入 SQL、同步图片,再执行迁移、撤销会话、启动和健康检查。它使用 current 指向的应用,不会自动切回备份里的旧镜像;历史数据会按当前应用迁移到所需结构。应用与数据版本不兼容、DDL 执行状态不明时,先在独立环境确认方案。恢复失败会保留维护,日志和容器检查命令见第 16.4 节。

**成功标志:**出现 Paired data restored,五个常驻容器健康,维护解除;按第 13 节重新验收,并检查文章、图片和后台登录。

14.4 异地副本和证书续期

本机备份不能应对整机丢失。准备另一台备份主机后,让 ECS root 能通过 SSH 密钥访问该主机,并提前核对主机指纹;在 /www/skeryb/shared/deploy.env 增加一行,替换其中的账号、主机和实际目录:

OFFSITE_DEST=backup-user@backup-host:/encrypted/skeryb/

该配置让备份脚本通过 rsync 复制副本。目标主机需安装 rsync、允许该账号写入目标目录,脚本远程复制所用的 daily、predeploy 子目录应提前建好;远端副本的保留周期单独设置。目录名 encrypted 只是示例,静态加密需要由备份主机另行配置。生产配置、TLS 私钥和恢复所需源码也应独立加密保管,并定期演练取回。

【ECS root】修改后手动运行一次 /www/skeryb/ops/backup.sh daily,再到备份主机核对副本和 SHA256SUMS。新服务器上的整机重建还需要安装 Docker、还原私密配置、准备兼容的应用版本和有效 current;不能仅复制 MySQL 物理目录替代恢复演练。

证书续期由 timer 触发。需要手动检查时执行:

systemctl list-timers --all 'skeryb-*'
/www/skeryb/ops/renew-cert.sh

续期时仍需两个域名解析正确、80 端口可访问;成功后从浏览器检查 HTTPS 证书有效期。

15. 后续发布与凭据变更

15.1 正常发布不再 SSH 操作 ECS

【本地】完成代码验证、提交和推送后,选择未使用的新版本:

git tag -a v1.0.1 -m 'Release v1.0.1'
git push origin v1.0.1
git rev-list -n 1 v1.0.1

等待 Gitee 两个构建任务和自动部署任务成功,再验收健康 SHA 与页面。Agent 会从该次提交检查 SHA、加锁、安装对应 ops、调用发布入口。**修改部署脚本也会自动同步,不需要每次在 /opt/skeryb-blog 手工 git pull。**新增配置项仍应按该版本说明补齐,安装器不会覆盖现有密钥。

生产发布统一由 Gitee Agent 调用 release-from-agent.sh。生产 Compose 只有镜像引用;若 ECS 日志出现 BuildKit 构建信息,按第 16 节检查任务入口和实际使用的 Compose。

15.2 Registry 密码改了,更新两处

  1. Gitee Go 的敏感变量 ACR_PASSWORD:供 API、Web 两个构建任务推送镜像。
  2. ECS root 重新运行 bash /www/skeryb/ops/configure-acr.sh:供部署时自动登录并拉镜像。

只更新一处,会出现“构建推送成功但 ECS 拉取失败”,或反过来。若 Registry 用户名也改了,两边一并更新。公钥或 Agent 注册失效属于另一类问题,按第 16 节对应处理。

15.3 更新代码会不会重置 admin 密码

不会。管理员密码保存在持久化 MySQL 中,正常更新镜像、执行迁移和 seed 都不会覆盖已有密码。后台改密或手工重置后,继续使用新密码;credentials.txt 仍是初始记录,不会自动更新。

恢复旧数据库备份可能让密码回到备份时的值;清空数据库重新初始化也会改变账号状态。不要为了找回密码重新生成生产配置、删除数据库或反复执行 seed;忘记密码按第 16.5 节处理。

16. 历史踩坑与问题处理索引

以下整理自项目部署中的实际故障及修复记录,按报错定位对应步骤。先找到第一条致命错误,再处理对应层,不用每次重装整套环境。

16.1 构建、变量与认证模式

现象 / 历史问题 原因与处理 验证
通用变量里明明有,任务里却为空或仍显示 ${ACR_...} 顶层缺 ref_variable、名称不一致,或运行的不是新 YAML 新运行中 ACR 域名、命名空间已展开;不打印密码
${{ACR_...}} 没被替换,invalid reference format 当前 Gitee 插件使用 ${变量名} 按第 11 节完整示例修改,创建新运行
DOCKER_REPOSITORY 为空或仍出现 CERTIFICATION...server 默认走 cert 凭证模式;API 和 Web 都补 type: account repository 为 ACR 域名,日志不再保留凭证模板
页面没有“认证方式 / 仓库凭证”选项 图形表单与插件版本存在差异 在 YAML 设置 type: account;不反复寻找不存在的选项
镜像地址重复出现两次 Registry 域名 tag 错填了完整地址 repository 只放域名,tag 只放命名空间/镜像名:SHA
API 修好了,Web 仍认证失败 两个任务参数独立,不能只改一个 API/Web 都配置 account、变量引用与各自 Dockerfile
找不到 go.mod 或 package.json 构建上下文与 COPY 路径不一致 使用 deploy/ci/ 的 Dockerfile,仓库根目录上下文
修改变量后重试仍报旧错 重试旧记录可能仍使用旧配置 / 旧 tag 从含修复的新提交创建新版本;核对实际使用的 SHA

16.2 镜像下载与构建慢

现象 原因与处理 验证
2 核 2G ECS 构建半小时、内存和 swap 压力大 旧方案把 Nuxt/Go 编译放到 ECS;现改由 Gitee Go 构建 ECS 日志只有登录、pull、迁移、启动,不再 build
卡在 load metadata for ...,报 insufficient_scope 先看 FROM 的基础镜像地址,不要立刻认定是自己的 ACR 密码 当前 CI 使用 DaoCloud 官方代理路径;核对镜像及版本存在
把镜像改成 registry.aliyuncs.com/library/... 后仍无权限 它不是 Docker Hub 官方镜像的通用代理 使用当前 CI 文件中的完整 FROM 地址
docker.mirrors.ustc.edu.cn 无法解析 使用了已不可用的旧地址 保留当前镜像源,不从过期教程复制镜像前缀
go mod download 超时 默认访问模块源受网络影响;在 ECS 执行 go env -w 不影响 CI 容器 当前 CI Dockerfile 内配置 ARG GOPROXY="https://goproxy.cn|https://goproxy.io",从新提交构建
getcwd 或插件 has no attribute 'message' 可能是工作目录提示或失败后的二次报错 向上找第一条实际 BuildKit / 网络 / COPY 错误,不只看最后一行

图 23:历史在线构建时的资源截图,仅用于说明为何迁到 CI;它不代表新方案的容量测试结论。基础镜像、Go 模块代理、npm registry 与私有 ACR 是不同下载链路,要按失败地址定位。

16.3 ECS 镜像拉取与首次初始化

现象 原因与处理 验证
pull access denied / unauthorized 地址、账号、密码、命名空间、仓库权限任一不符;或改过密码只更新了 Gitee ECS 重存 ACR 凭据,新任务中两张镜像都 Pull succeeded
手工 docker login 成功,Agent 仍失败 登录用户 / Docker 凭据目录不同 Agent 以 root 运行,自动入口读取明确的 shared/acr.env
manifest unknown 该 SHA 的镜像没推完、tag 填成版本号、命名空间错误 ACR 两个仓库都有同一完整提交 SHA
已有 ACR 成品,ECS 却在 build 任务入口或 Compose 文件不正确 使用 Agent + release-from-agent.sh,生产 Compose 无 build
13306 端口占用,出现 docker-proxy 历史环境的 mysql:9.0.1 容器将 13306 绑定到 0.0.0.0,与新 MySQL 冲突 查清旧数据后停止旧服务,重新检查监听;不删旧库强行抢端口
80/443 占用 已有主机 Nginx 或其他容器占用网站端口 先用 ss -lntp 和 docker ps 确认归属,处理冲突后再启动
MySQL Access denied,改 env 没有效果 env 仅用于首次空库建账号,已有数据库密码不会自动变 对照现有账号与原配置处理,不删除数据目录重来
证书验证失败或不知道邮箱填什么 两个域名未同时解析、80 不通,或使用了示例邮箱 www/裸域 A 与 AAAA 正确;使用自己可收信的邮箱
admin 登录失败 将本地初始密码当成线上密码、后台已经改密,或账号停用 私下查生产 credentials;忘记当前密码按第 16.5 节重置

排查端口和资源的只读命令:【ECS root】

ss -lntp | grep -E ':(80|443|13306)\b' || true
docker ps --filter publish=13306 --format 'table {{.ID}}\t{{.Names}}\t{{.Image}}\t{{.Ports}}'
docker stats --no-stream
docker system df
free -h
df -h / /www

16.4 Agent、公钥、旧脚本与维护状态

现象 原因与处理 验证
不知道主机组创建后下一步做什么 创建组还没有把 ECS 纳管 添加 Linux 主机,执行平台生成命令,等在线并开启任务
主机在线,任务不执行 仓库作用域、任务开关、选中主机或 hostID 不正确 用平台生成的组 ID 与 Agent UUID,不能填 ECS 实例 ID
Permission denied (publickey) root 的 SSH 公钥未授权,或 SSH 选了其他密钥 ECS root 的普通 git ls-remote ... HEAD 成功
known_hosts 已添加,仍说不能读取仓库 主机指纹确认与用户认证是两回事 按第 6 节补公钥,不更换 ACR 密码
Agent 找不到 release-from-agent.sh 代码拉取关闭、目标提交不含新脚本,或未在仓库根目录运行 开启 gitClone,发布含新入口的提交,检查任务检出目录
Agent checkout ... does not match image tag ... 运维源码与构建镜像不是同一提交 核对 tag 指向、源码检出和 GITEE_COMMIT,不拿 main 最新脚本配旧镜像
改了代码打新 tag,仍报旧 unknown flag: --pull 旧任务只调用 ECS 上安装的旧 ops,没有同步脚本 新任务必须先出现 Operations updated for commit;只重建镜像修不好主机脚本
备份完成、停止 API/Web 后报 unknown flag: --pull 部分 Compose 的 up 支持 --pull,run 不支持;旧迁移命令解析失败,尚未执行 SQL 当前脚本已去掉 run 的 --pull,并在维护前预检;保留 up --pull never
Maintenance retained,API/Web 为 Exited 后半段部署失败后保留维护;current 未切换不等于容器已恢复 查失败阶段,确认迁移状态,修复后重跑;不要直接删维护标记
Another deployment/backup is running 正常的互斥锁阻止并发发布 等现有任务结束或确认中断;不绕开锁并行迁移

!

图 24:真实失败点。前面的镜像拉取与成对备份已经通过,继续检查密码或内存并不能修复 run --pull 参数错误。

【ECS root】查看状态和最近日志:

/www/skeryb/ops/compose.sh ps -a
/www/skeryb/ops/compose.sh logs --tail=100 api web mysql nginx
ls -lt /www/skeryb/shared/logs
readlink -f /www/skeryb/current
readlink -f /www/skeryb/previous
test ! -e /www/skeryb/shared/runtime/maintenance || \
  cat /www/skeryb/shared/runtime/maintenance

若已确认是“参数解析前失败”,已安装修正版 ops,且相应两张镜像已在 ACR,可由部署者应急接续该 SHA;这不是日常发布步骤,也不会重新构建镜像:

read -rp '输入失败任务的完整40位提交SHA: ' FAILED_SHA
bash /www/skeryb/ops/deploy-acr.sh "$FAILED_SHA"

若 SQL 已开始执行或状态不明,不盲目补发、回滚或清表。先核验日志、迁移记录与备份;需要恢复数据时按第 14.3 节检查前提、确认恢复点并先演练。自动部署失败不会自动回退数据库或恢复旧容器。

16.5 找到初始密码,但 admin 仍无法登录

【ECS root】先私下查看生产凭据;文件在其他目录时,将路径替换为首次安装时的实际位置:

sed -n '/^生产后台/p' /opt/skeryb-blog/var/production-config/credentials.txt

页面提示“用户名或密码错误”时,可能是密码已变更、记录与数据库不对应,或账号停用;提示“登录尝试过多”时先等待 15 分钟。数据库只保存 bcrypt 摘要,无法读回当前明文密码。

忘记当前密码时,在 ECS 的 SSH / Workbench 交互式终端整段执行下面代码,输入两遍 12~72 字节的新密码,输入时不会显示字符。它仅更新仍启用的 admin 管理员并撤销其旧会话,不修改文章、图片或其他账号;使用现有容器,无需重新构建镜像:

sudo bash <<'BASH'
set -Eeuo pipefail
set +x
source /www/skeryb/ops/common.sh
require_root
exec 9>"$SHARED/operations.lock"
flock -n -x 9 || { echo '正在部署或备份,请结束后再试。'; exit 1; }
[[ ! -e "$MAINTENANCE_FILE" ]] || { echo '站点正在维护,请先核查失败任务。'; exit 1; }

read -r -s -p '输入新密码(12~72字节):' new_password </dev/tty
printf '\n' >/dev/tty
read -r -s -p '再次输入新密码:' confirmation </dev/tty
printf '\n' >/dev/tty
[[ "$new_password" == "$confirmation" ]] || { echo '两次输入不一致,未修改密码。'; exit 1; }

password_hash=$(printf '%s\n' "$new_password" |
  compose_for "$(current_release)" exec -T api skeryb-api hash-password)
unset new_password confirmation
[[ "$password_hash" =~ ^\$2[aby]\$12\$[./A-Za-z0-9]{53}$ ]] || {
  echo '生成密码摘要失败,未修改密码。'; exit 1;
}

changed=$(
  printf '%s\n' \
    'START TRANSACTION;' \
    'SET @uid=NULL;' \
    "SELECT id INTO @uid FROM users WHERE username='admin' AND role='admin' AND active=1 FOR UPDATE;" \
    "UPDATE users SET password_hash='$password_hash' WHERE id=@uid;" \
    'SET @changed=ROW_COUNT();' \
    'UPDATE auth_sessions SET revoked_at=UTC_TIMESTAMP(3) WHERE user_id=@uid AND revoked_at IS NULL;' \
    'COMMIT;' \
    'SELECT @changed;' |
  compose_for "$(current_release)" exec -T mysql mysql \
    --defaults-extra-file=/etc/skeryb/mysql-backup.cnf \
    --batch --skip-column-names "$DB_NAME"
)
[[ "$changed" == 1 ]] || { echo '未找到启用的 admin 管理员,请核对账号。'; exit 1; }
echo 'admin 密码已重置,旧登录会话已撤销。'
BASH

**成功标志:**出现“admin 密码已重置”。刷新登录页,用 admin 和新密码登录,并把新密码保存在自己的密码管理器中。若没有找到启用的管理员,先核查 users 表中的用户名、角色和启用状态,不自动创建或启用账号。

17. 最终对照清单与资料

检查项 正确结果
环境 Alibaba Cloud Linux 3 / x86_64,Docker 与 Compose 可用
网络 www / 裸域名指向当前 ECS,80/443 可用,数据库未对公网开放
ACR 同一实例下 API/Web 两个仓库,同一完整 SHA tag
Git ECS root 能非交互读取自己仓库,部署公钥已授权
Agent 正确组、作用域、UUID、在线和任务开关;拉取代码开启
变量 四项已引用;敏感值受保护;两构建任务均为 account 模式
发布 Agent 检出 SHA、ops 更新 SHA、镜像 SHA、健康接口 SHA 一致
运行 五个常驻容器健康,后台匿名 401,浏览器发布和图片访问正常
维护 新发布成功后维护解除;后续只推新 tag,不回 ECS 构建
恢复 本地备份、密钥保管、续证 timer 已检查;异地副本另行配置

本文命令对应项目源码中的 deploy/compose.production.yaml、scripts/prepare-production.sh、deploy/release-from-agent.sh、deploy/deploy-acr.sh 和 .workflow/ReleaseImages.yml。升级这些文件时,应同时核对命令参数和发布顺序。

官方资料:

配图说明:图 1、2、5、18、21 为根据项目部署逻辑绘制的示意图;图 6、11、13 引用 Gitee 官方 SSH 公钥和通用变量说明页面,图注中已标明来源;其余为项目实际操作截图,已裁切并遮挡敏感值。历史截图用于说明入口和故障,实际配置以正文为准。

本文按上述日期的代码与官方资料整理。控制台入口和套餐可能变化,以同名功能和当前官方说明为准;完成命令后仍需核对本次发布日志、健康接口和浏览器结果。

如需转载,请联系 skeryb001@gmail.com。

LEAVE A THOUGHT

评论 3

不需要注册。留下你的昵称,一起交流想法。

凯

写得挺详细的

001

跟着文档时了下,我也搞出我的网站了

小白

膜拜大佬