PC에 kubeflow 설치하기 – 3부 kubeflow 설치하기

Service Account Token Volume Projection 활성화

kubeflow에서는 인증/권한 기능을 위해서 istio 를 사용합니다. 그래서 istio-system 이라는 네임스페이스에 istio 관련 컴포넌트가 설치됩니다. 그 중에 하나인 istio-ingressgateway 포드의 내용을 보면 다음과 같은 부분을 발견할 수 있습니다.

...
  volumes:
  - name: istio-token
    projected:
      defaultMode: 420
      sources:
      - serviceAccountToken:
          audience: istio-ca
          expirationSeconds: 43200
          path: istio-token
...

바로 Service Account Token Volume Projection 이라는 것입니다. 이 기능은 쿠버네티스 1.15에서 비활성화 되어 있습니다. 그래서 해당 기능을 사용하기 위해서는 활성화 해줘야 합니다.

기능을 활성화 하기 위해서는, 다음과 같이 kube-apiserver 매니페스트에 몇 가지 플래그를 추가해야합니다. 매니페스트 파일은 /etc/kubernetes/manifests/kube-apiserver.yaml에 위치합니다.

$ sudo vi /etc/kubernetes/manifests/kube-apiserver.yaml
...
        - --service-account-signing-key-file=/etc/kubernetes/pki/sa.key
        - --service-account-issuer=api
        - --service-account-api-audiences=api,vault

매니페스트 파일을 수정하고, kube-apiserver 포드가 자동으로 다시 시작됩니다.

dynamic volume provisioner 설치

kubeflow를 쉽게 설치하기 위해서는 동적 볼륨 프로비져너(dynamic volume provisioner)가 필요합니다. 이 글에는 로컬 디렉토리를 이용하는 Local Path Provisioner 를 사용하겠습니다.

다음은 Local Path Provisioner 를 설치하는 명령어입니다.

$ kubectl apply -f https://raw.githubusercontent.com/rancher/local-path-provisioner/master/deploy/local-path-storage.yaml

namespace/local-path-storage created
serviceaccount/local-path-provisioner-service-account created
clusterrole.rbac.authorization.k8s.io/local-path-provisioner-role created
clusterrolebinding.rbac.authorization.k8s.io/local-path-provisioner-bind created
deployment.apps/local-path-provisioner created
storageclass.storage.k8s.io/local-path created
configmap/local-path-config created

스토리지 클래스(storage class)를 조회해 보겠습니다.

$ kubectl get storageclass
NAME         PROVISIONER             AGE
local-path   rancher.io/local-path   63s

kubeflow는 기본 스토리지 클래스를 사용하기 때문에, local-path 스토리지 클래스를 기본 클래스로 설정해야합니다..

다음은 기본 스토리지 클래스를 설정하는 명령어입니다.

$ kubectl patch storageclass local-path -p '{"metadata": {"annotations":{"storageclass.kubernetes.io/is-default-class":"true"}}}'

storageclass.storage.k8s.io/local-path patched

다시 스토리지 클래스를 조회해 보면, 기본 클래스가 설정된 것을 확인할 수 있습니다.

$ kubectl get sc
NAME                   PROVISIONER             AGE
local-path (default)   rancher.io/local-path   11m

kubeflow 설치하기

kubeflow를 설치하기 위해서, kftctl 릴리즈 페이지에서 다운로드 합니다. 이 글을 쓰는 시점에서는 v1.0버전이 최신이므로, v1.0 버전을 기준으로 설명하겠습니다.

$ mkdir ~/kubeflow
$ cd ~/kubeflow

$ curl -L -O https://github.com/kubeflow/kfctl/releases/download/v1.0/kfctl_v1.0-0-g94c35cf_linux.tar.gz

다운 받은 tar ball 을 풉니다.

$ tar -xvf kfctl_v1.0-0-g94c35cf_linux.tar.gz

kubflow 배포를 쉽게 하기 위해서, 다음과 같은 환경 변수들을 생성합니다. 환경 변수들의 자세한 내용은 해당 페이지를 확인 하시기 바랍니다.

export PATH=$PATH:"/home/kangwoo/kubeflow"

export CONFIG_URI="https://raw.githubusercontent.com/kubeflow/manifests/v1.0-branch/kfdef/kfctl_istio_dex.v1.0.1.yaml"


export KF_NAME=kf-test

export BASE_DIR=/home/kangwoo/kubeflow
export KF_DIR=${BASE_DIR}/${KF_NAME}

kfctl_existing_arrikto.yaml 설정 파일을 가지고, kubeflow를 배포하겠습니다. 해당 파일에는 다중 유저와 권한/인증 기능을 지원하는 부분이 정의 되어 있습니다.

mkdir -p ${KF_DIR}
cd ${KF_DIR}

# Download the config file and change the default login credentials.
wget -O kfctl_istio_dex.yaml $CONFIG_URI
export CONFIG_FILE=${KF_DIR}/kfctl_istio_dex.yaml

kfctl apply -V -f ${CONFIG_FILE}

kfctl apply 명령어를 실행하면, kubeflow가 설치되기 시작합니다.

다음은 kubeflow 네임스페이스와, istio-system 네임스페이스의 포드를 조회해 본 것입니다.

