搞来了一个GPU服务器来爽爽

背景交代

在公司的开发环境搞到一个tesla T4 16显存的GPU服务器(同时还有16core 64G),搭配我之前的 32core 128G的CPU服务器,这俩可以做一个集群了,我给他俩的分工就是:CPU当k8s-master,GPU当k8s-worker。

这么考虑的原因是:

  1. CPU服务器有32core和128 内存,毕竟master上又要运营api server、scheduler、etcd、controller,所以需要一个逻辑密集型的服务器,这些工作完全不需要GPU。而且我这个内存128G,能装很多东西~
  2. GPU唯一的使命就是把显存贡献给大模型,如果master节点的管理任务在GPU节点上,那么消耗GPU的CPU资源是不值当的。

他俩的配合流程是:

  1. 这个GPU上主要跑的就是docker和k3s-agent,在CPU master上执行kubectl apply xxx,master看到yaml里的nodeSeletor写的是gpu的服务器,就把任务下发。
  2. GPU服务器的k3s-agent监听6443端口知道属于自己的任务来了。
  3. GPU让docker去按照yaml里的要求拉取镜像。
  4. 启动任务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是在不断的重启:

这里我们一个一个看:

  1. Successfully assigned:调度器(Scheduler)成功地为这个 Pod 在集群中找到了一个合适的家(Node)。
  2. Container image … already present:运行这个 Pod 所需的容器镜像(nvcr.io/nvidia/k8s-device-plugin:v0.18.1)已经在本地存在了,不需要重新下载。
  3. Created container:kubelet 成功地根据镜像创建了容器实例。
  4. Started container:kubelet 成功地执行了启动容器的命令。
  5. Last State: Terminated, Reason: Error, Exit Code: 1:容器在启动后,因为内部程序错误,立刻就退出了,并且返回了一个非零的退出码(1 表示有错误)。
  6. 然后继续重启

遇到这种问题我觉得可以去看看具体的日志了,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了

至此,准备工作终于完成了:

  1. gpu服务器已经顺利加入集群,并且是worker.
  2. gpu服务器配置好了label。
  3. GPU服务器安装好了NVIDIA Container Toolkit,nvidia-device-plugin-XXX RS已经能OK了。
  4. 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是可以识图的:

感谢你请我喝咖啡~

Welcome to my other publishing channels