잠시 빌려쓰고, 낡아지면 버려지는 이 한몸. 빈손으로 태어나 결국 빈손으로 털고 돌아가는, 이 인생의 고갯길은, 그 어떤것도 내 것이 될 수 없고, 누구의 것도 될 수 없는, 구름과 바람같은 덧없는 인생살이인데, 하물며 이 마음은 오죽하랴. 그냥 잠시 허허 웃고 살다가리
# This file is generated from information provided by the datasource. Changes
# to it will not persist across an instance reboot. To disable cloud-init's
# network configuration capabilities, write a file
# /etc/cloud/cloud.cfg.d/99-disable-network-config.cfg with the following:
# network: {config: disabled}
network:
ethernets: {}
version: 2
현재 설정된 네트워크 인터페이스가 없기 때문에, ethernets 필드에 아무런 값이 없습니다.
/etc/netplan/*.yaml 파일에 네트워크 설정을 추가합니다.
sudo vi /etc/netplan/*.yaml
ifconfig -a 명령어를 통해서 확인한 네트워크 인터페이스 이름을 ethernets 필드에 추가하고, 설정값을 지정해줍니다. 아래 예제에서는 dhcp 를 사용하기 때문에, IP 직접 명시하지 않았습니다.
# This file is generated from information provided by the datasource. Changes
# to it will not persist across an instance reboot. To disable cloud-init's
# network configuration capabilities, write a file
# /etc/cloud/cloud.cfg.d/99-disable-network-config.cfg with the following:
# network: {config: disabled}
network:
ethernets:
eth0:
addresses: []
dhcp4: true
version: 2
만약 고정 IP를 사용한다면 아래처럼 설정할 수 있습니다.
# This file is generated from information provided by the datasource. Changes
# to it will not persist across an instance reboot. To disable cloud-init's
# network configuration capabilities, write a file
# /etc/cloud/cloud.cfg.d/99-disable-network-config.cfg with the following:
# network: {config: disabled}
network:
ethernets:
eth0:
addresses: [192.168.21.100/24]
gateway4: 192.168.56.1
nameservers:
addresses: [8.8.8.8,8.8.4.4]
dhcp4: no
dhcp6: no
version: 2
kubeflow에서는 인증/권한 기능을 위해서 istio 를 사용합니다. 그래서 istio-system 이라는 네임스페이스에 istio 관련 컴포넌트가 설치됩니다. 그 중에 하나인 istio-ingressgateway 포드의 내용을 보면 다음과 같은 부분을 발견할 수 있습니다.
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 스토리지 클래스를 기본 클래스로 설정해야합니다..
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 으로 되어있습니다.
그리고 재부팅 하였을때 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 모드로 변경합니다.
쿠버네티스 설치에 필요한 kubelet, kubeadm, kubectl을 설치합니다. 버전을 명시해 주지 않으면, 최선 버전으로 설치됩니다. kubeflow 문서에 따르면 현재 권장하는 쿠버네티스 버전은 1.14 입니다. 1.15 버전도 호환이 되기 때문에, 이 글에서는 1.15.10-00 버전으로 설치 하였습니다.
$ 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'.
쿠버네티스에서 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"
쿠버네티스 버전이 올라가면서, Daemonset의 extensions/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를 테스트 해 보는 예제입니다. 단순한 테스트이기 때문에 무시하고 넘어가도 됩니다.
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
포트 포워드가 활성화 되면, 브라우저에서 주피터 주소를 입력합니다. 포드 로그에서 봤던 주소를 입력하면 됩니다. 이 예제에서 주소는 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는 소중하니까요.
우분투는 데스크탑 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 드라이버가 설치합니다.
이 문서를 작성할 당시(2020-02-08)에 도커의 최선 버전은 19.03이였습니다. 19.03 버전 부터 GPU 관련한 내용이 변경되었습니다. 쿠버네티스상에서 GPU 관련 작업을 하라면, k8s-device-plugin이 필요한데, 아직 19.03 버전을 지원하지 않는 것 같습니다. 그래서 18.9 버전을 설치하였습니다.
도커가 정상적으로 설치되었는지 확인해 보기 위해서 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 을 설치합니다. 그리고 도커를 재시작 합니다.