AML避坑指南:手把手教你编写规范的K8s资源清单
在Azure Machine Learning(AML)中,使用Kubernetes作为计算目标进行模型训练和部署,已成为处理复杂、大规模机器学习工作负载的流行选择。其核心在于通过Kubernetes资源清单(通常是YAML文件)来定义计算环境。一份编写规范的清单是稳定、高效运行的基础,反之,则可能引发资源争抢、调度失败、服务不稳定等一系列问题。本文将手把手带你避开常见陷阱,编写出专业的K8s资源清单。
一、 理解AML与K8s的交互模式
在开始编写之前,需要明确AML如何与你的Kubernetes集群交互。当你将K8s集群附加为AML的计算目标后,AML服务会通过其操作员(Operator)或直接在指定命名空间中,根据你提供的资源清单创建和管理Kubernetes原生资源,如Pod、Service、Deployment等,来执行训练任务或托管在线端点。
因此,你的清单文件必须符合Kubernetes API规范,同时也要考虑AML工作流的特定要求。
二、 资源清单基础结构与核心字段
一个典型的用于AML训练任务的Pod规格清单如下所示。我们将以此为例,拆解关键部分。
apiVersion: v1
kind: Pod # 也可以是 Deployment、Job 等
metadata:
name: aml-training-pod # AML通常会覆盖此名称
namespace: aml-compute # 必须与AML附加集群时指定的命名空间一致
labels:
app: ml-training
aml.compute: “true” # 建议添加,便于识别
spec:
containers:
- name: training-container
image: <your-training-image>
command: [“python”, “train.py”]
args: [“--data-path”, “/mnt/data”]
resources:
requests:
memory: “8Gi”
cpu: “2”
nvidia.com/gpu: 1 # 申请GPU资源
limits:
memory: “10Gi”
cpu: “3”
nvidia.com/gpu: 1
volumeMounts:
- name: workspace-mount
mountPath: /mnt/azureml
- name: data-mount
mountPath: /mnt/data
volumes:
- name: workspace-mount
persistentVolumeClaim:
claimName: azureml-fileshare-pvc # AML自动创建的PVC
- name: data-mount
emptyDir: {}
nodeSelector: # 节点选择器
accelerator: nvidia-gpu
tolerations: # 容忍度
- key: “sku”
operator: “Equal”
value: “gpu”
effect: “NoSchedule”
restartPolicy: OnFailure
关键字段解析与避坑点
1. resources:资源请求与限制
这是最核心也最容易出问题的部分。
requests: 调度依据。K8s调度器根据此值为Pod分配节点。如果集群中没有节点能满足所有容器的requests之和,Pod将处于Pending状态。limits: 运行限制。容器运行时(如Docker)强制执行的上限。超过内存限制会导致容器被OOM Kill;超过CPU限制会被限制使用。
避坑指南:
- 必须同时设置
requests和limits: 只设limits不设requests,requests默认等于limits,可能导致调度困难。只设requests不设limits,容器可能耗尽节点资源。 requests和limits值应合理: 对于CPU,limits可略高于requests(如requests: 2,limits: 3)。对于内存,强烈建议requests等于limits,以避免因内存波动导致的不稳定OOM Kill。例如memory: “8Gi”。- GPU资源: 使用
nvidia.com/gpu这样的扩展资源。GPU通常不可超售,因此requests和limits必须相等。
2. nodeSelector 与 tolerations:精准调度
当你的集群混合了CPU和GPU节点,或者有不同规格的节点时,需要使用它们将Pod调度到正确节点。
nodeSelector: 简单直接,选择拥有特定标签的节点。tolerations: 与节点的taints配合使用,允许Pod被调度到带有特定污点的节点上。GPU节点通常带有NoSchedule污点。
避坑指南:
- 确保你指定的节点标签或污点存在于目标节点上。可以通过
kubectl describe node <node-name>查看。 - 在AML中,如果你通过UI/CLI为计算目标指定了节点选择器,它可能会与你清单中的
nodeSelector合并或冲突,需要仔细测试。
3. volumes 与 volumeMounts:持久化存储
机器学习任务需要处理代码、数据和模型。
- AML工作区挂载: AML会自动创建一个名为
azureml-fileshare-pvc的PVC,用于挂载你的工作区文件(如脚本、输出日志)。务必将其挂载到容器内,这是AML与容器通信、上传下载结果的通道。通常挂载到/mnt/azureml。 - 数据挂载: 对于训练数据,可以根据来源选择
azureFile、nfs或像示例中临时使用的emptyDir。
避坑指南:
- 不要修改AML自动创建的PVC名称或挂载路径,除非你非常清楚后果。
- 对于大型数据集,考虑使用高性能存储类(如
managed-premium)或直接使用AML Datastore挂载。
4. image 与 command/args:容器定义
image: 可以使用AML环境构建的镜像,或任何可公开访问或集群内可访问的私有镜像。command和args: 会覆盖Docker镜像中定义的ENTRYPOINT和CMD。在AML中,训练任务通常会由AML注入启动命令,所以在你自己的清单中定义这些时需要谨慎,避免冲突。
避坑指南:
- 如果使用AML的
Environment并指定了conda_dependencies_file,AML会构建镜像并自动处理启动命令。此时在Pod清单中通常不需要指定command/args。 - 若需自定义启动脚本,建议通过
command和args明确指定,确保可预测性。
三、 高级场景与最佳实践
1. 使用Deployment或Job而非裸Pod
对于在线端点,使用Deployment可以确保服务的多副本和高可用性。 对于训练任务,使用Job可以确保任务运行完成,并在失败时提供重试机制。
示例:一个用于模型部署的Deployment片段
apiVersion: apps/v1
kind: Deployment
metadata:
name: ml-model-deployment
spec:
replicas: 2 # 两个副本
selector:
matchLabels:
app: ml-model
template: # Pod模板
metadata:
labels:
app: ml-model
spec:
containers:
- name: inference
image: your-inference-image:latest
ports:
- containerPort: 5001
livenessProbe: # 存活探针
httpGet:
path: /health
port: 5001
initialDelaySeconds: 30
periodSeconds: 10
readinessProbe: # 就绪探针
httpGet:
path: /ready
port: 5001
initialDelaySeconds: 5
periodSeconds: 5
resources:
requests:
memory: “4Gi”
cpu: “1”
limits:
memory: “4Gi”
cpu: “2”
---
apiVersion: v1
kind: Service
metadata:
name: ml-model-service
spec:
type: LoadBalancer # 或 ClusterIP,AML在线端点通常使用LoadBalancer
selector:
app: ml-model
ports:
- port: 80
targetPort: 5001
2. 配置健康检查(探针)
如上面Deployment示例所示,livenessProbe和readinessProbe对于在线服务至关重要。
livenessProbe: 检查容器是否“活着”。失败则重启容器。readinessProbe: 检查容器是否“就绪”接收流量。失败则将其从Service的负载均衡池中移除。
避坑指南:
- 一定要根据你的应用启动逻辑设置合理的
initialDelaySeconds,避免应用还没启动完就被探针杀死了。 - 探针端点应轻量,避免对主业务逻辑造成性能影响。
3. 管理敏感信息:使用Secret
永远不要在YAML文件中明文写入密码、连接字符串或API密钥。
# 1. 在K8s中创建Secret (通过kubectl)
# kubectl create secret generic my-secret --from-literal=key=value
# 2. 在Pod清单中引用
env:
- name: DATABASE_PASSWORD
valueFrom:
secretKeyRef:
name: my-secret
key: key
# 或作为卷挂载
volumeMounts:
- name: secret-volume
mountPath: /etc/secrets
readOnly: true
volumes:
- name: secret-volume
secret:
secretName: my-secret
在AML中,你也可以使用AML工作区自带的Key Vault集成,通过环境变量传递秘密。
四、 调试与验证
- 语法验证: 使用
kubectl apply --dry-run=client -f your_file.yaml验证语法。 - AML SDK验证: 在提交训练任务前,使用AML Python SDK的
KubernetesCompute配置仔细检查你的清单。 - 集群内调试:
kubectl get pods -n <namespace>: 查看Pod状态。kubectl describe pod <pod-name> -n <namespace>: 查看Pod详细事件,这是诊断Pending或CrashLoopBackOff等问题的最重要工具。kubectl logs <pod-name> -c <container-name> -n <namespace>: 查看容器日志。
总结
编写规范的K8s资源清单是成功在AML中使用Kubernetes计算目标的关键。核心要点在于:
- 精细定义资源: 合理设置
requests和limits,特别是内存需保持相等。 - 明确调度要求: 正确使用
nodeSelector和tolerations锁定目标节点。 - 妥善挂载存储: 确保AML工作区卷正确挂载。
- 面向生产设计: 对在线服务使用
Deployment并配置健康检查。 - 安全第一: 敏感信息务必通过
Secret管理。
遵循这些指南,你将能有效避免大多数常见陷阱,构建出稳定、高效且可维护的AML on Kubernetes工作流。