gcloud k8s挂载gpu跑大模型代码及问题排查

一、远程服务器的容器推送至gcloud Artifact Registry(Google Container Registry)

因为对gcloud不太熟悉,可能方法比较笨拙,但是以后进行优化。

  1. 远程服务器的环境进行打包并推送至docker hub中。
    参考之前写的文档《进入容器内部配置docker项目镜像并上传仓库》
  2. Artifact Registry(Google Container Registry)(创建代码库)
    在这里插入图片描述
  3. google cloud中打开cloud shell,选择项目后拉取docker hub中的镜像,随后在推送到。
    参考:https://cloud.google.com/artifact-registry/docs/docker/pushing-and-pulling?hl=zh-CN
# docker hub中拉取镜像
docker pull lingyun92888/train_pose:240806
# 重置tag,为推送镜像做准备
docker tag 567185e860da europe-west4-docker.pkg.dev/boxwood-ellipse-419119/pose/trainpose:0806
# 推送镜像到google仓库中
docker push europe-west4-docker.pkg.dev/boxwood-ellipse-419119/pose/trainpose:0806

需要注意的是,这个过程感觉不是很稳定,容易调研,一旦掉线,docker所有信息就都没有了,后续可以不使用cloud shell,可以配置本地的方法进行连接。

二、挂载gpu连通Cloud Storage 跑模型

1.Standard 模式创建集群

方法参考:在 GKE Standard 模式下使用 GPU 训练模型

(1)创建 Standard 模式集群和 GPU 节点池

需要注意,不同类型集群下挂载gpu的方式是不一样的。nodeSelector根据指定的标签去选择在匹配的节点上进行容器的初始化。
GPU选择的注意事项:

  • gpu不同种类的配置信息
    参考:Autopilot 中的资源请求GPU 平台

  • gpu的区域和可用区
    参考:GPU 区域和可用区,了解什么样的区域可以建立什么样的gpu

  • 不同的gpu的machine-type怎么选
    参考:加速器优化机器系列

  • 查看当前可用gpu
    参考:gpu是否空闲

    gcloud compute accelerator-types list
    
  • 定义可用区域
    不然集群会报错,比如集群europe-west4区域,但是europe-west4-c是不能创建a100的,所以不指定就会报错。

    --node-locations=europe-west4-a
    
  • 代码示例

    # 标准模式下创建gpu
    # 创建gpu nvidia-a100-80gb
    gcloud container node-pools create gke-gpu-pool-1 \
        --accelerator=type=nvidia-a100-80gb,count=1,gpu-driver-version=default \
        --machine-type=a2-ultragpu-1g --num-nodes=1 \
        --location=europe-west4 \
        --cluster=gke-gpu-cluster \
        --node-locations=europe-west4-a
    # 创建gpu nvidia-tesla-a100
    gcloud container node-pools create gke-gpu-pool-2 \
        --accelerator=type=nvidia-tesla-a100,count=2,gpu-driver-version=default \
        --machine-type=a2-highgpu-2g --num-nodes=1 \
        --location=europe-west4 \
        --cluster=gke-gpu-cluster \
        --node-locations=europe-west4-a
    

在上述注意事项都搞清楚后,就可以开始进行集群和gpu的创建了

# 1.创建使用适用于 GKE 的工作负载身份联合的 Standard 集群,并安装 Cloud Storage FUSE 驱动程序:
gcloud container clusters create gke-gpu-cluster \
    --addons GcsFuseCsiDriver \
    --location=europe-west4 \
    --num-nodes=2 \
    --workload-pool=boxwood-ellipse-419119.svc.id.goog
# 2.创建 GPU 节点池:
# 查看是否有可用的gpu
gcloud compute accelerator-types list

# 已有的节点池进行升级
gcloud container node-pools update gke-gpu-pool-1 --cluster gke-gpu-cluster --machine-type a2-ultragpu-2g --location europe-west4

# 创建gpu nvidia-tesla-t4
gcloud container node-pools create gke-gpu-pool-1 \
    --accelerator=type=nvidia-tesla-t4,count=1,gpu-driver-version=default \
    --machine-type=n1-standard-16 --num-nodes=1 \
    --location=europe-west4 \
    --cluster=gke-gpu-cluster
    --node-locations=europe-west4-a
	# 创建gpu nvidia-a100-80gb
