一、为什么AI应用的容器化比Web应用难一个量级
把传统Web服务塞进Docker,通常几百MB就够了。但AI应用完全是另一个故事——Python运行时、PyTorch/Transformers、CUDA运行时、模型权重文件、系统依赖,镜像动辄3-5GB。镜像过大带来的后果不只是拉取慢和存储贵,还有安全层面的隐性成本:基础镜像本身贡献了60%-80%的CVE数量,87%的生产环境镜像携带高危漏洞。
更棘手的是,传统微服务的容器化经验在AI场景下大量失效。LLM推理服务是有状态的、内存密集型的进程,其性能瓶颈不在CPU而在HBM(高带宽内存)容量和KV Cache的管理效率。传统的HPA基于CPU利用率做扩缩容,对推理服务几乎无效——GPU利用率90%可能意味着GPU正在空转等待长序列解码完成,而非真正在处理请求。
这篇文章从工程实践出发,覆盖Docker镜像构建、K8s GPU调度、弹性伸缩以及AWS/Azure云服务选型四个核心环节。
二、Docker镜像构建:从3GB到500MB的实战路径
2.1 多阶段构建的正确姿势
AI应用的Dockerfile如果写成单阶段,会把编译工具链、开发依赖、测试文件全部塞进最终镜像。多阶段构建的核心思路是把构建环境与运行环境分离。
一个经过生产验证的AI应用多阶段Dockerfile结构如下:
# 阶段1:依赖安装(利用层缓存) FROM python:3.12-slim AS deps WORKDIR /app COPY requirements.txt . RUN pip install --no-cache-dir --user -r requirements.txt # 阶段2:模型预下载(独立缓存层) FROM python:3.12-slim AS model-cache RUN pip install huggingface-hub ENV HF_HOME=/models RUN huggingface-cli download BAAI/bge-m3 --local-dir /models/bge-m3 # 阶段3:最终运行镜像 FROM python:3.12-slim AS runtime RUN apt-get update && apt-get install -y --no-install-recommends \ libgomp1 && rm -rf /var/lib/apt/lists/* COPY --from=deps /root/.local /root/.local ENV PATH=/root/.local/bin:$PATH COPY --from=model-cache /models /models ENV HF_HOME=/models WORKDIR /app COPY src/ ./src/ RUN useradd -m -u 1000 aiuser USER aiuser EXPOSE 8000 CMD ["uvicorn", "src.main:app", "--host", "0.0.0.0", "--port", "8000"]关键细节:模型权重文件放在独立的构建阶段,代码变更时不会触发模型重新下载,这对于频繁迭代的项目能节省大量构建时间。
2.2 镜像瘦身的三个杠杆
第一,基础镜像选型。从完整的python:3.12切换到python:3.12-slim,镜像体积直接减少约70%。如果追求极致,distroless镜像可以进一步消除包管理器和shell,将攻击面降到最低。从完整发行版基础镜像切换到最小化或distroless镜像,扫描器报告的CVE数量通常能降低80%-95%,且不需要修改任何应用代码。
第二,PyTorch的CPU/GPU分离安装。在构建阶段如果没有显式指定GPU,pip install torch会拉取包含完整CUDA运行时的大包(2GB+)。通过--extra-index-url指向CPU-only wheel,可以避免在不需要GPU的构建阶段引入这些依赖。
第三,.dockerignore必须严格。.git目录、本地虚拟环境、测试数据、notebook文件——这些进入构建上下文后会被永久固化到镜像层中。
三、Kubernetes GPU调度与弹性伸缩
3.1 GPU节点的正确配置
在K8s上跑AI推理,GPU节点需要在集群层面做几件事:安装NVIDIA GPU Operator(自动发现GPU、暴露nvidia.com/gpu资源、部署DCGM Exporter提供Prometheus指标),以及通过GPU Feature Discovery自动给节点打标签(GPU型号、显存大小、CUDA版本),这样调度器才能根据模型需求将70B模型路由到H100节点、7B模型路由到L40S节点。
一个常见的配置陷阱是:普通工作负载可能被调度到昂贵的GPU节点上,浪费资源。解决方案是给GPU节点打上Taint,只有声明了对应Toleration的Pod才能调度上去。同时在Pod的Resource中显式声明nvidia.com/gpu: 1的requests和limits,确保调度器预留GPU资源。
3.2 用启动探针解决"冷启动误杀"
模型服务的启动过程非常慢——加载权重、初始化CUDA上下文、预热KV Cache,整个过程可能持续2-5分钟。如果用默认的livenessProbe来检测存活,容器会在模型还没加载完成时就被反复杀死,陷入CrashLoopBackOff。
正确的做法是单独配置startupProbe,给它足够的宽限时间:
startupProbe:httpGet:path:/healthport:8000failureThreshold:60periodSeconds:5readinessProbe:httpGet:path:/readyport:8000livenessProbe:httpGet:path:/healthport:8000initialDelaySeconds:120startupProbe在成功之前会阻塞livenessProbe的检查,failureThreshold×periodSeconds= 300秒的启动窗口,足够大多数模型完成加载。
3.3 基于队列深度的弹性伸缩
用GPU利用率做HPA指标是一个需要避开的常见做法。GPU利用率90%可能只是说明GPU在空转等待解码,而实际请求队列已经堆积。更可靠的扩缩容信号是推理框架暴露的队列指标——vLLM暴露的num_requests_waiting(等待处理的请求数)和kv_cache_usage_perc(KV Cache使用率),才是真正反映服务压力的信号。
实现路径是:通过Prometheus的PodMonitor采集vLLM指标 → 通过metrics-adapter桥接到K8s Custom Metrics API → HPA读取自定义指标做伸缩决策。在一个生产案例中,团队将HPA的扩缩容依据从GPU利用率改为p99_queue_latency后,p99延迟从2.3秒降到680ms,GPU有效吞吐提升32%。
四、推理框架选型:vLLM vs Triton vs TGI
在K8s上部署LLM推理服务,2026年有三个严肃的选择:vLLM、Triton Inference Server(配合TensorRT-LLM)和Hugging Face TGI。
| 维度 | vLLM | Triton + TRT-LLM | TGI |
|---|---|---|---|
| 吞吐(tokens/s, 并发128) | 4,300 | 6,200 | 3,980 |
| 首Token延迟(ms, 并发128) | 620 | 410 | 700 |
| 部署复杂度 | 低 | 高(引擎编译约12小时) | 中 |
| 多模型支持 | 需独立Deployment | 原生支持 | 需独立Deployment |
| AMD GPU支持 | 支持 | 不支持 | 不支持 |
选型原则很简单:如果你无法清晰地说出为什么需要Triton,就用vLLM。vLLM是通往生产环境最快的路径,原生OpenAI兼容API,模型支持最广泛。Triton + TRT-LLM在H100/H200上确实有20%-45%的吞吐优势,但代价是每个模型需要单独编译TensorRT引擎(约12小时)。
五、AWS EKS与Azure AKS的选型差异
两个云平台的K8s托管服务在AI推理场景下的关键差异如下:
AWS EKS的优势在于深度容器映像生态。AWS提供了预构建的深度学习容器(DLC),比如vllm:0.9-gpu-py312-ec2,内置了针对AWS GPU实例优化的NVIDIA库和性能配置。GPU节点推荐使用G5/G6实例系列,配合Karpenter做节点自动扩缩容。模型权重可以从S3直接流式加载到GPU内存,配合S3 Mountpoint CSI驱动实现高性能读取。
Azure AKS在GPU工作负载的隔离和治理上更加成熟。AKS支持NVIDIA GPU的节点池分区策略,包括节点箱打包(Node Bin Packing)来优化GPU利用率。多租户场景下,可以通过命名空间和网络策略在同一个AKS集群中隔离不同类型的GPU工作负载。AKS的GPU最佳实践文档明确建议使用验证策略或准入控制器,强制要求GPU工作负载包含所需的Toleration和Resource Limit。
一个实用的决策框架:如果团队已经在AWS生态中,EKS + DLC + vLLM是阻力最小的路径;如果需要在同一集群中运行训练、推理和数据处理多种GPU工作负载,AKS的节点分区和隔离能力更有优势。
六、总结
AI应用的容器化不是简单的"把模型塞进Docker"。Docker层面需要多阶段构建和严格的镜像瘦身策略,K8s层面需要GPU感知的调度配置和基于队列深度的弹性伸缩,推理框架需要在吞吐、延迟和运维成本之间做权衡。
几个关键原则值得记住:
- 镜像不瘦身就是在付安全债,基础镜像的选择比写什么Dockerfile指令更重要。
- 别用GPU利用率做扩缩容,队列深度和KV Cache使用率才是正确的信号。
- startupProbe是模型服务的保命符,没有它,CrashLoopBackOff几乎是必然的。
- 推理框架选型先排除Triton,除非你有足够的工程预算和明确的性能需求。
云原生基础设施已经足够成熟来承载AI工作负载,但前提是团队愿意把微服务时代的"默认配置"全部重新审视一遍。