数据科学家的Kubernetes实战:AWS EKS上训练与部署AI模型

📅 2026/7/9 4:47:39
数据科学家的Kubernetes实战:AWS EKS上训练与部署AI模型
1. 项目概述为什么数据科学家需要亲手碰Kubernetes而不是只盯着Jupyter Notebook“Kubernetes”这个词对很多数据科学家来说听着就带点距离感——它常出现在运维工程师的会议纪要里、DevOps招聘JD的技能栏中或者云厂商白皮书的架构图角落。但现实是当你在AWS上训练一个需要8张A10G显卡的LLM微调任务发现SageMaker Studio Notebook突然卡死、Spot实例被无情回收、模型推理API上线后扛不住每秒200次并发请求时你手里的pip install和jupyter notebook --ip0.0.0.0就突然不够用了。这不是理论危机而是我上周三下午三点的真实现场一个用PyTorch Lightning写的多模态推荐模型在EC2 c5.4xlarge上跑得飞起一迁到EKS集群就报OOMKilled日志里连GPU内存分配失败的提示都藏在第7层Pod事件里。这本《A Data Scientist’s Guide To Getting Started With Kubernetes On AWS》不是教你从零写Operator也不是让你去考CKA认证而是帮你把“数据科学工作流”真正变成“可部署、可伸缩、可复现的生产资产”。核心关键词很直白Kubernetes、AWS、EKS、数据科学家、模型训练、模型服务、CI/CD集成。它解决的是三个具体问题第一如何让本地调试通过的训练脚本在AWS上稳定跑完不中断第二如何把训练好的模型无论.pkl、.pt还是Hugging Facetransformers格式封装成能被业务系统调用的HTTP API且支持自动扩缩容第三当团队从单人开发进入多人协作阶段如何避免“在我机器上能跑”的交付灾难。适合人群非常明确已经会写Python数据处理Pipeline、用过SageMaker或EC2跑模型、但还没在真实生产环境维护过服务的数据工程师、ML工程师以及那些正被“模型上线慢、迭代周期长、资源利用率低”反复折磨的算法负责人。它不假设你懂YAML语法细节但默认你熟悉Linux命令行、Docker基本概念以及AWS控制台里找IAM角色和S3桶的位置。2. 整体设计思路为什么选EKS而非自建K8s又为何绕开SageMaker全托管2.1 EKS是数据科学家触达Kubernetes最平滑的跳板很多人第一反应是“AWS不是有SageMaker吗干嘛还要折腾Kubernetes” 这是个好问题答案藏在控制粒度和成本结构里。SageMaker确实省心——点几下就能启动Notebook、训练任务、实时推理端点。但它像一台预装好所有软件的笔记本电脑你无法决定底层内核版本、不能自由挂载NFS共享存储、无法精细控制GPU显存分配策略比如启用MIG模式切分A100、更没法把训练和推理部署在同一套网络策略下统一管理。而EKSElastic Kubernetes Service本质是AWS帮你托管了Kubernetes的Control PlaneAPI Server、etcd、Scheduler等你只需专注管理Worker Node计算节点和上面跑的WorkloadPod。这意味着你可以用kubectl apply -f train-job.yaml提交一个需要16GB GPU显存128GB内存的分布式训练任务同时用另一个kubectl apply -f api-deployment.yaml部署一个基于FastAPI的模型服务两者共享同一个VPC、同一个安全组、同一个私有DNS域名比如model-api.default.svc.cluster.local网络延迟压到毫秒级。更重要的是EKS完全兼容上游Kubernetes生态你想用Kubeflow Pipelines编排多步骤ML流水线可以想用MLflow Tracking Server记录实验指标并直接关联到Pod日志可以甚至想把PrometheusGrafana搭起来监控GPU利用率曲线也完全没问题。这种自由度不是为了炫技而是为了解决真实场景中的耦合问题——比如当你的特征工程需要访问公司内部的Oracle数据库而模型服务需要调用另一个微服务的REST接口SageMaker的沙箱网络模型会让你花三天时间配VPC Endpoint和Security Group规则而在EKS里你只需要在Deployment YAML里加两行env配置再确保Node Security Group放行对应端口即可。2.2 绕开SageMaker全托管是为掌控“最后一公里”的确定性这里必须澄清一个常见误解选择EKS不等于否定SageMaker的价值。恰恰相反我们经常把SageMaker当作EKS的补充工具。比如用SageMaker Ground Truth标注一批图像数据导出到S3再用EKS上的Spark Operator跑大规模特征清洗最后把训练好的模型推送到EKS的模型服务集群。但关键的“最后一公里”——即模型从训练完成到对外提供稳定API——我们坚持用EKS原生方案。原因很实际SageMaker的Real-time Inference Endpoint虽然开箱即用但它的自动扩缩容Auto Scaling基于CPU/Memory使用率对GPU密集型推理如Stable Diffusion文生图几乎无效它的冷启动时间在高并发场景下可能突破10秒更致命的是它的日志和指标深度绑定CloudWatch排查一个CUDA out of memory错误你需要在三个不同控制台界面间反复切换。而EKS方案中我们用KEDAKubernetes Event-driven Autoscaling监听Kafka消息队列积压量来触发Pod扩容用Prometheus抓取nvidia-smi指标实现GPU显存驱动的弹性伸缩所有日志统一输出到Fluent BitOpenSearch一条kubectl logs -l appmodel-api就能看到完整调用链。这种确定性是数据科学家从“做实验的人”转向“交付结果的人”时最需要的底气。2.3 架构设计的三个核心原则渐进式、容器优先、声明式整个方案的设计严格遵循三条铁律它们决定了技术选型和实操路径第一渐进式Progressive Adoption绝不强求一步到位。我们允许你今天只用EKS跑一个离线训练Job明天再加一个简单的Flask API服务后天接入CI/CD自动部署。每个环节都独立验证避免“全盘重构”带来的心理压力。比如第一个实践目标不是部署整套MLflow而是先用kubectl run启动一个临时Pod挂载S3存储桶运行python train.py --data-path s3://my-bucket/train/确认数据读写和GPU调用正常。第二容器优先Container-First所有代码、依赖、环境配置必须打包进Docker镜像。这是Kubernetes世界的“通用货币”也是消除“环境差异”的唯一解。我们禁止任何pip install -r requirements.txt在Pod里执行的操作——那意味着每次启动都要重装依赖既慢又不可控。正确的做法是在Dockerfile里用COPY requirements.txt . pip install -r requirements.txt再COPY . .利用Docker Layer Caching加速构建。一个典型的数据科学镜像大小控制在2.3GB以内含PyTorchCUDAXGBoostPandas比SageMaker默认镜像小40%启动速度快2.1倍实测数据。第三声明式Declarative所有基础设施和应用配置必须用YAML文件描述而非手动点击控制台。train-job.yaml定义训练任务的资源需求、镜像地址、启动命令api-deployment.yaml定义服务副本数、健康检查探针、环境变量ingress.yaml定义外部访问路由。这些文件和你的模型代码一起存进Git仓库成为可审查、可回滚、可自动化测试的“基础设施即代码”IaC。当你发现线上服务异常第一反应不是登录服务器查日志而是git blame api-deployment.yaml看是谁改了livenessProbe超时参数——这才是现代数据工程该有的样子。3. 核心细节解析从AWS账户准备到第一个EKS训练任务落地3.1 AWS账户与权限最小化权限原则下的精准授权在动手创建EKS集群前必须厘清AWS IAM权限边界。很多数据科学家踩的第一个坑就是用Root用户或AdministratorAccess策略直接开干结果后续排查问题时陷入“权限迷宫”。我们的做法是为EKS专门创建一个IAM Role只授予必要权限。核心权限分为三类第一类EKS集群管理权限附加给EKS Cluster Role{ Version: 2012-10-17, Statement: [ { Effect: Allow, Resource: *, Action: [ ec2:DescribeRouteTables, ec2:DescribeVpcs, ec2:DescribeSubnets, ec2:DescribeSecurityGroups, ec2:DescribeDhcpOptions, ec2:DescribeTags, ec2:DescribeInstances, ec2:DescribeVolumes, ec2:CreateSecurityGroup, ec2:CreateTags, ec2:DeleteTags, ec2:AuthorizeSecurityGroupIngress, ec2:RevokeSecurityGroupIngress, ec2:CreateVolume, ec2:DeleteVolume, ec2:AttachVolume, ec2:DetachVolume ] } ] }注意这里没有ec2:RunInstances因为EKS Worker Node的启动由Auto Scaling GroupASG管理权限应落在ASG Role上而非Cluster Role。这是权限分离的关键。第二类Worker Node节点权限附加给Node Instance Role{ Version: 2012-10-17, Statement: [ { Effect: Allow, Resource: [*], Action: [ ec2:DescribeInstances, ec2:DescribeRegions, ec2:DescribeRouteTables, ec2:DescribeSecurityGroups, ec2:DescribeSubnets, ec2:DescribeVpcs, ec2:CreateTags ] }, { Effect: Allow, Resource: [arn:aws:s3:::my-model-bucket/*], Action: [s3:GetObject, s3:PutObject] }, { Effect: Allow, Resource: [arn:aws:ecr:us-east-1:123456789012:repository/my-ds-images], Action: [ecr:GetDownloadUrlForLayer, ecr:BatchGetImage, ecr:BatchCheckLayerAvailability] } ] }重点看最后两段S3权限精确到Bucket路径my-model-bucket/*ECR权限精确到Repository名称my-ds-images。绝不使用Resource: [*]开放全部S3或ECR权限。第三类Pod内应用权限通过IRSA实现这是最高阶也最安全的权限控制。我们不把AWS凭证硬编码进Pod而是用IAM Roles for Service AccountsIRSA。操作流程是在EKS集群启用OIDC Provideraws eks associate-identity-provider-config创建IAM Policy如S3ReadOnlyForModels只允许读取指定S3前缀创建IAM Role信任EKS OIDC Provider并附加该Policy在Kubernetes中创建ServiceAccount标注eks.amazonaws.com/role-arn指向该RolePod启动时自动获得该Role的临时凭证存于/var/run/secrets/eks.amazonaws.com/serviceaccount/token这样你的训练脚本里写boto3.client(s3).get_object(Bucketmy-model-bucket, Keydata/train.parquet)无需任何AK/SK凭证自动轮换权限最小化。我在一个客户项目中用此方案将S3权限泄露风险降为零审计时直接通过。3.2 EKS集群创建CLI一键式与控制台可视化的取舍我们强烈推荐用AWS CLI eksctl工具创建集群而非控制台点击。原因很简单CLI命令可保存、可复现、可纳入CI/CD。以下是创建一个生产就绪EKS集群的核心命令已脱敏# 创建基础集群无Worker Node eksctl create cluster \ --name ds-prod-cluster \ --version 1.28 \ --region us-east-1 \ --vpc-cidr 10.100.0.0/16 \ --nodes 0 \ --managed \ --with-oidc \ --ssh-access \ --ssh-public-key my-aws-key \ --tags OwnerDataScience,EnvironmentProduction # 添加GPU Worker Node Group关键 eksctl create nodegroup \ --cluster ds-prod-cluster \ --region us-east-1 \ --name gpu-workers \ --node-type g4dn.xlarge \ --nodes 2 \ --nodes-min 1 \ --nodes-max 5 \ --node-volume-size 100 \ --ssh-access \ --ssh-public-key my-aws-key \ --spot \ --instance-role-arn arn:aws:iam::123456789012:role/eks-gpu-node-role \ --labels workloadml-training,acceleratornvidia \ --taints nvidia.com/gputrue:NoSchedule \ --asg-access \ --alb-ingress-access \ --external-dns-access逐条解释关键参数--version 1.28指定Kubernetes版本。我们固定用1.28因它是AWS当前GAGenerally Available版本对NVIDIA Device Plugin兼容性最好避免用1.29 Beta版引发驱动冲突。--nodes 0初始不创建Node完全由后续nodegroup命令管理便于精细化控制。--node-type g4dn.xlarge选择带Tesla T4 GPU的实例性价比最优$0.526/hr按需Spot价低至$0.12。若需更高性能可换g5.xlargeA10G$0.652/hr。--taints nvidia.com/gputrue:NoSchedule这是GPU调度的核心它确保只有明确声明需要GPU的Pod才能调度到此Node。否则普通Web服务Pod可能抢占GPU资源导致训练任务OOM。--labels workloadml-training,acceleratornvidia为Node打标签后续在Pod YAML中用nodeSelector精准匹配避免调度错乱。创建完成后执行aws eks update-kubeconfig --name ds-prod-cluster --region us-east-1本地kubectl即可连接集群。验证命令kubectl get nodes -o wide应看到Node状态为Ready且ROLES列为none表示未打Master标签符合安全规范。3.3 数据与模型存储S3作为唯一可信源的工程实践在EKS上做数据科学S3不是“备份选项”而是唯一的、权威的、跨环境一致的数据源。我们强制规定所有原始数据、清洗后数据、特征存储、模型权重、实验日志必须落盘S3路径结构遵循严格约定s3://my-ds-bucket/ ├── raw/ # 原始数据不可变 │ ├── weblogs/ # 按业务域分目录 │ └── user-behavior/ ├── processed/ # 清洗/转换后数据版本化 │ ├── v1.2.0/ # 语义化版本号非时间戳 │ └── v1.3.0/ ├── models/ # 模型资产按框架分类 │ ├── pytorch/ # .pt文件 model_config.json │ ├── sklearn/ # .pkl feature_names.json │ └── transformers/ # HF格式完整目录 ├── experiments/ # MLflow Tracking Server后端 │ └── mlflow/ # SQLite或PostgreSQL数据目录 └── notebooks/ # Jupyter Notebook源码.ipynb这个结构解决了三个痛点第一版本可追溯processed/v1.2.0/目录下所有Parquet文件其生成脚本、参数、Git Commit ID都记录在v1.2.0/_MANIFEST.json中。当业务方质疑“为什么上周预测准这周不准”我们直接对比两个版本的特征分布漂移报告而非争论“是不是数据有问题”。第二权限隔离清晰数据工程师有raw/和processed/的写权限但无models/写权限算法工程师有models/写权限但只能读raw/运维人员有experiments/读权限用于监控。第三跨平台无缝迁移SageMaker Notebook、EKS Training Job、Lambda预处理函数都用同一套S3路径。一个spark.read.parquet(s3a://my-ds-bucket/processed/v1.2.0/)命令在任何环境都能执行无需修改代码。实操中我们用AWS CLI配合--sse aws:kms参数强制加密密钥用AWS KMS自定义CMK审计日志开启S3 Server Access Logging。曾有个客户因未启用SSE被安全团队叫停整个项目教训深刻。3.4 容器镜像构建为数据科学定制的轻量级基础镜像数据科学家常犯的错误是直接用python:3.9-slim作为基础镜像然后在Dockerfile里疯狂pip install。结果镜像体积暴涨到4GBPull耗时2分钟且包含大量无关包如gcc、make增加安全扫描告警。我们的解决方案是构建一个专为数据科学优化的分层镜像。基础层ds-base:1.0基于ubuntu:22.04预装CUDA Toolkit 11.8适配g4dn实例的T4 GPU、cuDNN 8.6、NVIDIA Container Toolkit。关键优化删除所有文档和man pagesapt-get clean rm -rf /usr/share/doc/* /usr/share/man/*用apt-get install --no-install-recommends避免安装建议包预编译NumPy、SciPy、Pandas的wheel避免容器内编译耗时中间层ds-pytorch:1.0基于ds-base:1.0安装PyTorch 2.0.1cu118、Triton、FlashAttention。特别处理pip install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cu118pip install flash-attn --no-build-isolation跳过冗余构建应用层my-model-train:0.1基于ds-pytorch:1.0COPY代码和依赖FROM ds-pytorch:1.0 # 复制requirements.txt并安装利用Layer Caching COPY requirements.txt . RUN pip install --no-cache-dir -r requirements.txt # 复制代码单独一层便于快速迭代 COPY src/ /app/src/ WORKDIR /app # 设置入口点支持多种运行模式 ENTRYPOINT [python, -m, src.train]最终镜像大小ds-base:1.01.2GBds-pytorch:1.02.1GBmy-model-train:0.12.3GB。相比直接用pytorch/pytorch:2.0.1-cuda11.7-cudnn8-runtime3.8GB节省40%空间Pull速度提升2.7倍。我们在CI流水线中用docker buildx build --platform linux/amd64 --push -t 123456789012.dkr.ecr.us-east-1.amazonaws.com/ds-images:train-v0.1 .构建并推送到ECR全程90秒。4. 实操过程从训练Job到模型服务的端到端落地4.1 第一个EKS训练任务用PyTorch Lightning跑分布式训练现在我们把所有准备好的组件串起来运行第一个真正的训练任务。目标在EKS GPU Node上用PyTorch Lightning的DDPDistributed Data Parallel模式训练一个ResNet50图像分类模型数据来自S3。第一步编写训练脚本src/train.pyimport os import torch from torch import nn from torch.utils.data import DataLoader from torchvision import datasets, transforms from pytorch_lightning import LightningModule, Trainer from pytorch_lightning.strategies import DDPStrategy from pytorch_lightning.callbacks import ModelCheckpoint import boto3 from io import BytesIO class ImageClassifier(LightningModule): def __init__(self, num_classes1000): super().__init__() self.model torch.hub.load(pytorch/vision:v0.13.0, resnet50, pretrainedTrue) self.model.fc nn.Linear(self.model.fc.in_features, num_classes) self.criterion nn.CrossEntropyLoss() def forward(self, x): return self.model(x) def training_step(self, batch, batch_idx): x, y batch y_hat self(x) loss self.criterion(y_hat, y) self.log(train_loss, loss) return loss def configure_optimizers(self): return torch.optim.Adam(self.parameters(), lr1e-3) # 关键从S3加载数据非本地路径 def load_data_from_s3(bucket, prefix): s3 boto3.client(s3) # 列出S3目录下所有图片简化版实际用S3 Select或Parquet response s3.list_objects_v2(Bucketbucket, Prefixprefix) image_keys [obj[Key] for obj in response.get(Contents, []) if obj[Key].lower().endswith((.jpg, .png))] # 返回数据集对象此处简化实际用torchvision.datasets.ImageFolder S3FS return None if __name__ __main__: # 从环境变量读取S3路径 s3_bucket os.getenv(S3_BUCKET, my-ds-bucket) s3_prefix os.getenv(S3_PREFIX, raw/weblogs/) model ImageClassifier() # 使用DDPStrategy自动处理多GPU通信 trainer Trainer( max_epochs10, devicesauto, # 自动检测GPU数量 strategyDDPStrategy(find_unused_parametersFalse), acceleratorgpu, log_every_n_steps10, callbacks[ModelCheckpoint(dirpath/tmp/checkpoints/, save_top_k3)], default_root_dir/tmp/logs/ ) trainer.fit(model)第二步编写Kubernetes Job YAMLtrain-job.yamlapiVersion: batch/v1 kind: Job metadata: name: resnet50-train-job labels: app: ml-training spec: backoffLimit: 3 # 失败重试3次 template: spec: restartPolicy: Never # 关键调度到GPU Node nodeSelector: accelerator: nvidia tolerations: - key: nvidia.com/gpu operator: Equal value: true effect: NoSchedule containers: - name: train-container image: 123456789012.dkr.ecr.us-east-1.amazonaws.com/ds-images:train-v0.1 env: - name: S3_BUCKET value: my-ds-bucket - name: S3_PREFIX value: raw/weblogs/ # 挂载S3为本地路径用s3fs-fuse volumeMounts: - name: s3-bucket mountPath: /data resources: limits: nvidia.com/gpu: 1 # 请求1块GPU memory: 16Gi cpu: 8 requests: nvidia.com/gpu: 1 memory: 16Gi cpu: 8 volumes: - name: s3-bucket flexVolume: driver: aws/s3 options: bucket: my-ds-bucket region: us-east-1 # 使用IRSA权限ServiceAccount已配置 serviceAccountName: ds-training-sa第三步提交并监控任务# 提交Job kubectl apply -f train-job.yaml # 查看Pod状态 kubectl get pods -l job-nameresnet50-train-job # 实时查看日志关键 kubectl logs -f job/resnet50-train-job # 查看GPU使用率需部署nvidia-device-plugin kubectl exec -it pod-name -- nvidia-smi实测效果在2个g4dn.xlarge各1块T4节点上DDP训练速度比单卡快1.8倍nvidia-smi显示GPU利用率稳定在85%以上。日志中train_loss每步下降平滑无OOM报错。任务完成后检查/tmp/checkpoints/目录确认模型文件已生成并通过aws s3 cp /tmp/checkpoints/ s3://my-ds-bucket/models/pytorch/resnet50-v1/ --recursive同步到S3。4.2 模型服务化用Triton Inference Server部署PyTorch模型训练完模型下一步是提供API服务。我们放弃自己写Flask/FastAPI封装直接采用NVIDIA Triton Inference Server——它专为AI模型服务设计支持PyTorch、TensorFlow、ONNX等多种框架内置动态批处理、模型分析、GPU显存优化。第一步准备Triton模型仓库结构Triton要求模型以特定目录结构存放。我们将S3中的模型下载并组织triton-models/ └── resnet50/ ├── 1/ # 版本号 │ ├── model.pt # PyTorch模型文件 │ └── config.pbtxt # Triton配置文件 └── config.pbtxt # 模型级配置config.pbtxt内容name: resnet50 platform: pytorch_libtorch max_batch_size: 8 input [ [ INPUT__0, FP32, [3, 224, 224] ] ] output [ [ OUTPUT__0, FP32, [1000] ] ] instance_group [ [ { count: 2, kind: KIND_GPU } ] ]第二步构建Triton服务镜像FROM nvcr.io/nvidia/tritonserver:23.07-py3 # 复制模型仓库 COPY triton-models/ /models/ # 启动Triton服务 CMD tritonserver --model-repository/models --strict-model-configfalse --log-verbose1构建并推送docker build -t 123456789012.dkr.ecr.us-east-1.amazonaws.com/ds-images:triton-resnet50 .第三步部署Triton服务到EKSapiVersion: apps/v1 kind: Deployment metadata: name: triton-resnet50 labels: app: model-api spec: replicas: 2 selector: matchLabels: app: model-api model: resnet50 template: spec: nodeSelector: accelerator: nvidia tolerations: - key: nvidia.com/gpu operator: Equal value: true effect: NoSchedule containers: - name: triton-server image: 123456789012.dkr.ecr.us-east-1.amazonaws.com/ds-images:triton-resnet50 ports: - containerPort: 8000 # HTTP端口 - containerPort: 8001 # GRPC端口 - containerPort: 8002 # Metrics端口 resources: limits: nvidia.com/gpu: 1 memory: 12Gi cpu: 6 requests: nvidia.com/gpu: 1 memory: 12Gi cpu: 6 # 健康检查 livenessProbe: httpGet: path: /v2/health/live port: 8000 initialDelaySeconds: 60 periodSeconds: 30 readinessProbe: httpGet: path: /v2/health/ready port: 8000 initialDelaySeconds: 30 periodSeconds: 10 --- apiVersion: v1 kind: Service metadata: name: triton-resnet50-service spec: selector: app: model-api model: resnet50 ports: - port: 8000 targetPort: 8000 type: ClusterIP第四步暴露服务并测试# 创建Service后获取ClusterIP kubectl get service triton-resnet50-service # 从集群内测试用curl kubectl run curl-test --imagecurlimages/curl -i --tty --rm --restartNever \ -- sh -c curl http://triton-resnet50-service:8000/v2/health/ready # 从外部访问需Ingress或LoadBalancer # 我们用ALB Ingress Controller创建Ingress资源...Triton的优势在此刻凸显它自动处理GPU显存分配nvidia-smi显示两个Pod各占用约5.2GB显存T4总显存15GB留有余量并发请求时动态批处理将8个单图请求合并为1个批次吞吐量提升3.2倍Metrics端口暴露nv_gpu_utilization指标可接入Prometheus告警。4.3 CI/CD集成GitHub Actions自动构建-测试-部署流水线最后一步把所有手动操作变成自动化流水线。我们用GitHub Actions实现Push代码 → 构建镜像 → 运行单元测试 → 部署到EKS。.github/workflows/ml-ci-cd.yml核心逻辑name: ML CI/CD Pipeline on: push: branches: [main] paths: - src/** - Dockerfile - train-job.yaml - triton-deployment.yaml jobs: build-and-test: runs-on: ubuntu-latest steps: - uses: actions/checkoutv3 - name: Set up Python uses: actions/setup-pythonv4 with: python-version: 3.9 - name: Install dependencies run: pip install pytest pytest-cov - name: Run unit tests run: pytest tests/ --covsrc --cov-reportxml - name: Build and push Docker image uses: docker/build-push-actionv4 with: context: . push: true tags: | 123456789012.dkr.ecr.us-east-1.amazonaws.com/ds-images:train-${{ github.sha }} 123456789012.dkr.ecr.us-east-1.amazonaws.com/ds-images:train-latest cache-from: typeregistry,ref123456789012.dkr.ecr.us-east-1.amazonaws.com/ds-images:train-cache cache-to: typeregistry,ref123456789012.dkr.ecr.us-east-1.amazonaws.com/ds-images:train-cache,modemax deploy-to-eks: needs: build-and-test runs-on: ubuntu-latest steps: - uses: actions/checkoutv3 - name: Configure AWS credentials uses: aws-actions/configure-aws-credentialsv2 with: role-to-assume: arn:aws:iam::123456789012:role/github-eks-deployer aws-region: us-east-1 - name: Configure kubectl uses: aws-actions/amazon-eks-set-kubeconfigv1 with: cluster-name: ds-prod-cluster region: us-east-1 - name: Deploy training job run: | sed -i s|image:.*|image: 123456789012.dkr.ecr.us-east-1.amazonaws.com/ds-images:train-${{ github.sha }}| train-job.yaml kubectl apply -f train-job.yaml - name: Deploy Triton service run: | sed -i s|image:.*|image: 123456789012.dkr.ecr.us-east-1.amazonaws.com/ds-images:triton-resnet50| triton-deployment.yaml kubectl apply -f triton-deployment.yaml这个流水线的关键设计触发精准只在src/、Dockerfile等关键路径变更时触发避免无谓构建。镜像缓存用ECR作为Build Cachecache-from/to大幅缩短构建时间。Git SHA绑定镜像Tag用github.sha确保每次部署的镜像是可追溯的。权限最小化github-eks-deployerRole只拥有eks:DescribeCluster、eks:UpdateClusterConfig、ecr:GetAuthorizationToken等必要权限无S3或EC2写权限。实测效果一次完整流水线构建测试部署耗时4分38秒。当算法工程师Push新代码