gcloud container node-pools create gke-gpu-pool-3 \
    --accelerator=type=nvidia-a100-80gb,count=1,gpu-driver-version=default \
    --machine-type=a2-ultragpu-1g --num-nodes=1 \
    --location=europe-west4 \
    --cluster=gke-gpu-cluster \
    --node-locations=europe-west4-a
	# 创建gpu nvidia-tesla-a100
gcloud container node-pools create gke-gpu-pool-2 \
    --accelerator=type=nvidia-tesla-a100,count=1,gpu-driver-version=default \
    --machine-type=a2-highgpu-1g --num-nodes=1 \
    --location=europe-west4 \
    --cluster=gke-gpu-cluster \
    --node-locations=europe-west4-a
 # 3.和当前的集群建立连接,kubectl命令都是在autopilot-cluster集群下进行的  
gcloud container clusters get-credentials gke-gpu-cluster \
    --location=europe-west4 \
    --project=boxwood-ellipse-419119
(2)创建 Cloud Storage 存储桶
(3)在集群中创建 Kubernetes ServiceAccount
# 1.创建 Kubernetes 命名空间:
kubectl create namespace gke-ai-namespace
# 2.在该命名空间中创建 Kubernetes ServiceAccount:
kubectl create serviceaccount gpu-k8s-sa --namespace=gke-ai-namespace
(4)将 Kubernetes ServiceAccount 绑定到 Google Cloud 服务账号

(1)向 Google Cloud 服务账号添加 IAM 绑定:

gcloud iam service-accounts add-iam-policy-binding gke-ai-sa@boxwood-ellipse-419119.iam.gserviceaccount.com \
    --role roles/iam.workloadIdentityUser \
    --member "serviceAccount:boxwood-ellipse-419119.svc.id.goog[gke-ai-namespace/gpu-k8s-sa]"
    # --member 标志提供 Google Cloud 中 Kubernetes ServiceAccount 的完整身份。

(2)为 Kubernetes ServiceAccount 添加注解:

kubectl annotate serviceaccount gpu-k8s-sa \
    --namespace gke-ai-namespace \
    iam.gke.io/gcp-service-account=gke-ai-sa@boxwood-ellipse-419119.iam.gserviceaccount.com
(5) 挂载gpu结合桶存储跑大模型代码
# 1.在 Cloud Shell 中,创建以下环境变量:
export K8S_SA_NAME=gpu-k8s-sa
# 自己写的是export BUCKET_NAME=pose_infomation
# export BUCKET_NAME=boxwood-ellipse-419119-gke-gpu-bucket
export BUCKET_NAME=pose_infomation
# 2.创建具有 TensorFlow 容器的 Pod:
# 自己写的
envsubst < gputrainpose.yaml | kubectl --namespace=gke-ai-namespace apply -f -
# 示例 envsubst < src/gke-config/standard-tensorflow-bash.yaml | kubectl --namespace=gke-ai-namespace apply -f -
# 其中src/gke-config/standard-tensorflow-bash.yaml文件粘贴在后面

# 3.在存储桶中创建示例文件:
touch sample-file
gsutil cp sample-file gs://gs://pose_infomation

# 4.等待 Pod 准备就绪:
kubectl wait --for=condition=Ready pod/test-tensorflow-pod -n=gke-ai-namespace --timeout=180s

# Pod 准备就绪后,输出如下:
pod/test-tensorflow-pod condition met

# 5.在 Tensorflow 容器中打开 Shell:
kubectl -n gke-ai-namespace exec --stdin --tty trainpose-pod --container trainpose -- /bin/bash
# kubectl -n gke-ai-namespace exec --stdin --tty trainpose-pod --container pytorch -- /bin/bash
# 6.尝试读取您创建的示例文件:
ls /data
# 7.查看日志以确定挂接到 Pod 的 GPU:
python -c "import tensorflow as tf; print(tf.config.list_physical_devices('GPU'))"
# 输出显示挂接到 Pod 的 GPU,类似于以下内容:
...
PhysicalDevice(name='/physical_device:GPU:0',device_type='GPU')
# 8.检查 Tensorflow 容器中的日志:
kubectl logs -f jobs/mnist-training-job -c tensorflow -n gke-ai-namespace
# 9.退出该容器:
exit
# 10.删除示例 Pod:
kubectl delete -f src/gke-config/standard-tensorflow-bash.yaml \
    --namespace=gke-ai-namespace

2.Autopilot 模式创建集群

因为最终选用的是这个模式,所以在这个模式下具体写工作记录,标准模式作为案例参考。
方法参考:
创建 Autopilot 集群
在 Autopilot 中部署 GPU 工作负载

