TensorFlow 训练 (TFJob)

使用 TFJob 训练 TensorFlow 模型

此页面介绍了使用 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 的容器.
  • 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: 容器收到 SIGKILL
        • 143: 容器收到 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 文件中更改以下值

  1. 更改镜像以指向包含您代码的 docker 镜像

  2. 更改副本的数量和类型

  3. 更改分配给每个资源的资源(请求和限制)

  4. 设置任何环境变量

    • 例如,您可能需要配置各种环境变量以与 GCS 或 S3 等数据存储进行交互
  5. 如果您想使用 PV 进行存储,请附加 PV。

使用 GPU

要使用 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 已失败。

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 的一些步骤

  1. 您的 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 的命名空间。
  2. 检查 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 未运行
  3. 检查 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 镜像不存在或无法访问(例如由于权限问题)
  4. 如果容器启动了;请按照上一节中的说明查看容器的日志。

下一步

反馈

此页面有帮助吗?