본 포스팅은 Udemy Certificated Kubernetes Administrator 강좌를 공부한 내용입니다.

 

Certified Kubernetes Administrator (CKA) Practice Exam Tests

Prepare for the Certified Kubernetes Administrators Certification with live practice tests right in your browser - CKA

www.udemy.com

 

Kubernetes 자체적으로는 완전하게 monitoring과 logging에 대한 솔루션을 제공해 주지 않는다.

Metric-Server, Prometheus, Elastic Stack 등 별도의 솔루션을 이용해 모니터링 시스템을 구축하는 것을 권장한다.

 

이번에는 Metric-Server를 이용한 간략한 Monitoring과 Logging에 대해 살펴보자.

 

Monitoring

 

각 node의 kubelet에 있는 cAdvisor는 docker와 같은 runtime container로부터 container의 cpu/memory 등 metric을 수집한다.

수집된 metric은 Metric-Server에 모여 kubectl 명령어로 node와 pod의 상태를 모니터링할 수 있다.

 

Metric-Server 설치

$ git clone https://github.com/kodekloudhub/kubernetes-metrics-server.git

$ cd kubernetes-metrics-server/

$ kubectl create -f .
clusterrole.rbac.authorization.k8s.io/system:aggregated-metrics-reader created
clusterrolebinding.rbac.authorization.k8s.io/metrics-server:system:auth-delegator created
rolebinding.rbac.authorization.k8s.io/metrics-server-auth-reader created
apiservice.apiregistration.k8s.io/v1beta1.metrics.k8s.io created
serviceaccount/metrics-server created
deployment.apps/metrics-server created
service/metrics-server created
clusterrole.rbac.authorization.k8s.io/system:metrics-server created
clusterrolebinding.rbac.authorization.k8s.io/system:metrics-server created

Metric-Server Monitoring

$ kubectl top node
NAME     CPU(cores)   CPU%   MEMORY(bytes)   MEMORY%
master   152m         7%     1261Mi          66%
node01   2000m        100%   939Mi           24%

$ kubectl top pod
NAME       CPU(cores)   MEMORY(bytes)
elephant   12m          50Mi
lion       869m         1Mi
rabbit     959m         1Mi

 

Metric-Server가 배포되면 위와 같은 명령어를 이용해 node와 pod의 CPU/Memory 정보를 확인할 수 있다.

 

위와 같이 사용할 경우 현재의 Metric을 조회할 수 있지만 과거의 Metric과 함께 조회하거나 Metric 기반의 알람 등 Monitoring을 활용하기 위해서는 Prometheus 등의 솔루션을 사용해야 한다.

Logging

Kubernetes Logging은 각 node에서 실행 중인 container의 로그를 조회할 수 있게 해준다.

 

# <POD_NAME> Pod의 Container 로그 조회
$ kubectl logs <POD_NAME>

# <POD_NAME> Pod의 <CONTAINER_NAME> 로그 조회
$ kubectl logs <POD_NAME> <CONTAINER_NAME>

# <POD_NAME> Pod의 Container 로그 실시간 조회
$ kubectl logs -f <POD_NAME>

docker CLI를 이용해 log를 조회하는 방법과 비슷하다.

Monitoring과 마찬가지로 logging 역시 node에 저장하는 log에 제한이 있다보니 과거의 log까지 저장하기 위해서는 Elastic Search 등의 솔루션을 사용해야 한다.

본 포스팅은 Udemy Certificated Kubernetes Administrator 강좌를 공부한 내용입니다.

 

Certified Kubernetes Administrator (CKA) Practice Exam Tests

Prepare for the Certified Kubernetes Administrators Certification with live practice tests right in your browser - CKA

www.udemy.com

 

DaemonSet

Kubernetes의 node를 모니터링한다고 생각해보자.

각 node에는 docker daemon에서 모이는 로그, node의 CPU/Memory 사용량 등 수집해야할 정보가 많다.

이런 데이터를 수집하는 agent가 모든 node에 배포되어 이를 중앙 저장소에 모은다고 했을 때,

이 agent를 Pod로 생성할 수 있을까?

이를 위해서는 Pod가 모든 node에 하나씩 생성되어 있어야 한다.

이를 위해 있는 것이 DaemonSet이다.

 

Node가 새로 생성되거나 삭제될 때 DaemonSet의 Pod 역시 생성/삭제 된다.

DaemonSet을 생성하는 방법은 Deployment와 유사하다.

 

apiVersion: apps/v1
kind: DaemonSet
metadata:
  creationTimestamp: null
  labels:
    app: elasticsearch
  name: elasticsearch
spec:
  selector:
    matchLabels:
      app: elasticsearch
  template:
    metadata:
      creationTimestamp: null
      labels:
        app: elasticsearch
    spec:
      containers:
      - image: k8s.gcr.io/fluentd-elasticsearch:1.20
        name: fluentd-elasticsearch
        resources: {}
