GPU
DeepLearning
docker
kubernetes
TensorFlow

KubernetesからGPUを使ってTensorflowを動かす

KubernetesからGPUを使ってTensorflowを動かす

Kubernetes 1.8ではα版(実験的)機能としてPodからGPUを使うことが可能になっています。
KubernetesからGPUを使うメリットは、ディープラーニングとコンテナ・オーケストレーションを組み合わせられることです。
ディープラーニングのトレーニングフェーズではGPUを使うことでスピードアップするのが一般的です。
NvidiaもDockerからGPUとCUDAを使うためのNvidia Dockerを提供しており、コンテナ上でGPUを使うのは有効な手段として広まっていっていると感じます。
KubernetesでもGPU使用が要望されており、現在はα版として使用可能になっています。

KubernetesからGPUを使う方法

詳しくは以下で書かれていますが、ホストサーバにインストールしたNvidia CUDAやCudnnのライブラリをPodにマウントして呼び出す仕組みになっています。
https://kubernetes.io/docs/tasks/manage-gpus/scheduling-gpus/

なお、Kubernetes on GPUをKubernetes1.6で試している例もありますので、こちらも参考になります。
https://github.com/Langhalsdino/Kubernetes-GPU-Guide

環境

インフラ、ソフトウェアは以下を使います。
- 基盤:Google Cloud Platform(GCP)のCloud Engine(仮想マシン)1台
- スペック:4CPU, 15GB RAM, 100GB SSD, 1GPU(Nvidia K80)
- OS:Ubuntu16.04
- Docker CE 17.03
- Kubernetes 1.8
- Nvidia CUDA 8.0
- Nvidia Cudnn 6.0
- Podのコンテナイメージ:gcr.io/tensorflow/tensorflow:latest-gpu(現時点ではPython2.7、Tensorflow1.4)

0.png

各ソフトウェアのバージョンは、現時点(2017年11月)でいろいろ試した結果、うまくいった組み合わせです。
バージョン合わせるのって大変ですよね。

GCPを使ったのは仮想マシンの配備が速い&分単位課金だからです。
ソフトウェアのバージョンを変えつつ、安定して動かすのに何回も作って壊してを繰り返したので、すぐ作り直せてお金も無駄にならないことが必要でした。

・・・なんて思っていたら、AWS EC2は秒単位課金になっていたんですね(驚き)。
http://www.itmedia.co.jp/news/articles/1709/19/news049.html

まあ今回使うKubernetesもTensorflowもGoogle発ですし、クラウドもGoogleで揃えて良いでしょう。

構築

それでは早速構築していきます。
GCPのCloud EngineでUbuntu16.04仮想マシンを1台配備したら、まずはアップデートしてDocker CEをインストールして諸々設定します。
Docker CEのインストール方法は以下をご参照ください。
https://docs.docker.com/engine/installation/linux/docker-ce/ubuntu/
https://kubernetes.io/docs/setup/independent/install-kubeadm/

# update Linux and install docker
sudo su -
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | apt-key add -
apt-get -y update && apt-get -y upgrade

curl -fsSL https://download.docker.com/linux/ubuntu/gpg | apt-key add -
cat <<EOF >/etc/apt/sources.list.d/docker.list
deb https://download.docker.com/linux/$(lsb_release -si | tr '[:upper:]' '[:lower:]') $(lsb_release -cs) stable
EOF
apt-get update && apt-get install -y curl apt-transport-https git vim jq docker-ce=$(apt-cache madison docker-ce | grep 17.03 | head -1 | awk '{print $3}')

# start docker
systemctl start docker && systemctl enable docker

# set network
cat << EOF > /tmp/host
localhost
10.10.0.2
EOF
mkdir -p ~/.ssh/
for i in $(cat /tmp/host); do ssh-keyscan -H $i >> ~/.ssh/known_hosts ; done

続いてCUDAとCudnnのインストールです。
KubernetesからGPUを使うときはホストサーバのライブラリを読み込むので、ホストサーバ側にCUDAとCudnnが必要になります。
CUDAもCudnnもバージョン指定でインストールします。
https://developer.nvidia.com/cuda-toolkit

## find nvidia GPU
lspci | grep -i nvidia

