phase 5 part 4: java-spring awareness, k8s basics with validated manifests, run_examples supports single-file Java

This commit is contained in:
Shikui Wang committed 2026-10-23 16:42:04 +08:00
1 parent 075872e31e
commit f695bbd7cc
18 files changed
+1192 -23

No files matched your search

@@ -0,0 +1,89 @@
---
id: devops.k8s.scenario.agent-rollout
title: AI Agent 服务上 K8s:发布时流式回答不中断、慢启动、密钥管理和按负载扩容
type: scenario
domain: devops
stack: [k8s, docker, nginx, cicd]
tags: [滚动更新, 优雅退出, SSE, 探针, HPA, Secret, 回滚]
level: intermediate
prerequisites: [devops.k8s.core-objects, devops.docker.image-dockerfile]
est_hours: 2.5
related: [devops.k8s.interview, devops.cicd.release-rollback]
week: [14]
---
# AI Agent 服务上 K8s:发布时流式回答不中断、慢启动、密钥管理和按负载扩容
## 业务背景与问题
Agent API 从虚拟机迁移到公司的 K8s 集群后:
1. 每次发布,正在进行中的流式回答被切断,用户看到“网络错误”;
2. 新版本启动需要预热(加载提示词模板、建立连接池),偶尔被重启循环;
3. 有人把模型 API Key 直接写在 Deployment 的环境变量里提交到了仓库;
4. 高峰期响应变慢,但 CPU 使用率并不高,HPA 没有扩容。
## 约束
- 集群由平台团队维护,应用团队只能修改自己的清单。
- 回答可能持续 60 秒以上。
## 方案设计
1. **优雅退出**:
- 应用收到 SIGTERM 后:就绪检查立即返回失败、不再接受新请求、等待进行中的流结束(最长 80 秒)后退出;
- `preStop` 短暂等待,给 Ingress / Service 摘除端点的时间;
- `terminationGracePeriodSeconds: 90` 大于最长回答时间;
- 滚动更新 `maxUnavailable: 0`。
2. **慢启动**:startupProbe 给足启动时间;就绪探针在预热完成后才返回成功。
3. **密钥**:轮换已泄露的 Key;清单中只引用 Secret;Secret 由密钥管理系统同步(或平台提供的方案);CI 中加密钥扫描。
4. **扩容指标**:AI 服务主要在等待模型返回,CPU 低但并发连接数高。改用“每个 Pod 的进行中请求数”作为自定义指标(需要平台支持指标适配器),或者先按经验提高副本下限。
5. **发布与回滚**:镜像固定版本;CI 中先 `kubeconform` + 规范测试;发布后观察错误率和首 token 延迟,异常时 `kubectl rollout undo`。
## 关键代码
FastAPI 应用的优雅退出(示意):
```python
import asyncio, signal
active_streams = 0
shutting_down = False
@app.get("/readyz")
async def readyz():
if shutting_down:
raise HTTPException(503) # 让 K8s 尽快摘除这个 Pod
return {"ok": True}
async def drain(timeout: float = 80):
global shutting_down
shutting_down = True
loop = asyncio.get_running_loop()
deadline = loop.time() + timeout
while active_streams > 0 and loop.time() < deadline:
await asyncio.sleep(0.5) # 等待进行中的流式回答结束
```
(Uvicorn 自身也会在收到 SIGTERM 后停止接收新连接并等待请求完成,具体参数如 `--timeout-graceful-shutdown` 以官方文档为准。)
完整清单:[examples/agent-api](../examples/agent-api/)。SSE 在 Nginx 层的配置见 [AI 网关场景](../../nginx/scenarios/ai-gateway-sse-canary.md)。
## 取舍
| 选择 | 优点 | 缺点 |
| --- | --- | --- |
| **优雅退出 + 长宽限期(本方案)** | 用户无感知 | 发布时间变长 |
| 前端断线自动续传(从断点继续) | 对任何中断都有效 | 后端要保存生成进度,复杂 |
| 发布时间放在低峰期 | 简单 | 治标不治本 |
## 易错点
- 宽限期比最长回答时间短,SIGKILL 依然切断。
- 应用作为 shell 脚本的子进程启动,收不到 SIGTERM(使用 exec 形式的启动命令)。
- 就绪探针在退出阶段仍然返回成功,新请求继续进来。
- 以为 Secret 是加密的。
## 面试怎么讲
> “迁移到 K8s 后发布会切断流式回答。我做了优雅退出:收到 SIGTERM 后就绪探针立即失败,停止接新请求,等进行中的流结束;preStop 留出摘除端点的时间,宽限期设为 90 秒,大于最长回答;滚动更新不减少可用副本。慢启动用 startupProbe。泄露的 Key 立即轮换,清单只引用 Secret,CI 加密钥扫描。扩容方面,AI 服务 CPU 不高但并发高,改用进行中请求数作为指标。”