背景交代 在公司的开发环境搞到一个tesla T4 16显存的GPU服务器(同时还有16core 64G),搭配我之前的 32core 128G的CPU服务器,这俩可以做一个集群了,我给他俩的分工就是:CPU当k8s-master,GPU当k8s-worker。
这么考虑的原因是:
CPU服务器有32core和128 内存,毕竟master上又要运营api server、scheduler、etcd、controller,所以需要一个逻辑密集型的服务器,这些工作完全不需要GPU。而且我这个内存128G,能装很多东西~
GPU唯一的使命就是把显存贡献给大模型,如果master节点的管理任务在GPU节点上,那么消耗GPU的CPU资源是不值当的。
他俩的配合流程是:
这个GPU上主要跑的就是docker和k3s-agent,在CPU master上执行kubectl apply xxx,master看到yaml里的nodeSeletor写的是gpu的服务器,就把任务下发。
GPU服务器的k3s-agent监听6443端口知道属于自己的任务来了。
GPU让docker去按照yaml里的要求拉取镜像。
启动任务POD,反向给master同步。
搭建GPU的环境 首先,先确认GPU和CPU的服务器是互通的,互相的ping 和curl都是没问题的。
去下载一个docker,注意调整/etc/docker/daemon.json:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 root@gpu-worker-01:/data00/home/chenshuo.007# cat /etc/docker/daemon.json { "default-runtime": "nvidia", "data-root": "/data00/docker", # 这里镜像都去到数据盘里,不要放本地盘 "insecure-registries": [ "hub.byted.org", "hub.byted.org:443" ], "live-restore": true, "registry-mirrors": [ "https://docker.m.daocloud.io" ], "runtimes": { "nvidia": { "args": [], "path": "nvidia-container-runtime", "runtimeArgs": [] } } }
然后启动docker 看一下挂载的目录是否正确:
1 2 3 systemctl daemon-reload systemctl restart docker docker info | grep "Docker Root Dir"
执行 nvidia-smi 看一下显卡是否启动了:
然后去cpu master机器上看一下 node-token,默认是在/var/lib/rancher/k3s/server/node-token里,把这个token复制出来,在GPU的服务器里:
1 2 3 4 curl -Lo k3s_install.sh https://rancher-mirror.oss-cn-beijing.aliyuncs.com/k3s/k3s-install.sh chmod +x k3s_install.sh INSTALL_K3S_MIRROR=cn K3S_URL=https://master IP:6443 K3S_TOKEN=<node-token> ./k3s_install.sh agent --data-dir /data00/k3s --docker
在gpu的服务器使用sudo journalctl -u k3s-agent -f ,时刻关注日志的情况,如果日志没问题的话,就能在cpu master服务器上,看到2个node ready:
在master上给这个GPU 服务器加一个label:kubectl label node gpu-worker-01 accelerator=nvidia-t4。
然后我们要在gpu服务器上启动一个daemonset,内容如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 kubectl apply -f - <<EOF apiVersion: apps/v1 kind: DaemonSet metadata: name: nvidia-device-plugin-daemonset namespace: kube-system spec: selector: matchLabels: name: nvidia-device-plugin-ds template: metadata: labels: name: nvidia-device-plugin-ds spec: nodeSelector: accelerator: nvidia-t4 # 这里写明要去GPU服务器 containers: - image: nvcr.io/nvidia/k8s-device-plugin:v0.18.1 # 这个的链接用国内的链接,不然下载不下来。报错falling to pull image name: nvidia-device-plugin-ctr securityContext: privileged: true volumeMounts: - name: device-plugin mountPath: /var/lib/kubelet/device-plugins volumes: - name: device-plugin hostPath: path: /var/lib/kubelet/device-plugins EOF
然后我这里发现一个问题,用kubectl get pods -A来看,看到nvidia-device-plugin-XXX是在不断的重启:
这里我们一个一个看:
Successfully assigned:调度器(Scheduler)成功地为这个 Pod 在集群中找到了一个合适的家(Node)。
Container image … already present:运行这个 Pod 所需的容器镜像(nvcr.io/nvidia/k8s-device-plugin:v0.18.1)已经在本地存在了,不需要重新下载。
Created container:kubelet 成功地根据镜像创建了容器实例。
Started container:kubelet 成功地执行了启动容器的命令。
Last State: Terminated, Reason: Error, Exit Code: 1:容器在启动后,因为内部程序错误,立刻就退出了,并且返回了一个非零的退出码(1 表示有错误)。
然后继续重启
遇到这种问题我觉得可以去看看具体的日志了,kubectl logs nvidia-device-plugin-daemonset-r8ht9 -n kube-system --previous , 发现里面报错:
重点看第三句,If this is a GPU node, did you configure the NVIDIA Container Toolkit? 翻译过来就是“如果这是个GPU节点,你配置好NVIDIA容器工具包(NVIDIA Container Toolkit)了吗?”,这就是问题,我的GPU没有NVIDIA Container Toolkit,于是就退出了。
安装方法如下:
1 2 3 4 5 6 7 8 9 10 11 # 在 gpu-worker-01 节点上执行 curl -fsSL https://nvidia.github.io/libnvidia-container/gpgkey | sudo gpg --dearmor -o /usr/share/keyrings/nvidia-container-toolkit-keyring.gpg \ && curl -s -L https://nvidia.github.io/libnvidia-container/stable/deb/nvidia-container-toolkit.list | \ sed 's#deb https://#deb [signed-by=/usr/share/keyrings/nvidia-container-toolkit-keyring.gpg] https://#g' | \ sudo tee /etc/apt/sources.list.d/nvidia-container-toolkit.list sudo apt-get update sudo apt-get install -y nvidia-container-toolkit sudo nvidia-ctk runtime configure --runtime=containerd sudo systemctl restart k3s-agent kubectl describe node gpu-worker-01 | grep nvidia.com/gpu #通过这个确认状态应该是running了
至此,准备工作终于完成了:
gpu服务器已经顺利加入集群,并且是worker.
gpu服务器配置好了label。
GPU服务器安装好了NVIDIA Container Toolkit,nvidia-device-plugin-XXX RS已经能OK了。
GPU服务器启动一个nvidia-device-plugin的ds,然后他会起一个pod,这个pod的存在就是让master知道这个节点是有一个t4 显卡。
测试一下图片的ai 现在GPU服务器已经就位,那么我们就可以搞一个可以识别图片的ai了,毕竟我们之前那个CPU的大模型只能识别文字,工作中我们还是要扔一些图片进去的。
首先我们启动一个ollama的pod,让它去gpu的服务器:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 apiVersion: apps/v1 kind: Deployment metadata: name: ollama-gpu spec: replicas: 1 selector: matchLabels: app: ollama-gpu template: metadata: labels: app: ollama-gpu spec: nodeSelector: accelerator: nvidia-t4 # 定向投送到 GPU 机器 containers: - name: ollama image: ollama/ollama:latest imagePullPolicy: IfNotPresent ports: - containerPort: 11434 env: - name: NVIDIA_VISIBLE_DEVICES value: "all" - name: NVIDIA_DRIVER_CAPABILITIES value: "compute,utility" resources: limits: nvidia.com/gpu: 1 # 核心指令:索要显卡 volumeMounts: - name: ollama-storage mountPath: /root/.ollama volumes: - name: ollama-storage hostPath: path: /data00/k3s/ollama-models # 直接挂载 GPU 机器的 500G 大盘路径,也就是说容器里的/root/.ollama 对应的是宿主机上的/data00/k3s/ollama-models type: DirectoryOrCreate --- apiVersion: v1 kind: Service metadata: name: ollama-gpu-svc spec: selector: app: ollama-gpu ports: - protocol: TCP port: 11434 targetPort: 11434
启动了之后,就看到有个ollama的pod已经在GPU worker上生成了。然后我搞了一个姚明的图片,放到/data00/k3s/ollama-models,这样pod是可以获取到这个图片的。
然后通过kubectl exec -it POD名 -- /bin/bash进入到这个容器里:
1 2 3 ollama pull llava:latest # 目前它的识图能力不错 ollama run llava # 进入命令行 >>> /root/.ollama/yaoming.jpg 请描述一下这个图片
这样就看到GPU是可以识图的: