Daytona 错误日志分析:Failed to get initial runner 问题排查
先来看具体现象:SnapshotManager 每隔10秒就会抛出一条 WARN 级别的日志,内容是“Failed to get initial runner: Error: No a vailable runners”。乍一看像是严重故障,但注意级别是 WARN,非致命错误,系统会反复重试。这类日志通常表明资源分配遇到临时障碍,需要深入分析根本原因。错误日志详情
{"level": 40, // WARN 级别(非致命错误)"context": "SnapshotManager","msg": "Failed to get initial runner: Error: No a vailable runners"}
这条日志来自 SnapshotManager 的定时同步任务,每10秒精准复现一回。这种周期性报错提示系统正在持续尝试分配 runner,但始终无法满足条件。

根本原因分析
问题其实不复杂——a vailabilityScore 阈值没达标。换句话说,系统试图为一条 pending 状态的 snapshot 分配初始 runner,但现有的 runner 分数不够,被过滤掉了。简单来说,就是阈值设置过高,导致符合条件的 runner 不足。
来看数据库里的记录:
| snapshot | name | state | sandboxClass | initialRunnerId |
|---|---|---|---|---|
0e5a7b23... | sandbox-extra:0.6 | pending | container | (空,未分配) |
SnapshotManager 在 handleSnapshotStatePending() 中尝试为它分配 runner,关键逻辑在这里(snapshot.manager.ts:1210-1216):
const a vailabilityThreshold =this.configService.getOrThrow('runnerScore.thresholds.a vailability') +// 默认 10this.configService.getOrThrow('runnerScore.thresholds.initialRunnerScoreAddon')// 默认 20initialRunner = await this.runnerService.getRandomA vailableRunner({regions: ['us'],sandboxClass: 'container',a vailabilityScoreThreshold: a vailabilityThreshold,// = 10 + 20 = 30...})
而数据库里唯一的 runner 是这样的:
| name | state | region | sandboxClass | a vailabilityScore |
|---|---|---|---|---|
| default | ready | us | container | 5 |
findA vailableRunners() 里的过滤条件要求:
a vailabilityScore: MoreThanOrEqual(a vailabilityThreshold)// 要求 >= 30
runner 的 a vailabilityScore = 5,离 30 的阈值差了一大截,自然被无情过滤掉,返回空数组,最终抛出 No a vailable runners。这是导致报错的核心直接原因。
原因总结
把整个过程串起来看:
┌─────────────────────────────────────────────────────┐│Snapshot (pending) 需要初始 runner││要求: a vailabilityScore >= 30 ││(a vailability阈值10 + initialRunnerScoreAddon 20) ││ ││唯一 Runner: a vailabilityScore = 5❌ 不满足 ││ ││结果: "No a vailable runners" → 每10秒重试 │└─────────────────────────────────────────────────────┘
这里 a vailabilityScore 是一个动态计算的负载评分,基于 CPU、内存、磁盘使用率等,分数越低代表 runner 越空闲。但 initialRunnerScoreAddon 额外加了20分的门槛,目的是确保 snapshot 构建只分配到足够空闲的 runner,避免影响已有任务。这个设计本意是好的,但在开发环境只有一台 runner 且负载较高时,就容易出现这种尴尬局面。简单来说,就是评分机制与单节点环境不兼容。
可能的解决方案
遇到这种情况,有几种应对思路,按推荐程度排列:
方案 1:降低阈值(推荐,适合开发环境)
直接修改 docker-compose.yaml,为 API 容器添加环境变量,把额外门槛去掉或降低基数:
environment:- RUNNER_INITIAL_RUNNER_SCORE_ADDON=0# 取消额外门槛(默认20)# 或- RUNNER_A VAILABILITY_SCORE_THRESHOLD=0# 直接降低基础门槛
方案 2:手动更新 runner 的 a vailabilityScore
临时性的快速修复,直接改数据库把分数调高:
UPDATE runner SET "a vailabilityScore" = 100 WHERE name = 'default';
方案 3:删除卡住的 pending snapshot(如果不需要)
如果那条 snapshot 已经无关紧要,直接清理数据:
DELETE FROM snapshot_region WHERE "snapshotId" = '0e5a7b23-82dd-4fba-abfb-890dcfe754f6';DELETE FROM snapshot WHERE id = '0e5a7b23-82dd-4fba-abfb-890dcfe754f6';
方案 4:检查 runner 上报机制
a vailabilityScore 只有5,说明 runner 上报的资源信息偏低。可以翻一翻 runner 容器的日志,看它是否正确上报了 CPU、内存等数据:
docker logs daytona-runner-1 --tail 50
这几种方案各有适用场景,开发环境通常推荐方案1,最直接有效。生产环境则要根据实际情况权衡,不建议贸然降低阈值,最好先确认 runner 资源是否真的饱和。通过合理调整阈值或检查资源上报,可以快速解决"Failed to get initial runner"错误。
