部署排查
部署问题基本只有两类:CLI 命令执行时报错,或者 Runtime 一直没变成 ready。这一页先教你收集诊断信息,再按症状对照处理。
下面所有命令都在部署所在的那台主机上、部署目录里执行(服务器是 /opt/soln-tenant-runtime,本机是 $HOME/soln-tenant-runtime)。
第 1 步:收集诊断信息
cd /opt/soln-tenant-runtime
soln status --dir .
- 输出
Release:、Runtime: ready|not ready、Tunnel: …,如果有没走完的生命周期操作还会打印Operation: - 加
--json可以看到完整记录:各容器状态、隧道状态、更新检查结果,以及deploymentId DEPLOYMENT_NOT_INSTALLED—— 这个目录不是部署目录,检查路径
soln doctor --dir .
- 每项检查一行:
docker、compose、buildx、cosign、files、environment、tunnel、storage-security、disk、compose-config - 每条失败都会指明缺的是哪个变量、文件或工具;先把带
✗的行修掉再看别的
docker compose ps
- 列出全部服务:
tenant-runtime-service、tenant-runtime-web、tenant-workflow-worker、reverse-proxy,单机模式下还有postgres、temporal、minio、document-parser - 当前模式涉及的服务,
State应该是running(有健康检查的显示healthy)
docker compose logs --tail=200 tenant-runtime-service tenant-workflow-worker
- Runtime Service 的日志里是心跳和 Lease 相关消息;Workflow Worker 的日志里是 Temporal 连接问题
- 想看 TLS 或上游错误就加上
reverse-proxy,想看迁移输出就换成tenant-database-migrator
docker compose exec tenant-runtime-service cat /tmp/soln-agent-health.json
- Runtime 会把自己的状态写在这里:
businessRuntimeReadyAt、lunaTemporalReadyAt、lastHeartbeatAt、lastHeartbeatError、runtimeLeaseExpiresAt、stoppingAt,以及最近一次发布通知 - 没有
lastHeartbeatAt、或者lastHeartbeatError非空,比翻日志更快定位「离线」的原因
第 2 步:按症状对照
| 现象 | 先看哪一节 |
|---|---|
soln install 或 soln upgrade 报错退出 | 安装与升级报错 |
soln status 显示 Runtime: not ready | Runtime 未就绪 |
控制面显示离线,但 /health/ready 返回 200 | 控制面显示 Runtime 离线 |
生命周期操作卡住(status 里有 Operation:) | 升级或回滚卡住 |
| 浏览器打不开 Runtime Origin | 浏览器打不开地址 |
隧道状态是 not ready | 隧道未就绪 |
| 之前一直正常,主机重启后就不行 | 主机层面的问题 |
安装与升级报错
| 错误码 | 含义 | 怎么办 |
|---|---|---|
DEPLOYMENT_ALREADY_INSTALLED | 这个目录里已经有一次部署 | 直接用它,或者换一个空目录;不要在已有部署上重装 |
DEPLOYMENT_ENV_INCOMPLETE | .env 里还缺必填值或还是占位符 | 报错里会列出变量名,见环境变量 |
DEPLOYMENT_ENV_NOT_FOUND | .env 不存在 | 在部署目录里执行命令,它会从 .env.example 生成 .env |
LOCAL_ENCRYPTED_STORAGE_CONFIRMATION_REQUIRED | 本机安装没有带 --confirm-encrypted-storage | 先开启 FileVault / BitLocker / LUKS,再加上这个参数 |
LOCAL_RUNTIME_ORIGIN_INVALID | 本机 Origin 不是 https://*.localhost:8443 | 重新执行 soln prepare --deployment-target local,用生成的地址 |
DEPLOYMENT_BINDING_MISMATCH | 租户、Runtime 或 Agent 与已记录状态不一致 | 重新从控制面复制命令,不要复用旧的 |
DEPLOYMENT_DISK_INSUFFICIENT | 可用空间不足 10 GiB | 先清理磁盘;升级期间会同时存在两个发布和一份数据库备份 |
DEPLOYMENT_COMMAND_NOT_FOUND | 缺 Docker、Compose、Buildx 或 Cosign | 按服务器环境准备安装 |
DEPLOYMENT_COMMAND_TIMEOUT | 某条 Compose 命令超时 | 用 docker compose ps 看有没有卡住的容器,再重跑命令 |
REGISTRY_ACCESS_TOKEN_FAILED | 控制面没有签发 Registry 凭据 | 检查到控制面的出站 HTTPS,并确认 Agent 密钥是当前这一个 |
RELEASE_MANIFEST_SIGNATURE_INVALID | 发布签名校验失败 | 不要绕过;这个发布没有授权给本租户,联系管理员 |
MISSING_ARGUMENT | 粘贴的命令缺了必填参数 | 整段命令重新复制 |
DEPLOYMENT_MODE_INVALID / DEPLOYMENT_TARGET_INVALID | .env 里的值不是 singleMachine/externalServices,或不是 local/server | 用匹配的参数重新执行 soln prepare |
DEPLOYMENT_SETUP_FILE_MISMATCH | .env 里的模式和安装时传入的模式不一致 | 两边改成同一个值 |
安装中途失败时数据库还没有迁移:把报错的原因修掉,再执行同一条命令即可。但安装成功后不要再执行 soln install,后续统一用 soln upgrade。
Runtime 未就绪
/health/ready 只有在 Runtime 完成初始化、持有有效 Lease、且不处于维护模式时才返回 200。按顺序检查这三个条件:
curl -fsS https://runtime.customer.example.com/health/live
curl -fsS https://runtime.customer.example.com/health/ready
- 进程活着时
/health/live返回{"status":"ok"};connection refused说明容器或反向代理没起来 /health/ready返回的错误体里会写明条件:TENANT_RUNTIME_INITIALIZING、TENANT_RUNTIME_MAINTENANCE_MODE,或某个 Lease 错误
接着看 Runtime 自己的状态文件:
docker compose exec tenant-runtime-service cat /tmp/soln-agent-health.json
| 字段 | 含义 |
|---|---|
businessRuntimeReadyAt | 业务运行时完成初始化;缺失说明还在启动,或者访问数据库失败 |
lunaTemporalReadyAt | 智能体任务队列已连上 Temporal;缺失说明 TEMPORAL_ADDRESS 或 namespace 不对 |
lastHeartbeatAt | 最近一次成功向控制面发心跳的时间 |
lastHeartbeatError | 最近一次心跳失败的原因,Lease 或 Origin 问题会出现在这里 |
runtimeLeaseExpiresAt | 当前 Lease 的到期时间;已过期会让 /health/ready 一直失败 |
stoppingAt | 进程正在退出时会有值 |
对应的处理:
TENANT_RUNTIME_INITIALIZING持续几分钟以上:检查数据库连通性(DATABASE_URL),并看docker compose logs --tail=200 tenant-runtime-service- 缺
lunaTemporalReadyAt:核对TEMPORAL_ADDRESS、namespace;用了 TLS 还要确认 CA 和客户端证书挂在/run/secrets且权限0600 runtimeLeaseExpiresAt已过期:继续看下一节- 修完之后等 20~30 秒再刷新控制面,控制面状态是跟着心跳走的
控制面显示 Runtime 离线
心跳被拒时会有明确的错误类型,常见原因如下:
| 原因 | 怎么确认 | 怎么办 |
|---|---|---|
RUNTIME_ORIGIN 与注册值不一致(AGENT_RUNTIME_ORIGIN_MISMATCH) | 对比 .env 里的 RUNTIME_ORIGIN 和sl deployment status --json 显示的地址 | 改成完全一致,再执行一次 soln install/soln upgrade 让新值进入容器 |
| Agent 密钥被轮换或已过期 | lastHeartbeatError 里会提到 agent 或密钥 | 有计划地通过 sl deployment register --force 替换登记,重新生成配置并安排 Runtime 重连;不要因普通检查失败轮换密钥 |
发布没有授权给本租户(AGENT_RUNTIME_RELEASE_NOT_AUTHORIZED) | Runtime 日志里会写明发布版本 | 联系管理员发布或授权该发布 |
发布低于强制最低版本(AGENT_RUNTIME_RELEASE_UNSUPPORTED) | soln status 会打印 Update required | 升级到控制面要求的版本 |
Runtime 未注册(AGENT_RUNTIME_RUNTIME_NOT_REGISTERED) | 控制面不认识这个注册 ID | 先执行 sl deployment status --json 核对,再通过 sl deployment register 登记并准备安装 |
| 主机时间漂移 | date -u 与现实时间差了几分钟 | 打开时间同步(timedatectl status)后重启部署 |
主机访问不到 CP_URL | 在主机上执行 curl -I "$CP_URL" | 放通到控制面和镜像 Registry 的出站 HTTPS |
不要通过改 .soln/deployment.json、关检查、或把状态文件拷到别的主机来绕过 Lease 失败。修好原因后执行 soln install、soln upgrade 或 soln rollback 之一。
升级或回滚卡住
soln status --dir .
- 出现
Operation: upgrade -> <版本>或Operation: rollback -> <版本>,说明上一次生命周期命令没走完;在清除之前 Runtime 一直处于恢复状态 - 未完成的操作也记录在
.soln/deployment.json的operation.phase、startedAt里,迁移前的备份在.soln/backups
soln rollback --dir .
- 恢复上一个发布和它的数据库备份,然后清除未完成状态。这是官方支持的恢复方式;
soln upgrade报DEPLOYMENT_RECOVERY_REQUIRED也是同一个意思 DEPLOYMENT_ROLLBACK_UNAVAILABLE—— 没有可用的上一版本或备份。先确认是不是上一次回滚已经用掉了,再决定是否重装(重装会丢数据)
如果是 Migrator 本身失败,它的输出会保留:
docker compose run --rm tenant-database-migrator
- 成功:打印已应用的迁移并返回
0 - 这里失败属于数据库问题,不是发布问题:看输出里失败的那条迁移名,以及数据库账号权限
浏览器打不开地址
| 现象 | 怎么办 |
|---|---|
| 完全打不开,连接被拒绝 | 用 docker compose ps 看 reverse-proxy 和 tenant-runtime-service;用 docker compose up -d 启动它们 |
反向代理返回 502 Bad Gateway | 上游 Runtime 没就绪,按 Runtime 未就绪排查 |
服务器部署报 DNS_PROBE_FINISHED_NXDOMAIN | 为 Runtime Origin 添加 DNS 记录,等解析生效 |
SSL certificate problem 或证书告警 | 用覆盖确切 Runtime Origin 主机名的证书;Linux 本机还要导入本机根证书,见 Linux |
页面能开,但请求返回 401 或 403 | 在 Runtime Origin 上重新登录;会话 Cookie 只属于那个确切主机名 |
| 外网连不上端口 | 在主机防火墙或安全组放通;反向代理只监听回环地址,前面需要你自己的 TLS 终结器或隧道 |
本机部署的地址是 https://soln-<租户子域名>.localhost:8443,只有这台机器能访问,且 8443 必须空闲;被别的进程占用时反向代理会报 port is already allocated。
隧道未就绪
soln status --dir .
docker compose logs --tail=100 cloudflared
Tunnel: not ready且报TUNNEL_CREDENTIALS_INVALID:凭据 JSON 缺失或格式不对;把 Named Tunnel 凭据放到secrets/cloudflared-credentials.json,权限0600TUNNEL_ID_INVALID:CLOUDFLARE_TUNNEL_ID不是 UUID,从 Cloudflare 控制台重新复制TUNNEL_SERVICE_MISSING:Compose 文件里没有cloudflared服务,重新执行soln prepare生成- 连接器挂掉时公网访问失败、但本地
/health/ready仍然正常,这是预期行为:控制面不会转发任何业务流量
主机层面的问题
df -h /opt/soln-tenant-runtime
timedatectl status
systemctl is-active docker
- 可用空间不足 10 GiB:镜像、备份和升级时新旧版本共存都占用这块盘;清掉不属于当前发布的旧备份
System clock synchronized: no:先把时间同步修好;时间偏差会让 Lease 校验和 TLS 握手都失败- Docker 未运行:启动守护进程;主机重启后如果 Docker 没有开机自启,容器不会自己起来
- Docker Hub 拉取公共依赖镜像报
i/o timeout:检查 Docker 守护进程能否访问registry-1.docker.io和auth.docker.io;如使用云厂商镜像加速,采用当前账号提供的地址。网络恢复后保留原 setup 文件重试sl install,无需重新登记 - 主机重启后:先确认
findmnt /srv/soln/data显示加密卷已挂载,再启动 Docker、检查容器,最后执行soln status --dir .;不要让数据写入未挂载的目录
求助前需要收集的信息
- 执行的完整命令和它的全部输出,包含错误码
soln status --dir . --jsonsoln doctor --dir .docker compose pstenant-runtime-service容器里的/tmp/soln-agent-health.json- Runtime Service 和 Workflow Worker 日志的末尾部分
- 租户 ID、Runtime ID、部署目标、部署模式、当前发布版本
不要把 .env、Agent 密钥、数据库密码或数据库备份发给任何人。.soln/deployment.json 不含密钥,可以放心提供。
注意事项
- 不要手改
.soln/deployment.json和.soln/docker-compose.release.yml:它们记录当前发布、镜像摘要和未完成的操作 - 不要绕过签名校验和 Lease 校验,也不要手改控制面同样记录的那些
.env值 - 复用同一个部署目录,不要在已有部署上再装一次
- 页面和接口报错都带
code、reason、domain三个字段,含义见错误码