Harukaのnote

Linuxやプログラミング,写真,旅行等の記録帳

研究室のGPUリソースをKubernetesを使って管理

kubernetesk8s)を使って,ラボのWorkStation環境を再構成したので備忘録として残します.

はじめに

私の所属するラボでは,以前からk8sGPUを管理していましたが,UbuntuのバージョンやCUDAバージョンが古くなって動作も不安定担ってきたことから,全て初期化して再設定しました.なんとか素人の私と後輩たちでも構成できたのでk8sに詳しい方なら余裕な内容となってしまうと思います.しかし,同様の記事が少なくて手こずったため紹介しようと思います.

構成

こちら(https://www.nttpc.co.jp/gpu/article/benchmark15.html)の記事に近い構成を目指します.
WS-Master

WS-Worker

  • 4台
  • ワーカーノード
  • クラスターに参加し,計算を担う
  • 各マシン,GPU x 2を搭載

WS-Masterの準備

事前に以下の準備をします.

  • Ubuntu 20.04 LTS をインストール

ポートの開放

使用するポートを事前に開放します.

$ sudo ufw enable
$ sudo ufw status
状態: アクティブ

以下のコマンドで開放するポートを設定.

$ sudo ufw allow 53
$ sudo ufw allow 6443
$ sudo ufw allow 2379:2380/tcp
$ sudo ufw allow 8285
$ sudo ufw allow 8472
$ sudo ufw allow 9153
$ sudo ufw allow 10250:10252/tcp
$ sudo reboot

確認.

sudo ufw status
状態: アクティブ
To                         Action      From
--                         ------      ----
22                         ALLOW       Anywhere                  
22/tcp                     ALLOW       Anywhere                  
53                         ALLOW       Anywhere                  
6443                       ALLOW       Anywhere                  
2379:2380/tcp              ALLOW       Anywhere                  
8285                       ALLOW       Anywhere                  
8472                       ALLOW       Anywhere                  
9153                       ALLOW       Anywhere                  
10250:10252/tcp            ALLOW       Anywhere                  
22 (v6)                    ALLOW       Anywhere (v6)             
22/tcp (v6)                ALLOW       Anywhere (v6)             
53 (v6)                    ALLOW       Anywhere (v6)             
6443 (v6)                  ALLOW       Anywhere (v6)             
2379:2380/tcp (v6)         ALLOW       Anywhere (v6)             
8285 (v6)                  ALLOW       Anywhere (v6)             
8472 (v6)                  ALLOW       Anywhere (v6)             
9153 (v6)                  ALLOW       Anywhere (v6)             
10250:10252/tcp (v6)       ALLOW       Anywhere (v6)             

Swap領域のオフ

swap領域が有るとk8sが動作しないので,オフにします.

$ sudo vim /etc/fstab
# /etc/fstab: static file system information.
#
# Use 'blkid' to print the universally unique identifier for a
# device; this may be used with UUID= as a more robust way to name devices
# that works even if disks are added and removed. See fstab(5).
# /swapfile                                 none            swap    sw              0       0

/swapもしくは/swapfileと表記のある行をコメントアウトします.

$ sudo reboot

Dockerの設定

Dockerのインストール

Dockerはk8sで動作するバージョンに指定があるため,公式を参考にインストールします.
CRIのインストール | Kubernetes

HTTPS越しのリポジトリの使用をaptに許可するために、パッケージをインストールします.

$ sudo apt-get update && sudo apt-get install -y apt-transport-https ca-certificates curl software-properties-common gnupg2

Docker公式のGPG鍵を追加します.

$ curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add -

Dockerのaptレポジトリを追加します.

$ sudo add-apt-repository "deb [arch=amd64] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable"

Docker CEをインストールします.
Docker-ceがver.19代でなければ動作しなかったはずです.

$ sudo apt-get update && sudo apt-get install -y containerd.io=1.2.13-2 docker-ce=5:19.03.11~3-0~ubuntu-$(lsb_release -cs) docker-ce-cli=5:19.03.11~3-0~ubuntu-$(lsb_release -cs)
デーモンをセットアップ

以下を記載.

$ sudo vim /etc/docker/daemon.json
 {
   "exec-opts": ["native.cgroupdriver=systemd"],
   "log-driver": "json-file",
   "log-opts": {
     "max-size": "100m"
   },
   "storage-driver": "overlay2"
 }

service.dの作成.

$ sudo mkdir -p /etc/systemd/system/docker.service.d
dockerを再起動
$ sudo gpasswd -a $USER docker
$ sudo systemctl daemon-reload
$ sudo systemctl restart docker
$ sudo systemctl enable docker
情報を確認します.
$ docker version
Client: Docker Engine - Community
 Version:           19.03.11
 API version:       1.40
 Go version:        go1.13.10
 Git commit:        42e35e61f3
 Built:             Mon Jun  1 09:12:34 2020
 OS/Arch:           linux/amd64
 Experimental:      false

Server: Docker Engine - Community
 Engine:
  Version:          19.03.11
  API version:      1.40 (minimum version 1.12)
  Go version:       go1.13.10
  Git commit:       42e35e61f3
  Built:            Mon Jun  1 09:11:07 2020
  OS/Arch:          linux/amd64
  Experimental:     false
 containerd:
  Version:          1.2.13
  GitCommit:        7ad184331fa3e55e52b890ea95e65ba581ae3429
 nvidia:
  Version:          1.0.0-rc10
  GitCommit:        dc9208a3303feef5b3839f4323d9beb36df0a9dd
 docker-init:
  Version:          0.18.0
  GitCommit:        fec3683

k8sのインストールと設定

kubeadm, kubectl, kubeletのインストール

こちら(Ubuntu 20.04LTSにkubernetes環境をkubeadmで構築する手順 | Snow System)のサイトを参考にインストールします.

$ sudo apt-get update && sudo apt-get install -y apt-transport-https
$ curl -s https://packages.cloud.google.com/apt/doc/apt-key.gpg | sudo apt-key add -
$ sudo apt-add-repository "deb http://apt.kubernetes.io/ kubernetes-xenial main"
$ sudo apt update
$ sudo apt install -y kubeadm kubelet kubectl
クラスターを作成

kubernetesクラスタを初期化します.

$ sudo kubeadm init --pod-network-cidr=10.244.0.0/16
$ mkdir -p $HOME/.kube
$ sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
$ sudo chown $(id -u):$(id -g) $HOME/.kube/config

発行されたトークンをWorkerに共有します.
中断や失敗した場合,一時ファイルが残るのでリセットが必要です.

$ sudo kubeadm reset

一般ユーザへのkubectlコマンド開放をします.

$ sudo vim  /etc/profile
# 以下を追記
export KUBECONFIG=/etc/kubernetes/admin.conf
$ sudo chmod 644 /etc/kubernetes/admin.conf
flannelのインストール
$ sudo sysctl net.bridge.bridge-nf-call-iptables=1
$ kubectl apply -f https://raw.githubusercontent.com/coreos/flannel/master/Documentation/kube-flannel.yml
kubernetes Device Pluginの有効化
$ sudo vim /etc/default/kubelet

/etc/default/kubelet

KUBELET_EXTRA_ARGS=--feature-gates=DevicePlugins=true
$ sudo systemctl daemon-reload
$ sudo systemctl restart kubelet
k8s-device-plugin

以下の公式ドキュメントを参考にインストールします.
GitHub - NVIDIA/k8s-device-plugin: NVIDIA device plugin for Kubernetes

$ kubectl create -f https://raw.githubusercontent.com/NVIDIA/k8s-device-plugin/v0.10.0/nvidia-device-plugin.yml


次に,ワーカーノードを設定します.

WS-Workerの準備

事前に以下の準備をします.

  • Ubuntu 20.04 LTS をインストール
  • nvidia driver 470のインストール
  • CUDA 11.4のインストール

ポートの開放

使用するポートを事前に開放します.

$ sudo ufw enable
$ sudo ufw status
状態: アクティブ

以下のコマンドで開放するポートを設定します.

$ sudo ufw allow 53
$ sudo ufw allow 8285
$ sudo ufw allow 8472
$ sudo ufw allow 9153
$ sudo ufw allow 10250
$ sudo ufw allow 30000:32767/tcp
$ sudo reboot

確認.

sudo ufw status
Status: active
To                         Action      From
--                         ------      ----
22                         ALLOW       Anywhere                  
10250                      ALLOW       Anywhere                  
53                         ALLOW       Anywhere                  
8285                       ALLOW       Anywhere                  
8472                       ALLOW       Anywhere                  
9153                       ALLOW       Anywhere                  
30000:32767/tcp            ALLOW       Anywhere                  
22 (v6)                    ALLOW       Anywhere (v6)             
10250 (v6)                 ALLOW       Anywhere (v6)             
53 (v6)                    ALLOW       Anywhere (v6)             
8285 (v6)                  ALLOW       Anywhere (v6)             
8472 (v6)                  ALLOW       Anywhere (v6)             
9153 (v6)                  ALLOW       Anywhere (v6)             
30000:32767/tcp (v6)       ALLOW       Anywhere (v6)

Swap領域のオフ

WS-Masterと同様の手順で.

Dockerの設定

WS-Masterと同様の手順で.

nvidia-docker2のインストール

DockerからGPUを使用するためのソフトウェアであるnvidia-docker2をインストールします.
これはk8s-device-pluginで使用されるため,k8s-device-pluginのインストラクションに従ってインストールします.
GitHub - NVIDIA/k8s-device-plugin: NVIDIA device plugin for Kubernetes

$ 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
$ sudo apt-get update && sudo apt-get install -y nvidia-docker2
$ sudo systemctl restart docker

/etc/docker/daemon.jsonに追記します.

$ sudo vim /etc/docker/daemon.json 

/etc/docker/daemon.json

 {
   "default-runtime": "nvidia",
   "runtimes": {
     "nvidia": {
       "path": "/usr/bin/nvidia-container-runtime",
       "runtimeArgs": []
     }
   },
   "exec-opts": ["native.cgroupdriver=systemd"],
   "log-driver": "json-file",
   "log-opts": {
     "max-size": "100m"
   },
   "storage-driver": "overlay2"
 }

dockerを再起動します.

$ sudo pkill -SIGHUP dockerd
$ sudo systemctl daemon-reload
$ sudo systemctl restart docker

情報を確認します.

 $ sudo nvidia-docker version
 NVIDIA Docker: 2.8.0
 Client: Docker Engine - Community
  Version:           19.03.11
  API version:       1.40
  Go version:        go1.13.10
  Git commit:        42e35e61f3
  Built:             Mon Jun  1 09:12:22 2020
  OS/Arch:           linux/amd64
  Experimental:      false
 
 Server: Docker Engine - Community
  Engine:
   Version:          19.03.11
   API version:      1.40 (minimum version 1.12)
   Go version:       go1.13.10
   Git commit:       42e35e61f3
   Built:            Mon Jun  1 09:10:54 2020
   OS/Arch:          linux/amd64
   Experimental:     false
  containerd:
   Version:          1.2.13
   GitCommit:        7ad184331fa3e55e52b890ea95e65ba581ae3429
  nvidia:
   Version:          1.0.0-rc10
   GitCommit:        dc9208a3303feef5b3839f4323d9beb36df0a9dd
  docker-init:
   Version:          0.18.0
   GitCommit:        fec3683

dockerイメージからGPUが確認できるか確認します.
Docker Hubではnvidia/cudaのような暗黙のlatestタグがduplicatedになったため,nvidia/cuda:11.4.3-runtime-ubuntu20.04のように正しく指定する必要があります.

$ sudo docker run --rm nvidia/cuda:11.4.3-runtime-ubuntu20.04 nvidia-smi

k8sのインストールと設定

kubeadm, kubectl, kubeletのインストール

WS-Masterと同様の手順で.

クラスターに参加

WS-Masterで発行されたトークンを使用してクラスターに参加します.

$ sudo kubeadm join [IPアドレス]:6443 --token [Token] --discovery-token-ca-cert-hash [Token Hash]

エラーが起きてjoinできないときはresetをします.
途中でキャンセルした場合も一時ファイルが残るのでリセットが必要です.

$ sudo kubeadm reset

ここまでがWS-Workerでの作業となります.

Master側で確認

設定情報を確認します.

$ kubectl get nodes
    NAME             STATUS   ROLES                  AGE     VERSION
    workstation-01   Ready    compute                4d      v1.23.1
    workstation-02   Ready    compute                3d21h   v1.23.1
    workstation-03   Ready    compute                4d13h   v1.23.1
    workstation-04   Ready    compute                4d17h   v1.23.1
    ws-master        Ready    control-plane,master   4d17h   v1.23.1

workstation-01等のワーカーノードに設定したPCのホスト名が見えており,STATUSがReadyになっていると大丈夫です.





最後に,構成したクラスターの設定をWS-Masterでします.

WS-Master でクラスターの設定

node role, accelerator(GPUセレクタ)の設定

ワーカーノードに役割を付与します.
ワーカーノードのRoleをCompute(計算用)に設定

$ kubectl label nodes [worker hostname] node-role.kubernetes.io/compute=true

搭載GPUでノード選択可能に(GPU名をラベルとして設定).
gpu nameの例: nvidia-geforce-gtx1080ti, nvidia-tesla-v100

$ kubectl label nodes [worker hostname] accelerator=(gpu name)

# 例えば,
$ kubectl label nodes workstation-01 accelerator=nvidia-geforce-rtx2080ti
$ kubectl describe nodes    # role, acceleratorの確認(詳細)

Podの作成

ここは一般的なk8sの使い方に近くなるため,簡単に書きます.

WS-Masterに自身のユーザーを作成し,ログインします.

ネームスペースを作成します.

$ kubectl create ns haruka         # haruka 部分を適当なユーザー名に変更


test.yml を作成します.
test.yml

apiVersion: v1
kind: Pod
metadata:
  name: haruka-test-2080ti    # `kubectl get pod` で表示される名前
spec:
  containers:
  - name: haruka-pytorch-container    # 表示先が無い名前
    image: "nvidia/cuda:11.4.3-runtime-ubuntu20.04"   # 使用するdocker image
    resources:
      limits:
        nvidia.com/gpu: 1 # 使用するGPU枚数
    command: ["sh", "-c", "tail -f /dev/null",]  # Pod生成後,すぐ実行されるコマンド
    workingDir: /home/haruka   # Podの中に入ると最初にいる場所
  restartPolicy: OnFailure
  nodeSelector:                     # 使用するGPUの設定.記述がない場合は自動で割当られる
    accelerator: nvidia-geforce-rtx2080ti
    #accelerator: nvidia-titan-rtx
    #accelerator: nvidia-geforce-gtx1080

test.yml をapplyし,ポッドを作成します.

$ kubectl apply -n haruka -f test.yml


以下のコマンドでステータスが確認でき,RunningになるとPodの作成の成功を示します.ちなみに,PendingだとGPUリソースの空きを待っている状態です.

$ kubectl get pods -n haruka
NAME                 READY   STATUS    RESTARTS     AGE
haruka-test-2080ti   1/1     Running   1 (4d ago)   4d19h


以下のコマンドでPodに入ります.

$ kubectl exec -it -n haruka haruka-test-2080ti /bin/bash

root@haruka-test-2080ti:/home/haruka# 
root@haruka-test-2080ti:/home/haruka# nvidia-smi
Mon Jan 17 09:17:42 2022       
+-----------------------------------------------------------------------------+
| NVIDIA-SMI 495.29.05    Driver Version: 495.29.05    CUDA Version: 11.5     |
|-------------------------------+----------------------+----------------------+
| GPU  Name        Persistence-M| Bus-Id        Disp.A | Volatile Uncorr. ECC |
| Fan  Temp  Perf  Pwr:Usage/Cap|         Memory-Usage | GPU-Util  Compute M. |
|                               |                      |               MIG M. |
|===============================+======================+======================|
|   0  NVIDIA GeForce ...  On   | 00000000:86:00.0 Off |                  N/A |
| 27%   22C    P8    19W / 250W |      1MiB / 11019MiB |      0%      Default |
|                               |                      |                  N/A |
+-------------------------------+----------------------+----------------------+


以下のコマンドでPodを削除します.

$ kubectl delete pod -n haruka haruka-test-2080ti
pod "haruka-test-2080ti" deleted

GPUリソースの空きを確認する便利コマンドを作成

kubectl describe nodesではリソースの空きが確認し辛いため,確認用のシェルスクリプトを作成します.

$ sudo mkdir /usr/local/command

以下のスクリプト/usr/local/command/に配置します.

$ sudo vim /usr/local/command/showgpus

/usr/local/command/showgpus

var1=($(kubectl describe nodes  |  tr -d '\000' | sed -n -e '/^Name/,/Roles/p' -e '/^Capacity/,/Allocatable/p' -e '/^Allocated resources/,/Events/p'  | grep -e Name  -e  nvidia.com  | perl -pe 's/\n//'  |  perl -pe 's/Name:/\n/g' | sed 's/nvidia.com\/gpu:\?//g'  | sed '1s/^/Node Available(GPUs)  Used(GPUs)/' | sed 's/$/ 0 0 0/'  | awk '{print $1, $2, $3}'  | column -t))
var2=($(kubectl describe nodes | grep -e accelerator= | sed -e "s/.*accelerator=//"))
var3=($(kubectl describe nodes | grep -e Hostname: | sed -e "s/.*Hostname:\s*//"))
printf "|%-15s|%-30s|%-9s|\n" "Node" "GPU" "Used"
for i in `seq 0 $((${#var2[@]}-1))`
do
printf "|%-15s|%-30s|%4d/%-4d|\n" ${var3[$i]} ${var2[$i]} ${var1[$((5+3*$i))]} ${var1[$((4+3*$i))]}
done

実行できるようにします.

$ sudo chmod 777 /usr/local/command/showgpus
$ sudo vim /etc/profile
#末尾に以下を追記
export PATH=$PATH:/usr/local/command

実行結果は以下のようになります.

$ showgpus
|Node           |GPU                           |Used     |
|workstation-01 |nvidia-rtx-a5000              |   0/2   |
|workstation-02 |nvidia-titan-rtx              |   0/2   |
|workstation-03 |nvidia-geforce-rtx2080ti      |   0/2   |
|workstation-04 |nvidia-geforce-rtx2080ti      |   1/4   | 

おわりに

お疲れさまでした.これで,複数人でGPUを利用する研究室で自動でGPUリソースが割り振られる環境が整いました.
準備に少し手間がかかりましたが,他人の学習を誤って止める危険がなくなりました.

素人には辛かったです...


もし非常に役に立ちましたら,haruka | OFUSE (オフセ)