(1)创建 Autopilot 模式集群

Autopilot 集群只需要创建,因为这个模式是自动扩容,所以不需要在这里添加gpu节点,直接在后续的yaml中创建就可以。

# 创建 Autopilot 集群
gcloud container clusters create-auto autopilot-cluster \
    --location=europe-west4 \
    --project=boxwood-ellipse-419119
    --workload-pool=boxwood-ellipse-419119.svc.id.goog
# --workload-pool=boxwood-ellipse-419119.svc.id.goog这点非常重要,创建识别,否则后面没办法连通桶存储

# 和当前的集群建立连接,kubectl命令都是在autopilot-cluster集群下进行的
gcloud container clusters get-credentials autopilot-cluster \
    --location=europe-west4 \
    --project=boxwood-ellipse-419119

# 验证集群模式
gcloud container clusters describe autopilot-cluster \
    --location=europe-west4
    
# 输出包含以下内容:
autopilot:
  enabled: true
(2)创建 Cloud Storage 存储桶(参考Standard 模式创建集群,方法都是一样的)
(3)在集群中创建 Kubernetes ServiceAccount(参考Standard 模式创建集群,方法都是一样的)
(4)将 Kubernetes ServiceAccount 绑定到 Google Cloud 服务账号(参考Standard 模式创建集群,方法都是一样的)
(5)挂载gpu结合桶存储跑大模型代码(参考Standard 模式创建集群,方法都是一样的)
# 1.在 Cloud Shell 中,创建以下环境变量:
export K8S_SA_NAME=gpu-k8s-sa
# 自己写的是export BUCKET_NAME=pose_infomation
export BUCKET_NAME=pose_infomation
# 2.创建具有 TensorFlow 容器的 Pod:
envsubst < autogputrainpose.yaml | kubectl --namespace=gke-ai-namespace apply -f -
# 其中src/gke-config/standard-tensorflow-bash.yaml文件粘贴在后面
# 3.等待 Pod 准备就绪:
kubectl wait --for=condition=Ready pod/trainpose-pod -n=gke-ai-namespace --timeout=180s
# Pod 准备就绪后,输出如下:
pod/trainpose-pod condition met

# 4.在 Tensorflow 容器中打开 Shell:
kubectl -n gke-ai-namespace exec --stdin --tty trainpose-pod --container trainpose -- /bin/bash

# 5.尝试读取您创建的示例文件(这里主要看挂载的存储pose_infomation):
ls /data

# 6.查看日志以确定挂接到Pod的GPU
# 方法1
python -c "import torch; gpu_count = torch.cuda.device_count();print(f'Available GPUs: {gpu_count}')"
# 方法2
python -c "import torch; device = torch.device('cuda');memory_size=torch.cuda.get_device_properties(device).total_memory;print('显存大小:', memory_size)"
# 输出显示挂接到 Pod 的 GPU,类似于以下内容:
Available GPUs: 2
# 方法3
pip install gpustat
gpustat -i (彩色并简约的显示) 

# 7.检查 Tensorflow 容器中的日志(这个后期完善,代码不一定对):
kubectl logs -f pod/trainpose-pod -c trainpose -n gke-ai-namespace
# 9.退出该容器:
exit
# 10.删除示例 Pod:
kubectl delete -f gputrainpose.yaml \
    --namespace=gke-ai-namespace

三、yaml内容详细介绍

在“标题二”中介绍了其中的具体流程,但是实现的原因全部都是靠yaml实现的,这部分拿出进行详细介绍。
参考:skaffold.yaml
整体介绍几块重要内容:

1. 连通Cloud Storage

如下示例表示在容器/data的文件夹,已经替换了Cloud Storage的$BUCKET_NAME的路径。从而实现容器连通存储桶的问题。

  containers:
  - name: tensorflow
	...
	...
    volumeMounts:
    - name: gcs-fuse-csi-vol
      mountPath: /data
      readOnly: false
  serviceAccountName: $K8S_SA_NAME
  volumes:
  - name: gcs-fuse-csi-vol
    csi:
      driver: gcsfuse.csi.storage.gke.io
      readOnly: false
      volumeAttributes:
        bucketName: $BUCKET_NAME
        mountOptions: "implicit-dirs"

2.容器启动命令command和 args

看下示例之间的对比

# 示例1,表示永久启动,写了一个循环,只使用其中的环境
command: ["/bin/bash", "-c", "--"]
args: ["while true; do sleep infinity; done;"]

