跳转至

TFRSManager 部署排查手册

本文档基于首次生产部署中遇到的实际问题整理,覆盖 CNB 流水线 → SSH 传输 → 服务器部署全链路。


部署架构概览

CNB 流水线 (web_trigger_deploy_prod)
  ├── generate env file    → 从密钥仓库生成 .env.prod
  ├── prepare deploy files → 打包到 deploy_pkg/
  └── ssh-deploy 插件      → 传输文件 + 执行 deploy.sh
生产服务器 /opt/tfrs/
  ├── deploy_pkg/ → deploy.sh 自动解包到上级目录
  ├── docker-compose.prod.yml
  ├── .env.prod
  └── scripts/deploy.sh
Docker Compose
  ├── postgres (127.0.0.1:5432)
  ├── redis    (127.0.0.1:6379)
  ├── user-service  (127.0.0.1:8080)
  └── admin-service (127.0.0.1:8081)

一、CNB 流水线阶段

1.1 Pipeline init error: GetFileContent: not found

现象: 流水线在 init 阶段直接失败,未执行任何 stage。

原因: imports 引用的密钥仓库文件不存在或文件名不匹配。

排查步骤: 1. 检查 .cnb.yml 中所有 imports 的 URL 路径 2. 到 turingfocus/build_env 密钥仓库确认文件名完全一致(含大小写、后缀) 3. 确认文件在 main 分支上

实际案例: 仓库中文件名为 tfrsmanager_env.prod.example.yml,但 imports 引用的是 tfrsmanager_env.prod.yml

涉及文件: - .cnb.yml — pipeline 级别 imports(第 267-272 行) - .cnb.yml — ssh-deploy 插件级别 imports(第 348 行)

1.2 镜像标签找不到 / 部署失败

现象: [错误] 最近 20 个提交中未找到已构建的镜像

原因: 部署前未构建镜像,或构建时的 commit 已不在最近 20 个提交内。

排查步骤: 1. 确认已触发过 web_trigger_build_all_images 2. 在 CNB 制品页确认镜像标签存在 3. 手动指定 IMAGE_TAG 触发部署

解决: 部署流水线会自动遍历最近 20 个 commit,优先匹配 git tag(如 v1.0.0),其次匹配 commit SHA。


二、SSH 连接与传输阶段

2.1 SSH private key is missing

现象: ssh-deploy 插件报 Error: SSH private key is missing.

原因: 插件 settings 字段名错误。

正确的 ssh-deploy 插件参数: | 参数 | 说明 | |------|------| | ssh_host | 服务器 IP(不是 host) | | ssh_port | SSH 端口(不是 port) | | ssh_user | 用户名(不是 username) | | ssh_private_key | SSH 私钥(不是 key) | | transfer_files | yes/no | | source_file_path | 本地文件/目录路径(不支持逗号分隔多文件) | | destination_path | 远程绝对路径 | | execute_remote_script | yes/no | | deploy_script | 远程脚本绝对路径 | | copy_script | 是否先上传脚本 yes/no | | source_script | 本地脚本路径(copy_script=yes 时) |

