TensorFlow 训练 (TFJob)
旧版本
此页面介绍的是 Kubeflow Training Operator V1,最新信息请查阅Kubeflow Trainer V2 文档。
此页面介绍了使用 TensorFlow 训练机器学习模型的 TFJob
。
什么是 TFJob?
TFJob
是一个 Kubernetes 自定义资源 (custom resource),用于在 Kubernetes 上运行 TensorFlow 训练 Job。TFJob
在 Kubeflow 中的实现位于 training-operator
。
注意: TFJob
默认在用户命名空间中无法工作,因为 Istio 自动 sidecar 注入。为了使 TFJob
运行,它需要注解 sidecar.istio.io/inject: "false"
来为 TFJob
的 Pod 禁用 sidecar 注入。
TFJob
是一个 YAML 表示形式的资源,如下所示(请编辑以使用您自己的训练代码的容器镜像和命令)
apiVersion: kubeflow.org/v1
kind: TFJob
metadata:
generateName: tfjob
namespace: your-user-namespace
spec:
tfReplicaSpecs:
PS:
replicas: 1
restartPolicy: OnFailure
template:
metadata:
annotations:
sidecar.istio.io/inject: "false"
spec:
containers:
- name: tensorflow
image: gcr.io/your-project/your-image
command:
- python
- -m
- trainer.task
- --batch_size=32
- --training_steps=1000
Worker:
replicas: 3
restartPolicy: OnFailure
template:
metadata:
annotations:
sidecar.istio.io/inject: "false"
spec:
containers:
- name: tensorflow
image: gcr.io/your-project/your-image
command:
- python
- -m
- trainer.task
- --batch_size=32
- --training_steps=1000
如果您希望您的 TFJob
的 Pod 能够访问凭据 Secret,例如在基于 GKE 安装 Kubeflow 时自动创建的 Google Cloud 凭据,您可以像这样挂载和使用 Secret
apiVersion: kubeflow.org/v1
kind: TFJob
metadata:
generateName: tfjob
namespace: your-user-namespace
spec:
tfReplicaSpecs:
PS:
replicas: 1
restartPolicy: OnFailure
template:
metadata:
annotations:
sidecar.istio.io/inject: "false"
spec:
containers:
- name: tensorflow
image: gcr.io/your-project/your-image
command:
- python
- -m
- trainer.task
- --batch_size=32
- --training_steps=1000
env:
- name: GOOGLE_APPLICATION_CREDENTIALS
value: "/etc/secrets/user-gcp-sa.json"
volumeMounts:
- name: sa
mountPath: "/etc/secrets"
readOnly: true
volumes:
- name: sa
secret:
secretName: user-gcp-sa
Worker:
replicas: 1
restartPolicy: OnFailure
template:
metadata:
annotations:
sidecar.istio.io/inject: "false"
spec:
containers:
- name: tensorflow
image: gcr.io/your-project/your-image
command:
- python
- -m
- trainer.task
- --batch_size=32
- --training_steps=1000
env:
- name: GOOGLE_APPLICATION_CREDENTIALS
value: "/etc/secrets/user-gcp-sa.json"
volumeMounts:
- name: sa
mountPath: "/etc/secrets"
readOnly: true
volumes:
- name: sa
secret:
secretName: user-gcp-sa
如果您不熟悉 Kubernetes 资源,请参考页面 理解 Kubernetes 对象。
TFJob
与内置的 控制器 不同之处在于,TFJob
spec 旨在管理分布式 TensorFlow 训练 Job。
一个分布式 TensorFlow Job 通常包含以下 0 个或多个进程
- Chief Chief 负责协调训练并执行检查点模型等任务。
- Ps Ps 是参数服务器;这些服务器为模型参数提供分布式数据存储。
- Worker Worker 执行模型训练的实际工作。在某些情况下,worker 0 也可能充当 chief。
- Evaluator Evaluator 可用于在模型训练过程中计算评估指标。
TFJob
spec 中的字段 tfReplicaSpecs 包含一个从副本类型(如上所述)到该副本的 TFReplicaSpec 的映射。TFReplicaSpec 由 3 个字段组成
replicas 为此
TFJob
生成的此类型副本的数量。template 一个 PodTemplateSpec,描述为每个副本创建的 Pod。
- Pod 必须包含一个名为
tensorflow
的容器.
- Pod 必须包含一个名为
restartPolicy 决定 Pod 在退出时是否会重启。允许的值如下:
Always 表示 Pod 将始终重启。此策略适用于参数服务器,因为它们永远不会退出,并且在发生故障时应始终重启。
OnFailure 表示如果 Pod 因故障退出,则会重启。
- 非零退出代码表示失败。
- 退出代码为 0 表示成功,并且 Pod 不会重启。
- 此策略适用于 chief 和 workers。
ExitCode 表示重启行为取决于
tensorflow
容器的退出代码,具体如下:退出代码
0
表示进程成功完成,不会重启。以下退出代码表示永久性错误,容器将不会重启
1
: 一般错误2
: shell 内置命令误用126
: 调用的命令无法执行127
: 命令未找到128
: exit 命令参数无效139
: 容器被 SIGSEGV 终止 (无效内存引用)
以下退出代码表示可重试错误,容器将重启
130
: 容器被 SIGINT 终止 (键盘 Control-C)137
: 容器收到 SIGKILL143
: 容器收到 SIGTERM
退出代码
138
对应于 SIGUSR1,保留用于用户指定的可重试错误。其他退出代码未定义,对其行为没有保证。
有关退出代码的背景信息,请参阅GNU 终止信号指南和Linux 文档项目。
Never 表示终止的 Pod 将永远不会重启。应很少使用此策略,因为 Kubernetes 会由于各种原因(例如节点变得不健康)而终止 Pod,这将阻止 Job 恢复。
运行 Mnist 示例
请参阅分布式 MNIST 示例的 manifests。您可以根据自己的要求更改配置文件。
部署 TFJob
资源以开始训练
kubectl create -f https://raw.githubusercontent.com/kubeflow/training-operator/refs/heads/release-1.9/examples/tensorflow/simple.yaml
监控 Job(请参阅下面的详细指南)
kubectl -n kubeflow get tfjob tfjob-simple -o yaml
删除它
kubectl -n kubeflow delete tfjob tfjob-simple
定制 TFJob
通常您可以在 TFJob
的 YAML 文件中更改以下值
更改镜像以指向包含您代码的 docker 镜像
更改副本的数量和类型
更改分配给每个资源的资源(请求和限制)
设置任何环境变量
- 例如,您可能需要配置各种环境变量以与 GCS 或 S3 等数据存储进行交互
如果您想使用 PV 进行存储,请附加 PV。
使用 GPU
要使用 GPU,您的集群必须配置为使用 GPU。
- 节点必须附加 GPU。
- Kubernetes 集群必须识别
nvidia.com/gpu
资源类型。 - 集群上必须安装 GPU 驱动程序。
- 更多信息
要在包含 GPU 的副本的容器中附加 GPU,请指定 GPU 资源;例如。
apiVersion: "kubeflow.org/v1"
kind: "TFJob"
metadata:
name: "tf-smoke-gpu"
spec:
tfReplicaSpecs:
PS:
replicas: 1
template:
metadata:
creationTimestamp: null
spec:
containers:
- args:
- python
- tf_cnn_benchmarks.py
- --batch_size=32
- --model=resnet50
- --variable_update=parameter_server
- --flush_stdout=true
- --num_gpus=1
- --local_parameter_device=cpu
- --device=cpu
- --data_format=NHWC
image: gcr.io/kubeflow/tf-benchmarks-cpu:v20171202-bdab599-dirty-284af3
name: tensorflow
ports:
- containerPort: 2222
name: tfjob-port
resources:
limits:
cpu: "1"
workingDir: /opt/tf-benchmarks/scripts/tf_cnn_benchmarks
restartPolicy: OnFailure
Worker:
replicas: 1
template:
metadata:
creationTimestamp: null
spec:
containers:
- args:
- python
- tf_cnn_benchmarks.py
- --batch_size=32
- --model=resnet50
- --variable_update=parameter_server
- --flush_stdout=true
- --num_gpus=1
- --local_parameter_device=cpu
- --device=gpu
- --data_format=NHWC
image: gcr.io/kubeflow/tf-benchmarks-gpu:v20171202-bdab599-dirty-284af3
name: tensorflow
ports:
- containerPort: 2222
name: tfjob-port
resources:
limits:
nvidia.com/gpu: 1
workingDir: /opt/tf-benchmarks/scripts/tf_cnn_benchmarks
restartPolicy: OnFailure
遵循 TensorFlow 关于使用 GPU 的说明。
监控你的 Job
获取 Job 状态
kubectl -n kubeflow get -o yaml tfjobs tfjob-simple
以下是一个示例 Job 的输出示例
apiVersion: kubeflow.org/v1
kind: TFJob
metadata:
creationTimestamp: "2021-09-06T11:48:09Z"
generation: 1
name: tfjob-simple
namespace: kubeflow
resourceVersion: "5764004"
selfLink: /apis/kubeflow.org/v1/namespaces/kubeflow/tfjobs/tfjob-simple
uid: 3a67a9a9-cb89-4c1f-a189-f49f0b581e29
spec:
tfReplicaSpecs:
Worker:
replicas: 2
restartPolicy: OnFailure
template:
spec:
containers:
- command:
- python
- /var/tf_mnist/mnist_with_summaries.py
image: gcr.io/kubeflow-ci/tf-mnist-with-summaries:1.0
name: tensorflow
status:
completionTime: "2021-09-06T11:49:30Z"
conditions:
- lastTransitionTime: "2021-09-06T11:48:09Z"
lastUpdateTime: "2021-09-06T11:48:09Z"
message: TFJob tfjob-simple is created.
reason: TFJobCreated
status: "True"
type: Created
- lastTransitionTime: "2021-09-06T11:48:12Z"
lastUpdateTime: "2021-09-06T11:48:12Z"
message: TFJob kubeflow/tfjob-simple is running.
reason: TFJobRunning
status: "False"
type: Running
- lastTransitionTime: "2021-09-06T11:49:30Z"
lastUpdateTime: "2021-09-06T11:49:30Z"
message: TFJob kubeflow/tfjob-simple successfully completed.
reason: TFJobSucceeded
status: "True"
type: Succeeded
replicaStatuses:
Worker:
succeeded: 2
startTime: "2021-09-06T11:48:10Z"
条件
一个 TFJob
有一个 TFJobStatus
,它包含一个 TFJobConditions
数组,表示 TFJob
已经或尚未经过的状态。TFJobCondition
数组的每个元素有六个可能的字段
- 字段 lastUpdateTime 提供此条件上次更新的时间。
- 字段 lastTransitionTime 提供条件上次从一个状态转换到另一个状态的时间。
- 字段 message 是一个人类可读的消息,指示有关转换的详细信息。
- 字段 reason 是一个唯一的、一个单词的 CamelCase 原因,表示条件上次转换的原因。
- 字段 status 是一个字符串,可能的值为 “True”、“False” 和 “Unknown”。
- 字段 type 是一个字符串,可能的值如下:
- TFJobCreated 表示
TFJob
已被系统接受,但一个或多个 Pod/服务尚未启动。 - TFJobRunning 表示此
TFJob
的所有子资源(例如 services/pods)已成功调度和启动,并且 Job 正在运行。 - TFJobRestarting 表示此
TFJob
的一个或多个子资源(例如 services/pods)出现问题并正在重启。 - TFJobSucceeded 表示 Job 成功完成。
- TFJobFailed 表示 Job 已失败。
- TFJobCreated 表示
Job 的成功或失败确定如下:
- 如果 Job 有一个 chief,则成功或失败由 chief 的状态决定。
- 如果 Job 没有 chief,则成功或失败由 workers 决定。
- 在这两种情况下,如果被监控的进程以退出代码 0 退出,则
TFJob
成功。 - 在非零退出代码的情况下,行为由副本的 restartPolicy 决定。
- 如果 restartPolicy 允许重启,则进程将简单地重启,并且
TFJob
将继续执行。- 对于 restartPolicy ExitCode,行为取决于退出代码。
- 如果 restartPolicy 不允许重启,非零退出代码被认为是永久性故障,并且 Job 被标记为失败。
tfReplicaStatuses
tfReplicaStatuses 提供了一个映射,指示在给定状态下每个副本的 Pod 数量。有三种可能的状态
- Active 是当前正在运行的 Pod 数量。
- Succeeded 是成功完成的 Pod 数量。
- Failed 是出错完成的 Pod 数量。
事件
执行期间,TFJob
将发出事件以指示正在发生的事情,例如 Pod 和服务的创建/删除。Kubernetes 默认不保留超过 1 小时的事件。查看 Job 运行的近期事件:
kubectl -n kubeflow describe tfjobs tfjob-simple
这将产生如下输出:
Name: tfjob-simple
Namespace: kubeflow
Labels: <none>
Annotations: <none>
API Version: kubeflow.org/v1
Kind: TFJob
Metadata:
Creation Timestamp: 2021-09-06T11:48:09Z
Generation: 1
Managed Fields:
API Version: kubeflow.org/v1
Fields Type: FieldsV1
fieldsV1:
f:spec:
.:
f:tfReplicaSpecs:
.:
f:Worker:
.:
f:replicas:
f:restartPolicy:
f:template:
.:
f:spec:
Manager: kubectl-create
Operation: Update
Time: 2021-09-06T11:48:09Z
API Version: kubeflow.org/v1
Fields Type: FieldsV1
fieldsV1:
f:spec:
f:runPolicy:
.:
f:cleanPodPolicy:
f:successPolicy:
f:tfReplicaSpecs:
f:Worker:
f:template:
f:metadata:
f:spec:
f:containers:
f:status:
.:
f:completionTime:
f:conditions:
f:replicaStatuses:
.:
f:Worker:
.:
f:succeeded:
f:startTime:
Manager: manager
Operation: Update
Time: 2021-09-06T11:49:30Z
Resource Version: 5764004
Self Link: /apis/kubeflow.org/v1/namespaces/kubeflow/tfjobs/tfjob-simple
UID: 3a67a9a9-cb89-4c1f-a189-f49f0b581e29
Spec:
Tf Replica Specs:
Worker:
Replicas: 2
Restart Policy: OnFailure
Template:
Spec:
Containers:
Command:
python
/var/tf_mnist/mnist_with_summaries.py
Image: gcr.io/kubeflow-ci/tf-mnist-with-summaries:1.0
Name: tensorflow
Status:
Completion Time: 2021-09-06T11:49:30Z
Conditions:
Last Transition Time: 2021-09-06T11:48:09Z
Last Update Time: 2021-09-06T11:48:09Z
Message: TFJob tfjob-simple is created.
Reason: TFJobCreated
Status: True
Type: Created
Last Transition Time: 2021-09-06T11:48:12Z
Last Update Time: 2021-09-06T11:48:12Z
Message: TFJob kubeflow/tfjob-simple is running.
Reason: TFJobRunning
Status: False
Type: Running
Last Transition Time: 2021-09-06T11:49:30Z
Last Update Time: 2021-09-06T11:49:30Z
Message: TFJob kubeflow/tfjob-simple successfully completed.
Reason: TFJobSucceeded
Status: True
Type: Succeeded
Replica Statuses:
Worker:
Succeeded: 2
Start Time: 2021-09-06T11:48:10Z
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal SuccessfulCreatePod 7m9s tfjob-controller Created pod: tfjob-simple-worker-0
Normal SuccessfulCreatePod 7m8s tfjob-controller Created pod: tfjob-simple-worker-1
Normal SuccessfulCreateService 7m8s tfjob-controller Created service: tfjob-simple-worker-0
Normal SuccessfulCreateService 7m8s tfjob-controller Created service: tfjob-simple-worker-1
Normal ExitedWithCode 5m48s (x3 over 5m48s) tfjob-controller Pod: kubeflow.tfjob-simple-worker-1 exited with code 0
Normal ExitedWithCode 5m48s tfjob-controller Pod: kubeflow.tfjob-simple-worker-0 exited with code 0
Normal TFJobSucceeded 5m48s tfjob-controller TFJob kubeflow/tfjob-simple successfully completed.
这里的事件表明 Pod 和服务已成功创建。
TensorFlow 日志
日志记录遵循标准的 K8s 日志记录实践。
您可以使用 kubectl 获取任何尚未被删除的 Pod 的标准输出/错误。
首先找到 Job 控制器为目标副本创建的 Pod。Pod 的命名规则为:
${JOBNAME}-${REPLICA-TYPE}-${INDEX}
识别出您的 Pod 后,您可以使用 kubectl 获取日志。
kubectl logs ${PODNAME}
TFJob
spec 中的 CleanPodPolicy 控制 Job 终止时 Pod 的删除。策略可以是以下值之一:
- Running 策略意味着只有 Job 完成时仍在运行的 Pod(例如参数服务器)会立即删除;已完成的 Pod 不会被删除,以便保留日志。这是默认值。
- All 策略意味着即使是已完成的 Pod 也会在 Job 完成时立即删除。
- None 策略意味着 Job 完成时不会删除任何 Pod。
如果您的集群利用了 Kubernetes 集群日志记录,那么您的日志也可能被发送到适当的数据存储进行进一步分析。
GKE 上的 Stackdriver
使用 Stackdriver UI,您可以使用如下查询:
resource.type="k8s_container"
resource.labels.cluster_name="${CLUSTER}"
metadata.userLabels.job-name="${JOB_NAME}"
metadata.userLabels.replica-type="${TYPE}"
metadata.userLabels.replica-index="${INDEX}"
或者使用 gcloud
QUERY="resource.type=\"k8s_container\" "
QUERY="${QUERY} resource.labels.cluster_name=\"${CLUSTER}\" "
QUERY="${QUERY} metadata.userLabels.job-name=\"${JOB_NAME}\" "
QUERY="${QUERY} metadata.userLabels.replica-type=\"${TYPE}\" "
QUERY="${QUERY} metadata.userLabels.replica-index=\"${INDEX}\" "
gcloud --project=${PROJECT} logging read \
--freshness=24h \
--order asc ${QUERY}
故障排除
以下是排查 Job 的一些步骤
您的 Job 是否有状态?运行命令:
kubectl -n ${USER_NAMESPACE} get tfjobs -o yaml ${JOB_NAME}
USER_NAMESPACE 是为您用户配置文件创建的命名空间。
如果输出结果不包含 Job 的状态,则通常表明 Job spec 无效。
如果
TFJob
spec 无效,tf operator 日志中应该有日志消息kubectl -n ${KUBEFLOW_NAMESPACE} logs `kubectl get pods --selector=name=tf-job-operator -o jsonpath='{.items[0].metadata.name}'`
- KUBEFLOW_NAMESPACE 是您部署
TFJob
operator 的命名空间。
- KUBEFLOW_NAMESPACE 是您部署
检查 Job 的事件以查看是否创建了 Pod
获取事件有多种方式;如果您的 Job 不到 1 小时,则可以执行以下操作:
kubectl -n ${USER_NAMESPACE} describe tfjobs ${JOB_NAME}
输出底部应该包含 Job 发出的事件列表;例如:
Events: Type Reason Age From Message ---- ------ ---- ---- ------- Normal SuccessfulCreatePod 90s tfjob-controller Created pod: tfjob2-worker-0 Normal SuccessfulCreatePod 90s tfjob-controller Created pod: tfjob2-ps-0 Normal SuccessfulCreateService 90s tfjob-controller Created service: tfjob2-worker-0 Normal SuccessfulCreateService 90s tfjob-controller Created service: tfjob2-ps-0
Kubernetes 仅保留 1 小时的事件(请参阅 kubernetes/kubernetes#52521)
- 根据您的集群设置,事件可能会持久化到外部存储并可供更长时间访问
- 在 GKE 上,事件会持久化到 Stackdriver 中,并可以使用上一节中的说明进行访问。
如果 Pod 和服务未被创建,则表明
TFJob
未被处理;常见原因包括:TFJob
spec 无效(参见上文)TFJob
operator 未运行
检查 Pod 的事件以确保它们已被调度。
获取事件有多种方式;如果您的 Pod 不到 1 小时,则可以执行以下操作:
kubectl -n ${USER_NAMESPACE} describe pods ${POD_NAME}
输出底部应包含如下事件:
Events: Type Reason Age From Message ---- ------ ---- ---- ------- Normal Scheduled 18s default-scheduler Successfully assigned tfjob2-ps-0 to gke-jl-kf-v0-2-2-default-pool-347936c1-1qkt Normal SuccessfulMountVolume 17s kubelet, gke-jl-kf-v0-2-2-default-pool-347936c1-1qkt MountVolume.SetUp succeeded for volume "default-token-h8rnv" Normal Pulled 17s kubelet, gke-jl-kf-v0-2-2-default-pool-347936c1-1qkt Container image "gcr.io/kubeflow/tf-benchmarks-cpu:v20171202-bdab599-dirty-284af3" already present on machine Normal Created 17s kubelet, gke-jl-kf-v0-2-2-default-pool-347936c1-1qkt Created container Normal Started 16s kubelet, gke-jl-kf-v0-2-2-default-pool-347936c1-1qkt Started container
一些可能阻止容器启动的常见问题是:
- 没有足够的资源来调度 Pod
- Pod 尝试挂载不存在或不可用的 Volume(或 Secret)
- docker 镜像不存在或无法访问(例如由于权限问题)
如果容器启动了;请按照上一节中的说明查看容器的日志。
下一步
了解 Training Operator 中的分布式训练。