# 示例2,表示直接启动在存储桶中的训练代码,如果是一个基础容器,就可以考虑容器基础上进行创建,这样就可以引用的容器小一点。
command: ["/bin/bash", "-c", "--"]
args: ["cd /data/tensorflow-mnist-example; pip install -r requirements.txt; python tensorflow_mnist_train_distributed.py"]

# 示例3,我自己的项目,如果引用基础容器,可以这样操作命令。
  containers:
 - name: pytorch
    image: pytorch/pytorch:2.0.0-cuda11.7-cudnn8-runtime
    command: ["/bin/bash", "-c", "--"]
    args: ["cd /data/moore; pip install -r requirements.txt; apt-get update; apt-get install git; apt-get install libgl1; apt-get install libglib2.0-0; git clone https://github.com/emilianavt/OpenSeeFace.git; while true; do sleep infinity; done;"]

3.gpu的选择与挂载

(1)Standard模式下集群
参考:在 GKE Standard 节点池中运行 GPU

# 首先需要手动创建gpu节点。(标准模式的节点只能自己去创建)
# nvidia-tesla-t4的gpu比较好创建
gcloud container node-pools create gke-gpu-pool-1 \
    --accelerator=type=nvidia-tesla-t4,count=1,gpu-driver-version=default \
    --machine-type=n1-standard-16 --num-nodes=1 \
    --location=us-central1 \
    --cluster=gke-gpu-cluster
# nvidia-A100创建
gcloud container node-pools create gke-gpu-pool-1 \
    --accelerator=type=nvidia-a100-80gb,count=1,gpu-driver-version=default \
    --machine-type=a2-ultragpu-1g --num-nodes=1 \
    --location=europe-west4 \
    --cluster=gke-gpu-cluster \
    --node-locations=europe-west4-a
# yaml文件中进行配置
spec:
  nodeSelector:
    cloud.google.com/gke-accelerator: nvidia-tesla-t4
  tolerations:
  - key: "nvidia.com/gpu"
    operator: "Exists"
    effect: "NoSchedule"
  containers:
  - name: tensorflow
	...
	...
    resources:
      limits:
        nvidia.com/gpu: 1
        cpu: 1
        memory: 3Gi

(2)Autopilot模式下集群。
直接在yaml文件里写就行。如下所示定义gpu,需要注意的是,不但需要gpu,还需要cpu!!!所以一定要记得配置cpu的大小,否则会导致工作负载一直启动的状态!!

spec:
  nodeSelector:
    cloud.google.com/compute-class: "Accelerator"
    cloud.google.com/gke-accelerator: "nvidia-tesla-t4"
  containers:
  - name: pytorch
    image: pytorch/pytorch:2.0.0-cuda11.7-cudnn8-runtime
    command: ["/bin/bash", "-c", "--"]
    args: ["while true; do sleep infinity; done;"]
    resources:
      limits:
        nvidia.com/gpu: 2
        cpu: "40"
        memory: "40Gi"
      requests:
        cpu: "40"
        memory: "40Gi"

4.镜像选择

yaml中配置镜像,这个镜像可以是直接在dockerhub里的,也可以是google自己的镜像仓库 Artifact Registry。因为“标题一”中已经运用了,所以这里就直接说怎么用Artifact Registry里的镜像吧。

containers:
  - name: trainpose
    image: europe-west4-docker.pkg.dev/boxwood-ellipse-419119/pose/trainpose:0806

5.自己项目的yaml示例

丢出自己项目的yaml供参考。需要注意的是,我的项目是根据Autopilot集群下创建的。上述讲的过程中基本上把这里面的几部分逐步讲解是什么了。

# 自动伸缩
apiVersion: v1
kind: Pod
metadata:
  name: trainpose-pod
  annotations:
    gke-gcsfuse/volumes: "true"
spec:
  nodeSelector:
    cloud.google.com/compute-class: "Accelerator"
    cloud.google.com/gke-accelerator: "nvidia-tesla-t4"
  containers:
  - name: trainpose
    image: europe-west4-docker.pkg.dev/boxwood-ellipse-419119/pose/trainpose:0806
    command: ["/bin/bash", "-c", "--"]
    args: ["while true; do sleep infinity; done;"]
    resources:
      limits:
        nvidia.com/gpu: 2
        cpu: "40"
        memory: "40Gi"
      requests:
        cpu: "40"
        memory: "40Gi"
    volumeMounts:
    - name: gcs-fuse-csi-vol
      mountPath: /data
      readOnly: false
  serviceAccountName: $K8S_SA_NAME
  volumes:
  - name: gcs-fuse-csi-vol
    csi:
      driver: gcsfuse.csi.storage.gke.io
      readOnly: false
      volumeAttributes:
        bucketName: $BUCKET_NAME
        mountOptions: "implicit-dirs"
