从0开始手把手教你《阿里云arc自动部署go+nuxt全栈项目》
本文教会新手小白从购买ECS服务器开始,搭建流水线自动部署Go+Gin+MySQL+Redis+nux3全栈项目。
从零开通阿里云 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 保存前核对这六项
- 两个
build@docker都有type: account。缺省会走仓库凭证cert模式;页面没“认证方式”选项时直接改 YAML,不需要盲找凭证下拉框。 repository只有${ACR_PUBLIC_REGISTRY};tag是${ACR_NAMESPACE}/skeryb-api:${GITEE_COMMIT}或 Web 对应项,不重复拼域名。- Dockerfile 是
deploy/ci/backend.Dockerfile、deploy/ci/web.Dockerfile,构建上下文为仓库根目录。 - 四个
ACR_*已在顶层ref_variable引用;语法为${变量名},不是${{变量名}}。 shell@agent绑定自己的主机,gitClone: "true",运行bash deploy/release-from-agent.sh '${GITEE_COMMIT}'。- 触发器为
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 怎么建库建表
- 空数据目录首次启动 MySQL:创建
skeryb数据库及应用/备份账号。 - 发布脚本用已拉取的 API 镜像执行
migrate:按版本执行程序内嵌的 SQL,记录校验和并加迁移锁;不会使用 GORM AutoMigrate 随启动改生产表。 - 再执行
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 密码改了,更新两处
- Gitee Go 的敏感变量
ACR_PASSWORD:供 API、Web 两个构建任务推送镜像。 - 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。升级这些文件时,应同时核对命令参数和发布顺序。
官方资料:
- 阿里云:自定义购买 ECS
- 阿里云:安全组规则
- 阿里云:创建 ACR 个人版实例与限制
- 阿里云:创建镜像仓库
- 阿里云:个人版镜像推送与拉取
- Gitee:主机管理
- Gitee:镜像构建与部署
- Gitee:触发事件
- Gitee:主机执行插件及代码克隆前提
- Gitee:通用变量
- Gitee:SSH 公钥
配图说明:图 1、2、5、18、21 为根据项目部署逻辑绘制的示意图;图 6、11、13 引用 Gitee 官方 SSH 公钥和通用变量说明页面,图注中已标明来源;其余为项目实际操作截图,已裁切并遮挡敏感值。历史截图用于说明入口和故障,实际配置以正文为准。
本文按上述日期的代码与官方资料整理。控制台入口和套餐可能变化,以同名功能和当前官方说明为准;完成命令后仍需核对本次发布日志、健康接口和浏览器结果。
如需转载,请联系 skeryb001@gmail.com。
评论 3
不需要注册。留下你的昵称,一起交流想法。
写得挺详细的
跟着文档时了下,我也搞出我的网站了
膜拜大佬