$ kubectl -n kubeflow get pod
NAME                                                           READY   STATUS      RESTARTS   AGE
admission-webhook-deployment-7b7888fc9b-9dlj9                  1/1     Running     0          2d23h
application-controller-stateful-set-0                          1/1     Running     0          2d23h
argo-ui-7ffb9b6577-xqcxc                                       1/1     Running     0          2d23h
centraldashboard-6944c87dd5-sfqc7                              1/1     Running     0          2d23h
jupyter-web-app-deployment-679d5f5dc4-6278n                    1/1     Running     0          2d23h
katib-controller-7f58569f7d-62pgx                              1/1     Running     1          2d23h
katib-db-manager-54b66f9f9d-wsxv5                              1/1     Running     5          2d23h
katib-mysql-dcf7dcbd5-4fpmj                                    1/1     Running     0          2d23h
katib-ui-6f97756598-dk8fz                                      1/1     Running     0          2d23h
metadata-db-65fb5b695d-b92zm                                   1/1     Running     0          2d23h
metadata-deployment-65ccddfd4c-242t7                           1/1     Running     1          2d23h
metadata-envoy-deployment-7754f56bff-6vftm                     1/1     Running     0          2d23h
metadata-grpc-deployment-7557fdc6bb-n7jd7                      1/1     Running     7          2d23h
metadata-ui-7c85545947-wwkh9                                   1/1     Running     0          2d23h
minio-69b4676bb7-zglc6                                         1/1     Running     0          2d23h
ml-pipeline-5cddb75848-tjbh9                                   1/1     Running     1          2d23h
ml-pipeline-ml-pipeline-visualizationserver-7f6fcb68c8-pw529   1/1     Running     0          2d23h
ml-pipeline-persistenceagent-6ff9fb86dc-ghj74                  1/1     Running     3          2d23h
ml-pipeline-scheduledworkflow-7f84b54646-mztnf                 1/1     Running     0          2d23h
ml-pipeline-ui-6758f58868-qc72b                                1/1     Running     0          2d23h
ml-pipeline-viewer-controller-deployment-745dbb444d-2xwdn      1/1     Running     0          2d23h
mysql-6bcbfbb6b8-6qb6p                                         1/1     Running     0          2d23h
notebook-controller-deployment-54f455c5c9-rpv8s                1/1     Running     0          2d23h
profiles-deployment-6fcb86d54c-9pdrs                           2/2     Running     0          2d23h
pytorch-operator-cf8c5c497-lntsm                               1/1     Running     0          2d23h
seldon-controller-manager-6b4b969447-b4gcj                     1/1     Running     0          2d23h
spark-operatorcrd-cleanup-knfpw                                0/2     Completed   0          2d23h
spark-operatorsparkoperator-76dd5f5688-2689x                   1/1     Running     0          2d23h
spartakus-volunteer-5dc96f4447-6ss4g                           1/1     Running     0          2d23h
tensorboard-5f685f9d79-7d8kp                                   1/1     Running     0          2d23h
tf-job-operator-5fb85c5fb7-vxkgv                               1/1     Running     0          2d23h
workflow-controller-689d6c8846-lmmdz                           1/1     Running     0          2d23h
k -n istio-system get pod
NAME                                                         READY   STATUS      RESTARTS   AGE
authservice-0                                                1/1     Running     0          2d23h
istio-citadel-79b5b568b-kqjw9                                1/1     Running     0          3d
istio-galley-756f5f45c4-kxkhr                                1/1     Running     0          3d
istio-ingressgateway-77f74c944c-z5frv                        1/1     Running     0          3d
istio-nodeagent-478g7                                        1/1     Running     0          3d
istio-nodeagent-rmbc5                                        1/1     Running     0          3d
istio-nodeagent-z49bb                                        1/1     Running     0          3d
istio-pilot-55f7f6f6df-k5mdm                                 2/2     Running     0          3d
istio-policy-76dbd68445-vskkp                                2/2     Running     0          3d
istio-security-post-install-release-1.3-latest-daily-2j5tr   0/1     Completed   0          3d
istio-sidecar-injector-5d9f474dcb-l5qjj                      1/1     Running     0          3d
istio-telemetry-697c8fd794-9hz9q                             2/2     Running     0          3d
prometheus-b845cc6fc-d7cq6                                   1/1     Running     0          3d

kubeflow 접속하기

kubeflow GUI에 접속해 보겠습니다. istio-ingressgateway 를 통해서 접속합니다. 여기서는 편의를 위해서 노드 포트(NodePort)를 사용하겠습니다.

다음은 istio-ingressgateway 서비스를 조회해 본 결과입니다.

$ kubectl -n istio-system get service istio-ingressgateway
NAME                   TYPE       CLUSTER-IP      EXTERNAL-IP   PORT(S)                                                                                                                                      AGE
istio-ingressgateway   NodePort   172.30.86.239   <none>        15020:30113/TCP,80:31380/TCP,443:31390/TCP,31400:31400/TCP,15029:31134/TCP,15030:31251/TCP,15031:32398/TCP,15032:30455/TCP,15443:32764/TCP   3d

서비스 타입이 NodePort 이고, 80번 포트가 31380이라는 노드 포트로 열려있습니다. 브라우저를 실행하고, 해당 포트로 접속해보겠습니다.

기본 설정을 바꾸지 않았다면, 사용자 이름은 admin@kubeflow.org 이고, 비밀번호는 12341234 입니다.

로그인이 성공적으로 되면, 다음과 같이 사용할 네임스페이스를 생성하는 화면이 나옵니다. 원하는 이름을 입력하시면 됩니다. 기본값을 admin 으로 되어있습니다.

네임스페이스가 생성되면, kubeflow 대시보드 화면을 볼 수 있습니다.

참고

PC에 kubeflow 설치하기 – 2부 kubernetes, nvidia device-plugin 설치하기

swap 비활성화 하기

쿠버네티스(kubernetes)를 설치 하기 위해서 swap을 비활성화 합니다.

$ sudo swapoff -a

그리고 재부팅 하였을때 swap이 다시 활성화되는 것을 막기 위해서, /etc/fstab 에 있는 swap 관련 부분을 주석 처리하거나, 제거해 줍니다.

$ sudo vi /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).
#
# <file system> <mount point>   <type>  <options>       <dump>  <pass>
/dev/mapper/ubuntu--vg-root /               ext4    errors=remount-ro 0       1
# /boot/efi was on /dev/nvme0n1p1 during installation
UUID=D21A-9B89  /boot/efi       vfat    umask=0077      0       1
# /dev/mapper/ubuntu--vg-swap_1 none            swap    sw              0       0

iptables 설치하기