# 标准模式
apiVersion: v1
kind: Pod
metadata:
  name: trainpose-pod
  annotations:
    gke-gcsfuse/volumes: "true"
spec:
  nodeSelector:
    cloud.google.com/gke-accelerator: "nvidia-a100-80gb"
  containers:
  - name: trainpose
    image: europe-west4-docker.pkg.dev/boxwood-ellipse-419119/pose/trainpose:0806
    command: ["/bin/bash", "-c", "--"]
    args: ["while true; do sleep infinity; done;"]
    resources:
      limits:
        nvidia.com/gpu: 2
        cpu: "20"
        memory: "150Gi"
      requests:
        cpu: "20"
        memory: "150Gi"
    volumeMounts:
    - name: gcs-fuse-csi-vol
      mountPath: /data
      readOnly: false
    - name: dshm
      mountPath: /dev/shm
  serviceAccountName: $K8S_SA_NAME
  volumes:
  - name: gcs-fuse-csi-vol
    csi:
      driver: gcsfuse.csi.storage.gke.io
      readOnly: false
      volumeAttributes:
        bucketName: $BUCKET_NAME
        mountOptions: "implicit-dirs"
  - name: dshm
    emptyDir:
      medium: Memory
      sizeLimit: 50Gi
 # job
 apiVersion: batch/v1
kind: Job
metadata:
  name: trainpose-job
spec:
  template:
    metadata:
      name: mnist
      annotations:
        gke-gcsfuse/volumes: "true"
    spec:
      nodeSelector:
        cloud.google.com/gke-accelerator: "nvidia-a100-80gb"
      containers:
      - name: trainpose
        image: europe-west4-docker.pkg.dev/boxwood-ellipse-419119/pose/trainpose:0806
        command: ["/bin/bash", "-c", "--"]
        args: ["cd /data/moore; accelerate launch train_stage_1.py --config configs/train/stage1.yaml; accelerate launch train_stage_2.py --config configs/train/stage2.yaml;"]
        resources:
          limits:
            nvidia.com/gpu: 2
            cpu: "20"
            memory: "150Gi"
          requests:
            cpu: "20"
            memory: "150Gi"
        volumeMounts:
        - name: gcs-fuse-csi-vol
          mountPath: /data
          readOnly: false
        - name: dshm
          mountPath: /dev/shm
      serviceAccountName: $K8S_SA_NAME
      volumes:
      - name: gcs-fuse-csi-vol
        csi:
          driver: gcsfuse.csi.storage.gke.io
          readOnly: false
          volumeAttributes:
            bucketName: $BUCKET_NAME
            mountOptions: "implicit-dirs"
      - name: dshm
        emptyDir:
          medium: Memory
          sizeLimit: 50Gi
      restartPolicy: "Never"

5. 容忍度tolerations介绍

参考:忽略节点污点
在每个节点上运行 DaemonSet,建议您在每个节点(即使是应用了污点的节点)上运行 DaemonSet。例如,某些日志记录和监控代理必须在集群中的每个节点上运行。您可以将这些 DaemonSet 配置为忽略节点污点,以便 GKE 将这些工作负载放置在每个节点上。
如需在集群中的每个节点(包括 GPU 节点)上运行 DaemonSet,请将以下容忍添加到规范中:

apiVersion: apps/v1
kind: DaemonSet
metadata:
  name: logging-agent
spec:
  tolerations:
  - key: ""
    operator: "Exists"
    effect: ""
  containers:
  - name: logging-agent-v1
    image: IMAGE_PATH
# 自己的
 tolerations:
 - key: "nvidia.com/gpu"
   operator: "Exists"
   effect: "NoSchedule"
   
# kind遇到的模式有job(作业)、pod(副本),记得还有一个部署模式,大概清楚,但是没有细看,以后有时间好好研究下。这里不重要,不做重点介绍了
apiVersion: batch/v1
kind: Job

apiVersion: v1
kind: Pod

四、动态工作负载调度(gpu紧缺方案一)

在google k8s的应用中,经常会出现gpu资源不够的情况,为了实现动态的工作负载调度,可以使用以下介绍的方法,预定gpu资源。
参考:google k8s动态工作负载调度