# install requirements for cuda and cndnn
wget https://developer.download.nvidia.com/compute/cuda/repos/ubuntu1604/x86_64/cuda-repo-ubuntu1604_8.0.61-1_amd64.deb
dpkg -i cuda-repo-ubuntu1604_8.0.61-1_amd64.deb
apt-get -y update
apt-get -y install --allow-unauthenticated cuda-8-0

echo 'export CUDA_HOME=/usr/local/cuda' >> /etc/profile
echo 'export PATH=/usr/local/cuda-8.0/bin:${PATH}' >> /etc/profile
echo 'export LD_LIBRARY_PATH=/usr/local/cuda-8.0/lib64:/usr/lib/nvidia-384:/usr/lib/nvidia:${CUDA_HOME}:${LD_LIBRARY_PATH}' >> /etc/profile
source /etc/profile

CUDAはwgetでインストーラを入手できますが、Cudnnはブラウザからダウンロードしたものを仮想マシンにアップロードする必要があります。
以下のコマンドの前に、Cudnnをダウンロードして仮想マシンにアップロードしておいてください。
CudnnのダウンロードにはNvidiaへの登録とアンケート回答が必要になります。
https://developer.nvidia.com/rdp/cudnn-download

# prerequisite: upload cudnn to the server
tar zxvf cudnn-8.0-linux-x64-v6.0.tgz
cp cuda/include/cudnn.h /usr/local/cuda/include
cp cuda/lib64/libcudnn* /usr/local/cuda/lib64
chmod a+r /usr/local/cuda/include/cudnn.h /usr/local/cuda/lib64/libcudnn*

nvcc --version
nvidia-smi

以下のようにGPUが認識されていることを確認してください。

1.PNG

続いてKubernetesをインストールします。
Kubeadmで構築しますので、まずはKubeadmをインストールします。
https://kubernetes.io/docs/setup/independent/install-kubeadm/

# install kube tools
apt-get update && apt-get install -y apt-transport-https
curl -s https://packages.cloud.google.com/apt/doc/apt-key.gpg | apt-key add -
cat <<EOF >/etc/apt/sources.list.d/kubernetes.list
deb http://apt.kubernetes.io/ kubernetes-xenial main
EOF

apt-get -y update
apt-get install -y --allow-unauthenticated kubelet kubeadm kubectl kubernetes-cni

KubernetesでGPUを使うためには、Kubeletの起動コマンドに--feature-gates=Accelerators=trueオプションを追加する必要があります。
このオプションは/etc/systemd/system/kubelet.service.d/配下の*-kubeadm.confファイルに追記することで追加可能です。