iptables을 설치하고, 필요에 따라서 iptables tooling을 legacy 모드로 변경합니다.

# ensure legacy binaries are installed
$ sudo apt-get install -y iptables arptables ebtables

# switch to legacy versions
sudo update-alternatives --set iptables /usr/sbin/iptables-legacy
sudo update-alternatives --set ip6tables /usr/sbin/ip6tables-legacy
sudo update-alternatives --set arptables /usr/sbin/arptables-legacy
sudo update-alternatives --set ebtables /usr/sbin/ebtables-legacy

kubelet, kubeadm, kubectl 설치하기

쿠버네티스 설치에 필요한 kubelet, kubeadm, kubectl을 설치합니다. 버전을 명시해 주지 않으면, 최선 버전으로 설치됩니다. kubeflow 문서에 따르면 현재 권장하는 쿠버네티스 버전은 1.14 입니다. 1.15 버전도 호환이 되기 때문에, 이 글에서는 1.15.10-00 버전으로 설치 하였습니다.

$ sudo apt-get update && sudo apt-get install -y apt-transport-https curl
$ curl -s https://packages.cloud.google.com/apt/doc/apt-key.gpg | sudo apt-key add -
$ cat <<EOF | sudo tee /etc/apt/sources.list.d/kubernetes.list
deb https://apt.kubernetes.io/ kubernetes-xenial main
EOF
$ sudo apt-get update
$ sudo apt-get install -y kubelet=1.15.10-00 kubeadm=1.15.10-00 kubectl=1.15.10-00
$ sudo apt-mark hold kubelet kubeadm kubectl

쿠버네티스 설치하기

kubeadm을 사용해서 쿠버네티스를 설치합니다. 포드 네트워크 애드온을 cilium을 사용할 것이기 때문에 --pod-network-cidr=10.217.0.0/16 옵션을 사용하겠습니다.

다음 명령어를 실행해서 쿠버네티스를 설치합니다.

$ sudo kubeadm init --pod-network-cidr=10.217.0.0/16

설치가 완료되면, kubectl을 사용하기 위해서 관리자 설정 파일을 유저 디렉토리로 복사합니다.

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

쿠버네티스 접속을 테스트 하기 위해서, 다음 명령어를 실행합니다.

$ kubectl cluster-info
Kubernetes master is running at https://192.168.21.36:6443
KubeDNS is running at https://192.168.21.36:6443/api/v1/namespaces/kube-system/services/kube-dns:dns/proxy

To further debug and diagnose cluster problems, use 'kubectl cluster-info dump'.

이제 Cilium을 쿠버네티스에 설치합니다.

$ kubectl create -f https://raw.githubusercontent.com/cilium/cilium/v1.6/install/kubernetes/quick-install.yaml

cilim 포드의 READY가 1/1이 되면, 쿠버네티스 클러스터를 사용할 수 있습니다.

$ kubectl get pods -n kube-system --selector=k8s-app=cilium
NAME           READY   STATUS    RESTARTS   AGE
cilium-k4l5b   1/1     Running   0          70s

Control plane 노드 격리 해제하기

기본적으로 쿠버네티스 클러스터의 컨트롤 플레인(control-plane) 노드에는 보안상의 이유로 노드가 격리되어 있어서, 포드가 스케줄링되지 않습니다. 이 문서에는 1대의 머신만을 사용하기 때문에 노드 격리를 해제하겠습니다.

다음 명령어로 노드 격리를 해제 시킵니다.

$ kubectl taint nodes --all node-role.kubernetes.io/master-
node/mortar untainted

nvidia plugin 설치하기

쿠버네티스에서 GPU를 사용하기 위해서, nvidia k8s-device-plugin 을 설치합니다. 이 문서를 작성하는 시점에서는 1.12가 최신버전이라서 1.12로 설치하겠습니다.

$ kubectl apply -f https://raw.githubusercontent.com/NVIDIA/k8s-device-plugin/v1.12/nvidia-device-plugin.yml

daemonset.extensions/nvidia-device-plugin-daemonset-1.12 created

참고로, 쿠버네티스 1.15 버전을 설치했을 경우에는 문제가 없을 건데, 1.16 버전 이상을 설치 했을 경우, 다음과 같은 에러가 발생할 것입니다. 자세한 사항은 해당 페이지를 참고 바랍니다.

error: unable to recognize "https://raw.githubusercontent.com/NVIDIA/k8s-device-plugin/v1.12/nvidia-device-plugin.yml": no matches for kind "DaemonSet" in version "extensions/v1beta1"

쿠버네티스 버전이 올라가면서, Daemonsetextensions/v1beta1 버전을 더 이상 지원하지 않아서 입니다. 버전을 apps/v1 으로 변경하고 selector를 추가한 후, k8s-device-plugin 을 다시 설치합니다.

다음은 변경한 1.17 버전에 맞게 변경한 메니페스트 파일입니다.

cat <<EOF | kubectl apply -f -
apiVersion: apps/v1
kind: DaemonSet
metadata:
  name: nvidia-device-plugin-daemonset-1.12
  namespace: kube-system
spec:
  updateStrategy:
    type: RollingUpdate
  selector:
    matchLabels:
      name: nvidia-device-plugin-ds
  template:
    metadata:
      # Mark this pod as a critical add-on; when enabled, the critical add-on scheduler
      # reserves resources for critical add-on pods so that they can be rescheduled after
      # a failure.  This annotation works in tandem with the toleration below.
      annotations:
        scheduler.alpha.kubernetes.io/critical-pod: ""
      labels:
        name: nvidia-device-plugin-ds
    spec:
      tolerations:
      # Allow this pod to be rescheduled while the node is in "critical add-ons only" mode.
      # This, along with the annotation above marks this pod as a critical add-on.
      - key: CriticalAddonsOnly
        operator: Exists
      - key: nvidia.com/gpu
        operator: Exists
        effect: NoSchedule
      containers:
      - image: nvidia/k8s-device-plugin:1.11
        name: nvidia-device-plugin-ctr
        securityContext:
          allowPrivilegeEscalation: false
          capabilities:
            drop: ["ALL"]
        volumeMounts:
          - name: device-plugin
            mountPath: /var/lib/kubelet/device-plugins
      volumes:
        - name: device-plugin
          hostPath:
            path: /var/lib/kubelet/device-plugins
