Scale-to-zero 的目标很直接:没有业务流量时不运行 Pod,从而减少空闲资源消耗。但在真实集群里,负载均衡器、外部 uptime monitor 或平台探测器仍会定期请求 /healthz、/readyz 等端点。对支持按请求激活工作负载的系统来说,这些探测流量和普通业务请求没有区别,于是刚缩到零的服务又被唤醒。
KubeElasti 的 ProbeResponse 针对的正是这类流量:在请求真正触发后端扩容之前,识别健康检查并直接返回预设响应。探测系统继续看到健康结果,业务 Pod 则能保持休眠。
先分清是谁在发送健康检查
Kubernetes 中有几类容易混在一起的探测:
livenessProbe、readinessProbe和startupProbe由 kubelet 针对已经存在的 Pod 执行。Pod 数量为零时,它们本身不会创建 Pod。- 云负载均衡器或 Ingress Controller 可能持续访问后端健康路径。
- 外部监控服务可能每隔几十秒请求一次公开 URL。
- 平台中的请求激活层可能把所有到达 Service 的 HTTP 请求都视为扩容信号。
因此,真正需要拦截的通常不是 Pod 清单里的 kubelet probe,而是从服务入口进入、会经过按需扩容链路的健康检查请求。
可以先检查访问日志,确认唤醒请求的路径和来源:
kubectl logs -n demo deploy/my-api --since=30m | \
grep -E '(/healthz|/readyz|/livez|kube-probe|HealthChecker|Uptime)'
如果每次扩容前都出现固定路径、固定 User-Agent 或固定来源地址,就有了可用于匹配的信号。
ProbeResponse 改变了请求路径
没有探测响应层时,请求链路通常是:
健康检查 -> Service/激活层 -> 扩容工作负载 -> 启动 Pod -> 返回 200
加入 ProbeResponse 后,健康检查可以走短路径:
健康检查 -> ProbeResponse -> 直接返回 200
业务请求 -> Service/激活层 -> 必要时扩容工作负载
这里的关键不是简单地“忽略健康检查”,而是为调用方提供一个符合预期的 HTTP 响应。负载均衡器仍然可以收到状态码、响应头和响应体,监控系统也不必因为连接失败而报警。
匹配规则应尽量窄。只按 GET 方法匹配通常不够,因为大量业务请求同样使用 GET;更可靠的条件是专用路径,再结合主机名或明确的请求头。也不要把 / 配置为合成健康响应,否则真实用户请求可能在服务休眠时拿到一个虚假的 200 OK。
一份可改造的配置示例
下面的 YAML 展示了推荐的配置形状:仅为 api.example.com 的 /healthz 返回固定响应。由于不同 KubeElasti 版本的 CRD 组名、版本和字段可能不同,运行前应先用 kubectl explain 对照集群中实际安装的 ProbeResponse 定义调整字段。
# probe-response.yaml
# 示例结构:请根据集群内实际 CRD 修改 apiVersion 和 spec 字段。
apiVersion: kubeelasti.io/v1alpha1
kind: ProbeResponse
metadata:
name: my-api-health
namespace: demo
spec:
target:
serviceName: my-api
servicePort: 8080
match:
method: GET
path: /healthz
host: api.example.com
response:
statusCode: 200
headers:
Content-Type: text/plain
Cache-Control: no-store
body: "ok\n"
先确认 CRD 的真实接口,再应用配置:
kubectl api-resources | grep -i proberesponse
kubectl explain proberesponse --recursive
kubectl apply -f probe-response.yaml
kubectl get proberesponse -n demo
如果 kubectl api-resources 没有输出,说明该 CRD 尚未安装,或者当前版本使用了不同的资源名称。不要直接应用示例中的假定版本。
部署后,可以持续请求健康端点,同时观察工作负载是否仍保持零副本:
for i in $(seq 1 10); do
curl --fail --silent --show-error \
-H 'Host: api.example.com' \
https://YOUR_INGRESS_ADDRESS/healthz
sleep 5
done
kubectl get deploy -n demo my-api -w
把 YOUR_INGRESS_ADDRESS 换成实际入口地址。如果 TLS 证书依赖域名,直接使用正式域名测试,或者通过 curl --resolve 指定解析:
curl --resolve api.example.com:443:203.0.113.10 \
https://api.example.com/healthz
随后再发送一个真实业务请求,确认它仍能触发扩容:
curl --fail https://api.example.com/v1/orders
kubectl get pods -n demo -l app=my-api -w
合成的 200 不等于应用真的健康
ProbeResponse 回答的是“服务入口能够接收探测请求”,不一定代表业务进程、数据库连接或下游依赖已经就绪。把深度健康检查直接替换成固定 200,会掩盖故障。
更稳妥的做法是把健康语义拆开:
/healthz用于证明入口和 ProbeResponse 链路可达,可以合成响应。/readyz由运行中的应用返回,用于判断当前 Pod 是否能接收流量。/livez继续作为 kubelet 的进程存活检查。- 端到端监控使用低频真实业务请求,并接受它会触发冷启动。
安全边界同样重要。匹配条件过宽可能绕过认证、限流或正常路由。配置后应验证非健康路径、不同 Host、POST 请求以及携带查询参数的请求不会误命中。
上线前的检查清单
- 从日志或指标确认真正触发扩容的是外部健康流量,而不是业务请求。
- 为探测器分配专用且无业务语义的路径,例如
/healthz。 - 使用路径、Host 和必要的请求头进行精确匹配。
- 对照当前 KubeElasti CRD 校验
apiVersion与字段,而不是照搬示例。 - 连续执行探测请求,确认副本数保持为零。
- 执行真实 API 请求,确认服务仍会扩容并完成冷启动。
- 保留一条低频端到端检查,用来发现应用或依赖本身的故障。
ProbeResponse 解决的是 scale-to-zero 系统中的流量分类问题:健康检查需要得到回答,但不应自动获得启动整个业务工作负载的权力。只要把合成探测与真实就绪状态明确分开,就能同时保留空闲成本优势和可观测性。