FILE_NAME=$(ls /etc/systemd/system/kubelet.service.d/*-kubeadm.conf)
sed -i '/^ExecStart=\/usr\/bin\/kubelet/ s/$/ --feature-gates=Accelerators=true/' ${FILE_NAME}

systemctl daemon-reload
systemctl restart kubelet

KubeadmからKubernetesクラスターを構築します。
https://kubernetes.io/docs/setup/independent/create-cluster-kubeadm/

# initialize kubeadm and kube cluster
kubeadm init --pod-network-cidr=10.244.0.0/16 --apiserver-advertise-address=$(hostname -i)
systemctl enable kubelet && systemctl start kubelet
systemctl status kubelet -l

mkdir -p $HOME/.kube
cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
chown $(id -u):$(id -g) $HOME/.kube/config

kubectl cluster-info

# install flannel
kubectl apply -f https://raw.githubusercontent.com/coreos/flannel/v0.9.0/Documentation/kube-flannel.yml
kubectl get pods --all-namespaces

今回はマスター1台のみの構成にするので、マスターへのデプロイを有効化します。

# allow deployment to Kubernetes master
kubectl taint nodes --all node-role.kubernetes.io/master-

稼働確認として靴下ショップをデプロイします。

# deploy sample sock-shop app
kubectl create namespace sock-shop
kubectl apply -n sock-shop -f "https://github.com/microservices-demo/microservices-demo/blob/master/deploy/kubernetes/complete-demo.yaml?raw=true"
kubectl -n sock-shop get svc front-end
kubectl get pods -n sock-shop

無事デプロイできたら消します。

# delete after deployment
kubectl delete namespace sock-shop

KubernetesからGPUを使ってTensorflowを動かす

これで構築完了です。
それではKubernetesのPodからGPUを使ってTensorlfowを動かします。

Podの定義ymlは以下です。
環境変数にCUDAやCudnn等Nvidia GPUライブラリへのパスを設定します。
また、Kubernetesはホストサーバのライブラリを使ってGPUを動かすので、ホストサーバのCUDAやCudnn等Nvidia GPU系のディレクトリをコンテナにマウントします。
(いろいろマウントしまくっていますが、もしかしたら不要なものもあるかもしれません・・・)

cat <<- EOF > kubegpu.yml
kind: Pod
apiVersion: v1
metadata:
  name: gpu-pod
spec:
  containers:
  - name: gpu-container
    image: gcr.io/tensorflow/tensorflow:latest-gpu
    resources:
      limits:
        alpha.kubernetes.io/nvidia-gpu: 1
    env:
    - name: CUDA_HOME
      value: '/usr/local/cuda'
    - name: PATH
      value: '/usr/local/cuda-8.0/bin:${PATH}'
    - name: LD_LIBRARY_PATH
      value: '/usr/local/cuda-8.0/lib64:/usr/lib/nvidia-384:/usr/lib/nvidia:${CUDA_HOME}:${LD_LIBRARY_PATH}'
    volumeMounts:
    - mountPath: /usr/lib/nvidia-384/bin
      name: bin
    - mountPath: /usr/lib/nvidia-384
      name: lib
    - mountPath: /usr/local/cuda-8.0
      name: cuda
    - mountPath: /usr/lib/x86_64-linux-gnu
      name: gnu
    - mountPath: /usr/bin/nvidia-smi
      name: nvs
    - mountPath: /usr/local/cuda/lib64
      name: clib64
    - mountPath: /usr/local/cuda/include
      name: include
  volumes:
  - hostPath:
      path: /usr/lib/nvidia-384/bin
    name: bin
  - hostPath:
      path: /usr/lib/nvidia-384
    name: lib
  - hostPath:
      path: /usr/local/cuda-8.0
    name: cuda
  - hostPath:
      path: /usr/lib/x86_64-linux-gnu
    name: gnu
  - hostPath:
      path: /usr/bin/nvidia-smi
    name: nvs
  - hostPath:
      path: /usr/local/cuda/lib64
    name: clib64
  - hostPath:
      path: /usr/local/cuda/include
    name: include
EOF

動かしてみましょう。

# create pod
kubectl create -f kubegpu.yml

# see if it is running
kubectl get pods

2.PNG

起動(Running)になったらpodにログインし、GPUを認識しているか確認してみましょう。

kubectl exec -it gpu-pod /bin/bash
nvidia-smi

GPUを認識できていたら、以下のように表示されます。

3.PNG

最後にTensorflowからGPUを使えるか試してみましょう。
プログラムは以下のものを使用しています。
https://www.tensorflow.org/tutorials/using_gpu

cat << EOF > ten.py
import tensorflow as tf
# Creates a graph.
a = tf.constant([1.0, 2.0, 3.0, 4.0, 5.0, 6.0], shape=[2, 3], name='a')
b = tf.constant([1.0, 2.0, 3.0, 4.0, 5.0, 6.0], shape=[3, 2], name='b')
c = tf.matmul(a, b)
# Creates a session with log_device_placement set to True.
sess = tf.Session(config=tf.ConfigProto(log_device_placement=True))
# Runs the op.
print(sess.run(c))
EOF

python ten.py

試しに上記のような簡単な計算を動かしていますが、無事GPUを認識して利用しているようです。

4.PNG

最後に

KubernetesからGPUを使ってTensorflowを動かすことができました。
ディープラーニングのトレーニングは一時的な稼働になることが多く、都度コンテナで環境を用意するのがちょうど良いと思います。
ディープラーニングを活用し始めると、違うモデル作成のために多数のコンテナを適時デプロイしてトレーニングを実行することになると思います。
そのためにはKubernetesのようなコンテナ・オーケストレーションが便利です。

コンテナでディープラーニングを行うためにNvidia-dockerでGPUを使う方法が有効ですが、多数のコンテナを管理しようとなると、KubernetesでGPUを使う必要が出てきます。

今回はこの両方のニーズを実現する方法を紹介しました。
KubernetesからGPUを使うのはまだ実験的なα版機能ですが、はやくGAしてもっと手軽に使えるようになってほしいものです。