EOF

device-plugin 포드가 정상적으로 작동했는지 확인해 봅니다.

$ kubectl -n kube-system get pod -l name=nvidia-device-plugin-ds
NAME                                        READY   STATUS    RESTARTS   AGE
nvidia-device-plugin-daemonset-1.12-4kt95   1/1     Running   0          24s

$ kubectl -n kube-system logs  -l name=nvidia-device-plugin-ds
2020/02/09 09:05:10 Loading NVML
2020/02/09 09:05:10 Fetching devices.
2020/02/09 09:05:10 Starting FS watcher.
2020/02/09 09:05:10 Starting OS watcher.
2020/02/09 09:05:10 Starting to serve on /var/lib/kubelet/device-plugins/nvidia.sock
2020/02/09 09:05:10 Registered device plugin with Kubelet

GPU 테스트 하기

다음은 텐서플로를 이용해서, GPU를 테스트 해 보는 예제입니다. 단순한 테스트이기 때문에 무시하고 넘어가도 됩니다.

cat <<EOF | kubectl apply -f -
apiVersion: apps/v1
kind: Deployment
metadata:
  labels:
    app: tf-gpu-jupyter
  name: tf-gpu-jupyter
  namespace: default
spec:
  replicas: 1
  selector:
    matchLabels:
      app: tf-gpu-jupyter
  template:
    metadata:
      labels:
        app: tf-gpu-jupyter
    spec:
      containers:
      - image: tensorflow/tensorflow:2.1.0-gpu-py3-jupyter
        imagePullPolicy: IfNotPresent
        name: tf-gpu-jupyter
        ports:
        - containerPort: 8888
          protocol: TCP
        resources:
          limits:
            nvidia.com/gpu: "1"
EOF

tf-gpu-jupyter 라는 이름을 가진 GPU를 사용할 수 있는 텐서플로 주피터(jupyter)를 생성하였습니다. 포드가 정상적으로 작동했는지 확인해 봅니다.

$ kubectl get pod -l app=tf-gpu-jupyter
NAME                              READY   STATUS    RESTARTS   AGE
tf-gpu-jupyter-66f89b64cd-vrllc   1/1     Running   0          5m58s

주피터 접속에 필요한 토큰 정보를 얻기 위해서 로그를 조회해 보겠습니다.

$ kubectl logs -l app=tf-gpu-jupyter
[I 09:15:25.009 NotebookApp] Use Control-C to stop this server and shut down all kernels (twice to skip confirmation).
[C 09:15:25.012 NotebookApp] 
    
    To access the notebook, open this file in a browser:
        file:///root/.local/share/jupyter/runtime/nbserver-1-open.html
    Or copy and paste one of these URLs:
        http://tf-gpu-jupyter-66f89b64cd-vrllc:8888/?token=6527214998b8d895f6f14a8901a39ba6d8420c43e68f6919
     or http://127.0.0.1:8888/?token=6527214998b8d895f6f14a8901a39ba6d8420c43e68f6919
[I 09:17:20.916 NotebookApp] 302 GET / (127.0.0.1) 0.53ms
[I 09:17:20.926 NotebookApp] 302 GET /tree? (127.0.0.1) 0.65ms

포드에 접속하기 위해서 port-forward를 사용하겠습니다.

$ kubectl port-forward pod/tf-gpu-jupyter-66f89b64cd-vrllc 8888:8888
Forwarding from 127.0.0.1:8888 -> 8888
Forwarding from [::1]:8888 -> 8888

kubectl delete deployment tf-gpu-jupyter
deployment.apps "tf-gpu-jupyter" deleted

포트 포워드가 활성화 되면, 브라우저에서 주피터 주소를 입력합니다. 포드 로그에서 봤던 주소를 입력하면 됩니다. 이 예제에서 주소는 http://127.0.0.1:8888/?token=6527214998b8d895f6f14a8901a39ba6d8420c43e68f6919 입니다.

정상적으로 접속이 되면, 다음과 같은 화면을 보실 수 있습니다.

사용 가능한 GPU 갯수를 확인하겠습니다. 파이썬3 노트북을 생성한 후, 다음 코드를 입력합니다.

from __future__ import absolute_import, division, print_function, unicode_literals

import tensorflow as tf
print("Num GPUs Available: ", len(tf.config.experimental.list_physical_devices('GPU')))

코드를 실행하면 다음과 같이 사용 가능한 GPU 개수가 출력될 것입니다.

확인을 다 했으면, 브라우저를 종료하고, 자원 낭비를 막기 위해서 디플로이먼트를 삭제하도록 하겠습니다. 우리의 GPU는 소중하니까요.

$ kubectl delete deploy tf-gpu-jupyter

참고

  • https://kubernetes.io/docs/setup/production-environment/tools/kubeadm/install-kubeadm/
  • https://kubernetes.io/docs/setup/production-environment/tools/kubeadm/create-cluster-kubeadm/
  • https://github.com/NVIDIA/k8s-device-plugin
  • https://hub.docker.com/r/tensorflow/tensorflow/
  • https://www.tensorflow.org/guide/gpu

PC에 kubeflow 설치하기 – 1부 nvidia 드라이버, docker 설치하기

이 글은 지적 유희를 위해서 작성하였습니다. kubeflow 자체가 목적이신 분들은, miniKFGCP를 사용하시길 추천드립니다.

시스템 사항

다음은 이 글에서 사용한 PC의 사양입니다. kubeflow를 원활하게 설치하기 위해서는 램이 16GB 이상, CPU는 4코어 이상을 추천합니다. GPU를 사용하기 위해서 nvidia 그래픽 카드도 필요합니다.