1.创建集群

按照之前介绍的方法创建集群会报错。

gcloud container clusters create info-cluster \
    --zone europe-west4-a \
    --node-locations europe-west4-a

2.创建节点池

详细介绍看参考资料介绍

gcloud beta container node-pools create aipool \
    --cluster=info-cluster \
    --location=europe-west4-a \
     --enable-queued-provisioning \
    --accelerator type=nvidia-a100-80gb,count=1,gpu-driver-version=default \
    --machine-type=a2-ultragpu-1g \
    --enable-autoscaling  \
    --num-nodes=0   \
    --total-max-nodes 2  \
    --location-policy=ANY  \
    --reservation-affinity=none  \
    --no-enable-autorepair

3.创建pod

使用参考资料中的例子。

git clone https://github.com/GoogleCloudPlatform/ai-on-gke
cd ai-on-gke/tutorials-and-examples/workflow-orchestration/dws-examples

4.安装Kueue 版本

VERSION=v0.7.0
kubectl apply --server-side -f https://github.com/kubernetes-sigs/kueue/releases/download/$VERSION/manifests.yaml

5.创建集群级队列和 LocalQueue 命名空间

使用以下清单时,创建名为 dws-cluster-queue 的集群级队列和名为 dws-local-queue 的 LocalQueue 命名空间。在此命名空间中引用 dws-cluster-queue 队列的作业使用动态工作负载调度器来获取 GPU 资源。
./dws-queues.yaml

apiVersion: kueue.x-k8s.io/v1beta1
kind: ResourceFlavor
metadata:
  name: "default-flavor"
---
apiVersion: kueue.x-k8s.io/v1beta1
kind: AdmissionCheck
metadata:
  name: dws-prov
spec:
  controllerName: kueue.x-k8s.io/provisioning-request
  parameters:
    apiGroup: kueue.x-k8s.io
    kind: ProvisioningRequestConfig
    name: dws-config
---
apiVersion: kueue.x-k8s.io/v1beta1
kind: ProvisioningRequestConfig
metadata:
  name: dws-config
spec:
  provisioningClassName: queued-provisioning.gke.io
  managedResources:
  - nvidia.com/gpu
---
apiVersion: kueue.x-k8s.io/v1beta1
kind: ClusterQueue
metadata:
  name: "dws-cluster-queue"
spec:
  namespaceSelector: {} 
  resourceGroups:
  - coveredResources: ["cpu", "memory", "nvidia.com/gpu"]
    flavors:
    - name: "default-flavor"
      resources:
      - name: "cpu"
        nominalQuota: 10000  # Infinite quota.
      - name: "memory"
        nominalQuota: 10000Gi # Infinite quota.
      - name: "nvidia.com/gpu"
        nominalQuota: 10000  # Infinite quota.
  admissionChecks:
  - dws-prov
---
apiVersion: kueue.x-k8s.io/v1beta1
kind: LocalQueue
metadata:
  namespace: "default"
  name: "dws-local-queue"
spec:
  clusterQueue: "dws-cluster-queue"
---

6. 开始部署 LocalQueue

kubectl create -f ./dws-queues.yaml

7.运行 Job

  1. 官方举例
# 例子
apiVersion: batch/v1
kind: Job
metadata:
  name: sample-job
  namespace: default
  labels:
    kueue.x-k8s.io/queue-name: dws-local-queue
  annotations:
    provreq.kueue.x-k8s.io/maxRunDurationSeconds: "600"
spec:
  parallelism: 1
  completions: 1
  suspend: true
  template:
    spec:
      nodeSelector:
        cloud.google.com/gke-nodepool: aipool
      tolerations:
      - key: "nvidia.com/gpu"
        operator: "Exists"
        effect: "NoSchedule"
      containers:
      - name: dummy-job
        image: gcr.io/k8s-staging-perf-tests/sleep:v0.0.3
        args: ["120s"]
        resources:
          requests:
            cpu: "100m"
            memory: "100Mi"
            nvidia.com/gpu: 1
          limits:
            cpu: "100m"
            memory: "100Mi"
            nvidia.com/gpu: 1

      restartPolicy: Never
  1. 仿写
    jobpose.yaml
apiVersion: batch/v1
kind: Job
metadata:
  name: sample-job
  namespace: default
  labels:
    kueue.x-k8s.io/queue-name: dws-local-queue
  annotations:
    provreq.kueue.x-k8s.io/maxRunDurationSeconds: "60000"
