LiteLLM 心跳 / Health Check 全链路文档¶
本系列文档完整描述 LiteLLM proxy 的"模型心跳"机制:何时启动、按什么节奏跑、向上游打什么请求、多 Pod 怎么协调、配置怎么调、出问题怎么定位。
适用场景:
- 排查"前端 logs 里每隔 5 分钟出现一大批模型请求,怎么压下来"
- 排查"我设了 DEFAULT_SHARED_HEALTH_CHECK_TTL 但好像没生效"
- 排查"K8s /health/readiness 一直不就绪"
- 配置贵模型不被自动 ping、把心跳改打便宜的同家族模型
- 理解多 Pod 共享心跳的 Redis 协调如何工作、有什么坑
⚠️ 重要前置概念:"心跳"在 LiteLLM 里有两层含义。本系列文档的"心跳"特指对每一个上游 LLM deployment 的周期性真实推理调用,不是 K8s 的 liveness/readiness 探针。两者代码位置和触发机制不同,详见 01-mechanism.md §1。
文档目录¶
| 文件 | 内容 |
|---|---|
| 01-mechanism.md | 启动入口、后台 task 主循环、SharedHealthCheckManager 决策逻辑、真实 ping 调用栈、通配符模型扇出、DB 持久化 |
| 02-config-reference.md | 全部配置项:YAML general_settings、DEFAULT_* 环境变量、model_info 单模型字段、HTTP 探针端点速查 |
| 03-cost-reduction.md | 心跳成本三因子(频率 × 模型数 × 单次成本)+ 推荐组合 + 常见误区(health_check_details / num_retries / wildcard) |
| 04-troubleshooting.md | 排障流程图、4 类常见现象的定位 checklist、5min/242 条 真实案例完整复盘、模块常量"导入即冻结"陷阱、多 Pod fallback 放大 bug |
整体架构一览¶
flowchart TB
subgraph "外部触发(被动)"
K8S[K8s kubelet]
MON[监控/巡检]
end
subgraph "HTTP 探针层<br/>litellm/proxy/health_endpoints/_health_endpoints.py"
EP1["/health/liveliness<br/>(无状态)"]
EP2["/health/readiness<br/>(检 DB / Cache / Callback)"]
EP3["/health<br/>(查 health_check_results)"]
EP4["/health/services"]
end
subgraph "后台心跳 task(主动)<br/>litellm/proxy/proxy_server.py"
START["asyncio.create_task<br/>_run_background_health_check<br/>:872"]
LOOP["while True:<br/>tick every health_check_interval s<br/>:2160-2232"]
BRANCH{shared_health_manager<br/>!= None ?}
SHARED["SharedHealthCheckManager.<br/>perform_shared_health_check"]
DIRECT["perform_health_check<br/>(直接打模型)"]
end
subgraph "Redis 共享协调<br/>litellm/proxy/health_check_utils/shared_health_check_manager.py"
CACHE["health_check_results<br/>TTL=DEFAULT_SHARED_HEALTH_CHECK_TTL"]
LOCK["health_check_lock<br/>TTL=DEFAULT_SHARED_HEALTH_CHECK_LOCK_TTL"]
end
subgraph "真实 LLM 调用<br/>litellm/proxy/health_check.py + litellm/main.py"
PERFORM["_perform_health_check<br/>filter_deployments_by_id<br/>:83"]
AHC["litellm.ahealth_check<br/>main.py:7041"]
MODE{mode 派发}
REAL["acompletion / aembedding /<br/>aspeech / arerank / ..."]
end
subgraph "结果落地"
MEM["health_check_results<br/>(进程内全局 dict)"]
DB["LiteLLM_HealthChecks 表<br/>(状态变化时才写)"]
end
K8S -->|liveness probe| EP1
K8S -->|readiness probe| EP2
MON --> EP3
MON --> EP4
START --> LOOP
LOOP --> BRANCH
BRANCH -- "use_shared_health_check + redis_usage_cache OK" --> SHARED
BRANCH -- "其它情况" --> DIRECT
SHARED -->|"读"| CACHE
SHARED -->|"抢锁"| LOCK
SHARED -->|"未命中且抢到锁"| PERFORM
DIRECT --> PERFORM
PERFORM --> AHC
AHC --> MODE
MODE -->|"chat"| REAL
MODE -->|"embedding"| REAL
MODE -->|"audio_speech"| REAL
MODE -->|"...(共 12 种)"| REAL
PERFORM --> MEM
PERFORM --> DB
EP3 -.读.- MEM
EP4 -.读.- MEM
style BRANCH fill:#fdd,stroke:#c33,stroke-width:2px
style CACHE fill:#dfd,stroke:#3a3
style LOCK fill:#dfd,stroke:#3a3
几点关键事实:
- K8s 探针(左上)和后台心跳(中间)是两套独立机制。
/health/liveliness永远返回 200,不会触发模型调用;前端 logs 里看到的"批量心跳请求"全部来自后台 task。 - 后台 task 是单例的,每个 Pod 进程内只有一份
_run_background_health_check协程在跑。 - 是否走 SharedManager 由两个条件决定:YAML 的
use_shared_health_check=true且litellm.cache.cache是RedisCache/RedisClusterCache实例。任一不满足就走 DIRECT 路径,每个 Pod 独立地按health_check_interval节奏打模型。 - 真实模型调用通过
litellm.ahealth_check,按mode派发到 12 种实际 API(chat / embedding / audio_speech / ...)。每个 deployment 一次真实推理调用。 - 请求数 ≈ 1 × 参与轮询的 deployment 数 ÷(TTL/小时)。修复前是
Pod 数 × deployment 数,本 fork 已消除 Pod 放大(详见 04 §5)。
🔧 本 fork 修复说明:上游 PR #15380 的
SharedHealthCheckManager在 follower 抢锁失败后只等 2 秒就回退到本地自打,导致多 Pod 部署下 cache 过期那一刻仍然 N×deployment 数请求。本仓库已把 follower 路径改为"polling 直到 lock_ttl 超时,超时跳过这一 tick,绝不本地自打"。如未来同步上游需保留本 fork 改动。详见 01 §3.3 和 04 §5。
速查:配置项与节奏¶
| 配置 | 类型 | 默认 | 说明 | 详见 |
|---|---|---|---|---|
background_health_checks |
YAML general_settings | false |
不开就完全没有后台心跳 | 02 §1.1 |
use_shared_health_check |
YAML general_settings | false |
多 Pod 共享 Redis 协调(去重) | 02 §1.1 |
health_check_interval |
YAML general_settings | 300 |
后台 loop tick 间隔(秒) | 02 §1.1 |
health_check_details |
YAML general_settings | true |
仅控制 /health 返回字段,不减请求数 |
02 §1.1 / 03 §3 |
DEFAULT_HEALTH_CHECK_INTERVAL |
env | 300 |
YAML 未给值时的兜底 | 02 §2 |
DEFAULT_SHARED_HEALTH_CHECK_TTL |
env | 300 |
shared 模式下真实 ping 节奏由它决定,YAML interval 只是 tick | 02 §2 |
DEFAULT_SHARED_HEALTH_CHECK_LOCK_TTL |
env | 60 |
leader pod 抢锁超时 | 02 §2 |
disable_background_health_check |
model_info(per model) | false |
单个 deployment 跳过轮询 | 02 §3 |
health_check_model |
model_info(per model) | — | 心跳时把 model 替换成便宜的同家族模型 |
02 §3 |
health_check_timeout |
model_info(per model) | 60 |
单次心跳超时(秒) | 02 §3 |
mode |
model_info(per model) | 自动推断 | 决定打 chat / embedding / 等 | 02 §3 |
决策树:你应该看哪一篇?¶
flowchart TD
Q{你的问题是?}
Q -->|"想理解整体怎么跑的"| A1[01 mechanism]
Q -->|"配置在哪里写、有哪些字段"| A2[02 config-reference]
Q -->|"心跳请求太多想压下去"| A3[03 cost-reduction]
Q -->|"线上有异常想排查"| A4[04 troubleshooting]
Q -->|"想看真实案例"| A4
A1 -->|"还想知道单模型怎么配"| A2
A1 -->|"还想知道怎么省钱"| A3
A3 -->|"配置改完没生效"| A4
style Q fill:#cef,stroke:#369
一分钟自检¶
如果你怀疑当前 proxy 的心跳行为不对,按以下顺序快速验证:
1. 后台 task 是否启动?¶
启动日志里 grep:
- 看到
Initialized shared health check manager→ 走 SHARED 路径,节奏受DEFAULT_SHARED_HEALTH_CHECK_TTL控制 - 没有这行但 YAML 里
background_health_checks: true→ 走 DIRECT 路径,节奏受health_check_interval控制 - YAML 里
background_health_checks: false或没写 → 没有后台心跳
2. 真实节奏是多少?¶
进 Redis 看 cache key:
.venv312/bin/python -c "
import os, redis, time
r = redis.Redis(host=os.environ.get('REDIS_HOST','127.0.0.1'),
port=int(os.environ.get('REDIS_PORT','6379')),
password=os.environ.get('REDIS_PASSWORD'))
ttl = r.ttl('health_check_results')
print(f'TTL={ttl}s (key {\"exists\" if ttl > 0 else \"missing/no-ttl\"})')
"
- TTL ≈ 你期望值(比如 1000 或 3000)→ shared 模式工作正常
- TTL ≈ 300 → env var 没生效(pod 没真重启 / configmap 不一致)
- key 不存在(ttl=-2)→ shared 模式没跑起来;走的是 DIRECT 路径
3. 一次 burst 多少个请求?¶
近似公式:
/v1/model/info 拿到 deployment 总数:
curl -sH "Authorization: Bearer $LITELLM_MASTER_KEY" \
http://your-proxy/v1/model/info \
| python -c "import json,sys; print(len(json.load(sys.stdin)['data']))"
如果实际观察的"请求数/burst"远大于这个数,可能是命中过的"多 Pod fallback 放大 bug"——本 fork 已修复,详见 04-troubleshooting.md §5。如果你的 prod 还在用未修复版本(上游 main),就是这个 bug。
三个最常用配置组合¶
A. 完全关掉后台心跳(最省成本)¶
代价:上游故障靠 cooldown 机制自然探测(某个 deployment 失败几次后被冷却),不再主动巡检。/health 端点仍可手动调。
B. 单 Pod / 不需要 Redis 协调¶
general_settings:
background_health_checks: true
use_shared_health_check: false
health_check_interval: 600 # 10 分钟一次
每 10 分钟把 model_list 全部 ping 一遍。
C. 多 Pod + Redis 协调 + 贵模型跳过(生产推荐)¶
general_settings:
background_health_checks: true
use_shared_health_check: true
health_check_interval: 30 # tick 高频,但 cache 命中时不打模型
# k8s deployment env
DEFAULT_SHARED_HEALTH_CHECK_TTL=1800 # 真实 ping 每 30 min 一次
DEFAULT_SHARED_HEALTH_CHECK_LOCK_TTL=180 # 必须大于一次完整 health check 总耗时
# UI 或 model_list 里给贵模型加
model_info:
disable_background_health_check: true
# 或保留心跳但用便宜模型替代
model_info:
health_check_model: gpt-4o-mini
mode: chat
详细场景化推荐见 03-cost-reduction.md §推荐组合。
关于本系列文档¶
- 行号引用基于
master分支同步点(2026-05),上游持续重构可能漂移,遇到 grep 不到时按文件名 + 函数名定位 - 实战案例和踩坑总结来自本项目 prod / dev 环境的真实排查,不是上游 docs 的搬运
/health端点行为、HTTP API 等通用内容请直接看上游 https://docs.litellm.ai/docs/proxy/health