프로세서amd 라이젠 5 3600
RAM32GB
그래픽 카드RTX-2060
스토리지 공간 다다익선

설치 목록

  • ubuntu 18.04 LTS
  • nvidia driver 435
  • docker CE 18.9
  • nvidia-docker2
  • kubernetes 1.15.10
  • cilium 1.6
  • nvidia-device-plugin-daemonset 1.12
  • kubeflow 1.0RC4 with istio 1.3

전체 목록

우분투 설치하기

우분투는 데스크탑 18.04 LTS 버전을 사용합니다. 설치 방법은 다른곳에 많이 나와있기 때문에 생략합니다. 다만 nvidia 그래픽 카드를 사용할 경우 문제가 생기기 때문에, 그 부분만 다루겠습니다.

nvidia driver 설치하기

우분투 18.04 환경에서 RTX-2060을 사용할 경우 nouveau 문제가 있습니다. RTX-2060가 장착되어 있는 장비에서 우분투를 설치할 경우 nouveau로 자동 설정되기 때문이다. 그래서 nouveau 를 제거하는 작업이 필요합니다.

우분투 설치 화면이 깨져서 보이지 않는다면, 설치 전에 nomodset 옵션을 추가해줘야 정상적인 화면을 볼 수 있습니다.

우분투를 설치 하기 전 GRUB 메뉴 화면에서 e 키를 누룹니다.

e 키를 누르면, 다음과 같이 파라메터를 편집할 수 있는 화면이 나옵니다.

quiet splash 뒤에 nomodeset 을 추가해 줍니다. 그리고 F10 키를 눌러서 부팅 합니다.

정상적으로 화면이 보일 것입니다. 우분투를 설치하는 나머지 과정은 생략하겠습니다.

nouveau 설치 확인 하기

우분투가 정상적으로 설치되었다면, 재부팅 후 nouveau 확인 작업을 합니다. 시스템이 다시 시작되면, 앞서 한 것과 동일한 방법으로, nomodset 옵션을 추가해줘야 정상적인 화면을 볼 수 있을것입니다.

부팅이 완료되면 터미널을 열어서 작업을 시작합니다.

터미널에서 다음 명령어를 실행한 후, 결과가 보이면 nouveau가 설치되어 있는 것입니다. nvidia 드라이버 설치를 위해서 제거해야 합니다.

$ lsmod | grep nouveau
nouveau              1863680  0
mxm_wmi                16384  1 nouveau
video                  49152  1 nouveau
i2c_algo_bit           16384  1 nouveau
ttm                   102400  1 nouveau
drm_kms_helper        180224  1 nouveau
drm                   479232  3 drm_kms_helper,ttm,nouveau
wmi                    28672  3 wmi_bmof,mxm_wmi,nouveau

/etc/modprobe.d/ 경로에 blacklist 파일을 생성합니다.

$ sudo vi /etc/modprobe.d/blacklist-nouveau.conf

blacklist nouveau
options nouveau modset=0

다음 명령어를 실행 한 후, 재부팅 합니다.

$ sudo update-initramfs -u
$ sudo service gdm stop

Nvidia 드라이버 설치하기

컨테이너(Container)를 이용해서 GPU를 사용할 예정이기 때문에, Nvidia 드라이버가 설치합니다.

$ sudo add-apt-repository ppa:graphics-drivers/ppa
$ sudo apt-get update

$ sudo apt-get install nvidia-driver-435
$ sudo reboot

재부팅 후, nvidia-smi 명령어를 실행해서, 드라이버가 정상적으로 설치되어 있는지 확인해 볼 수 있습니다.

$ nvidia-smi
Sun Feb 16 17:26:22 2020       
+-----------------------------------------------------------------------------+
| NVIDIA-SMI 435.21       Driver Version: 435.21       CUDA Version: 10.1     |
|-------------------------------+----------------------+----------------------+
| GPU  Name        Persistence-M| Bus-Id        Disp.A | Volatile Uncorr. ECC |
| Fan  Temp  Perf  Pwr:Usage/Cap|         Memory-Usage | GPU-Util  Compute M. |
|===============================+======================+======================|
|   0  GeForce RTX 2060    Off  | 00000000:26:00.0  On |                  N/A |
| 32%   45C    P8     9W / 190W |    189MiB /  5931MiB |      1%      Default |
+-------------------------------+----------------------+----------------------+
                                                                               
+-----------------------------------------------------------------------------+
| Processes:                                                       GPU Memory |
|  GPU       PID   Type   Process name                             Usage      |
|=============================================================================|
|    0       975      G   /usr/lib/xorg/Xorg                            96MiB |
|    0      1123      G   /usr/bin/gnome-shell                          91MiB |
+-----------------------------------------------------------------------------+

docker 설치하기

apthttps저장소를 사용할 수 있도록 패키지를 추가합니다.

$ sudo apt-get install \
    apt-transport-https \
    ca-certificates \
    curl \
    gnupg-agent \
    software-properties-common

도커의 GPG 키를 추가합니다.

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

저장소를 추가합니다

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

apt 패키지의 인덱스를 업데이트합니다.

$ sudo apt-get update

이 문서를 작성할 당시(2020-02-08)에 도커의 최선 버전은 19.03이였습니다. 19.03 버전 부터 GPU 관련한 내용이 변경되었습니다. 쿠버네티스상에서 GPU 관련 작업을 하라면, k8s-device-plugin이 필요한데, 아직 19.03 버전을 지원하지 않는 것 같습니다. 그래서 18.9 버전을 설치하였습니다.

도커 엔진 18.9 버전을 설치합니다.

$ sudo apt-get install docker-ce=5:18.09.9~3-0~ubuntu-bionic docker-ce-cli=5:18.09.9~3-0~ubuntu-bionic containerd.io

$ sudo apt-mark hold docker-ce docker-ce-cli

