AI agent chết giữa chừng trên Kubernetes: 3 lỗi thường gặp và cách Google Cloud khắc phục
Google Cloud vừa đăng hướng dẫn chính thức chạy agent AI nhiều lượt trên GKE. Bài này tóm ba lỗi khiến session chết giữa chừng — nghẽn CPU lúc boot, rớt stream khi rollout, mất checkpoint khi restart — và ba cơ chế vá từng lỗi, kèm cấu hình YAML và code cốt lõi.
Google Cloud vừa đăng hướng dẫn chính thức về cách chạy agent AI "có trạng thái" trên GKE (Google Kubernetes Engine, dịch vụ Kubernetes của Google Cloud). Bài hướng dẫn giải quyết một vấn đề cụ thể: agent chạy nhiều lượt hội thoại thường chết theo ba cách khác nhau khi deploy lên Kubernetes với cấu hình mặc định.
Ba cách chết đó là: nghẽn CPU lúc khởi động, rớt stream giữa chừng khi rollout bản mới, và mất toàn bộ lịch sử hội thoại khi Pod restart. Bài này tóm tắt ba cơ chế mà Google Cloud dùng để vá từng lỗi, kèm cấu hình cốt lõi để bạn áp dụng.
Vì sao agent không giống web service thường
Hầu hết kiến trúc Kubernetes được thiết kế quanh một giả định đơn giản: request HTTP đến, chạy logic không trạng thái trong chưa đầy 200 mili giây, rồi trả response.
Agent đảo ngược toàn bộ giả định đó. Một request của người dùng có thể mở ra một session streaming hai chiều kéo dài 3 phút, trong đó agent lập kế hoạch, gọi công cụ bên ngoài, nạp tài liệu nhiều megabyte vào cửa sổ ngữ cảnh, và stream từng bước suy luận về cho client.
Khi bạn deploy những agent chạy lâu và có trạng thái này lên GKE bằng cấu hình web-service mặc định, ba lỗi production xuất hiện gần như ngay lập tức.
Lỗi 1: nghẽn CPU lúc khởi động
Image của agent thường đóng gói SDK của Google, thư viện gRPC, bộ kiểm tra schema và thư viện đọc tài liệu. Nếu bạn xin CPU cho container theo mức chạy ổn định — lúc Pod chủ yếu chờ mạng, chỉ cần 500m CPU (nửa core) — thì 10 giây đầu khởi động nó cần tới 1500m vì phải nạp SDK, tokenizer và pipeline tracing.
Kết quả: Pod mới bị bóp nghẹt CPU ngay lúc boot, rớt kiểm tra readiness, hoặc làm kẹt sự kiện tự động tăng Pod.
Cách sửa của Google Cloud là VPA CPU Startup Boost (tính năng tăng CPU lúc khởi động của Vertical Pod Autoscaler). GKE tạm cấp gấp 3 CPU lúc Pod được tạo, rồi dùng cơ chế resize tại chỗ để hạ về 500m ngay khi Pod qua kiểm tra readiness — không cần restart container. Đặt updateMode: "Off" để chỉ boost lúc khởi động mà không bật VPA chạy liên tục (tránh xung đột với bộ tự động tăng giảm Pod theo tải).
apiVersion: autoscaling.k8s.io/v1
kind: VerticalPodAutoscaler
metadata:
name: gemini-agent-startup-boost
namespace: agent-runtime
spec:
targetRef:
apiVersion: apps/v1
kind: Deployment
name: gemini-session-orchestrator
updatePolicy:
updateMode: "Off"
startupBoost:
cpu:
type: Factor
factor: 3
durationSeconds: 0
Lưu ý cho GKE Autopilot: Autopilot kiểm tra tỉ lệ CPU/RAM lúc Pod được tạo, nên memory request phải tương xứng với CPU được boost, hoặc gán workload sang nhóm máy hiệu năng cao.
Lỗi 2: rớt stream khi rollout
Load balancer mặc định đóng connection sau 30 giây. Khi GKE thu hẹp số Pod hoặc rollout image mới, tín hiệu kết thúc SIGTERM đến đột ngột sẽ cắt ngang stream WebSocket hoặc SSE (Server-Sent Events, cơ chế server đẩy dữ liệu về client) đang chạy giữa lượt suy luận.
Cách sửa phải phối hợp hai tầng: vòng đời Pod của Kubernetes và load balancer của GKE.
Ở tầng Pod: cho Pod 300 giây để tắt êm (terminationGracePeriodSeconds: 300) và thêm hook preStop chờ 10 giây. 10 giây này quan trọng vì Kubernetes gỡ Pod khỏi danh sách phục vụ và load balancer cập nhật cấu hình không diễn ra đồng bộ với SIGTERM; chờ một chút giúp request đang bay không đâm vào socket đang đóng.
Ở tầng load balancer: dùng GCPBackendPolicy để nâng timeout lên 600 giây và thời gian draining lên 300 giây, kèm session affinity bằng cookie để stream của một user luôn về đúng Pod.
apiVersion: networking.gke.io/v1
kind: GCPBackendPolicy
metadata:
name: gemini-streaming-backend-policy
namespace: agent-runtime
spec:
default:
timeoutSec: 600
connectionDraining:
drainingTimeoutSec: 300
sessionAffinity:
type: GENERATED_COOKIE
cookieTtlSec: 3600
targetRef:
group: ""
kind: Service
name: gemini-session-svc
Và trong Deployment, đặt maxUnavailable: 0 để GKE chỉ tắt Pod cũ khi Pod mới (đã được boost CPU) sẵn sàng phục vụ. Nhờ đó không session nào bị giết chỉ vì rollout.
Lỗi 3: mất checkpoint khi Pod restart
Dù có 5 phút draining, một session agent có thể kéo dài hàng giờ qua nhiều lượt tương tác. Và nếu máy ảo bị thu hồi trên Spot capacity (nhóm máy giá rẻ có thể bị lấy lại bất cứ lúc nào), Pod chỉ được báo trước 30 giây. Lưu lịch sử hội thoại trong RAM của Pod nghĩa là restart xong agent phải nạp lại toàn bộ ngữ cảnh từ đầu — tốn tiền và tốn thời gian.
Cách sửa: checkpoint trạng thái sau mỗi lượt của agent ra Redis (Google gợi ý Memorystore for Redis, dịch vụ Redis của Google Cloud). Pod nào trong Deployment cũng đọc được checkpoint, nên Pod mới tiếp quản session một cách trong suốt.
Đoạn code cốt lõi trong server (rút gọn từ bài gốc): bắt SIGTERM để đánh dấu "đang draining", cho readiness probe trả lỗi để không nhận session mới, chờ các lượt đang chạy xong rồi mới thoát.
draining = False
active_turns = 0
def handle_sigterm(*_args):
global draining
draining = True # ngừng nhận session mới
@app.post("/v1/sessions/{session_id}/stream")
async def stream_agent_turn(session_id: str, request: TurnRequest):
# Nạp lịch sử từ checkpoint Redis, không phải từ RAM
raw = await redis_client.get(f"session:{session_id}")
history = json.loads(raw) if raw else []
...
# Sau mỗi lượt, checkpoint lại vào Redis, giữ 24 giờ
await redis_client.set(f"session:{session_id}",
json.dumps(history), ex=86400)
Điểm tinh tế: readiness probe trả lỗi khi đang draining, nên load balancer ngừng đổ session mới về Pod sắp tắt, trong khi các lượt cũ vẫn được chạy nốt.
Tóm tắt: lỗi nào, vá bằng gì
| Thách thức | Hành vi mặc định của Kubernetes | Cách làm production trên GKE |
|---|---|---|
| Nặng lúc khởi động SDK | Container bị bóp CPU lúc boot | VPA Startup Boost: cấp tạm 1500m, hạ về 500m tại chỗ khi ready |
| Session streaming dài | Load balancer đóng connection sau 30 giây | GCPBackendPolicy: timeout 600 giây, draining 300 giây, affinity bằng cookie |
| Pod bị tắt khi rollout | SIGTERM rồi cưỡng chế tắt sau 30 giây |
Tắt êm 300 giây + checkpoint Redis sau mỗi lượt |
Kiểm chứng sau khi deploy
Sau khi apply manifest, bạn xác nhận boost có hiệu lực bằng cách xem Pod và chờ event resize tại chỗ:
kubectl get pods -n agent-runtime -l app=gemini-session-orchestrator \
-o custom-columns="NAME:.metadata.name,CPU_REQ:.spec.containers[0].resources.requests.cpu"
kubectl get events -n agent-runtime --field-selector reason=InPlaceResizedByVPA
Event mong đợi: Pod was in-place resized by VPA Updater (CPU: 1500m -> 500m). Nghĩa là Pod đã boot xong nhờ CPU boost và được hạ về mức chạy ổn định mà không restart.
Checklist trước khi đưa agent lên production
- Đo CPU lúc boot và lúc chạy ổn định của image agent. Nếu chênh nhau từ 2 lần trở lên, cấu hình VPA Startup Boost.
- Liệt kê mọi timeout trên đường đi của stream: load balancer, gateway, Pod. Đặt chúng đủ dài cho lượt suy luận dài nhất bạn chấp nhận.
- Đừng lưu trạng thái session trong RAM của Pod. Checkpoint ra ngoài (Redis/Memorystore) sau mỗi lượt, để Pod nào cũng tiếp quản được.
- Trong
preStop, chờ vài giây trước khi ngừng nhận connection. Việc gỡ Pod và cập nhật load balancer không bao giờ đồng bộ tuyệt đối. - Đặt
maxUnavailable: 0cho Deployment chạy agent có trạng thái. Rollout không được giết session đang sống.
Kubernetes mặc định được tối ưu cho request ngắn và không trạng thái. Agent thì ngược lại: session dài và đầy trạng thái. Ba cơ chế trên không phải "tối ưu thêm" mà là điều kiện cần để agent sống sót qua boot, rollout và restart trên production.
Nguồn tham khảo: Google Cloud, "Scaling stateful Gemini 4 Argon agent sessions on GKE: Connection draining, startup boost, and session persistence", DEV Community, 22/10/2026 — https://dev.to/googlecloud/scaling-stateful-gemini-4-argon-agent-sessions-on-gke-connection-draining-startup-boost-and-13ln