쿠버네티스 메트릭 서버(metrics-server) 설치하기

쿠버네티스 v1.11부터 heapster가 deprecated 되었습니다 (자세한 내용은 문서를 참고 바랍니다.) 그래서 HPA(horizontal pod autoscaler)나 kubectl top 명령어를 사용하라면 metrics-server를 사용해야 합니다.

Metrics Server 란?

Metrics Server는 클래스터 전체의 리소스 사용 데이터를 어그리게이션합니다. 각 노드에 설치된 kublet을 통해서 노드나 컨테인너의 CPU나 메모리 사용량 같은 메트릭을 수집합니다.

설치 방법

요구 사항

Metrics Server를 배포하려면, 쿠버네티스 클러스터에 어그리게이션 레이어가 활성화되어 있어야합니다. 대부분은 기본적으로 활성화되어 있습니다. 혹시 직접 활성화해야하는 경우라면, 아래 링크를 참조하세요. https://kubernetes.io/docs/tasks/access-kubernetes-api/configure-aggregation-layer

설치

Metrics Server git 저장소(https://github.com/kubernetes-incubator/metrics-server)를 복제(clone)하고, 다음과 같이 설치하세요.

git clone https://github.com/kubernetes-incubator/metrics-server.git
cd metrics-server
kubectl apply -f deploy/1.8+/

kubectl을 이용해서 적용하면, v1beta1.metrics.k8s.io 라는 apiservce가 생성되고, metrics-server 라는 디플로이먼트와 서비스가 생성됩니다.

설치가 잘 진행되었다면, 다음과 같이 apiservice를 확인할 수 있습니다.

kubectl get apiservices | grep metrics

v1beta1.metrics.k8s.io                 2019-05-31T06:24:16Z

그리고 디플로이먼트와 서비스도 확인할 수 있습니다

kubectl -n kube-system get deploy,svc | grep metrics-server

deployment.extensions/metrics-server               1         1         1            1           1h
service/metrics-server               ClusterIP   10.96.106.172   <none>        443/TCP         1h

kubectl top node 명령어를 사용하면, 노드의 사용현황을 볼 수 있습니다.

$ kubectl top node
NAME                           CPU(cores)   CPU%      MEMORY(bytes)   MEMORY%
kube-node-001                  9736m        24%       99265Mi         38%
kube-node-002                  12060m       30%       115793Mi        44%
kube-node-003                  12349m       30%       117894Mi        45%
kube-master-001                248m         0%        20110Mi         7%
kube-master-002                289m         0%        7035Mi          2%
kube-master-003                268m         0%        7087Mi          2%

Troubleshooting

metrics-server 의 포드에 다음과 같은 에러 로그가 있을 경우, 파라메터를 추가해야합니다.

E0531 08:27:57.249840       1 manager.go:111] unable to fully collect metrics: [unable to fully scrape metrics from source kubelet_summary:kube-xxxx: unable to fetch metrics from Kubelet kube-xxx (kube-xxx): Get https://kube-xxx:10250/stats/summary/: dial tcp: lookup kube-xxx on xx.xx.xx.xx:x: no such host]

E0531 08:34:42.750007       1 manager.go:111] unable to fully collect metrics: [unable to fully scrape metrics from source kubelet_summary:kube-xxxx: unable to fetch metrics from Kubelet kube-xxx (xx.xx.xx.xx): Get https://xx.xx.xx.xx:10250/stats/summary/: x509: cannot validate certificate for xx.xx.xx.xx because it doesn't contain any IP SANs]

dial tcp: lookup kube-xxx on xx.xx.xx.xx:x: no such host 에러인 경우에는 kubelet-preferred-address-types 파라메터를, x509: cannot validate certificate for xx.xx.xx.xx because it doesn't contain any IP SANs인 경우에는 kubelet-insecure-tls 파라메터를 사용하면 된다.

metrics-server-deployment.yaml 파일을 편집해서, image: k8s.gcr.io/metrics-server-amd64:v0.3.3 밑에다 아래 파라메터를 추가하면 됩니다.

        command:
          - /metrics-server
          - --kubelet-preferred-address-types=InternalIP
          - --kubelet-insecure-tls

Unable to get metrics for resource cpu

상황

쿠버네티스 v1.11.x에서 HPA를 사용하려고 했으나, 에러가 발생하였습니다.

에러 메시지

Warning  FailedGetResourceMetric       3m (x21 over 13m)  horizontal-pod-autoscaler  unable to get metrics for resource cpu: unable to fetch metrics from resource metrics API: the server could not find the requested resource (get pods.metrics.k8s.io)

해결 방법

위의 에러 메시지는는 metrics-server가 설치되어 있지 않아서 생기는 것입니다. metrics-server 설치 문서를 참고해서 설치 하시기 바랍니다.

kubeadm : unable to select an IP from default routes

상황

쿠버네티스 1.13 버전을 kubeadm을 이용해서 설치하려고 했으나, IP를 찾을 수 없어서 에러가 발생하였습니다.

$ kubeadm init --config=kubeadm-config.yaml
unable to select an IP from default routes.
 
$ kubeadm init --config=kubeadm-config.yaml --v 10
I0114 20:04:41.384877 49609 interface.go:384] Looking for default routes with IPv4 addresses
I0114 20:04:41.384895 49609 interface.go:389] Default route transits interface "eth0.100"
I0114 20:04:41.386764 49609 interface.go:196] Interface eth0.100 is up
I0114 20:04:41.386833 49609 interface.go:244] Interface "eth0.100" has 1 addresses :[fe80::e642:4bff:fe1f:28b0/64].
I0114 20:04:41.386857 49609 interface.go:211] Checking addr fe80::e642:4bff:fe1f:28b0/64.
I0114 20:04:41.386873 49609 interface.go:224] fe80::e642:4bff:fe1f:28b0 is not an IPv4 address
I0114 20:04:41.386892 49609 interface.go:384] Looking for default routes with IPv6 addresses
I0114 20:04:41.386903 49609 interface.go:389] Default route transits interface "eth1.100"
I0114 20:04:41.389546 49609 interface.go:196] Interface eth0.100 is up
I0114 20:04:41.389592 49609 interface.go:244] Interface "eth0.100" has 1 addresses :[fe80::e642:4bff:fe1f:28b0/64].
I0114 20:04:41.389608 49609 interface.go:211] Checking addr fe80::e642:4bff:fe1f:28b0/64.
I0114 20:04:41.389622 49609 interface.go:221] Non-global unicast address found fe80::e643:4bff:fe1f:38b0
I0114 20:04:41.389634 49609 interface.go:400] No active IP found by looking at default routes
unable to fetch the kubeadm-config ConfigMap: unable to select an IP from default routes.