설치 가능한 도커 버전을 보려면 다음 명령어를 실행하면 됩니다.

$ apt-cache madison docker-ce
 docker-ce | 5:19.03.5~3-0~ubuntu-bionic | https://download.docker.com/linux/ubuntu bionic/stable amd64 Packages
 docker-ce | 5:19.03.4~3-0~ubuntu-bionic | https://download.docker.com/linux/ubuntu bionic/stable amd64 Packages
 docker-ce | 5:19.03.3~3-0~ubuntu-bionic | https://download.docker.com/linux/ubuntu bionic/stable amd64 Packages
 docker-ce | 5:19.03.2~3-0~ubuntu-bionic | https://download.docker.com/linux/ubuntu bionic/stable amd64 Packages
 docker-ce | 5:19.03.1~3-0~ubuntu-bionic | https://download.docker.com/linux/ubuntu bionic/stable amd64 Packages
 docker-ce | 5:19.03.0~3-0~ubuntu-bionic | https://download.docker.com/linux/ubuntu bionic/stable amd64 Packages
 docker-ce | 5:18.09.9~3-0~ubuntu-bionic | https://download.docker.com/linux/ubuntu bionic/stable amd64 Packages
 docker-ce | 5:18.09.8~3-0~ubuntu-bionic | https://download.docker.com/linux/ubuntu bionic/stable amd64 Packages
 docker-ce | 5:18.09.7~3-0~ubuntu-bionic | https://download.docker.com/linux/ubuntu bionic/stable amd64 Packages
...

도커가 정상적으로 설치되었는지 확인해 보기 위해서 hello-world 이미지를 실행합니다.

$ sudo docker run hello-world
Unable to find image 'hello-world:latest' locally
latest: Pulling from library/hello-world
1b930d010525: Pull complete 
Digest: sha256:9572f7cdcee8591948c2963463447a53466950b3fc15a247fcad1917ca215a2f
Status: Downloaded newer image for hello-world:latest

Hello from Docker!
This message shows that your installation appears to be working correctly.
...

nvidia-docker2 설치하기

컨테이너에서 GPU를 사용하기 위해서 nvidia-docker2 을 설치합니다. 그리고 도커를 재시작 합니다.

# Add the package repositories
$ 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 nvidia-docker2
$ sudo systemctl restart docker

nvidia-docker2 가 정상적으로 설치되었는지 확인해 보기 위해서, 다음 명령어를 실행합니다.

$ sudo docker run --runtime nvidia nvidia/cuda:10.0-base nvidia-smi
Sat Feb  8 11:19:15 2020       
+-----------------------------------------------------------------------------+
| NVIDIA-SMI 435.21       Driver Version: 435.21       CUDA Version: 10.1     |
|-------------------------------+----------------------+----------------------+
| GPU  Name        Persistence-M| Bus-Id        Disp.A | Volatile Uncorr. ECC |
| Fan  Temp  Perf  Pwr:Usage/Cap|         Memory-Usage | GPU-Util  Compute M. |
|===============================+======================+======================|
|   0  GeForce RTX 2060    Off  | 00000000:26:00.0  On |                  N/A |
| 29%   40C    P8     9W / 190W |    229MiB /  5931MiB |      1%      Default |
+-------------------------------+----------------------+----------------------+
                                                                               
+-----------------------------------------------------------------------------+
| Processes:                                                       GPU Memory |
|  GPU       PID   Type   Process name                             Usage      |
|=============================================================================|
+-----------------------------------------------------------------------------+

도커의 기본 런타임을 변경해 줍니다. /etc/docker/daemon.json 파일이 생성되었을 것입니다. 해당 파일을 열여서 "default-runtime": "nvidia"을 추가해주면 됩니다.

$ sudo vi /etc/docker/daemon.json

    {
      "default-runtime": "nvidia", 
      "runtimes": {
        "nvidia": {
          "path": "nvidia-container-runtime",
          "runtimeArgs": []
        }
      }
    }

파일을 수정한 후, 도커를 재시작합니다.

$ sudo systemctl restart docker

참고

  • https://docs.docker.com/install/linux/docker-ce/ubuntu/
  • https://github.com/NVIDIA/nvidia-docker

Prometheus를 사용해서 NVIDIA GPU 모니터링 하기

Node의 GPU 모니터링 하기

prometheus를 사용해서 노드들의 매트틱을 수집하고 있다면, 아마 node-exporter를 사용하고 있을 것이다. NVIDIA에서는 dcgm-exporter라는 GPU 매트릭 출력용 이미지를 제공하고 있다. 이 dcgm-exporter과 node-exporter를 결합하여 사용하면, GPU 매트릭을 수집할 수 있다.

dcgm-exporter

dcgm(Data Center GPU Manager) exporter는 nv-hostenging을 시작해서, 매초마다 GPU 매트릭을 읽어서 prometheus 형식으로 출력해주는 간단한 쉘 스크립트이다.

Node 설정하기

우선 일반 노드와 GPU 노드를 분리하기 위해서 taint와 label을 설정해주었다. 대부분 node-exporter를 실행하기 위해서 DaemonSet을 사용했을 것이다.

일반 노드에서는 node-exporter만을 실행하기 위해서 taint nvidia.com/gpu=:NoSchedule를 사용하였고, GPU 노드에서는 node-exporter + dcgm-exporter를 실행하기 위해서 label hardware-type=NVIDIAGPU를 사용하였다.

nvidia.com/brand는 현재로는 별의미가 없지만 붙여주었다.

kubectl taint nodes ${node} nvidia.com/gpu=:NoSchedule

kubectl label nodes ${node} "nvidia.com/brand=${label}"
kubectl label nodes ${node} hardware-type=NVIDIAGPU

기존 node-exporter에 dcgm-exporter 추가하기

dcgm-exporter가 GPU 매트릭을 파일로 남기고, prometheus는 그 파일을 읽어서 GPU 매트릭을 같이 출력한다.

GPU 노드용

