1. 项目概述:这不是一次普通升级,而是一次面向生产级AI服务的云原生重构
“降SpringAI阿里第18掌-神龙摆尾-登云K8s”——光看这个标题,老运维一眼就懂:这不是在教你怎么跑个Hello World,而是在讲一个真实落地的、扛得住日均百万调用量的SpringAI服务,如何从单机Jar包形态,彻底蜕变为部署在阿里云ACK(Alibaba Cloud Container Service for Kubernetes)上的高可用、可灰度、能弹性伸缩的云原生AI应用。我带团队做过三轮SpringAI上云迁移,前两次都卡在配置漂移和环境不一致上,直到这次用“神龙摆尾”这套打法,才真正把模型推理服务的SLA从99.5%拉到99.95%。核心关键词SpringAI、阿里、K8s,不是简单堆砌,而是三层咬合:SpringAI是能力底座,阿里是基础设施载体,K8s是调度与治理中枢。它解决的不是“能不能跑”,而是“能不能稳、能不能扩、能不能查、能不能管”。适合两类人:一类是正在用SpringAI做智能审核、内容生成、RAG问答的Java后端,正被本地部署的OOM、CPU飙高、日志难追踪折磨;另一类是阿里云上的SRE或平台工程师,手头有现成ACK集群,但苦于AI服务缺乏标准化交付路径。它不教Docker基础,不讲K8s概念,只聚焦一件事:怎么把你的SpringAI工程,变成一个能在阿里云K8s里“呼吸、心跳、自愈”的活体服务。
2. 整体设计思路拆解:为什么必须是“神龙摆尾”,而不是直上直下?
2.1 “神龙摆尾”不是玄学,是规避三大落地陷阱的工程策略
所谓“神龙摆尾”,本质是一种渐进式、可回滚、带观测闭环的迁移路径。很多团队一上来就想“一步到位”,直接把SpringBoot+SpringAI打包成镜像扔进K8s,结果三天两头Pod CrashLoopBackOff,排查发现全是环境依赖没对齐。我们踩过最深的坑有三个:
第一是模型加载路径陷阱。本地开发时,resources/models/llama3.bin路径写死,打包进Jar后通过ClassPathResource读取没问题;但容器里,/app.jar!/BOOT-INF/classes!/models/这种嵌套路径,Spring Resource机制会失效,模型根本加载不出来。硬改代码?不行,业务逻辑和部署逻辑耦合太重。
第二是配置中心割裂陷阱。SpringAI的系统提示词、大模型API Key、向量库连接串,如果全写在application.yml里,每次换环境就得重新打包。更糟的是,阿里云上你得同时对接ACM(应用配置管理)、KMS(密钥管理服务)、RDS(关系型数据库),这些配置源格式、权限模型、刷新机制完全不同,硬编码等于自废武功。
第三是资源弹性失配陷阱。AI推理服务的内存消耗不是线性的——1个Qwen2-7B模型实例,冷启动要4GB,热加载后稳定在2.8GB,但并发突增时,GC压力会让RSS瞬间冲到5.2GB。如果K8s里只设requests: 3Gi, limits: 4Gi,要么OOMKilled,要么资源浪费严重。
“神龙摆尾”的解法,就是把这三座大山,拆解成四个可验证、可度量、可中断的阶段:
- 尾部探路(Tail Probe):不碰主服务,先用Sidecar模式,在现有K8s集群里起一个独立的SpringAI推理Pod,只暴露健康检查端点,验证模型加载、基础HTTP调用通路;
- 腰身承压(Waist Load):把原SpringBoot服务的AI能力抽离为独立微服务,通过Feign Client调用,主服务无感,新服务走K8s Service暴露;
- 首部切换(Head Switch):灰度流量,用Istio VirtualService把5%的
/api/v1/ai/analyze请求路由到新K8s服务,监控TP99、错误率、GPU显存占用; - 全身登云(Full Cloud):确认稳定后,将旧服务下线,新服务接管全部流量,并接入ARMS(应用实时监控服务)做全链路追踪。
这个过程像神龙摆尾——尾巴先动,带动腰身,再抬首,最后腾空。每一步都有明确的成功指标(比如Tail Probe阶段,curl -I http://springai-test:8080/actuator/health必须返回200),失败即回滚,绝不赌一把。
2.2 为什么选阿里云ACK而非自建K8s?三个硬指标决定
有人问:为什么非得是阿里?用腾讯云TKE或华为云CCE不行吗?行,但成本、效率、生态适配度差一截。我们对比过三朵云的AI服务上云方案,阿里云在三个关键维度形成碾压:
第一,GPU资源调度精度。SpringAI跑Llama3-8B,需要A10或V100显卡。阿里云ACK的GPU共享调度器(基于device plugin v2)支持按显存MB粒度分配,比如一个Pod申请nvidia.com/gpu: 1,nvidia.com/gpu-memory: 8192(8GB),调度器会精准找到一块剩余显存≥8GB的GPU卡,而不是粗暴地整卡分配。腾讯云TKE目前只支持整卡调度,华为云CCE虽支持显存切分,但需手动维护Device Plugin版本,升级易出错。我们实测,同样16台A10服务器,阿里云ACK的GPU利用率稳定在78%,自建K8s集群只有52%。
第二,ACM配置热更新可靠性。SpringAI的系统提示词(system prompt)需要频繁迭代优化。ACM的配置推送是基于长连接+增量diff,变更后3秒内推送到所有Pod,且自带版本回滚按钮。我们试过用Consul做配置中心,网络抖动时出现过17%的Pod未收到更新,导致部分请求用旧提示词,审核误判率飙升。ACM的推送成功率是99.999%,背后是阿里自研的HSF(High Speed Framework)通信协议,不是简单的HTTP轮询。
第三,ARMS与Prometheus无缝集成深度。SpringAI服务的关键指标不是CPU%,而是ai_inference_duration_seconds_bucket(推理耗时分布)、ai_model_load_success_total(模型加载成功数)。阿里云ARMS的K8s监控模板,预置了Spring Boot Actuator + Micrometer的指标采集规则,开箱即用。你不用写一行Prometheus exporter配置,kubectl get pods -n springai后,ARMS控制台自动列出所有Pod的JVM GC时间、线程数、以及SpringAI特有的ai.prompt.token.count(提示词token数)指标。自建方案要自己写Exporter、配ServiceMonitor、调Grafana面板,至少多花2人日。
所以,“阿里”不是品牌选择,是技术选型的必然结果——它解决了SpringAI上云最痛的三个点:GPU细粒度调度、配置热更新零丢失、AI专属指标开箱监控。
2.3 K8s不是容器编排工具,而是AI服务的“操作系统”
很多人把K8s当成高级版Docker Compose,这是致命误区。在SpringAI场景下,K8s承担的是传统操作系统内核的角色:
进程管理(Process Management)→ 对应K8s的Pod生命周期管理。SpringAI服务启动慢(模型加载需30~90秒),K8s的
startupProbe能精准定义“服务真正就绪”的条件(比如连续3次/actuator/health返回UP),避免流量打到未加载完模型的Pod上,造成雪崩。内存管理(Memory Management)→ 对应K8s的Resource Limits + OOMScoreAdj。我们给SpringAI Pod设置
limits.memory: 6Gi,并配合JVM参数-XX:+UseContainerSupport -XX:MaxRAMPercentage=75.0,让JVM自动识别容器内存限制,避免JVM无视cgroup限制疯狂堆内存,触发OOMKilled。设备驱动(Device Driver)→ 对应NVIDIA Device Plugin。它把物理GPU抽象成K8s可调度的资源,让
kubectl describe node能看到nvidia.com/gpu: 4,让Pod声明resources.limits."nvidia.com/gpu": "1"就能获得GPU句柄,SpringAI底层的HuggingFace Transformers库通过CUDA_VISIBLE_DEVICES自动绑定。文件系统(File System)→ 对应K8s的ConfigMap + Secret + PersistentVolume。模型权重文件(.bin/.safetensors)放PV(NAS),配置放ConfigMap,API Key放Secret,三者解耦,互不影响更新。
理解这点,才能跳出“K8s就是yaml写多了”的浅层认知。它不是把应用塞进容器,而是为AI服务构建一套云原生的运行时环境。
3. 核心细节解析与实操要点:从代码到YAML,每一行都经得起生产检验
3.1 SpringAI工程改造:剥离“环境感知”,只留“能力契约”
改造SpringAI服务的第一步,不是写Dockerfile,而是重构代码。核心原则:让SpringAI模块完全不知道自己运行在K8s还是本地,只认接口契约。
我们原来的代码是这样的:
@Service public class AiReviewService { private final Llama3Model model; // 直接new Llama3Model() public AiReviewService() { // 从classpath加载模型,硬编码路径 this.model = new Llama3Model("classpath:/models/llama3-8b-q4_k_m.gguf"); } }问题在于:classpath:在容器里失效;new Llama3Model()把模型加载逻辑和业务逻辑耦合;没有考虑模型热加载。
改造后,变成标准的Spring Boot Starter风格:
@Configuration public class SpringAiAutoConfiguration { @Bean @ConditionalOnMissingBean public Llama3Model llama3Model( @Value("${ai.model.path:/mnt/models/llama3-8b-q4_k_m.gguf}") String modelPath, @Value("${ai.model.n-gpu-layers:40}") int nGpuLayers) { // 模型路径从配置中心读取,支持file://、http://、oss://协议 return Llama3Model.builder() .modelPath(modelPath) // 支持oss://bucket-name/models/... .nGpuLayers(nGpuLayers) .build(); } } // 业务Service只依赖接口 @Service public class AiReviewService { private final AiModel aiModel; // 接口,不是具体实现 public AiReviewService(AiModel aiModel) { this.aiModel = aiModel; } public ReviewResult review(String content) { // 调用统一AI接口,不关心底层是Llama还是Qwen return aiModel.generate(content, systemPrompt); } }关键改动点:
- 模型路径外置化:
@Value("${ai.model.path}"),值来自ACM配置,支持OSS路径(oss://springai-prod/models/llama3.bin?accessKeyId=xxx&accessKeySecret=xxx&endpoint=oss-cn-hangzhou.aliyuncs.com),避免把敏感信息写进代码。 - 构造函数注入:
Llama3ModelBean由Spring容器管理,生命周期与Pod一致,Pod销毁时自动释放GPU显存。 - 接口抽象:
AiModel是顶层接口,未来换Qwen模型,只需新增QwenModel implements AiModel,业务代码零修改。
提示:
@ConditionalOnMissingBean确保用户自定义Bean优先级高于自动配置,方便测试时Mock。
3.2 Maven配置阿里云仓库:不只是提速,更是构建确定性的基石
maven配置阿里云仓库看似小事,却是CI/CD流水线稳定性的命脉。我们曾因中央仓库超时,导致Jenkins构建卡在Downloading spring-boot-starter-web-3.2.0.jar长达12分钟,最终超时失败。
标准配置(settings.xml):
<mirrors> <mirror> <id>aliyunmaven</id> <mirrorOf>*</mirrorOf> <name>Aliyun Maven</name> <url>https://maven.aliyun.com/repository/public</url> </mirror> </mirrors> <profiles> <profile> <id>aliyun</id> <repositories> <repository> <id>central</id> <url>https://maven.aliyun.com/repository/central</url> <releases><enabled>true</enabled></releases> <snapshots><enabled>false</enabled></snapshots> </repository> <!-- 关键:Spring官方仓库镜像 --> <repository> <id>spring-milestones</id> <url>https://maven.aliyun.com/repository/spring-milestones</url> <releases><enabled>true</enabled></releases> <snapshots><enabled>false</enabled></snapshots> </repository> <!-- 关键:Spring Snapshot镜像,用于尝鲜版SpringAI --> <repository> <id>spring-snapshots</id> <url>https://maven.aliyun.com/repository/spring-snapshots</url> <releases><enabled>false</enabled></releases> <snapshots><enabled>true</enabled></snapshots> </repository> </repositories> </profile> </profiles> <activeProfiles> <activeProfile>aliyun</activeProfile> </activeProfiles>为什么必须配spring-milestones和spring-snapshots?因为SpringAI 1.0.0-M3(里程碑版)和最新快照版(如1.0.0-SNAPSHOT)只发布在Spring官方仓库,中央仓库同步有延迟。阿里云镜像站已与Spring官方建立直连通道,同步延迟<5分钟。我们实测,用默认中央仓库下载spring-ai-core-1.0.0-M3.jar需47秒,用阿里云镜像仅需1.8秒,且100%成功率。
注意:
<mirrorOf>*</mirrorOf>不能写成<mirrorOf>central</mirrorOf>,否则会绕过Spring官方仓库镜像。必须用*全局覆盖。
3.3 Docker镜像构建:Slim不是目标,是手段;安全才是底线
SpringAI服务的Dockerfile,我们坚持“三不原则”:不装apt、不装curl、不跑root。基础镜像选eclipse-jetty:11-jre17-slim,而非openjdk:17-jdk-slim,因为Jetty内置Web容器,省去Tomcat依赖,镜像体积从428MB压到286MB。
关键安全加固点:
# 使用非root用户 USER jetty:jetty # 复制jar时指定owner,避免root权限残留 COPY --chown=jetty:jetty target/*.jar /app.jar # JVM参数强制指定容器内存限制 ENTRYPOINT ["java", "-XX:+UseContainerSupport", "-XX:MaxRAMPercentage=75.0", \ "-XX:+UnlockExperimentalVMOptions", "-XX:+UseZGC", \ "-Dspring.profiles.active=k8s", \ "-jar", "/app.jar"]为什么用ZGC?SpringAI服务GC停顿是最大瓶颈。我们压测发现,用G1 GC,100并发下,ai_inference_duration_seconds的P99高达2.8秒,其中1.2秒是GC停顿;换成ZGC后,P99降到1.1秒,GC停顿<10ms。ZGC在JDK17+已生产就绪,阿里云ACK节点默认装JDK17,无需额外安装。
实操心得:
-XX:+UseContainerSupport必须加,否则JVM会按宿主机内存计算堆大小,导致OOM。我们曾漏掉这参数,Pod在8GB内存节点上,JVM却申请了12GB堆,直接被K8s OOMKill。
3.4 K8s部署清单:不是yaml堆砌,是服务契约的具象化
一个生产级SpringAI Deployment,绝不是kubectl create deployment生成的模板。我们手写的deployment.yaml包含7层防护:
apiVersion: apps/v1 kind: Deployment metadata: name: springai-inference labels: app: springai-inference spec: replicas: 3 # 至少3副本,防止单点故障 selector: matchLabels: app: springai-inference template: metadata: labels: app: springai-inference annotations: # 关键:配置热更新触发器,ACM变更时自动滚动更新 k8s.aliyun.com/acm-config-update-trigger: "true" spec: # 关键:使用专用GPU节点组,避免CPU密集型Pod抢占GPU nodeSelector: aliyun.com/eci: "false" # 排除ECI(弹性容器实例),只用ECS GPU节点 node.kubernetes.io/instance-type: "ecs.gn7i-c16g1.4xlarge" # 指定A10机型 # 关键:容忍GPU节点污点 tolerations: - key: "nvidia.com/gpu" operator: "Exists" effect: "NoSchedule" containers: - name: springai image: registry.cn-hangzhou.aliyuncs.com/springai/springai-inference:v1.8.0 ports: - containerPort: 8080 name: http # 关键:StartupProbe确保模型加载完成才接收流量 startupProbe: httpGet: path: /actuator/health port: 8080 failureThreshold: 30 # 最多尝试30次(30*10s=5分钟) periodSeconds: 10 # 关键:LivenessProbe检测服务是否存活 livenessProbe: httpGet: path: /actuator/health/liveness port: 8080 initialDelaySeconds: 120 # 模型加载需2分钟,延迟探测 periodSeconds: 30 # 关键:ReadinessProbe检测是否可服务 readinessProbe: httpGet: path: /actuator/health/readiness port: 8080 initialDelaySeconds: 180 # 等待模型加载+warmup periodSeconds: 10 resources: requests: memory: "4Gi" cpu: "2" nvidia.com/gpu: "1" # 申请1块GPU limits: memory: "6Gi" cpu: "4" nvidia.com/gpu: "1" env: - name: JAVA_HOME value: "/opt/java/openjdk" # 关键:从Secret挂载API Key,避免明文 envFrom: - secretRef: name: springai-secrets # 关键:挂载OSS模型文件到/mnt/models volumeMounts: - name: models-pv mountPath: /mnt/models volumes: - name: models-pv persistentVolumeClaim: claimName: springai-models-pvc --- # Service暴露,带SessionAffinity确保同一用户请求落到同一Pod(对stateful模型重要) apiVersion: v1 kind: Service metadata: name: springai-inference spec: type: ClusterIP sessionAffinity: ClientIP sessionAffinityConfig: clientIP: timeoutSeconds: 10800 # 3小时 selector: app: springai-inference ports: - port: 8080 targetPort: 8080这份yaml的价值,在于把运维经验固化为代码:
startupProbe.failureThreshold: 30对应模型加载最长5分钟,超时则重启Pod;sessionAffinity: ClientIP对需要模型warmup的场景至关重要,避免用户首次请求打到刚启动的Pod,触发长冷启;envFrom.secretRef确保API Key不泄露,Secret由ACM KMS加密托管;nodeSelector锁定GPU机型,防止调度到CPU节点导致服务不可用。
4. 实操过程与核心环节实现:从ACK集群创建到ARMS监控大盘
4.1 阿里云ACK集群创建:避开“默认配置”这个最大坑
创建ACK集群时,90%的人直接点“创建”,结果发现GPU节点无法调度。根源在三个默认配置:
第一,默认Worker节点池不启用GPU支持。在ACK控制台创建集群时,Worker节点池配置页,必须勾选“启用GPU支持”,并选择“NVIDIA GPU驱动”版本(我们选535.54.03,兼容A10/V100)。不勾选,节点上就不会装NVIDIA驱动,kubectl get nodes -o wide看不到nvidia.com/gpu资源。
第二,默认VPC路由表不放行GPU节点网段。GPU节点(如ecs.gn7i)的内网IP段是172.16.0.0/12,但ACK默认VPC路由表只允许172.16.0.0/16。必须手动添加路由条目:目标网段172.16.0.0/12,下一跳vpc。否则Pod间跨节点通信失败,Service无法访问。
第三,默认安全组不开放GPU节点SSH端口。虽然生产环境不该开SSH,但调试时必须能登录GPU节点看nvidia-smi。默认安全组只放行22端口给127.0.0.1,需编辑入方向规则,添加0.0.0.0/0的TCP:22(临时,调试完立即删)。
创建后验证命令:
# 1. 检查GPU节点是否Ready且有GPU资源 kubectl get nodes -o wide | grep gn7i kubectl describe node <gpu-node-name> | grep -A 5 "nvidia.com/gpu" # 2. 检查Device Plugin是否正常 kubectl get daemonset -n kube-system | grep nvidia kubectl logs -n kube-system <nvidia-device-plugin-pod-name> # 3. 运行GPU测试Pod kubectl run gpu-test --rm -t -i --restart=Never --image=nvcr.io/nvidia/cuda:11.8.0-base-ubuntu20.04 --limits=nvidia.com/gpu=1 --command -- bash -c "nvidia-smi && echo 'GPU OK'"4.2 ACM配置中心接入:让系统提示词像发微信一样更新
SpringAI的system prompt是业务灵魂,必须支持秒级更新。ACM配置接入分三步:
Step 1:在ACM控制台创建配置
- Data ID:
springai-inference.properties - Group:
DEFAULT_GROUP - 配置格式:Properties
- 内容:
ai.model.path=oss://springai-prod/models/llama3-8b-q4_k_m.gguf ai.system-prompt=你是一个专业的电商内容审核员,请严格按以下规则判断:1. 含违禁词则拒绝;2. 含营销话术则标记... ai.temperature=0.3
Step 2:Spring Boot工程引入ACM Starter
<dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-acm</artifactId> <version>2.2.10.RELEASE</version> </dependency>Step 3:配置bootstrap.yml启用动态刷新
spring: cloud: acm: endpoint: acm.aliyuncs.com namespace: xxxxx-xxxx-xxxx-xxxx-xxxxxxxx # ACM命名空间ID group: DEFAULT_GROUP >apiVersion: networking.istio.io/v1beta1 kind: VirtualService metadata: name: springai-vs spec: hosts: - springai.example.com http: - route: - destination: host: springai-inference.default.svc.cluster.local subset: v1 # 旧服务 weight: 95 # 95%流量 - destination: host: springai-inference.default.svc.cluster.local subset: v2 # 新K8s服务 weight: 5 # 5%灰度 --- apiVersion: networking.istio.io/v1beta1 kind: DestinationRule metadata: name: springai-dr spec: host: springai-inference.default.svc.cluster.local subsets: - name: v1 labels: version: v1.7.0 # 旧Deployment标签 - name: v2 labels: version: v1.8.0 # 新Deployment标签灰度期间,我们紧盯三个指标:
- 错误率对比:
v1和v2的4xx/5xx错误率,偏差>0.1%立即暂停; - P99耗时对比:
v2的P99不能比v1高20%,否则说明K8s调度或网络有损耗; - GPU显存对比:
v2的container_gpu_memory_used_bytes峰值不能比v1高15%,否则模型加载有差异。
实测中,我们发现v2的P99比v1高12%,排查发现是新Pod的startupProbe.initialDelaySeconds设为120秒,但模型warmup需150秒,导致部分请求打到未warmup的Pod。调整为initialDelaySeconds: 180后,P99回归正常。
5. 常见问题与排查技巧实录:那些文档里不会写的血泪教训
5.1 问题速查表:高频故障与一键定位命令
| 故障现象 | 可能原因 | 定位命令 | 解决方案 |
|---|---|---|---|
Pod状态Pending,事件显示0/3 nodes are available: 3 Insufficient nvidia.com/gpu. | GPU资源未注册或调度器未启用 | kubectl get nodes -o wide,kubectl describe node <node> | 检查NVIDIA Device Plugin DaemonSet是否Running,kubectl logs -n kube-system <plugin-pod> |
Pod状态CrashLoopBackOff,日志显示java.lang.OutOfMemoryError: Java heap space | JVM未识别容器内存限制 | kubectl exec -it <pod> -- jps -l,kubectl exec -it <pod> -- jinfo -flag MaxRAMPercentage <pid> | 在Dockerfile中添加-XX:+UseContainerSupport -XX:MaxRAMPercentage=75.0 |
/actuator/health返回DOWN,status为MODEL_LOAD_FAILED | OSS模型路径权限错误或网络不通 | kubectl exec -it <pod> -- curl -v "oss://bucket/models/llama3.bin" | 检查Secret中AKSK是否正确,OSS Bucket是否开启Public Read,Pod所在节点能否ping oss-cn-hangzhou.aliyuncs.com |
P99耗时突增,ARMS显示container_gpu_utilization接近0% | GPU未被正确调用,Fallback到CPU | kubectl exec -it <pod> -- nvidia-smi,kubectl logs <pod> | grep "Using CUDA" | 检查Dockerfile是否用了nvidia/cuda:11.8.0-runtime-ubuntu20.04基础镜像,检查resources.limits."nvidia.com/gpu"是否设置 |
ACM配置更新后,Pod日志无Configuration refreshed记录 | ACM Starter未生效或配置错误 | kubectl exec -it <pod> -- curl http://localhost:8080/actuator/env | grep acm | 检查bootstrap.yml是否在src/main/resources下,检查spring-cloud-starter-alibaba-acm版本是否匹配Spring Boot版本 |
5.2 独家避坑技巧:来自37次上线的实战笔记
技巧1:模型文件OSS上传的MD5校验,比任何文档都重要
OSS上传模型文件后,必须用ossutil64 head oss://bucket/models/llama3.bin查看x-oss-hash-crc64ecma,再在本地用crc32c llama3.bin计算,两者必须一致。我们曾因OSS断点续传bug,上传了一个损坏的.gguf文件,Pod启动时llama.cpp报invalid magic number,但日志只显示Segmentation fault,排查3小时才发现是文件损坏。现在,CI流水线最后一步强制校验MD5,不一致则构建失败。
技巧2:startupProbe的failureThreshold不是越大越好
有人设failureThreshold: 100,以为更保险。错!K8s的startupProbe失败后,会立即杀死Pod并重启,重启又触发新的startupProbe,形成恶性循环。我们设30(300秒),是因为模型加载最长5分钟,超过就说明环境有问题(如OSS网络不通),必须人工介入,而不是无限重启。
技巧3:readinessProbe的initialDelaySeconds必须大于模型warmup时间readinessProbe默认在容器启动后立即执行,但SpringAI模型加载后,首次推理仍需warmup(加载KV Cache)。我们实测,Llama3-8B warmup需60秒。所以initialDelaySeconds: 180(模型加载120秒 + warmup60秒),否则流量打进来,P99爆表。
技巧4:GPU节点的kubelet参数必须调优
默认kubelet的--eviction-hard参数(如memory.available<100Mi)对GPU节点太激进。GPU显存被模型占用,但kubelet只看系统内存,可能误判OOM。我们在GPU节点上,通过ACK节点池自定义脚本,修改/var/lib/kubelet/config.yaml:
evictionHard: memory.available: "500Mi" # 从100Mi放宽到500Mi nodefs.available: "10%"并重启kubelet。否则GPU节点会频繁NotReady。
技巧5:ACM配置的>