kubernetesでGPUコンテナを動かす
はじめに
1.7まではこちらに書いてある方法でしかGPUコンテナが扱えませんでしたが,
k8s-device-pluginを使うことでk8s上でもう少し簡単にGPUコンテナを扱えるようになるみたいなので試してみました.
前提
基本的に前回と同じで物理マシン2台ですがk8sのバージョンだけあげています.
v1.9.1のk8s環境が構築されていることと各pod間とグローバルにつながっていることくらいです.
バージョンアップ自体はドキュメント通りやればあがりますが色々検証しているうちに動かなくなってしまったので一度k8s絡みのパッケージを削除した上でクリーンなk8s環境を作り直しています.
環境
Master
1root@kubernetes:~# cat /etc/lsb-release
2DISTRIB_ID=Ubuntu
3DISTRIB_RELEASE=16.04
4DISTRIB_CODENAME=xenial
5DISTRIB_DESCRIPTION="Ubuntu 16.04.3 LTS"
6root@kubernetes:~# uname -a
7Linux kubernetes 4.4.0-62-generic #83-Ubuntu SMP Wed Jan 18 14:10:15 UTC 2017 x86_64 x86_64 x86_64 GNU/Linux
8root@kubernetes:~# kubectl version
9Client Version: version.Info{Major:"1", Minor:"9", GitVersion:"v1.9.1", GitCommit:"3a1c9449a956b6026f075fa3134ff92f7d55f812", GitTreeState:"clean", BuildDate:"2018-01-04T11:52:23Z", GoVersion:"go1.9.2", Compiler:"gc", Platform:"linux/amd64"}
10Server Version: version.Info{Major:"1", Minor:"9", GitVersion:"v1.9.1", GitCommit:"3a1c9449a956b6026f075fa3134ff92f7d55f812", GitTreeState:"clean", BuildDate:"2018-01-04T11:40:06Z", GoVersion:"go1.9.2", Compiler:"gc", Platform:"linux/amd64"}
11root@kubernetes:~# kubeadm version
12kubeadm version: &version.Info{Major:"1", Minor:"9", GitVersion:"v1.9.1", GitCommit:"3a1c9449a956b6026f075fa3134ff92f7d55f812", GitTreeState:"clean", BuildDate:"2018-01-04T11:40:06Z", GoVersion:"go1.9.2", Compiler:"gc", Platform:"linux/amd64"}
Worker
今回も1台だけです.GPUが1つ載っています.
1root@kubernetes-node:~# cat /etc/lsb-release
2DISTRIB_ID=Ubuntu
3DISTRIB_RELEASE=16.04
4DISTRIB_CODENAME=xenial
5DISTRIB_DESCRIPTION="Ubuntu 16.04.2 LTS"
6root@kubernetes-node:~# uname -a
7Linux kubernetes-node 4.4.0-92-generic #115-Ubuntu SMP Thu Aug 10 09:04:33 UTC 2017 x86_64 x86_64 x86_64 GNU/Linux
8root@kubernetes-node:~# kubeadm version
9kubeadm version: &version.Info{Major:"1", Minor:"9", GitVersion:"v1.9.1", GitCommit:"3a1c9449a956b6026f075fa3134ff92f7d55f812", GitTreeState:"clean", BuildDate:"2018-01-04T11:40:06Z", GoVersion:"go1.9.2", Compiler:"gc", Platform:"linux/amd64"}
10root@kubernetes-node:~# lspci | grep NVIDIA
1101:00.0 VGA compatible controller: NVIDIA Corporation GM200 [GeForce GTX 980 Ti] (rev a1)
k8s-device-plugin
基本的にはREADMEに書いてある通りに設定するだけです.
最後にコンテナを配備する部分以外はGPUが搭載されているWorkerでのみ作業を行えば良いです.
動作要件
- NVIDIA drivers ~= 361.93
ドライバはUbuntu公式のレポジトリでもこの辺が提供されてるので心配しなくても良いですね. - nvidia-docker version > 2.0 (see how to install and it’s prerequisites)
公式ドキュメントを見ながら入れればいけます. - docker configured with nvidia as the default runtime.
これちょっとわかりにくいですがnvidia-dockerがv2からdockerコマンドにオプションをつける方式に変わったのでその辺周りでdockerコマンドにデフォでこのオプションが付くようにする…んだと思います. - Kubernetes version = 1.9
これのためにk8sのバージョンアップが必要になりました… - The DevicePlugins feature gate enabled
後述します.起動時に追加するオプションです.
セットアップ
NVIDIA driver
k8s 1.7の頃にテストしてた名残でCUDAが入れてあったので特に何もしませんでしたがドライバだけ入っていれば良いみたいなのでUbuntu公式とかにある最新のドライバを入れれば良いと思います.
Nvidia公式レポジトリから入れるとかソースからビルドして入れるとか色々ありますがメンドイのでUbuntu公式レポジトリにあるので良いと思います.
現時点(2018年1月10日)では384が提供されている最新版みたいです.
1# apt-get install -y nvidia-384
nvidia-dockerインストール
https://github.com/NVIDIA/nvidia-docker
事前に1.0をインストールしている場合所定の手順でアンインストールする必要があります(v1とv2で挙動が違うため).
その辺はドキュメントを読んで下さい.
まずGPGキーの登録とレポジトリの登録をします.
最後にパッケージリストを更新しておきます.
1# curl -s -L https://nvidia.github.io/nvidia-docker/gpgkey | apt-key add -
2# curl -s -L https://nvidia.github.io/nvidia-docker/ubuntu16.04/amd64/nvidia-docker.list | tee /etc/apt/sources.list.d/nvidia-docker.list
3# apt-get update
あとはインストールして終わりです.
1# apt-get install -y nvidia-docker2
2# pkill -SIGHUP dockerd
最後にバージョンと動作確認します.
1# nvidia-docker version
2NVIDIA Docker: 2.0.1 ←ここが重要
3Client:
4 Version: 17.09.1-ce ← k8sの動作サポートバージョンにしとくのが無難
5 API version: 1.32
6 Go version: go1.8.3
7 Git commit: 19e2cf6
8 Built: Thu Dec 7 22:24:23 2017
9 OS/Arch: linux/amd64
10
11Server:
12 Version: 17.09.1-ce ← 同上
13 API version: 1.32 (minimum version 1.12)
14 Go version: go1.8.3
15 Git commit: 19e2cf6
16 Built: Thu Dec 7 22:23:00 2017
17 OS/Arch: linux/amd64
18 Experimental: false
19# docker run --runtime=nvidia --rm nvidia/cuda nvidia-smi
20Wed Jan 10 07:35:46 2018
21+-----------------------------------------------------------------------------+
22| NVIDIA-SMI 387.26 Driver Version: 387.26 |
23|-------------------------------+----------------------+----------------------+
24| GPU Name Persistence-M| Bus-Id Disp.A | Volatile Uncorr. ECC |
25| Fan Temp Perf Pwr:Usage/Cap| Memory-Usage | GPU-Util Compute M. |
26|===============================+======================+======================|
27| 0 GeForce GTX 980 Ti Off | 00000000:01:00.0 Off | N/A |
28| 22% 33C P8 26W / 250W | 0MiB / 6076MiB | 0% Default |
29+-------------------------------+----------------------+----------------------+
30
31+-----------------------------------------------------------------------------+
32| Processes: GPU Memory |
33| GPU PID Type Process name Usage |
34|=============================================================================|
35| No running processes found |
36+-----------------------------------------------------------------------------+
docker configured with nvidia as the default runtime
https://github.com/nvidia/nvidia-container-runtime
Dockerのデフォルトランタイムを変更します.
不要かもしれませんが一応入れます.
1# apt-get install nvidia-container-runtime
次にデフォルトランタイムを変える方法が3つ程あるので適当に選択します.
ちなみに2つ以上同時にやると同じパラメータを2重に設定しようとして既にそのパラメータは設定済みですってエラーが出て詰みます.
今回はドキュメントの「Daemon configuration file」を選択.
1# tee /etc/docker/daemon.json <<EOF
2{
3 "default-runtime": "nvidia",
4 "runtimes": {
5 "nvidia": {
6 "path": "/usr/bin/nvidia-container-runtime",
7 "runtimeArgs": []
8 }
9 }
10}
11EOF
12# pkill -SIGHUP dockerd
13# systemctl daemon-reload
14# systemctl restart docker
追記 2018-04-06
"default-runtime": "nvidia",
を記事中のconfigに書くのを忘れていたため追加しました
追記終わり
動作確認します.先程nvidia-dockerのテストした時のコマンドを確認します.
1# docker run --runtime=nvidia --rm nvidia/cuda nvidia-smi
良く見るとランタイムをきちんと指定していますね.
正しく設定出来ていれば指定しなくてもデフォルトのランタイムがこのランタイムに変更されているはずです.
1# docker run --rm nvidia/cuda nvidia-smi
2~同じなので略~
The DevicePlugins feature gate enabled
kubeletの起動時にデバイスプラグインを有効にする必要があるため有効にします.
当該ファイルは/etc/systemd/system/kubelet.service.d/10-kubeadm.confにあります.
空気を読んで追加します.
この時言うまでもないですがKUBELET_EXTRA_ARGSが既に宣言されている場合は後に書いた方が優先されてしまいます.
その場合はその行に--feature-gates=DevicePlugins=trueだけ追加しましょう.
1# vi /etc/systemd/system/kubelet.service.d/10-kubeadm.conf
2Environment="KUBELET_EXTRA_ARGS=--feature-gates=DevicePlugins=true"
最後にこの変更を適用させるためにkubeletを再起動します.
1# systemctl daemon-reload
2# systemctl restart kubelet
kubeletの起動ログをチェックし正しくプラグインが読み込まれているか確認します.
1# journalctl -u kubelet | grep feature
2~省略~
31月 06 17:05:03 ホスト名 kubelet[15771]: I0106 17:05:03.167655 15771 feature_gate.go:220] feature gates: &{{} map[DevicePlugins:true]}
大丈夫そうですね.
k8s-device-pluginのコンテナの配備
この作業はMasterで行います
1# kubectl create -f https://raw.githubusercontent.com/NVIDIA/k8s-device-plugin/v1.9/nvidia-device-plugin.yml
しばらく待ってGPUノードのClusterで当該コンテナが起動しているか確認します.
1# kubectl get pods --all-namespaces -o wide | grep nvidia
2NAMESPACE NAME READY STATUS RESTARTS AGE IP NODE
3kube-system nvidia-device-plugin-daemonset-hb9l8 1/1 Running 0 3d 10.244.1.3 kubernetes-node
これで環境構築終わりです.
実際に使ってみます.
実際にGPUコンテナを動かす
適当なファイル名で以下のymlを作ります.
1apiVersion: v1
2kind: Pod
3metadata:
4 name: gpu-pod
5spec:
6 containers:
7 - name: cuda-container
8 image: nvidia/cuda:9.0-cudnn7-devel ← GPU利用可能なイメージを選択する(devel付きでないとnvccが入ってない GPUドライバのみ)
9 tty: true ← nvidiaのデフォルトコンテナはCMDがないのでttyで開いておかないと即時クラッシュする
10 resources:
11 limits:
12 nvidia.com/gpu: 1 ← 必要なGPUの数を指定する
いつも通りPodを作成します.
1# kubelet create -f 作成したyml
Podの起動を確認します.
1# kubectl get pods --all-namespaces -o wide | grep gpu-pod
2NAMESPACE NAME READY STATUS RESTARTS AGE IP NODE
3default gpu-pod 1/1 Running 0 3d 10.244.1.7 lotus
コンソールにアクセスしてmnistを動かしてみます.
1# kubectl exec -it gpu-pod /bin/bash
2root@gpu-pod:/# apt-get install python-pip git
3root@gpu-pod:/# pip install chainer
4root@gpu-pod:/# git clone https://github.com/chainer/chainer
5root@gpu-pod:/# cd chainer
6root@gpu-pod:/# git checkout v3
7root@gpu-pod:/# cd examples/mnist/
8root@gpu-pod:/# python train_mnist.py -g 0
9GPU: 0
10# unit: 1000
11# Minibatch-size: 100
12# epoch: 20
13~中略~
1419 0.0124792 0.107687 0.996549 0.9801 38.4196
1520 0.0113122 0.119661 0.996782 0.9821 40.4097
無事学習が回りました.