네트워크 인터페이스(network interface)에서 IP를 찾을 수 없기 때문에 발생하는 에러였습니다. 해당 서버는 loopback 인터페이스에 IP가 바인딩 되어 있었는데, kubeadm이 사용하는 코드에서는 loopback 인터페이스를 확인하지 않았습니다. 아마 IPv4 BGP over IPv6 (rfc5549)을 사용한거 같은데, 자세한 사항은 모르겠습니다.

이 문제에 대한 이슈는 등록되어 있고, 해결 방법으로 PR이 올라가 있지만 반영되지 않았습니다.

이 문제를 해결하기 위한 방법은, 네트워커 설정을 변경하거나, kubeam 코드를 패치하면 됩니다. 이 문서에는 간단히 kubeam 코드 패치를 선탁하였습니다.

kubeadm 빌드하기

소스 코드 빌드 과정은 Docker 컨테이너 안에서 일어나기 때문에, 별도의 golang 환경을 구축할 필요는 없습니다.

kubeadm 빌드하는 과정은 간단합니다.

우선 쿠버네티스 소스를 받습니다.

git clone https://github.com/kubernetes/kubernetes.git
cd kubernetes

태그로 버전을 확인한 후, 본인이 원하는 버전으로 체크아웃 합니다.

git tag
git checkout v1.13.6