$ kubectl create -f daemonset.yaml
daemonset.apps/elasticsearch created

$ kubectl get pod -o wide
NAME                  READY   STATUS    RESTARTS   AGE   IP          NODE     NOMINATED NODE   READINESS GATES
elasticsearch-l87cf   1/1     Running   0          29s   10.44.0.1   node01   <none>           <none>

$ kubectl get daemonset
NAME            DESIRED   CURRENT   READY   UP-TO-DATE   AVAILABLE   NODE SELECTOR   AGE
elasticsearch   1         1         1       1            1           <none>          34s

 

 

DaemonSet 역시 Pod이기 때문에 이전에 배운 Taint와 NodeAffinity가 마찬가지로 적용될지 살펴보자.

 

Taint & Toleration

$ kubectl taint nodes node01 color=red:NoExecute
node/node01 tainted

$ kubectl get daemonset
NAME            DESIRED   CURRENT   READY   UP-TO-DATE   AVAILABLE   NODE SELECTOR   AGE
elasticsearch   0         0         0       0            0           <none>          3m14s

$ kubectl get node
NAME     STATUS   ROLES    AGE     VERSION
master   Ready    master   5m8s    v1.16.0
node01   Ready    <none>   4m25s   v1.16.0

color=red taint를 node01에 적용했더니 DaemonSet의 Pod 갯수가 0이 된걸 확인할 수 있다.

master node 역시 node-role.kubernetes.io/master:NoSchedule taint가 설정되어 있어 Pod가 생성되지 않았다.

 

 

Node Affinity

apiVersion: apps/v1
kind: DaemonSet
metadata:
  creationTimestamp: null
  labels:
    app: elasticsearch
  name: elasticsearch
spec:
  selector:
    matchLabels:
      app: elasticsearch
  template:
    metadata:
      creationTimestamp: null
      labels:
        app: elasticsearch
    spec:
      containers:
      - image: k8s.gcr.io/fluentd-elasticsearch:1.20
        name: fluentd-elasticsearch
      affinity:
        nodeAffinity:
          requiredDuringSchedulingIgnoredDuringExecution:
            nodeSelectorTerms:
            - matchExpressions:
              - key: size
                operator: In
                values:
                - small
$ kubectl label node node01 size=large

$ kubectl create -f daemonset-small.yaml
daemonset.apps/elasticsearch created

$ kubectl get daemonset
NAME            DESIRED   CURRENT   READY   UP-TO-DATE   AVAILABLE   NODE SELECTOR   AGE
elasticsearch   0         0         0       0            0           <none>          12s

size=large 로 label이 된 node에 NodeAffinity size=small인 daemonset의 pod가 생성되지 않은 것을 확인할 수 있다.

참고로 Node Affinity는 kubernetes 1.12 버전 이후에 적용되었다고 한다.

 

 

 

Static Pod

Kubernetes는 위 그림처럼 Control Plane과 kubelet에 의해 관리되는 Worker Node들이 존재한다.

Control Plane의 역할을 하는 api-server, kube-scheduler 등이 없다면 Worker Node는 동작하지 않을까?

각 노드에서 Container를 실행시키고 관리하는 것은 kubelet이기 때문에 Control Plane이 없더라도 각 Worker Node는 정상 동작한다.

하지만 yaml 형식으로 api-server에 Pod 생성을 스케쥴링할 수 없게 된다.

 

kubelet은 각 node에 특정 path(/etc/kubernetes/manifests)에서 control plane이 아닌 노드 자체적으로 생성하고 관리할 수 있는 Pod yaml을 가지게 할 수 있다.

여기서 생성되는 Pod는 api-server를 통해서가 아닌 kubelet이 자체적으로 path로부터 pod를 생성/재생성/삭제 한다.

이를 Static Pod이라고 한다.

 

Static Pod을 이용하는 곳은 어디일까?

Master Node에서 Control Plane의 역할을 하는 Pod를 Static Pod을 이용해 생성할 수 있다.

 

Static Pod는 kubelet이 api-server에 pod에 대한 정보를 주기 때문에 kubectl을 사용해 조회가 가능하다.


 

 

 



 

 

 

본 포스팅은 Udemy Certificated Kubernetes Administrator 강좌를 공부한 내용입니다.

 

Certified Kubernetes Administrator (CKA) Practice Exam Tests

Prepare for the Certified Kubernetes Administrators Certification with live practice tests right in your browser - CKA

www.udemy.com

CKA 준비 (6) - Scheduling 1 (Manual, taint and toleration)를 통해 Node의 입장에서 POD의 실행 여부를 판단하는 taint & toleration에 대해 배웠다.

Node Selector와 Node Affinity 역시 앞선 내용과 비슷하게 POD를 특정 Node에서 실행시켜 물리적 isolation을 하기 위한 개념이다.

 

이 둘의 의미와 사용 방법에 대해 살펴보고 taint & toleration과 어떻게 같이 사용될 수 있는지 확인해보자.

 