apiVersion: apps/v1
kind: DaemonSet
metadata:
  labels:
    app.kubernetes.io/name: node-exporter
    app.kubernetes.io/instance: gpu-node-exporter
    app.kubernetes.io/part-of: prometheus
    app.kubernetes.io/managed-by: argo-system
  name: prometheus-gpu-node-exporter
  namespace: argo-system
spec:
  revisionHistoryLimit: 10
  selector:
    matchLabels:
      app.kubernetes.io/name: node-exporter
      app.kubernetes.io/instance: gpu-node-exporter
      app.kubernetes.io/part-of: prometheus
      app.kubernetes.io/managed-by: argo-system
  template:
    metadata:
      labels:
        app.kubernetes.io/name: node-exporter
        app.kubernetes.io/instance: gpu-node-exporter
        app.kubernetes.io/part-of: prometheus
        app.kubernetes.io/managed-by: argo-system
    spec:
      nodeSelector:
        hardware-type: NVIDIAGPU
      containers:
      - args:
        - --path.procfs=/host/proc
        - --path.sysfs=/host/sys
        - "--collector.textfile.directory=/run/prometheus"
        image: prom/node-exporter:v0.18.1
        imagePullPolicy: IfNotPresent
        name: prometheus-node-exporter
        ports:
        - containerPort: 9100
          hostPort: 9100
          name: metrics
          protocol: TCP
        resources:
          limits:
            cpu: 500m
            memory: 200Mi
          requests:
            cpu: 100m
            memory: 100Mi
        terminationMessagePath: /dev/termination-log
        terminationMessagePolicy: File
        volumeMounts:
        - mountPath: /host/proc
          name: proc
          readOnly: true
        - mountPath: /host/sys
          name: sys
          readOnly: true
        - name: collector-textfiles
          readOnly: true
          mountPath: /run/prometheus
      - image: nvidia/dcgm-exporter:1.4.6
        name: nvidia-dcgm-exporter
        securityContext:
          runAsNonRoot: false
          runAsUser: 0
        volumeMounts:
          - name: collector-textfiles
            mountPath: /run/prometheus
      dnsPolicy: ClusterFirst
      hostNetwork: true
      hostPID: true
      restartPolicy: Always
      serviceAccount: prometheus-node-exporter
      serviceAccountName: prometheus-node-exporter
      terminationGracePeriodSeconds: 30
      tolerations:
      - effect: NoSchedule
        key: node-role.kubernetes.io/master
      - effect: NoSchedule
        key: node-role.kubernetes.io/ingress
        operator: Exists
      - effect: NoSchedule
        key: nvidia.com/gpu
        operator: Exists
      volumes:
      - hostPath:
          path: /proc
          type: ""
        name: proc
      - hostPath:
          path: /sys
          type: ""
        name: sys
      - name: collector-textfiles
        emptyDir:
          medium: Memory
      - name: pod-gpu-resources
        hostPath:
          path: /var/lib/kubelet/pod-resources
  updateStrategy:
    type: OnDelete

일반 노드용

apiVersion: apps/v1
kind: DaemonSet
metadata:
  labels:
    app.kubernetes.io/name: node-exporter
    app.kubernetes.io/instance: node-exporter
    app.kubernetes.io/part-of: prometheus
    app.kubernetes.io/managed-by: argo-system
  name: prometheus-node-exporter
  namespace: argo-system
spec:
  revisionHistoryLimit: 10
  selector:
    matchLabels:
      app.kubernetes.io/name: node-exporter
      app.kubernetes.io/instance: node-exporter
      app.kubernetes.io/part-of: prometheus
      app.kubernetes.io/managed-by: argo-system
  template:
    metadata:
      labels:
        app.kubernetes.io/name: node-exporter
        app.kubernetes.io/instance: node-exporter
        app.kubernetes.io/part-of: prometheus
        app.kubernetes.io/managed-by: argo-system
    spec:
      containers:
      - args:
        - --path.procfs=/host/proc
        - --path.sysfs=/host/sys
        image: prom/node-exporter:v0.18.1
        imagePullPolicy: IfNotPresent
        name: prometheus-node-exporter
        ports:
        - containerPort: 9100
          hostPort: 9100
          name: metrics
          protocol: TCP
        resources:
          limits:
            cpu: 500m
            memory: 200Mi
          requests:
            cpu: 100m
            memory: 100Mi
        terminationMessagePath: /dev/termination-log
        terminationMessagePolicy: File
        volumeMounts:
        - mountPath: /host/proc
          name: proc
          readOnly: true
        - mountPath: /host/sys
          name: sys
          readOnly: true
      dnsPolicy: ClusterFirst
      hostNetwork: true
      hostPID: true
      restartPolicy: Always
      serviceAccount: prometheus-node-exporter
      serviceAccountName: prometheus-node-exporter
      terminationGracePeriodSeconds: 30
      tolerations:
      - effect: NoSchedule
        key: node-role.kubernetes.io/master
      - effect: NoSchedule
        key: node-role.kubernetes.io/ingress
        operator: Exists
      volumes:
      - hostPath:
          path: /proc
          type: ""
        name: proc
      - hostPath:
          path: /sys
          type: ""
        name: sys
  updateStrategy:
    type: OnDelete

Kubernetes 에서 NVIDIA GPU container 사용하기

기본적인 docker를 이용하면, GPU 자원을 사용할 수 없습니다. 쿠버네티스 환경에서 NVIDIA GPU를 사용하기 위해서는 nvidia-docker를 이용해야 합니다.

nvidia-docker 설치하기

nvidia-smi

repository 추가

드라이버가 설치되어 있다면, nvidia-docker 설치를 위한 repository를 추가해 줍니다.

Debian-based distributions

curl -s -L https://nvidia.github.io/nvidia-docker/gpgkey | \
  sudo apt-key add -
distribution=$(. /etc/os-release;echo $ID$VERSION_ID)
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

RHEL-based distributions