staging/src/k8s.io/apimachinery/pkg/util/net/interface.go 파일을 열어서, 아래와 같은 코드를 추가해 줍니다. (https://github.com/kubernetes/kubernetes/pull/69578/files 을 참고하시기 바랍니다.)

			klog.V(4).Infof("Default route exists for IPv%d, however interface %q does not have global unicast addresses. Checking loopback interface", uint(family), route.Interface)
			loopbackIP, err := getIPFromInterface("lo", family, nw)
			if err != nil {
				return nil, err
			}
			if loopbackIP != nil {
				klog.V(4).Infof("Found active IP %v on loopback interface", loopbackIP)
				return loopbackIP, nil
			}

빌드 스크립트를 실행서, kubeadm을 빌드합니다.

build/run.sh make kubeadm KUBE_BUILD_PLATFORMS=linux/amd64

빌드가 정상적으로 끝나면, _output/dockerized/bin/linux/amd64/kubeadm 에 파일을 생성합니다.

생성한 파일을 서버로 이동해서, 설치하시면 됩니다.

다른 방법

특정 NIC에 아이피를 직접 할당해 줍니다.

- name: check if we need to re-assign ip
  shell: |
    if ip addr show eth0.100 > /dev/null; then
      ip addr show eth0.100 | grep 'inet ' > /dev/null;
    else
      true;
    fi;
  # need_to_reassign.rc = 0
  #  => not need to reassign
  # need_to_reassign.rc = 1
  #  => need to reassign
  register: need_to_reassign
  changed_when: False
  ignore_errors: True
   
- name: re-assing ip onto ethX.100
  shell: |
    private_ip=$(/sbin/ip a show dev private | grep 'inet '  | awk '{print $2}') && /sbin/ip addr add ${private_ip} dev eth0.100 && /sbin/ip addr add ${private_ip} dev eth1.100
  when: need_to_reassign.rc != 0

참고 문서

  • https://github.com/kubernetes/kubeadm/issues/1156
  • https://github.com/kubernetes/kubernetes/pull/69578
  • https://kubernetes.io/docs/setup/release/building-from-source/
  • https://github.com/kubernetes/kubernetes/tree/master/build/

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

참고 문서

Ingress nginx 파일 업로드 크기 제한 늘리기

Ingress NginX : Custom max body size

ingress-nginx를 사용하는 중, 파일 업로드 중 413 에러가 발생하였습니다. 이 경우는 nginx가 허용하는 것보다, 큰 파일이 업로드 되어 에러가 발생한 것이었습니다.

허용하는 최대 파일 크기를 늘리는 방법은 두 가지가 있습니다.
첫번째는 configmap을 에 설정하는 것이고, 두번째는 ingress 에 커스텀 어노테이션을 추가하는 것입니다. configmap에 설정할 경우는 글로벌하게 적용되고, 커스텀 어노테이션을 사용할 경우는 해당 ingress만 영향을 받습니다.

ConfigMap

nginx-configuration 이름의 nginx configmap에 proxy-body-size 값을 설정합니다.

apiVersion: v1
data:
  proxy-body-size: "1024m"
kind: ConfigMap
metadata:
labels:
  app: ingress-nginx
name: nginx-configuration

Ingress annotation

Ingress 리소스에 아래와 같이 어노테이션을 추가하면 됩니다.

nginx.ingress.kubernetes.io/proxy-body-size: 10m\
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  annotations:
    nginx.ingress.kubernetes.io/proxy-body-size: 1024m
  labels:
    app: kubeflow
  name: kubeflow
  namespace: kubeflow
spec:
  rules:
  - host: kubeflow.xxx.yyy
    http:
      paths:
      - backend:
          serviceName: ambassador
          servicePort: 79
        path: /

참고 문서