はじめに
約1ヵ月前に、この記事でkubernetesを使ってGPUクラスタを構築する記事を書いた。この時はGPUポッドがPending状態で動作しなかった。その後、とある方の助言もあって何とか動くようになったので、ここにまとめる。
自分の環境では、あるノードを起動しないとGPUポッドが立ち上がらないという問題もあるし、更にノード内のGPU指定、更にノードの指定など、想定していることも出来ていないので、「はじめの一歩」とした。
情報源
-
[NVIDIA Docker(NVIDIA Container Toolkit)からnvidia-container-runtime + containerdに移行するために知っておくべきこと] このページをとなる方から教えてもらって、Pending状態のGPUポッドが動作するようになった。
-
NVIDIA/k8s-device-plugin NVIDIAのkubernetesに関するdevice pluginのページ。
-
NVIDIA Kubernetes Device Plugin このページで、最新版のdevice pluginの情報を得た。
結論
以下のまとめは、自分の環境で色々と試した限りのことなので、各々の環境で、以下が当てはまることは保証の限りではない。
- Step 1: Install a Container Engineの手順中の/etc/containerd/config.tomlの「CsytemdCgroup = false」はfalseのままでtrueに変更しない。
- NVIDIA GPU Operatorの手順では、上手くいかなかった。
- Step 4: Setup NVIDIA Software の手順を変更してインストールした。具体的には、Helmを使わず、「kubectl apply -f」でdevice pluginを組み込んだ。なお、最新版(v0.12.3)で動作した。
インストール操作
Step 1: container Engineをインストール
(1) 必要なパッケージをインストール
sudo apt-get update\
> && sudo apt-get install -y apt-transport-https \
> ca-certificates curl software-properties-common
自分の環境では、全て最新バージョンだった。
(2)overlay, br_netfilter(カーネル)モジュールをロードするように設定
$ cat <<EOF | sudo tee /etc/modules-load.d/containerd.conf
> overlay
> br_netfilter
> EOF
$ sudo modprobe overlay \
> && sudo modprobe br_netfilter
(3)sysctlパラメータをconfファイルに設定
$ cat <<EOF | sudo tee /etc/sysctl.d/99-kubernetes-cri.conf
> net.bridge.bridge-nf-call-iptables = 1
> net.ipv4.ip_forward = 1
> net.bridge.bridge-nf-call-ip6tables = 1
> EOF
$ sudo sysctl --system
(4)Dockerリポジトリを設定
$ curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key --keyring /etc/apt/trusted.gpg.d/docker.gpg add -
$ sudo add-apt-repository "deb [arch=amd64] https://download.docker.com/linux/ubuntu \
> $(lsb_release -cs) \
> stable"
(5)containerdをインストール
$ sudo apt-get update \
> && sudo apt-get install -y containerd.io
自分の環境では、containerd.ioは最新バージョン(1.6.7-1)がインストール済だった。
(6)containerdのデフォルトパラメータをconfig.tomlを(作成し)設定
$ sudo mkdir -p /etc/containerd \
> && sudo containerd config default | sudo tee /etc/containerd/config.toml
既にcontainerd.ioがインストール済だったので、/etc/containerdディレクトリ、config.tomlは既に存在していた。config.tomlはリネームして保存しておいた。
(7) (containerdが)systemd cgroup driverを使うように、config.tomlを変更
結論で述べたように「SystemdCgroup = false」はそのまま。ここでは、/etc/containerd/config.tomlへの変更は加えない。
(8) containerdデーモンを再起動
$ sudo systemctl restart containerd
Step 2: Kubernetesコンポーネントをインストール
(1) 必要なパッケージをインストール
$ sudo apt-get update \
> && sudo apt-get install -y apt-transport-https curl
自分の環境では最新版がインストールされていた。
(2) リポジトリキーを追加
$ curl -s https://packages.cloud.google.com/apt/doc/apt-key.gpg | sudo apt-key add -
(3) リポジトリを追加
$ cat <<EOF | sudo tee /etc/apt/sources.list.d/kubernetes.list
> deb https://apt.kubernetes.io/ kubernetes-xenial main
> EOF
(4) kubeletをインストール
$ sudo apt-get update \
> && sudo apt-get install -y -q kubelet kubectl kubeadm
(5) Noteの1:Kuberlet 用にcgroupドライバーを設定
NVIDIAのドキュメントでは、Noteの項番1の箇所。
この時点で既に、/etc/systemd/system/kuberlet.service.d配下に10-kubeadm.confが既に存在。
$ sudo cat << EOF | sudo tee /etc/systemd/system/kubelet.service.d/0-containerd.conf
> [Service]
> Environment="KUBELET_EXTRA_ARGS=--container-runtime=remote --runtime-request-timeout=15m --container-runtime-endpoint=unix:///run/containerd/containerd.sock --cgroup-driver='systemd'"
> EOF
(6) Noteの2:kubeletを再起動
$ sudo systemctl daemon-reload \
> && sudo systemctl restart kubelet
(7) swapを無効化
$ swapon --show
NAME TYPE SIZE USED PRIO
/swapfile file 2G 0B -2
$ sudo swapoff -a
$ swapon --show
$
上記の操作は一時的なもので、サーバを再起動すると再びswapが有効な状態。 永続的に無効化するためには、/etc/fstabの「swap」を含むの行の行頭に#を挿入し、無効化する。 (自分の環境では、/swapfileから始まる行)
(8) kubeadm initを実行
$ sudo kubeadm init --pod-network-cidr=192.168.0.0/16
[init] Using Kubernetes version: v1.24.3
・・・省略・・・
Then you can join any number of worker nodes by running the following on each as root:
kubeadm join 192.168.11.3:6443 --token 7ffwr1.xm119vzqvmhqgevl \
--discovery-token-ca-cert-hash sha256:5d2f3065e38020b668ba1b766d95aea197182e35143511db7062f247f12c81d3
$
最後の「kubeadm join … sha256…」の部分はメモっておくこと。worker nodeとするワークステーションで、次のように実行することで、クラスタを作成できる。
$ sudo kubeadm join 192.168.11.3:6443 --token 7ffwr1.xm119vzqvmhqgevl \
> --discovery-token-ca-cert-hash sha256:5d2f3065e38020b668ba1b766d95aea197182e35143511db7062f247f12c81d3
[preflight] Running pre-flight checks
・・・省略・・・
This node has joined the cluster:
* Certificate signing request was sent to apiserver and a response was received.
* The Kubelet was informed of the new secure connection details.
Run 'kubectl get nodes' on the control-plane to see this node join the cluster.
(9) 認証用ファイルを$HOME配下にコピー
$ mkdir -p $HOME/.kube \
> && sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config \
> && sudo chown $(id -u):$(id -g) $HOME/.kube/config
Step 3: ネットワークを設定
(1) Calicoでネットワークを設定
$ kubectl apply -f https://docs.projectcalico.org/manifests/calico.yaml
(2) masterにもworker役を割当
NVIDIAドキュメントに「最も単純化したsingle-nodeクラスターに、GPU Podをスケジューリングできる」とある通り、master(control plane) nodeにもPodをスケジューリングできるようになる。
$ kubectl taint nodes --all node-role.kubernetes.io/master-
自分の環境では、master nodeにはPodをスケジューリングさせたくなかったので、これは実行していない。
ここまでで、master(control plane) node(kubeadm init操作)とworker node(kubeadm join操作)を各々の1つ構築した状態となる。自分の環境でのnodeの状態は次のとおり。
$ kubectl get nodes
NAME STATUS ROLES AGE VERSION
europe Ready control-plane 2m57s v1.25.0
saisei Ready <none> 21s v1.25.0
前の記事では、更にもう1台をクラスタ構成に追加したが、追加しなかった。
Step 4: NVDIAソフトウェアを設定
自分の環境では、control planeにもGPUが挿さっているので、既にNVIDIAドライバーはインストール済み。結論で述べたように、helmを使ってのインストールはしなかった。
具体的な手順は、以下の通り。
(1) (nvidia-container-runtimeパッケージをインストールのため)nvidia-dockerのリポジトリを設定
$ distribution=$(. /etc/os-release;echo $ID$VERSION_ID) \
&& curl -s -L https://nvidia.github.io/nvidia-docker/gpgkey | sudo apt-key add - \
&& curl -s -L https://nvidia.github.io/nvidia-docker/$distribution/nvidia-docker.list | sudo tee /etc/apt/sources.list.d/nvidia-docker.list
(2) nvidia-container-runtimeパッケージをインストール
$ sudo apt-get update \
&& sudo apt-get install -y nvidia-container-runtime
(3) config.tomlを編集
/etc/containerd/config.tomlを次のように編集。
79c79
< default_runtime_name = "nvida"
---
> default_runtime_name = "runc"
125,132d124
< SystemdCgroup = true
< [plugins."io.containerd.grpc.v1.cri".containerd.runtimes.nvidia]
< privileged_without_host_devices = false
< runtime_engine = ""
< runtime_root = ""
< runtime_type = "io.containerd.runc.v1"
< [plugins."io.containerd.grpc.v1.cri".containerd.runtimes.nvidia.options]
< BinaryName = "/usr/bin/nvidia-container-runtime"
その後、containerdデーモンを再起動。
$ sudo systemctl restart containerd
(4) NVIDIA Device Pluginをインストール
$ kubectl apply -f https://raw.githubusercontent.com/NVIDIA/k8s-device-plugin/v0.12.3/nvidia-device-plugin.yml
Step 5: 確認
以下のように、GPUポッドを起動して、状態を確認し、ログを確認し、動作していることが確認できた。
$ cat gpu-pod.yaml
apiVersion: v1
kind: Pod
metadata:
name: gpu-operator-test
spec:
restartPolicy: OnFailure
containers:
- name: cuda-vector-add
image: "nvidia/samples:vectoradd-cuda10.2"
resources:
limits:
nvidia.com/gpu: 1
$ kubectl apply -f gpu-pod.yaml
pod/gpu-operator-test created
$ kubectl get pods
NAME READY STATUS RESTARTS AGE
gpu-operator-test 0/1 Completed 0 8s
$ kubectl logs gpu-operator-test
[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
まとめ
以上の手順で、めでたしめでたし、と言いたいところだが、GPU搭載されている別のノード(mokusei)をクラスタに追加しても、そのノードでは動作しない。Pending状態のままとなる。
saiseiが起動中は動くのだが、次のとおりsaiseiをシャットダウンして、mokuseiのみのクラスタでは、GPUポッドを起動しても、実行されない。
$ kubectl get nodes
NAME STATUS ROLES AGE VERSION
europe Ready control-plane 68m v1.25.0
mokusei Ready <none> 66m v1.25.0
saisei NotReady <none> 9m51s v1.25.0
$ kubectl apply -f gpu-pod.yaml
pod/gpu-operator-test created
$ kubectl get pods gpu-operator-test
NAME READY STATUS RESTARTS AGE
gpu-operator-test 0/1 Pending 0 2m50s
次は、上記の問題をおいおい解決していく予定。
また、上記問題解決のために、ノードに搭載されているGPUの指定、ノードの指定などを一緒に調べていく必要がありそうだ。