spec:
  parallelism: 1
  completions: 1
  suspend: true
  template:
    spec:
      nodeSelector:
        cloud.google.com/gke-nodepool: aipool
      tolerations:
      - key: "nvidia.com/gpu"
        operator: "Exists"
        effect: "NoSchedule"
      containers:
        - name: dummy-job
          image: europe-west4-docker.pkg.dev/boxwood-ellipse-419119/pose/trainpose:0806
          command: ["/bin/bash", "-c", "--"]
          args: ["apt-get install vim; while true; do sleep infinity; done;"]
          resources:
            requests:
              nvidia.com/gpu: 1
            limits:
              nvidia.com/gpu: 1
          volumeMounts:
          - name: gcs-fuse-csi-vol
            mountPath: /data
            readOnly: false
          - name: dshm
            mountPath: /dev/shm
      volumes:
      - name: gcs-fuse-csi-vol
        csi:
          driver: gcsfuse.csi.storage.gke.io
          readOnly: false
          volumeAttributes:
            bucketName: $BUCKET_NAME
            mountOptions: "implicit-dirs"
      - name: dshm
        emptyDir:
          medium: Memory
          sizeLimit: 100Gi
      restartPolicy: Never
  1. 运行job
kubectl create -f ./jobpose.yaml
  1. 检查作业状态
kubectl describe job sample-job

五、预留获取gpu资源(gpu紧缺方案二)

除了动态调度获取gpu资源外,还可以尝试使用预留的方式获取gpu资源。因为操作简单就不具体说了。
参考:google预留gpu资源

六、问题排查

一边学习,一边记录,一边摸索,一边踩坑,一边解决。
参考记录:问题排查1(git版);问题排查2(官方)

1.工作负载创建后一直显示正在初始化PodInitializing

创建十几个小时了还是正在创建
解决方法:
(1)首先要知道去哪里排查原因,一开始没有经验,一直去找日志,但日志什么内容都没有。后来才知道,应该在事件里找。里面会详细记录哪一步的问题。(截图与这个问题不符,只是说明去哪里看问题。)
在这里插入图片描述
(2)后面定位到原因是容器太大了(9.6g),后续可以把各种包拆出来,只封装一个关于基础环境的镜像,其余的用requirement.py去安装,但是最主要的原因还是yaml中没有配置cpu,最开始只配置了个gpu,稍微大点的容器都没办法进行部署,并且之前还是在标准模式,选择的指定gpu,所以无论如何也得不到结果。

	# 之前的错误示范,就给了一个gpu,怎么搞也不会出结果。
	spec:
	  nodeSelector:
	    cloud.google.com/gke-accelerator: nvidia-tesla-t4
	  tolerations:
	  - key: "nvidia.com/gpu"
	    operator: "Exists"
	    effect: "NoSchedule"
	  containers:
		...
		...
	    resources:
	      limits:
	        nvidia.com/gpu: 1

(3)随后yaml配置cpu和gpu也分别出现问题。
发生问题及解决:1.设置的cpu:50,被要求最多46,所以改成40。2.gpu设置3,被要求只能1,2,4)

2.块存储一直挂载不上,导致工作负载无法启动。

排查记录:
(1)发生报错,怀疑配置存储桶发生问题,于是去掉存储桶。去掉存储桶结果后就可以跑成功了。后续查官方git文档发现问题。
在这里插入图片描述
这个报错对应第一个问题,按道理选用自动代理集群,会自动进行解决,不需要人工操作。
在这里插入图片描述
在这里插入图片描述
解决问题1:
最麻烦的是这个报错,报错原因是没有以工作负载身份联合创建集群,创建自动代理时候忘记加入一行代码。

	# 错误的,创建 Autopilot 集群
	gcloud container clusters create-auto autopilot-cluster-ai \
	    --location=europe-west4 \
	    --project=boxwood-ellipse-419119
	# 正确的,创建 Autopilot 集群
	gcloud container clusters create-auto autopilot-cluster-ai \
	    --location=europe-west4 \
	    --project=boxwood-ellipse-419119
	    --workload-pool=boxwood-ellipse-419119.svc.id.goog
	# 就是因为这一行让我研究了几个小时!!!--workload-pool=boxwood-ellipse-419119.svc.id.goog

解决问题2:
后续又遇到了这个问题,后来发现因为我的yaml文件中有$BUCKET_NAME,所以需要先进行以下说明。

export K8S_SA_NAME=gpu-k8s-sa
# 自己写的是export BUCKET_NAME=pose_infomation
export BUCKET_NAME=pose_infomation