distribution=$(. /etc/os-release;echo $ID$VERSION_ID)
curl -s -L https://nvidia.github.io/nvidia-docker/$distribution/nvidia-docker.repo | \
  sudo tee /etc/yum.repos.d/nvidia-docker.repo

nvidia-docker 설치

Debian-based distributions

sudo apt-get install -y nvidia-docker2
sudo pkill -SIGHUP dockerd

RHEL-based distributions

sudo yum install -y nvidia-docker2
sudo pkill -SIGHUP dockerd

nvidia-docker 확인

설치가 끌나면, 아래처럼 --runtime=nvidia 플래그를 이용해서 GPU를 사용할 수 있습니다. 아래 명령어를 실행하면 GPU를 사용할 수 있는 상태인지 확인할 수 있습니다.

$ docker run --runtime=nvidia --rm nvidia/cuda nvidia-smi
Mon Jul 15 12:45:56 2019
+-----------------------------------------------------------------------------+
| NVIDIA-SMI 418.67       Driver Version: 418.67       CUDA Version: 10.1     |
|-------------------------------+----------------------+----------------------+
| GPU  Name        Persistence-M| Bus-Id        Disp.A | Volatile Uncorr. ECC |
| Fan  Temp  Perf  Pwr:Usage/Cap|         Memory-Usage | GPU-Util  Compute M. |
|===============================+======================+======================|
|   0  Tesla M40           Off  | 00000000:02:00.0 Off |                    0 |
| N/A   35C    P0    63W / 250W |    115MiB / 11448MiB |      0%      Default |
+-------------------------------+----------------------+----------------------+
|   1  Tesla M40           Off  | 00000000:82:00.0 Off |                    0 |
| N/A   26C    P8    16W / 250W |      0MiB / 11448MiB |      0%      Default |
+-------------------------------+----------------------+----------------------+
|   2  Tesla M40           Off  | 00000000:85:00.0 Off |                    0 |
| N/A   24C    P8    16W / 250W |      0MiB / 11448MiB |      0%      Default |
+-------------------------------+----------------------+----------------------+
|   3  Tesla M40           Off  | 00000000:86:00.0 Off |                    0 |
| N/A   24C    P8    16W / 250W |     11MiB / 11448MiB |      0%      Default |
+-------------------------------+----------------------+----------------------+

+-----------------------------------------------------------------------------+
| Processes:                                                       GPU Memory |
|  GPU       PID   Type   Process name                             Usage      |
|=============================================================================|
|    0     21141      C   python                                       104MiB |
+-----------------------------------------------------------------------------+

쿠버네티스에서 nvidia-docker 사용하기

쿠버네티스 환경에서 nvidia-docker를 사용하려면, docker의 기본 런타임(runtime)을 변경하고, NVIDIA 디바이스 플러그인을 설치해줘야 합니다.

docker 기본 runtime 변경

정상적으로 nvidia-docker2가 설치되었다면, /etc/docker/daemon.json 파일이 생성되었을 것입니다. 해당 파일을 열여서 "default-runtime": "nvidia"을 추가해주면 됩니다.

{
  "default-runtime": "nvidia", 
  "runtimes": {
    "nvidia": {
      "path": "nvidia-container-runtime",
      "runtimeArgs": []
    }
  }
}

파일을 수정한 후, docker daemon을 재시작 하여야합니다.

sudo systemctl restart docker

kubernetes-device-plugin 설치

kubectl을 이용해서 nvidia-device-plugin을 설치해 줍니다.

kubectl apply -f https://raw.githubusercontent.com/NVIDIA/k8s-device-plugin/v1.11/nvidia-device-plugin.yml

참고) nvidia-device-plugin.yml

apiVersion: extensions/v1beta1
kind: DaemonSet
metadata:
  name: nvidia-device-plugin-daemonset
  namespace: kube-system
spec:
  updateStrategy:
    type: RollingUpdate
  template:
    metadata:
      # Mark this pod as a critical add-on; when enabled, the critical add-on scheduler
      # reserves resources for critical add-on pods so that they can be rescheduled after
      # a failure.  This annotation works in tandem with the toleration below.
      annotations:
        scheduler.alpha.kubernetes.io/critical-pod: ""
      labels:
        name: nvidia-device-plugin-ds
    spec:
      tolerations:
      # Allow this pod to be rescheduled while the node is in "critical add-ons only" mode.
      # This, along with the annotation above marks this pod as a critical add-on.
      - key: CriticalAddonsOnly
        operator: Exists
      - key: nvidia.com/gpu
        operator: Exists
        effect: NoSchedule
      containers:
      - image: nvidia/k8s-device-plugin:1.11
        name: nvidia-device-plugin-ctr
        securityContext:
          allowPrivilegeEscalation: false
          capabilities:
            drop: ["ALL"]
        volumeMounts:
          - name: device-plugin
            mountPath: /var/lib/kubelet/device-plugins
      volumes:
        - name: device-plugin
          hostPath:
            path: /var/lib/kubelet/device-plugins

참고 : Feature Gates

nvidia-device-plugin을 사용하려면, 해당 노드 kubelet의 DevicePlugins이 “true”로 설정되어 있어야한다. 쿠버네티스 1.10 이상에서는 기본값이 “true”이기 때문에 별도의 설정이 필요없으나, 혹시라도 1.8이나 1.9를 사용한다면, KUBELET_EXTRA_ARGS=--feature-gates=DevicePlugins=true 로 값을 설정해주어야한다.

  • https://kubernetes.io/docs/reference/command-line-tools-reference/feature-gates/

GPU 자원(Resource) 이용

GPU 자원을 이용하려면, 리소스 요구사항에 nvidia.com/gpu을 사용하면 됩니다.

apiVersion: v1
kind: Pod
metadata:
  name: gpu-pod
spec:
  containers:
    - name: cuda-container
      image: nvidia/cuda:9.0-devel
      resources:
        limits:
          nvidia.com/gpu: 1 # requesting 1 GPUs

참고 문서