gcloud k8s挂载gpu跑大模型代码及问题排查
gcloud k8s挂载gpu跑大模型代码
一、远程服务器的容器推送至gcloud Artifact Registry(Google Container Registry)
因为对gcloud不太熟悉,可能方法比较笨拙,但是以后进行优化。
- 远程服务器的环境进行打包并推送至docker hub中。
参考之前写的文档《进入容器内部配置docker项目镜像并上传仓库》 - Artifact Registry(Google Container Registry)(创建代码库)
- 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
- 官方举例
# 例子
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
- 仿写
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
- 运行job
kubectl create -f ./jobpose.yaml
- 检查作业状态
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/shm;K8S 容器调大共享内存(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设置的资源申请的才可以。
更多推荐
所有评论(0)