Node Selector

앞서 POD의 spec에서 nodeName 을 통해 POD가 실행될 Node를 manual 적으로 실행시킬 수 있다는 것을 배웠다.

Node Selector는 그와 비슷하지만 하나의 node에서가 아닌 Node Selector에 설정된 Label을 가지고 있는 Node 중에서 하나를 선택한다.

 

Node Label 지정

$ kubectl labels node <NODE_NAME> <label-key>=<label-value>

label을 지정할 <NODE_NAME>에 대해 key-value를 설정한다.

 

POD NodeSelector 지정

apiVersion: v1
kind: Pod
metadata:
  creationTimestamp: null
  labels:
    run: nginx
  name: nginx
spec:
  containers:
  - image: nginx
    name: nginx
    resources: {}
  dnsPolicy: ClusterFirst
  restartPolicy: Never
  nodeSelector:
    <label-key>: <label-value>

spec에서 nodeSelector를 통해 실행될 node의 label key-value를 설정한다.

 

 

Node Affinity

Node Selector의 한계점

node의 CPU/Memory 크기에 따라 label을 지정했다고 가정해보자.

$ kubectl labels node node01 size=small
$ kubectl labels node node02 size=medium
$ kubectl labels node node03 size=large

새로운 POD를 실행시킬 때 해당 POD는 size=medium 또는 size=large 둘 중 어느 node에서 실행되도 상관없다면 이것을 nodeSelector로 어떻게 표현할 수 있을까?

같은 의미로 size=small만 아닌 node에서 POD를 실행시키고 싶다면 어떻게 표현할 수 있을까?

이러한 표현을 위해서는 operator가 필요하고 이를 제공해주는 것이 Node Affinity이다.

 

Node Affinity 설정

kind: Pod
metadata:
  creationTimestamp: null
  labels:
    run: nginx
  name: nginx
spec:
  containers:
  - image: nginx
    name: nginx
    resources: {}
  dnsPolicy: ClusterFirst
  restartPolicy: Never
  affinity:
    nodeAffinity:
      requiredDuringSchedulingIgnoredDuringExecution:
        nodeSelectorTerms:
        - matchExpressions:
          - key: size
            operator: In
            values:
            - medium
            - large

In operator를 통해 nginx POD를 size=medium 또는 size=large label이 있는 Node에서 실행시킬 수 있다.

 

Node Affinity가 바라보는 node의 label은 동적으로 생성/삭제가 가능하기 때문에 이에 대한 설정을 할 수 있다.

위의 예제에서 requiredDuringSchedulingIgnoredDuringExecution 가 설정 부분이 된다.

 

  Scheduling Execution
Required POD를 생성할 때, node affinity가 일치하지 않으면 해당 node에 scheduling 하지 않는다. Node Label 변경 등의 이벤트 발생 시, 실행 중인 POD의 node affinity가 일치하지 않으면 퇴출(eviction)한다. 
Ignored POD를 생성할 때, node affinity가 일치하지 않아도 무시한다. 이미 실행 중인 POD는 관여하지 않는다.
Preferred POD를 생성할 때, node affinity가 일치하지 않으면 해당 node에 scheduling 하지 않지만 cluster resource 부족 등으로 POD가 scheduling 되지 않는다면 node에 배정한다. X

 

Taint & Toleration And Node Affinity

앞서 살펴본 Taint & Toleration과 Node Affinity는 비슷한 기능인 것 같다.

둘의 차이가 어떤 것이 있고 같이 사용할 방법에 대해 알아보자.

 

Node1, Node2에 red taint가 존재하고 POD1이 red toleration을 가지고 있다면

POD1은 무조건 Node1과 Node2에 배정될까?

아무런 taint도 존재하지 않는 Node3에도 배정될 수 있다.

 

이 문제를 해결하기 위해 Node Affinity를 쓸 수 있다.

POD1에 대해 large label이 있는 Node에만 배정될 수 있도록 설정했다.

그렇게 되면 POD1은 Node1 또는 Node2에 배정받을 것이다.

하지만 Node Affinity가 없는 새로운 POD2 역시 Node1 또는 Node2에 배정될 수 있다.

이것은 POD1 과 POD2가 서로 물리적으로 isolation이 되지 않는 문제점이 발생한다.

 

 

Tinat & Toleration과 Node Affinity를 같이 사용한다면 위처럼 POD간의 isolation을 완벽하게 할 수 있다.

POD1은 Node Affinity에 의해 Node1 또는 Node2에만 배정받게 되며

POD2는 Taint & Toleration에 의해 Node1 또는 Node2에 배정받지 못한다.

정리

Taint & Toleration과 Node Affinity는 kubernetes app 개발할 때 무심코 지나칠 수 있는 부분인데 시험 준비를 하면서 자세하게 알 수 있었던 것 같다.

 

+ Recent posts