Kubernetes에 gpu를 사용 가능한 리소르로 제공하기 위해서는, nvidia driver, toolkit과 plugin들을 설치해야 하며, 이 과정은 쉽고 간단하지 않다. 따라서 한 번에 쉽게 모든 환경을 구축할 수 있는 GPU OPERATOR가 굉장히 매력적으로 느껴졌다. 거기다 microk8s에서는 제공하는 커맨드로 해당 gpu operator을 설치할 수 있다. 따라서 해당 글에서는 microk8s를 이용하여 클러스터를 구축하려고 한다.
OS는 Ubuntu 20.04 LTS 서버이고 1080ti를 장착하고 있다.
microk8s를 이용한 GPU Operator 설치
Nouveau driver 비활성화
GPU operator을 실행하기 전에, nouveau GPU driver가 사용 중이리면, blacklist에 등록해야 한다. 아래의 커맨드를 통해 모듈들이 사용되고 있는지 확인할 수 있다.
$ lsmod | grep -i -e cuda -e nvidia -e nouveau
사용 중이라면 아래의 커맨드를 통해 blacklist에 등록하고 해당 내용을 반영하고 재부팅하도록 한다.
$ cat << EOF | sudo tee /etc/modprobe.d/blacklist-nouveau.conf
blacklist nouveau
options nouveau modeset=0
EOF
$ sudo update-initramfs -u
$ sudo reboot
<After reboot..>
$ lsmod | grep -i -e cuda -e nvidia -e nouveau
$
위와 같이 lsmod를 했을 때 모듈들이 나오지 않으면 nouveau 드라이버를 하지 않는 중이라는 것이다.
microk8s install
microk8s 설치를 위해 아래의 커맨드들을 입력한다. 정확한 설치 절차는 해당 링크를 참고하라.
$ sudo snap install microk8s --classic
$ sudo usermod -a -G microk8s $USER
$ sudo chown -f -R $USER ~/.kube
$ su - $USER
$ microk8s status --wait-ready
이제 아래의 커맨드를 실행하여 gpu를 활성화하자.
아래처럼 파일을 수정하여야 동작을 해야 했다. 만약 다른 방법이 있다면 수정 바람.
$ sudo vim /etc/systemd/system/multi-user.target.wants/snap.microk8s.daemon-containerd.service
- Restart=on-failure
- Type=simple
+ Restart=always
+ Type=notify
+ Delegate=yes
+ KillMode=process
$ sudo systemctl daemon-reload
$ sudo systemctl restart snap.microk8s.daemon-containerd
$ microk8s stop
$ microk8s start
$ microk8s enable gpu
gpu-operator-resources 네임 스페이스에 위치한 리소스들을 살벼보자.
$ microk8s kubectl get all -n gpu-operator-resources
NAME READY STATUS RESTARTS AGE
pod/nvidia-operator-validator-ng5kb 0/1 Init:0/4 0 20s
pod/nvidia-device-plugin-daemonset-jkw52 0/1 Init:0/1 0 20s
pod/nvidia-driver-daemonset-fdl8f 0/1 ContainerCreating 0 20s
pod/nvidia-container-toolkit-daemonset-xmphg 0/1 Init:0/1 0 20s
pod/nvidia-dcgm-exporter-snrwk 0/1 Init:0/1 0 19s
pod/gpu-feature-discovery-pz8bh 0/1 Init:0/1 0 19s
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service/nvidia-dcgm-exporter ClusterIP 10.152.183.62 <none> 9400/TCP 19s
NAME DESIRED CURRENT READY UP-TO-DATE AVAILABLE NODE SELECTOR AGE
daemonset.apps/nvidia-driver-daemonset 1 1 0 1 0 nvidia.com/gpu.deploy.driver=true 20s
daemonset.apps/nvidia-container-toolkit-daemonset 1 1 0 1 0 nvidia.com/gpu.deploy.container-toolkit=true 20s
daemonset.apps/nvidia-operator-validator 1 1 0 1 0 nvidia.com/gpu.deploy.operator-validator=true 20s
daemonset.apps/nvidia-device-plugin-daemonset 1 1 0 1 0 nvidia.com/gpu.deploy.device-plugin=true 20s
daemonset.apps/nvidia-dcgm-exporter 1 1 0 1 0 nvidia.com/gpu.deploy.dcgm-exporter=true 19s
daemonset.apps/nvidia-mig-manager 0 0 0 0 0 nvidia.com/gpu.deploy.mig-manager=true 19s
daemonset.apps/gpu-feature-discovery 1 1 0 1 0 nvidia.com/gpu.deploy.gpu-feature-discovery=true 19s
위 로그에서 볼 수 있듯이 7개의 데몬 셋이 생성된다. 각각의 데몬 셋들은 node selector를 통해 해당하는 옵션(gpu.deploy.*)이 true일 때 실행된다.
그리고 시간이 지나면 아래와 같이 모든 파드가 생성이 된 것을 확인할 수 있다.
$ microk8s kubectl get all -n gpu-operator-resources
NAME READY STATUS RESTARTS AGE
pod/nvidia-driver-daemonset-fdl8f 1/1 Running 0 9m51s
pod/nvidia-container-toolkit-daemonset-xmphg 1/1 Running 0 9m51s
pod/nvidia-cuda-validator-x6kdl 0/1 Completed 0 7m2s
pod/nvidia-dcgm-exporter-snrwk 1/1 Running 0 9m50s
pod/gpu-feature-discovery-pz8bh 1/1 Running 0 9m50s
pod/nvidia-device-plugin-daemonset-jkw52 1/1 Running 0 9m51s
pod/nvidia-device-plugin-validator-4l64w 0/1 Completed 0 5m40s
pod/nvidia-operator-validator-ng5kb 1/1 Running 0 9m51s
...
nvidia-driver-daemonset는 아래에서 볼 수 있듯이 "/run/nvidia" Host path 볼륨을 mount한다. 해당 경로에 driver을 설치하는 것으로 보인다.
Name: nvidia-driver-daemonset-fdl8f
Namespace: gpu-operator-resources
Containers:
nvidia-driver-ctr:
Image: nvcr.io/nvidia/driver:460.73.01-ubuntu20.04
Command:
nvidia-driver
Args:
init
Mounts:
/dev/log from dev-log (rw)
/etc/containers/oci/hooks.d from config (rw)
/host-etc/os-release from host-os-release (ro)
/run/nvidia from run-nvidia (rw)
/var/log from var-log (rw)
/var/run/secrets/kubernetes.io/serviceaccount from kube-api-access-r548d (ro)
Volumes:
run-nvidia:
Type: HostPath (bare host directory volume)
Path: /run/nvidia
HostPathType:
var-log:
Type: HostPath (bare host directory volume)
Path: /var/log
HostPathType:
dev-log:
Type: HostPath (bare host directory volume)
Path: /dev/log
HostPathType:
config:
Type: ConfigMap (a volume populated by a ConfigMap)
Name: nvidia-driver
Optional: false
host-os-release:
Type: HostPath (bare host directory volume)
Path: /etc/os-release
HostPathType:
해당 설치 과정이 완료되면 nvidia 커널 모듈들이 올라와 있는 것을 확인할 수 있다. 또한 마운트한 /run/nvidia에는 driver에 관련된 파일들이 설치된 것을 확인할 수 있다.
$ lsmod | grep nvidia
nvidia_modeset 1228800 1
nvidia_uvm 1011712 0
nvidia 34123776 50 nvidia_uvm,nvidia_modeset
$ ls /run/nvidia/driver/bin/ | grep nvidia
nvidia-bug-report.sh
nvidia-cuda-mps-control
nvidia-cuda-mps-server
nvidia-debugdump
nvidia-installer
nvidia-ngx-updater
nvidia-persistenced
nvidia-settings
nvidia-smi
nvidia-uninstall
nvidia-xconfig
$ microk8s kubectl logs -n gpu-operator-resources pod/nvidia-driver-daemonset-fdl8f
Creating directory NVIDIA-Linux-x86_64-460.73.01
Verifying archive integrity... OK
Uncompressing NVIDIA Accelerated Graphics Driver for Linux-x86_64 460.73.01...........................
......................................................................................................
중략
Image: nvcr.io/nvidia/k8s/container-toolkit:1.4.4-ubuntu18.04
Mounts:
/run/nvidia from nvidia-run-path (rw)
/runtime/config-dir/ from containerd-config (rw)
/runtime/sock-dir/ from containerd-socket (rw)
/usr/local/nvidia from nvidia-local (rw)
/usr/share/containers/oci/hooks.d from crio-hooks (rw)
/var/run/secrets/kubernetes.io/serviceaccount from kube-api-access-5wp4h (ro)
Volumes:
nvidia-run-path:
Type: HostPath (bare host directory volume)
Path: /run/nvidia
HostPathType: DirectoryOrCreate
run-nvidia-validations:
Type: HostPath (bare host directory volume)
Path: /run/nvidia/validations
HostPathType: DirectoryOrCreate
driver-install-path:
Type: HostPath (bare host directory volume)
Path: /run/nvidia/driver
HostPathType:
nvidia-local:
Type: HostPath (bare host directory volume)
Path: /usr/local/nvidia
HostPathType:
crio-hooks:
Type: HostPath (bare host directory volume)
Path: /run/containers/oci/hooks.d
HostPathType:
containerd-config:
Type: HostPath (bare host directory volume)
Path: /var/snap/microk8s/2346/args
HostPathType:
containerd-socket:
Type: HostPath (bare host directory volume)
Path: /var/snap/microk8s/common/run
HostPathType:
$ ls /usr/local/nvidia/toolkit/ -l
total 4720
lrwxrwxrwx 1 root root 30 Jul 28 05:50 libnvidia-container.so.1 -> ./libnvidia-container.so.1.3.3
-rwxr-xr-x 1 root root 175120 Jul 28 05:50 libnvidia-container.so.1.3.3
-rwxr-xr-x 1 root root 154 Jul 28 05:50 nvidia-container-cli
-rwxr-xr-x 1 root root 43024 Jul 28 05:50 nvidia-container-cli.real
-rwxr-xr-x 1 root root 166 Jul 28 05:50 nvidia-container-runtime
lrwxrwxrwx 1 root root 26 Jul 28 05:50 nvidia-container-runtime-hook -> ./nvidia-container-toolki
t
-rwxr-xr-x 1 root root 2257080 Jul 28 05:50 nvidia-container-runtime.real
-rwxr-xr-x 1 root root 195 Jul 28 05:50 nvidia-container-toolkit
-rwxr-xr-x 1 root root 2335640 Jul 28 05:50 nvidia-container-toolkit.real
nvidia-operator-validator
nvidia-device-plugin-daemonset
nvidia-dcgm-exporter
nvidia-mig-manager
gpu-feature-discovery
위와 같이 전부 설치되었다면 아래와 같이 cuda 예제를 살펴보도록 하자.
$ cat << EOF | microk8s kubectl create -f -
apiVersion: v1
kind: Pod
metadata:
name: cuda-vectoradd
spec:
restartPolicy: OnFailure
containers:
- name: cuda-vectoradd
image: "nvidia/samples:vectoradd-cuda11.2.1"
resources:
limits:
nvidia.com/gpu: 1
EOF
어느 정도 시간이 지난 후 아래와 같이 로그를 살펴보면 성공적으로 실행한 것을 확인할 수 있다.
$ microk8s kubectl logs cuda-vectoradd
[Vector addition of 50000 elements]
Copy input data from the host memory to the CUDA device
CUDA kernel launch with 196 blocks of 256 threads
Copy output data from the CUDA device to the host memory
Test PASSED
Done
Multi node cluster
새로운 노드를 추가하기 위해서 새로운 토큰을 할당받는다. 이 과정은 한 노드에 한 번씩 수행해야 한다.
<Cluster Nonde>
$ microk8s add-node
From the node you wish to join to this cluster, run the following:
microk8s join 10.0.0.250:25000/b123aa2d7b57caddf13e1bdc2fba3c56/f1e71b50a42c
<New Node>
$ microk8s join 10.0.0.250:25000/b123aa2d7b57caddf13e1bdc2fba3c56/f1e71b50a42c
$ microk8s enable openebs
$ microk8s enable dashboard
This is exactly what I'm trying to do today. Thank you!