关键注意: - 插件的 imports单独声明在插件步骤内,不继承 pipeline 级别的 imports - source_file_path 不支持逗号分隔,传多文件需先打包到一个目录 - 传目录时,目录本身会成为 destination 的子目录(如 deploy_pkg//opt/tfrs/deploy_pkg/

2.2 Failed to set permissions / sudo: a password is required

现象: SSH 连接成功,但插件内部 sudo 失败。

原因: deploy 用户无免密 sudo 权限,或目标目录不属于 deploy 用户。

解决(在服务器上以 root 执行):

# 创建部署目录并授权
mkdir -p /opt/tfrs/{scripts,backups}
chown -R deploy:deploy /opt/tfrs

# 配置免密 sudo(ssh-deploy 插件内部会用 sudo)
echo 'deploy ALL=(ALL) NOPASSWD: ALL' > /etc/sudoers.d/deploy
chmod 440 /etc/sudoers.d/deploy

2.3 Remote script does not exist

现象: Error: Remote script does not exist: /opt/tfrs/scripts/deploy.sh

原因: deploy_script 路径与实际传输的文件路径不一致。

排查: SSH 到服务器 find /opt/tfrs/ -name "deploy.sh" 确认实际路径,然后修改 deploy_script 设置。

当前方案: 文件传输到 /opt/tfrs/deploy_pkg/,deploy.sh 位于 /opt/tfrs/deploy_pkg/scripts/deploy.sh,脚本内部 Step 0 会自动将文件移动到 /opt/tfrs/ 下。


三、服务器部署阶段(deploy.sh)

3.1 Docker Registry 登录失败 / 镜像拉取失败

现象: docker pull 报认证错误。

排查步骤: 1. 检查 .env.prod 中是否包含 REGISTRYPROD_DOCKER_USERPROD_DOCKER_PASSWORD 2. 确认密钥仓库 tfrsmanager_env.prod.yml 中有这三个变量 3. 确认 scripts/env-keys.txt 白名单中包含 PROD_DOCKER_USERPROD_DOCKER_PASSWORD 4. SSH 到服务器手动验证: cat /opt/tfrs/.env.prod | grep DOCKER

deploy.sh 自动登录逻辑(Step 2):

REGISTRY=$(grep -E '^REGISTRY=' .env.prod | cut -d= -f2-)
DOCKER_USER=$(grep -E '^PROD_DOCKER_USER=' .env.prod | cut -d= -f2-)
DOCKER_PASSWORD=$(grep -E '^PROD_DOCKER_PASSWORD=' .env.prod | cut -d= -f2-)
echo "${DOCKER_PASSWORD}" | docker login -u "${DOCKER_USER}" --password-stdin "${REGISTRY}"

3.2 健康检查超时

现象: [错误] admin-service 健康检查超时(或 user-service)

排查步骤: 1. SSH 到服务器检查容器状态: docker ps | grep tfrs 2. 手动测试健康端点: curl -sv http://127.0.0.1:8081/health 3. 查看容器日志: docker logs --tail=50 tfrs_admin 4. 检查容器是否在反复重启: docker ps -a | grep tfrs

常见原因:

现象 原因 解决
Connection reset by peer 容器内监听 127.0.0.1,Docker 端口映射无法转发 compose 中加 ADMIN_BIND_ADDR: "0.0.0.0"
Connection refused 服务未启动/崩溃 查日志,检查环境变量是否完整
容器 Restarting 启动时 Fatal 错误 docker logs tfrs_xxx 查看崩溃原因
日志停在迁移后无输出 日志写入文件非 stdout 正常现象(production 模式),检查 /app/logs/
SECRET_ENCRYPTION_KEY 未配置 .env.prod 缺少必要变量 检查 env-keys.txt 和密钥仓库

重要: admin-service 默认监听 127.0.0.1:8081(代码中 ADMIN_BIND_ADDR 默认值),在 Docker 容器内必须改为 0.0.0.0 才能被端口映射访问。已通过 docker-compose.prod.ymlenvironment 注入 ADMIN_BIND_ADDR: "0.0.0.0"

3.3 traces export 连接拒绝

现象: traces export: Post "http://localhost:14268/v1/traces": dial tcp [::1]:14268: connect: connection refused

原因: Jaeger 未部署,OpenTelemetry 导出失败。

影响: 不影响服务运行,仅丢失链路追踪数据。生产环境如不需要 Jaeger 可忽略。


四、快速排查流程图

部署失败
├── Pipeline init 失败?
│   └── → 检查 imports 文件名是否匹配(§1.1)
├── SSH 连接失败?
│   └── → 检查 ssh-deploy settings 参数名(§2.1)
├── SSH 权限失败?
│   └── → 服务器上配置 deploy 用户权限(§2.2)
├── 远程脚本不存在?
│   └── → 检查 deploy_script 路径与传输路径是否一致(§2.3)
├── 镜像拉取失败?
│   └── → 检查 .env.prod 中 Registry 凭证(§3.1)
├── 健康检查超时?
│   └── → SSH 到服务器 curl + docker logs 排查(§3.2)
└── 服务启动但功能异常?
    └── → 检查 .env.prod 环境变量是否完整(对比 env-keys.txt)

五、常用排查命令

# === 服务器端 ===
# 查看所有 TFRS 容器状态
docker ps --format "table {{.Names}}\t{{.Status}}\t{{.Ports}}" | grep tfrs

# 健康检查
curl -s http://127.0.0.1:8080/health
curl -s http://127.0.0.1:8081/health

# 查看服务日志
docker logs --tail=100 tfrs_user
docker logs --tail=100 tfrs_admin

# 查看 .env.prod 内容(脱敏)
grep -E '^[A-Z]' /opt/tfrs/.env.prod | cut -d= -f1

# 查看部署目录结构
ls -la /opt/tfrs/

# 手动重启服务
cd /opt/tfrs && docker compose -f docker-compose.prod.yml --env-file .env.prod up -d

# 回滚到上次配置
cp /opt/tfrs/backups/.env.prod.latest /opt/tfrs/.env.prod
cd /opt/tfrs && docker compose -f docker-compose.prod.yml --env-file .env.prod up -d

# === CNB 侧 ===
# 不重新构建,指定旧标签部署
# 在 CNB 界面触发 web_trigger_deploy_prod,IMAGE_TAG 填旧的 commit SHA 或 git tag

六、关键文件清单

文件 作用 修改后是否需要重新构建镜像
.cnb.yml CI/CD 流水线定义
.cnb/web_trigger.yml CNB 界面手动触发按钮
docker-compose.prod.yml 生产 compose 编排 否(重新部署即可)
scripts/deploy.sh 服务器端部署脚本 否(重新部署即可)
scripts/env-keys.txt 环境变量白名单 否(重新部署即可)
cmd/*/main.go 服务入口代码
internal/**/*.go 业务代码
Dockerfile.* 镜像构建定义

七、密钥仓库文件对照

turingfocus/build_env 密钥仓库中需要的文件:

文件名 引用位置 内容
cnb_env.yaml pipeline imports 企业微信机器人 webhook
tfrsmanager_env.prod.yml pipeline imports 所有生产环境变量(对应 env-keys.txt)
tfrsmanager_deploy_ssh.yml ssh-deploy 插件 imports PROD_SSH_HOST, PROD_SSH_PORT, PROD_SSH_USER, PROD_SSH_KEY, PROD_DOCKER_USER, PROD_DOCKER_PASSWORD