尧图建网站 尧图建网站 YAOTU WEB BUILD 免费咨询
ARTICLE DETAIL

资讯详情

深耕网站建设与建站编程的一线实战洞察。

Spring AI部署到Kubernetes后频繁重启和503?Liveness、Readiness、启动探针与优雅停机完整排查

Spring AI部署到Kubernetes后频繁重启和503?Liveness、Readiness、启动探针与优雅停机完整排查 文章摘要Spring AI应用部署到Kubernetes后经常出现一组看似矛盾的问题Pod明明已经启动Ingress却持续返回503模型Provider短暂抖动后Kubernetes开始反复重启应用知识索引加载需要两分钟Pod还没准备好就被Liveness杀死滚动发布期间旧Pod收到SIGTERM但仍有流式回答、长任务和Tool Calling正在执行结果被强制中断。根因通常不是Kubernetes不稳定而是健康检查把“进程是否活着”“实例能否接收新流量”“依赖是否暂时可用”“启动是否完成”混成了一个/actuator/health。对于AI应用模型Provider、向量库、Reranker和对象存储都可能短暂不可用但这些外部依赖不应该轻易进入Liveness否则一次第三方故障会触发所有Pod同时重启形成重启风暴。Spring Boot Actuator提供Liveness和Readiness状态并能在启动和优雅停机阶段反映应用可用性Kubernetes则分别使用Startup、Liveness和Readiness Probe控制启动保护、重启和流量摘除。本文给出AI应用的探针分层、依赖健康策略、冷启动、连接池预热、流式请求排空、长任务租约释放、preStop、terminationGracePeriodSeconds和滚动发布参数的完整配置。一、先看一个典型事故Deployment配置livenessProbe:httpGet:path:/actuator/healthport:8080periodSeconds:10readinessProbe:httpGet:path:/actuator/healthport:8080periodSeconds:5/actuator/health中包含PostgreSQLRedisVectorStore模型ProviderReranker对象存储。某个模型Provider发生30秒抖动。结果/actuator/health DOWN ↓ Liveness失败 ↓ Kubernetes重启Pod ↓ 所有Pod同时重新建立连接 ↓ 冷启动和依赖压力升高 ↓ 更多探针失败 ↓ 服务持续503真正应该发生的是Provider暂时不可用 → 路由到备用模型 或 → Readiness按策略拒绝部分新请求 或 → 返回可解释降级 而不是 → 重启整个JVM二、四种状态必须分开1. Process AliveJVM和事件循环仍然工作应用没有进入不可恢复死锁。对应Liveness2. Startup Completed应用是否完成初始化配置加载Bean创建数据库迁移必要模型Profile加载本地词典或规则加载连接池预热。对应Startup Probe3. Ready for New Traffic实例是否可以接受新的业务请求。对应Readiness4. Business Dependency Capability某个具体能力是否可用主模型可用备用模型可用向量检索可用Tool Gateway可用异步队列可用。这不一定映射到Pod级探针更适合能力矩阵、业务健康端点、指标和降级策略。三、Liveness应该检查什么Liveness的目标是回答重启这个进程 是否可能恢复适合进入Liveness的情况JVM不可响应事件循环严重阻塞核心线程死锁应用内部状态不可恢复必需本地资源损坏自检确认必须重启。不适合模型Provider 5xxRedis短暂超时VectorStore延迟升高数据库主从切换Reranker限流外部Tool不可用。外部依赖故障通常不会因为重启Pod而恢复。四、Readiness应该检查什么Readiness回答这个实例现在是否应该接收新流量适合考虑应用是否完成启动本实例是否正在优雅停机核心配置是否加载必需数据库是否可用当前实例是否具备至少一个可用模型路由是否达到严重过载阈值是否正在执行不可兼容迁移。但也不能把所有依赖直接加入Readiness。若所有实例同时UnreadyService将无法接收新连接因此外部依赖是否进入Readiness需要结合系统的降级能力判断。五、AI应用的推荐健康分层Liveness JVM内部可恢复性 Readiness 当前实例是否接受新的AI请求 Capability Health chat/rag/tool/embedding等能力矩阵 Dependency Metrics Provider、VectorStore、Redis、DB详细指标能力状态publicrecordAiCapabilityStatus(booleanchatAvailable,booleanragAvailable,booleantoolAvailable,booleanembeddingAvailable,SetStringavailableModelProfiles,SetStringdegradedReasons){}六、Spring Boot探针配置management:endpoint:health:probes:enabled:trueshow-details:neverhealth:livenessstate:enabled:truereadinessstate:enabled:trueserver:shutdown:gracefulspring:lifecycle:timeout-per-shutdown-phase:45s典型端点/actuator/health/liveness /actuator/health/readiness管理端口可独立management:server:port:9090但管理端口健康不代表业务端口一定可用。生产中应验证两者共享的关键线程池、网络栈和进程状态。七、Startup Probe解决什么AI应用冷启动可能很慢大量Bean数据源初始化本地模型或Tokenizer加载Prompt模板编译知识库元数据缓存证书加载远程配置连接预热。如果只设置Liveness应用尚未启动完成 → Liveness失败 → 重启 → 永远无法启动Startup Probe在成功前保护应用不执行普通Liveness判定。startupProbe:httpGet:path:/actuator/health/livenessport:9090periodSeconds:5failureThreshold:36最大启动窗口5秒 × 36 180秒窗口应来自真实冷启动P99而不是凭经验随意设置。八、推荐Kubernetes探针startupProbe:httpGet:path:/actuator/health/livenessport:9090periodSeconds:5timeoutSeconds:2failureThreshold:36livenessProbe:httpGet:path:/actuator/health/livenessport:9090periodSeconds:10timeoutSeconds:2failureThreshold:3readinessProbe:httpGet:path:/actuator/health/readinessport:9090periodSeconds:5timeoutSeconds:2failureThreshold:2successThreshold:1不要把timeoutSeconds设得比健康检查内部调用的依赖超时还短否则会制造随机失败。九、不要在探针里调用真实模型错误实现ComponentpublicclassModelHealthIndicatorimplementsHealthIndicator{publicHealthhealth(){StringanswerchatClient.prompt().user(ping).call().content();returnanswer!null?Health.up().build():Health.down().build();}}问题每几秒产生Token费用Provider限流时探针失败模型延迟拖慢探针所有Pod同时发请求健康检查增加生产负载真实Prompt可能进入日志。更合理的方式后台主动探测并缓存结果检查路由器是否至少有一个可用Profile探针只读本地聚合状态Provider探测使用短TTL和熔断业务流量仍执行独立重试和Fallback。十、自定义Readiness策略ComponentpublicclassAiReadinessPolicy{privatefinalProviderCapabilityRegistryproviderRegistry;privatefinalOverloadGuardoverloadGuard;privatefinalMigrationStatemigrationState;publicReadinessDecisionevaluate(){if(migrationState.blocksTraffic()){returnReadinessDecision.refuse(MIGRATION);}if(overloadGuard.isSeverelyOverloaded()){returnReadinessDecision.refuse(OVERLOAD);}if(!providerRegistry.hasAnyUsableChatProfile()){returnReadinessDecision.refuse(NO_CHAT_MODEL);}returnReadinessDecision.accept();}}通过ApplicationAvailability发布状态ComponentpublicclassAiReadinessPublisher{privatefinalApplicationEventPublisherpublisher;privatefinalAiReadinessPolicypolicy;Scheduled(fixedDelay5000)publicvoidrefresh(){ReadinessDecisiondecisionpolicy.evaluate();AvailabilityChangeEvent.publish(publisher,this,decision.accepting()?ReadinessState.ACCEPTING_TRAFFIC:ReadinessState.REFUSING_TRAFFIC);}}十一、Readiness抖动治理外部依赖可能一秒好、一秒坏。若每次都立即切换ReadinessEndpoint会频繁加入和移出Service。publicfinalclassHysteresisHealthGate{privatefinalintfailureThreshold;privatefinalintsuccessThreshold;privateintconsecutiveFailures;privateintconsecutiveSuccesses;privatebooleanreadytrue;publicsynchronizedbooleanupdate(booleancurrentHealthy){if(currentHealthy){consecutiveSuccesses;consecutiveFailures0;if(!readyconsecutiveSuccessessuccessThreshold){readytrue;}}else{consecutiveFailures;consecutiveSuccesses0;if(readyconsecutiveFailuresfailureThreshold){readyfalse;}}returnready;}}还可以增加最短保持时间避免秒级来回切换。十二、数据库应该进入Readiness吗强依赖数据库如果每个请求都必须完成鉴权、读取租户配置、写审计和扣预算数据库不可用时无法服务可以进入Readiness。可短时降级如果只读缓存仍能回答部分FAQ可以保持Ready但标记Capability降级。不要把“依赖Down”机械等价为“整个Pod Unready”。十三、模型Provider故障的正确策略Primary失败 ↓ 路由Fallback ↓ Fallback也失败 ↓ 按任务类型 返回降级 转异步 拒绝新请求只有所有关键Profile不可用且没有可接受降级时才考虑Readiness拒绝AI流量。Provider故障更适合进入provider_availability provider_error_rate routing_fallback_rate而不是Liveness。十四、向量库故障怎么处理对于RAG专用接口VectorStore不可用 → RAG Capability不可用对于同时支持非RAG任务的应用Chat仍可用 → 整个Pod不必Unready可以按路径拆分服务也可以由API Gateway根据能力状态路由。十五、滚动发布期间为什么会503常见时序新Pod启动 ↓ Readiness过早变为Ready ↓ 流量进入 ↓ Prompt、连接池、索引元数据尚未预热 ↓ 超时与503另一个方向旧Pod收到SIGTERM ↓ 仍保持Ready数秒 ↓ 继续收到新请求 ↓ 容器被终止 ↓ 流式回答中断需要同时处理新Pod进入与旧Pod退出。十六、预热与Readiness启动完成不等于业务已预热。可以预热数据库连接Redis连接模型路由配置Prompt模板Tokenizer向量库Collection元数据HTTP连接池和DNS必需证书。ComponentpublicclassAiWarmupRunnerimplementsApplicationRunner{privatefinalWarmupCoordinatorwarmupCoordinator;privatefinalApplicationEventPublisherpublisher;Overridepublicvoidrun(ApplicationArgumentsargs){warmupCoordinator.warmup();AvailabilityChangeEvent.publish(publisher,this,ReadinessState.ACCEPTING_TRAFFIC);}}预热不要调用高费用的真实生成可使用Metadata接口、低成本连接测试和专用测试Profile。十七、优雅停机的正确时序Pod进入Terminating ↓ preStop执行 ↓ 应用切换ReadinessREFUSING_TRAFFIC ↓ Endpoint从Service摘除 ↓ 等待流量传播 ↓ SIGTERM ↓ Spring Boot Graceful Shutdown ↓ 停止接收新请求 ↓ 排空短请求与流式请求 ↓ 释放任务租约 ↓ 退出Spring Boot在优雅停机阶段拒绝新流量并允许在途请求在配置窗口内完成。十八、preStoplifecycle:preStop:exec:command:-/bin/sh--c-wget --post-data -qO- http://127.0.0.1:9090/internal/drain || true; sleep 10/internal/drain应只允许本机或管理网络切换Readiness禁止新长任务通知Worker停止拉取不暴露公网。十九、Drain ManagerServicepublicclassDrainManager{privatefinalApplicationEventPublisherpublisher;privatefinalAtomicBooleandrainingnewAtomicBoolean();publicvoidbegin(){if(!draining.compareAndSet(false,true)){return;}AvailabilityChangeEvent.publish(publisher,this,ReadinessState.REFUSING_TRAFFIC);}publicbooleanisDraining(){returndraining.get();}}新任务入口先检查if(drainManager.isDraining()){thrownewServiceDrainingException();}二十、异步长任务如何停机AI任务中心不能依赖HTTP请求完成。Pod停机时Worker停止领取新任务当前步骤尽量Checkpoint不可快速完成时停止续租让租约到期新Worker接管不要直接把任务标记FAILED。EventListenerpublicvoidonShutdown(ContextClosedEventevent){workerAdmission.stopAccepting();taskCheckpointCoordinator.flushInProgress();leaseManager.stopRenewingAfterCheckpoint();}二十一、流式回答如何排空SSE或Flux流可能持续几分钟。spring:lifecycle:timeout-per-shutdown-phase:60sKubernetesterminationGracePeriodSeconds:75Kubernetes窗口应大于应用优雅停机窗口并预留preStop时间。若允许无限长流任何固定窗口都可能截断请求。产品应提供Response ID、断线重连、结果持久化或异步任务模式。二十二、Tool Calling停机风险工具可能正在发邮件提交订单发权益写ERP。SIGTERM后不能盲目重试。工具执行记录必须包含toolCallId幂等键状态外部操作IDUNKNOWN状态。新Pod恢复时先查询外部结果不要直接重做。二十三、Deployment滚动参数strategy:type:RollingUpdaterollingUpdate:maxUnavailable:0maxSurge:1minReadySeconds:20progressDeadlineSeconds:600revisionHistoryLimit:10含义maxUnavailable: 0尽量不减少现有可用实例maxSurge: 1额外启动一个新PodminReadySeconds新Pod持续Ready一段时间才视为可用progressDeadlineSeconds发布长期无进展时标记失败revisionHistoryLimit保留旧ReplicaSet用于回滚。资源紧张时要确认集群能容纳Surge。二十四、PodDisruptionBudgetapiVersion:policy/v1kind:PodDisruptionBudgetmetadata:name:spring-ai-apispec:minAvailable:2selector:matchLabels:app:spring-ai-apiPDB主要约束自愿中断不替代副本数、探针和发布策略。二十五、资源配置AI编排应用常见瓶颈HTTP连接内存中的Prompt流式连接线程池JSON序列化本地TokenizerTool并发。resources:requests:cpu:500mmemory:1Gilimits:cpu:2memory:2GiCPU Limit过低时冷启动和GC可能导致探针超时。探针失败应同时排查CPU throttling、GC pause、线程池饱和、连接池和DNS。二十六、探针端点安全Actuator不要全部公网暴露。management:endpoints:web:exposure:include:-health-prometheus详细健康信息可能泄露数据库、Provider、区域和内部版本。公网只返回最小状态详细信息放内部监控。二十七、监控指标kube_pod_container_status_restarts_total kube_pod_status_ready spring_ai_readiness_transition_total{ reason } spring_ai_drain_in_progress spring_ai_inflight_requests spring_ai_streaming_requests spring_ai_worker_active_tasks spring_ai_task_lease_released_total spring_ai_provider_available{ profile } spring_ai_routing_fallback_total spring_ai_startup_duration_seconds spring_ai_graceful_shutdown_duration_seconds spring_ai_forced_termination_total二十八、告警Pod重启率突然上升 Readiness频繁抖动 新版本Ready后错误率升高 Terminating Pod仍有大量新请求 强制终止流式请求 租约过期任务上升 Provider故障导致全Pod重启二十九、自动化测试1. Provider故障不影响LivenessFake Provider返回503断言livenessUP2. 无可用模型时Readiness拒绝主备Profile全部不可用断言Readiness拒绝流量。3. Startup保护Warmup未完成时不接收业务请求但不会被Liveness重启。4. Drain调用Drain后Readiness拒绝新长任务返回503或429在途短请求继续Worker停止领取任务。5. SIGTERM发送SIGTERM验证Checkpoint和租约状态。6. Rolling Update发布新镜像确认可用副本不低于目标无请求丢失旧Pod排空新Pod满足minReadySeconds。三十、完整Deployment示例apiVersion:apps/v1kind:Deploymentmetadata:name:spring-ai-apispec:replicas:3revisionHistoryLimit:10minReadySeconds:20progressDeadlineSeconds:600strategy:type:RollingUpdaterollingUpdate:maxUnavailable:0maxSurge:1selector:matchLabels:app:spring-ai-apitemplate:metadata:labels:app:spring-ai-apispec:terminationGracePeriodSeconds:75containers:-name:appimage:registry.example.com/spring-ai-api:release-v42ports:-name:httpcontainerPort:8080-name:managementcontainerPort:9090startupProbe:httpGet:path:/actuator/health/livenessport:managementperiodSeconds:5timeoutSeconds:2failureThreshold:36livenessProbe:httpGet:path:/actuator/health/livenessport:managementperiodSeconds:10timeoutSeconds:2failureThreshold:3readinessProbe:httpGet:path:/actuator/health/readinessport:managementperiodSeconds:5timeoutSeconds:2failureThreshold:2lifecycle:preStop:exec:command:-/bin/sh--c-wget --post-data -qO- http://127.0.0.1:9090/internal/drain || true; sleep 10resources:requests:cpu:500mmemory:1Gilimits:cpu:2memory:2Gi三十一、最终排查清单□ Startup、Liveness和Readiness已分离 □ 外部模型故障不会直接触发Liveness失败 □ 探针不会调用真实付费模型 □ Readiness使用本地聚合能力状态 □ Readiness具有失败和恢复滞回 □ 冷启动P99用于计算Startup窗口 □ 新Pod完成必要预热后才Ready □ 管理端口与业务端口可用性关系已验证 □ server.shutdowngraceful □ preStop先切换Readiness并等待流量摘除 □ terminationGracePeriod大于preStop停机窗口 □ Worker停机前Checkpoint并停止续租 □ 流式回答具备断线恢复策略 □ Tool副作用具备幂等和UNKNOWN状态 □ RollingUpdate设置合理的Surge和Unavailable □ minReadySeconds和progressDeadline已配置 □ 详细Actuator信息不对公网暴露 □ 已进行Provider故障、SIGTERM和滚动发布测试总结Spring AI应用在Kubernetes中频繁重启和503根本原因通常不是“探针不够多”而是健康语义设计错误。正确分层是Startup 保护慢启动 Liveness 只判断进程是否需要重启 Readiness 决定是否接收新流量 Capability Health 表达模型、RAG和工具的细粒度能力再配合预热、Readiness滞回、优雅停机、流量排空、任务Checkpoint和工具幂等才能避免外部Provider故障触发重启风暴也避免滚动发布中断流式回答和异步任务。
返回列表