3.创建新集群后,api命令无法在集群下使用

在启动yaml时候发生了点小意外,创建了新的Autopilot 集群后,直接启动yaml,报错显示找不到集群,才意识到需要先连接集群,并将之前配置的命名空间重新配置下,才能启动

```bash
gcloud container clusters get-credentials autopilot-cluster \
    --location=europe-west4 \
    --project=boxwood-ellipse-419119
# 验证集群模式
gcloud container clusters describe autopilot-cluster \
    --location=europe-west4
# 输出包含以下内容:
autopilot:
  enabled: true
```

4.添加a100gpu后pod没办法正常创建

此类问题解决参考:节点纵向扩容失败:GCE 资源不足

在这里插入图片描述
(1)标准模式
如果是标准模式,难点再创建a100的gpu上,在“标题三”下“gpu的选择和挂载中会详细说明问题”
(2)自动代理模式
遇事不慌,只要确定配额没有问题,区域也没写错,集群会自动生成,就是速度会很慢,需要耐心等待解决。

5.标准集群创建gpu节点池

详见文章“标题二”1.(1)介绍。需要指定好创建gpu的区域,不然集群会报错,比如集群europe-west4区域,但是europe-west4-c是不能创建a100的,所以不指定就会报错。

	--node-locations=europe-west4-a

6.大模型运行报错-共享内存不够及dataworker怎么选?

问题解决:
理论参考:共享内存解决方案卷介绍pod容器资源管理k8s各类存储卷介绍
博客参考:
k3s配置docker容器/dev/shmK8S 容器调大共享内存(shm)Pytorch DataLoader中的num_workers (选择最合适的num_workers值)

ERROR: Unexpected bus error encountered in worker. This might be caused by insufficient shared memory (shm).
# 错误:工作线程中遇到意外的总线错误。这可能是由共享内存 (shm) 不足引起的。

RuntimeError: DataLoader worker (pid 1384) is killed by signal: Bus error. It is possible that dataloader's workers are out of shared memory. Please try to raise your shared memory limit.

RuntimeError: DataLoader worker (pid(s) 1384) exited unexpectedly 

怎么调大?
如下所示,调大共享内存

# 容器启动
docker run ... --shm-size 8G ...
# k8s yaml
spec:
  ...
  containers:
  ...
    volumeMounts:
    - name: gcs-fuse-csi-vol
      mountPath: /data
      readOnly: false
    - name: dshm
      mountPath: /dev/shm
  volumes:
  - name: gcs-fuse-csi-vol
    csi:
      driver: gcsfuse.csi.storage.gke.io
      readOnly: false
      volumeAttributes:
        bucketName: $BUCKET_NAME
        mountOptions: "implicit-dirs"
  - name: dshm
    emptyDir:
      medium: Memory
      sizeLimit: 10Gi

问题解决:
在这里插入图片描述

7.多卡gpu配置一直没办法启动

对k8s理解存在误区,一开始以为k8s是把几个节点的资源合在一起,可以随便调用,就导致调用多卡的时候一直没办法成功,后来才知道,原来负载调用的资源只能是一个节点的,所以要保证至少一个节点的资源是够yaml设置的资源申请的才可以。

8.顺利创建

正确的事件创建,如果能如此顺利,代表最后就没有问题了(正常启动)。
在这里插入图片描述

五、总结

通过这几天的学习,初步对谷歌k8s有个大致的了解。几句话总结下,详细了解可以看官方帖子,介绍的更详细。
(1)集群。包含节点池,可以作为一个整体,供应整个应用系统。
(2)节点池。包含节点,每个节点可以理解为一个计算机资源,可以是cpu也可以是gpu。
(3)deployment。deployment>pod>容器。deployment是用来管理pod的(扩缩容)。
(4)pod(副本)。大概了解,学的不好。pod就是为了便于管理容器在机器和容器之间抽象出的一层概念。一个节点可以包括多个pod,init容器启动完了pod才算起来。比如配置副本数为2,他就一个pod初始化2份。即使一个pod挂了,还有一个pod,保证稳定性和负载均衡。
(5)工作原理。之前对k8s理解存在误区,导致容器一直起不来。一开始以为k8s是把几个节点的资源合在一起,可以随便调用,就导致调用多卡的时候一直没办法成功,后来才知道,原来负载调用的资源只能是一个节点的,所以要保证至少一个节点的资源是够yaml设置的资源申请的才可以。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值