地质岩芯编录(core logging)是矿产勘探中最耗人力的环节之一——地质师需要逐段观察钻探取出的岩芯,记录岩性、结构、矿物含量,一条钻孔往往产生数百米样本,全靠肉眼和经验判断。ALS GeoAnalytics 的 LITHOLENS™ 把这件事交给机器学习:对岩芯高分辨率图像做自动识别与分类,大幅压缩编录时间,同时让结果更可复现。
但模型从实验室走向生产,瓶颈不在算法本身,而在如何以可控成本把训练和推理同时跑起来。ALS 的选择是 Amazon EKS——用 Kubernetes 统一调度 GPU 训练任务与 CPU 推理服务,按需扩缩,避免资源闲置。下面拆解这条路径的关键决策与可落地的配置思路。
岩芯图像识别的工程挑战
LITHOLENS 的输入是岩芯箱的高分辨率照片,输出是逐段岩性标签与结构描述。这带来几个工程约束:
- 训练数据量大:一个项目可能产生上万张图像,每张需要切块、标注,训练周期长。
- 推理要跟钻探节奏:野外钻探不停,岩芯照片持续涌入,推理服务必须低延迟、高可用。
- 成本敏感:勘探项目预算刚性,GPU 不能空转,CPU 推理节点也不能全天候待命却只处理零星请求。
ALS 的解法是把训练和推理都放进 EKS,用同一套集群调度两类截然不同的负载——训练吃 GPU、跑完就释放;推理用 CPU、随流量伸缩。
EKS 集群设计:训练与推理共享调度
核心思路是 工作负载隔离、资源池共享。训练任务以 Job 或批处理 Pod 形式提交,推理服务以 Deployment + HPA 方式运行。两者共享集群控制面,但节点组分开管理。
节点组策略
训练节点组挂载 GPU(如 p3.2xlarge 或 g5 系列),使用 Karpenter 或 Cluster Autoscaler 按队列深度扩缩——队列里有待训练任务才拉起节点,训练完成 Pod 退出后节点缩回。推理节点组用普通 CPU 实例(如 c5 / c6g),配合 HPA 按 CPU 利用率或自定义指标伸缩。
下面是一个最小化的节点组与部署配置,可直接改造用于类似场景:
# karpenter-nodepool-gpu.yaml — GPU 训练节点池
apiVersion: karpenter.sh/v1beta1
kind: NodePool
metadata:
name: gpu-training
spec:
template:
spec:
nodeClassRef:
name: default
requirements:
- key: karpenter.k8s.aws/instance-category
operator: In
values: ["g"] # g5 系列,性价比优
- key: karpenter.k8s.aws/instance-gpu-count
operator: Gt
values: ["0"]
- key: kubernetes.io/arch
operator: In
values: ["amd64"]
limits:
cpu: "1000"
disruption:
consolidationPolicy: WhenUnderutilized
expireAfter: 720h
---
# karpenter-nodepool-cpu.yaml — CPU 推理节点池
apiVersion: karpenter.sh/v1beta1
kind: NodePool
metadata:
name: cpu-inference
spec:
template:
spec:
nodeClassRef:
name: default
requirements:
- key: karpenter.k8s.aws/instance-category
operator: In
values: ["c"]
- key: kubernetes.io/arch
operator: In
values: ["amd64"]
limits:
cpu: "500"
disruption:
consolidationPolicy: WhenUnderutilized
---
# inference-deployment.yaml — 推理服务
apiVersion: apps/v1
kind: Deployment
metadata:
name: litholens-inference
spec:
replicas: 2
selector:
matchLabels:
app: litholens-inference
template:
metadata:
labels:
app: litholens-inference
spec:
nodeSelector:
karpenter.k8s.aws/instance-category: c
containers:
- name: server
image: 123456789012.dkr.ecr.ap-southeast-2.amazonaws.com/litholens-inference:v2.1
ports:
- containerPort: 8080
resources:
requests:
cpu: "2"
memory: "4Gi"
limits:
cpu: "4"
memory: "8Gi"
env:
- name: MODEL_PATH
value: "/models/litholens-v2.1.onnx"
volumeMounts:
- name: model-store
mountPath: /models
volumes:
- name: model-store
persistentVolumeClaim:
claimName: litholens-model-pvc
---
# inference-hpa.yaml — 按 CPU 利用率自动伸缩
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: litholens-inference-hpa
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: litholens-inference
minReplicas: 2
maxReplicas: 20
metrics:
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: 60
运行前需要替换的值:
123456789012→ 你的 AWS 账号 IDap-southeast-2→ 你集群所在 Regionlitholens-v2.1.onnx→ 实际模型文件名- PVC 需提前创建并挂载到 EFS 或 S3 CSI,确保模型文件在 Pod 启动时可用
训练任务调度:跑完即走
训练不适合长驻 Deployment,更适合用 Kubernetes Job + 队列 的模式。ALS 的做法是将训练任务打包为容器镜像,通过 Job 提交到 EKS;GPU 节点池收到调度请求后拉起实例,训练结束 Pod 成功退出,节点随后被 Karpenter 缩掉。
一个训练 Job 的骨架如下:
# training-job.yaml
apiVersion: batch/v1
kind: Job
metadata:
name: litholens-train-20240601
spec:
backoffLimit: 3
template:
spec:
nodeSelector:
karpenter.k8s.aws/instance-category: g
tolerations:
- key: nvidia.com/gpu
operator: Exists
effect: NoSchedule
containers:
- name: trainer
image: 123456789012.dkr.ecr.ap-southeast-2.amazonaws.com/litholens-trainer:v2.1
command: ["python", "train.py"]
env:
- name: TRAINING_DATA_PATH
value: "s3://als-geo-data/training/2024-q2/"
- name: OUTPUT_MODEL_PATH
value: "s3://als-geo-models/litholens-v2.2/"
- name: EPOCHS
value: "50"
- name: BATCH_SIZE
value: "32"
resources:
limits:
nvidia.com/gpu: "1"
requests:
cpu: "4"
memory: "16Gi"
nvidia.com/gpu: "1"
restartPolicy: OnFailure
提交与观察:
# 提交训练任务
kubectl apply -f training-job.yaml
# 观察 Job 状态
kubectl get jobs -w
# 查看训练日志
kubectl logs -f job/litholens-train-20240601
# 训练完成后确认 Pod 退出、节点缩回
kubectl get nodes -l karpenter.k8s.aws/instance-category=g
# 几分钟后该列表应变空
训练镜像内部用 boto3 从 S3 拉数据、写模型回 S3,不依赖集群内持久存储,节点随时可以销毁。这是成本控制的关键——GPU 实例只在训练窗口内存在。
成本控制的三个实操要点
ALS 在文章中强调了"minimizing cost",结合 EKS 场景,可提炼出三条可迁移的做法:
1. 用 Karpenter 替代 Cluster Autoscaler 做快速缩容
Karpenter 的 WhenUnderutilized 策略会在 Pod 退出后秒级决定是否合并或删除节点,比 Cluster Autoscaler 的 10 分钟评估窗口快得多。对 GPU 节点来说,每分钟闲置都是真金白银。
2. 推理用 ONNX Runtime 替代原生 PyTorch
推理服务不需要反向传播,ONNX Runtime 在 CPU 上的推理速度通常比 PyTorch 快 2-5 倍,内存占用更低。这意味着同样流量下可以跑更少 Pod、用更小实例。
3. 训练数据与模型走 S3,不走 EBS
EBS 卷随节点生命周期创建和销毁,管理成本高且容易遗漏。训练数据直接从 S3 流式读取,模型写回 S3,集群内只保留推理服务需要的热模型(通过 EFS 或 S3 CSI 挂载),存储账单与计算账单解耦。
上手检查清单
如果你在做类似的"GPU 训练 + CPU 推理同集群调度"项目,落地前逐项确认:
- [ ] EKS 集群已安装 Karpenter,NodePool 按 GPU / CPU 分类,
consolidationPolicy设为WhenUnderutilized - [ ] GPU 节点组已配置
nvidia.com/gpu资源声明与对应 toleration,训练 Job 能正确调度 - [ ] 推理镜像已切换到 ONNX Runtime 或 TensorRT,CPU 利用率基线已测量,HPA 阈值据此设定
- [ ] S3 bucket 与 IAM 策略已就位,训练容器能读写指定路径,推理容器能拉取最新模型
- [ ] 训练 Job 的
backoffLimit与restartPolicy已设定,失败任务不会无限重试占用 GPU - [ ] 集群已配置 Pod priority / preemption:训练 Job priority 低于推理 Deployment,确保推理流量突增时训练可被让道
ALS LITHOLENS 的实践说明,地质 AI 的瓶颈不在模型精度,而在能否把训练和推理两类负载在同一基础设施上低成本跑稳。EKS + Karpenter + S3 的组合,让 GPU 只在需要时出现、CPU 随流量起伏,算力与勘探预算终于可以对齐。