본 포스팅은 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 Upgrade

Kubernetes 역시 소프트웨어이기 때문에 버전이 존재하고 하위 버전을 호환하기 위한 backward compatibility를 갖추고 있어야 한다.

Kubernetes는 Master Node에서 동작하는 ControlPlane의 구성요소와

Worker Node에서 동작하는 kubelet, kube-proxy,

사용자가 사용하는 kubectl 등 다양한 프로세스로 구성되어 있다.

 

그리고 다양한 프로세스는 같은 버전을 유지하는 것을 권장한다.

하지만 모든 프로세스의 버전을 맞출 수 없다면, Kubernetes는 프로세스의 모든 버전에 대해 backward compatibility를 갖추고 있어야 할까?

현실적으로 그것은 불가능하기 때문에 Kubernetes는 버전과 프로세스의 하위 버전 지원의 범위를 정해놓고 있다.

Kubernetes의 버전은 api-server의 버전을 기준으로 한다.

 

프로세스 지원 버전 범위 예시
kube-apiserver X v1.10
controller-manager X-1 v1.9 or v1.10
kube-scheduler X-1 v1.9 or v1.10
kubelet X-2 v1.8 or v1.9 or v1.10
kube-proxy X-2 v1.8 or v1.9 or v1.10
kubectl X+1 > X-1 v1.9 or v1.10 or v1.11

 

또한 kubernetes를 업그레이드 할 때에는 minor 버전을 하나씩 올리는 것을 권장한다. (eg,. v1.9 -> v1.10 -> v1.11)

(버전 명 : v{major}.{minor}.{patch})

 

Kubernetes를 설치할 때와 마찬가지로 업그레이드 시에도 Manual하게 하는 방법과 kubeadm을 사용하는 방법이 있다.

이번에도 역시 kubeadm으로 업그레이드를 진행한다.

 

 

Upgrade Strategy

Kubernetes는 여러 노드로 구성되어 있기 때문에 모든 노드를 전부 업그레이드 해주어야 한다.

이 과정에서 노드의 downtime에 따른 3가지 전략이 있다.

 

1. Master 노드를 업그레이드한 후, 모든 Worker 노드를 한 번에 업그레이드 한다.

당연하겠지만 모든 Worker 노드의 kubelet, kube-proxy 등이 한번에 재시작 되기 때문에 클러스터에서 동작하는 Pod의 downtime이 존재한다.

 

2. Master 노드를 업그레이드한 후, 한 Worker 노드씩 업그레이드 한다.

Worker 노드를 하나씩 재시작하며 업그레이드를 진행한다. 업그레이드 하기 전 node drain을 통해 노드에 있는 Pod를 퇴출시킨 뒤 업그레이드 한다.

 

3. 업그레이드가 이미 된 새로운 Worker 노드를 추가하고 기존 노드 하나를 업그레이드 한다.

언뜻 2번과 비슷한 전략 같지만 이렇게 진행하기 위해선 전체 Worker 노드 수 + 1개의 노드가 필요하다. 업그레이드 중에도 노드의 갯수를 동일하게 유지할 수 있기 때문에 클러스터의 리소스 양을 유지할 수 있다.

 

Kubeadm Upgrade

이제 kubeadm을 사용해 노드 업그레이드를 진행해보자.

$ kubectl get node
NAME     STATUS   ROLES    AGE    VERSION
master   Ready    master   112s   v1.16.0
node01   Ready    <none>   82s    v1.16.0

2개의 노드가 있고 각 노드의 버전은 v1.16.0이다.

노드 하나씩 업그레이드 하는 Strategy 2를 사용해서 업그레이드 해보자.

kubeadm을 사용하면 현재 업그레이드 할 수 있는 버전을 알 수 있다.

$ kubeadm upgrade plan
[upgrade/config] Making sure the configuration is correct:
[upgrade/config] Reading configuration from the cluster...
...
...
Components that must be upgraded manually after you have upgraded the control plane with 'kubeadm upgrade apply':
COMPONENT   CURRENT       AVAILABLE
Kubelet     2 x v1.16.0   v1.17.7

Upgrade to the latest stable version:

COMPONENT            CURRENT   AVAILABLE
API Server           v1.16.0   v1.17.7
Controller Manager   v1.16.0   v1.17.7
Scheduler            v1.16.0   v1.17.7
Kube Proxy           v1.16.0   v1.17.7
CoreDNS              1.6.2     1.6.5
Etcd                 3.3.15    3.4.3-0

You can now apply the upgrade by executing the following command:

        kubeadm upgrade apply v1.17.7

Note: Before you can perform this upgrade, you have to update kubeadm to v1.17.7.

v1.17.7 버전으로 업그레이드 할 수 있다고 나온다.

 

v1.17.7으로 업그레이드 하기 위해서는 kubeadm의 버전이 일치해야한다.

일치하지 않을 경우 kubeadm 버전을 업그레이드 하고

kubeadm upgrade plan v1.17.7을 통해 해당 버전으로 업그레이드가 가능한지 확인한다.

$ apt-get upgrade kubeadm=1.17.7-00

$ kubeadm version
kubeadm version: &version.Info{Major:"1", Minor:"17", GitVersion:"v1.17.7", GitCommit:"b4455102ef392bf7d594ef96b97a4caa79d729d9", GitTreeState:"clean", BuildDate:"2020-06-17T11:37:34Z", GoVersion:"go1.13.9", Compiler:"gc", Platform:"linux/amd64"}

$ kubeadm upgrade plan v1.17.7
[upgrade/config] Making sure the configuration is correct:
[upgrade/config] Reading configuration from the cluster...
[upgrade/config] FYI: You can look at this config file with 'kubectl -n kube-system get cm kubeadm-config -oyaml'
[preflight] Running pre-flight checks.
[upgrade] Making sure the cluster is healthy:
[upgrade] Fetching available versions to upgrade to
[upgrade/versions] Cluster version: v1.16.0
[upgrade/versions] kubeadm version: v1.17.7

Components that must be upgraded manually after you have upgraded the control plane with 'kubeadm upgrade apply':
COMPONENT   CURRENT       AVAILABLE
Kubelet     2 x v1.16.0   v1.17.7

Upgrade to the latest version in the v1.16 series:

COMPONENT            CURRENT   AVAILABLE
API Server           v1.16.0   v1.17.7
Controller Manager   v1.16.0   v1.17.7
Scheduler            v1.16.0   v1.17.7
Kube Proxy           v1.16.0   v1.17.7
CoreDNS              1.6.2     1.6.5
Etcd                 3.3.15    3.4.3-0

You can now apply the upgrade by executing the following command:

        kubeadm upgrade apply v1.17.7

_____________________________________________________________________

 

 

업그레이드를 하기 전 노드에서 Pod를 퇴출시키는 명령어에 대해 알아보자.

# <NODE>의 Pod 퇴출 및 unscheduling
$ kubectl drain <NODE>

# <NODE> unscheduling
$ kubectl cordon <NODE>

# <NODE> scheduling
$ kubectl uncordon <NODE>

drain은 현재 Pod를 모두 퇴출시키고 앞으로도 kube-scheduler에 의해 Pod가 해당 노드로 스케쥴링 되지 않는다.

$ kubectl describe node master
Name:               master
...
...
Taints:             node.kubernetes.io/unschedulable:NoSchedule

$ kubectl get node
NAME     STATUS                     ROLES    AGE   VERSION
master   Ready,SchedulingDisabled   master   11m   v1.16.0
node01   Ready                      <none>   10m   v1.16.0

위와 같이 스케쥴링이 되지 않도록 Taint가 설정되고 STATUS에 SchedulingDisabled가 나온다.

cordon은 drain처럼 스케쥴링이 되지 않지만 현재 실행중인 Pod는 퇴출시키지 않는다.

uncordon은 drain/cordon된 node를 다시 스케쥴링 할 수 있도록 변경한다.

 

 

master 노드에 있는 Pod를 퇴출시킨 두 업그레이드 한다.

$ kubectl drain master --ignore-daemonsets
node/master already cordoned
WARNING: ignoring DaemonSet-managed Pods: kube-system/kube-proxy-7qcdn, kube-system/weave-net-k2z8q
node/master drained

$ kubeadm upgrade apply v1.17.7
...
...
[upgrade/successful] SUCCESS! Your cluster was upgraded to "v1.17.7". Enjoy!
...

$ kubectl get node
NAME     STATUS                     ROLES    AGE   VERSION
master   Ready,SchedulingDisabled   master   31m   v1.16.0
node01   Ready                      <none>   30m   v1.16.0

$ apt-get install kubelet=1.17.7-00

$ kubectl get node
NAME     STATUS                     ROLES    AGE   VERSION
master   Ready,SchedulingDisabled   master   33m   v1.17.7
node01   Ready                      <none>   32m   v1.16.0

kubeadm으로 업그레이드를 한 뒤 kubelet까지 업그레이드를 해야 노드의 업그레이드가 완료된다.

 

kubeadm과 kubelet은 자동적으로 업그레이드가 되면 안되기 때문에 자동 업그레이드를 막아준다.

$ apt-mark hold kubeadm
kubeadm set on hold.

$ apt-mark hold kubelet
kubelet set on hold.

 

이제 master노드를 다시 스케쥴링 될 수 있도록 만든다.

$ kubectl uncordon master
node/master uncordoned

$ kubectl get node
NAME     STATUS   ROLES    AGE   VERSION
master   Ready    master   34m   v1.17.7
node01   Ready    <none>   33m   v1.16.0

 

이제 node01을 업그레이드 해보자.

$ kubectl drain node01
WARNING: ignoring DaemonSet-managed Pods: kube-system/kube-proxy-hggqf, kube-system/weave-net-rw9th
evicting pod kube-system/coredns-6955765f44-pwtbn
evicting pod kube-system/coredns-6955765f44-tnbjn
pod/coredns-6955765f44-pwtbn evicted
pod/coredns-6955765f44-tnbjn evicted
node/node01 evicted

$ ssh node01

node01 $ kubeadm upgrade node
[upgrade] Reading configuration from the cluster...
[upgrade] FYI: You can look at this config file with 'kubectl -n kube-system get cm kubeadm-config -oyaml'
[upgrade] Skipping phase. Not a control plane node[kubelet-start] Downloading configuration for the kubelet from the "kubelet-config-1.17" ConfigMap in the kube-system namespace
[kubelet-start] Writing kubelet configuration to file "/var/lib/kubelet/config.yaml"
[upgrade] The configuration for this node was successfully updated!
[upgrade] Now you should go ahead and upgrade the kubelet package using your package manager.

node01 $ apt-get install kubelet=1.17.7-00

node01 $ apt-mark hold kubeadm
kubeadm set on hold.

node01 $ apt-mark hold kubelet
kubelet set on hold.

node01 $ logout

$ kubectl uncordon node01
node/node01 uncordoned

$ kubectl get node
NAME     STATUS   ROLES    AGE   VERSION
master   Ready    master   42m   v1.17.7
node01   Ready    <none>   42m   v1.17.7

 

이미 master 노드에서 업그레이드한 kubernetes의 정보가 있기 때문에 kubeadm upgrade node 를 통해서 업그레이드 할 수 있다.

 

정리하자면, 업그레이드 순서는 아래와 같다.

 

1. master node drain (kubectl drain <MASTER_NODE>)

2. upgrade master node (apt-get install kubeadm=<VERSION> kubeadm upgrade apply <VERSION>

3. upgrade kubelet (apt-get install kubelet=<VERSION>)

4. master node uncordon (kubectl uncordon <MASTER_NODE>)

 

5. worker node drain (kubectl drain <WORKER_NODE>)

6. upgrade worker node (kubeadm upgrade node)

7. upgrade kubelet (apt-get install kubelet=<VERSION>)

8. worker node uncordon (kubectl uncordon <WORKER_NODE>)

본 포스팅은 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 개발할 때 무심코 지나칠 수 있는 부분인데 시험 준비를 하면서 자세하게 알 수 있었던 것 같다.

 

본 포스팅은 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 준비 (2) - Basic Control Plane Components )을 통해 POD가 배정받을 때 어떤 Worker Node에 배정받을지를 kubernetes Control Plane의 kube-scheduler가 결정한다고 했다.

 

여기서 배우게될 주된 주제는 POD가 어떤 node에 배정되고 퇴출(eviction)될지에 대한 내용이다.

이전 포스팅에서 namespace는 논리적인 단위로 POD 들을 격리(isolation) 시킨다고 했다.

그렇다면 실제로 각 POD의 역할에 맞게 다른 node에 POD를 실행시켜 물리적으로 격리시킬 방법이 있을까?

kubernetes에서는 다양한 방법을 통해 이러한 기능을 제공해준다.

 

Manual

kubernetes에서는 POD spec에서 배정될 node를 직접 명시할 수 있게 제공해준다.

apiVersion: v1
kind: Pod
metadata:
  labels:
    run: nginx
  name: nginx
spec:
  nodeName: master
  containers:
  - image: nginx
    name: nginx
  dnsPolicy: ClusterFirst
  restartPolicy: Never

이렇게 실행할 경우 POD는 kube-scheduler에 의해서 scheduling 되지 않고 바로 master node에 배정된다.

$ $ kubectl get pods -o wide
NANAME    READY   STATUS    RESTARTS   AGE    IP           NODE     NOMINATED NODE   READINESS GATES
ngnginx   1/1     Running   0          102s   10.244.0.4   master   <none>           <none>

하지만  Scheduler에 의하지 않고 명시적으로 node를 명시하는 것은 여러 node의 리소스를 다양한 application이 사용한다는 kubernetes의 컨셉과는 맞지 않는다.

 

Taint & Toleration

Node는 POD가 실제로 실행되는 물리 또는 가상 머신이다.

각 Node는 자의적으로 자신이 실행시켜줄 POD를 구분지을 수 있어야 한다.

 

이러한 기능을 제공해주는게 Taint & Toleration이다.

 

사전적으로 Taint는 더러움, 감염 등을 의미한다.

kubernetes는 각 노드에 taint를 설정해 감염으로부터 용인(toleration)된 POD만 해당 node에서 실행시켜준다.

 

 

Pod 1은 red와 blue taint에 대해 toleration이 있기 때문에 Node1, Node2에 실행될 수 있고

Pod 2는 허용된 toleration이 없기 때문에 3 곳 모두에서 실행될 없고

Pod 3은 green toleration이 있기 때문에 Node 3에서 실행될 수 있다.

 

실제 taint와 toleration 설정 방법을 살펴보자.

 

taint 설정

$ kubectl taint nodes {NODE_NAME} key=value:{TAINT_EFFECT}

taint를 지정하고 싶은 NODE_NAME을 명시하고 key와 value를 입력한다.

그리고 TAINT_EFFECT를 통해 taint에 허용되지 않은 POD에 대한 node의 액션을 지정한다.

TAINT_EFFECT 내용
NoSchedule taint가 허용되지 않는 POD는 Scheduling 하지 않는다.
이미 실행 중인 POD는 관여하지 않는다.
PreferNoSchedule taint가 허용되지 않는 POD는 Scheduling 하지 않는 것을 시도한다.
하지만 cluster의 리소스가 부족한 것과 같은 상황이 되면 toleration이 만족하지 않아도 node에 Scheduliig이 된다.
NoExecute taint가 허용되지 않는 POD는 Scheduling 하지 않는다.
이미 실행 중인 POD도 eviction(퇴출) 한다.

taint 를 삭제할 때는 가장 뒤에 "-"를 붙인다.

$ kubectl taint nodes {NODE_NAME} key=value:{TAINT_EFFECT}-

 

toleration 설정

apiVersion: v1
kind: Pod
metadata:
  creationTimestamp: null
  labels:
    run: nginx
  name: nginx
spec:
  containers:
  - image: nginx
    name: nginx
    resources: {}
  dnsPolicy: ClusterFirst
  restartPolicy: Never
  tolerations:
  - key: "color"
    operator: "Equal"
    value: "red"
    effect: "NoSchedule"
status: {}

tolerations은 위와 같이 key/operator/value/effect를 명시한다.

tolerations 설정이 taint와 일치하면 POD는 해당 node에 배정될 수 있다.

(node에 여러개의 taint가 있다면 pod는 모든 taint를 만족해야 배정될 수 있다.)

 

toleration seconds

taint effect인 NoExecute를 활용한 toleration seconds에 대해 알아보자.

 

NoExecute는 이미 실행중인 POD에 대해서도 toleration이 맞지 않으면 퇴출한다.

 

$ kubectl taint nodes node01 color=red:NoExecute

위와 같이 설정되어 있다면 color=red:NoExecute에 만족되지 않는 POD는 node01에서 모두 퇴출된다.

그런데 toleration을 만족하더라도 특정 시간 이후에는 퇴출되도록 할 수 있다.

apiVersion: v1
kind: Pod
metadata:
  creationTimestamp: null
  labels:
    run: nginx
  name: nginx
spec:
  containers:
  - image: nginx
    name: nginx
    resources: {}
  dnsPolicy: ClusterFirst
  restartPolicy: Never
  tolerations:
  - key: "color"
    operator: "Equal"
    value: "red"
    effect: "NoExecute"
    tolerationSeconds: 3600
status: {}

 

위처럼 설정되어 있다면 nginx POD는 node01에서 3600초 이후에 퇴출된다.

 

이 기능은 kubernetes의 node controller에 의해서 사용된다.

 

Control Plane의 controller-manager는 모든 node의 상태를 모니터링 한다.

그런데 특정 node가 응답하지 않는다면 controller-manager는 node에 아래와 같은 taint를 지정한다.

key: node.kubernetes.io/unreachable
effect: NoExecute

또한 모든 POD는 아래와 같은 toleration이 default로 설정된다.

tolerations:
- key: "node.kubernetes.io/unreachable"
  operator: "Exists"
  effect: "NoExecute"
  tolerationSeconds: 6000

즉, node의 상태가 이상하다고 판단되면 node controller는 taint를 설정하고 POD는 6000초 후에 해당 node에서 퇴출된다.

퇴출된 POD는 다른 정상상태의 node에 새롭게 scheduling된다.

 

이처럼 tolerationSeconds를 이용해 node 상태에 따른 scheduling이 가능하다.

 

더 자세한 내용은 아래 링크를 참고하자.

 

https://kubernetes.io/ko/docs/concepts/scheduling-eviction/taint-and-toleration/

 

테인트(Taints)와 톨러레이션(Tolerations)

 

kubernetes.io

 

정리

kubernetes의 scheduling에 대해서는 모르는 부분이 많았는데 도움이 많이 되었다.

본 포스팅은 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는 어플리케이션 사이의 isolation을 위해 namespace라는 리소스 제공하고 있다.

여기서 isolation은 실제 리소스를 물리적으로 격리시키는 것이 아니라 논리적인 단위이다.

또한 POD를 외부에서 접근할 수 있는 endpoint를 제공하기 위해 service라는 리소스를 제공한다.

이 둘에 대해 알아보자.

 

namespace

하나의 kubernetes cluster에서 dev application과 prod application을 같이 운영한다고 가정해보자.

개발자는 dev application을 수시로 업데이트 할 것이고 prod application은 정기 점검이나 새로운 버전을 release할 때 접근할 것이다.

그런데 dev와 prod의 POD가 같은 cli로 보이게 된다면 POD 설정에 실수를 범할 가능성이 높아진다.

이뿐 아니라 kubernetes의 control plane의 역할을 하는 POD 역시 같이 보인다면 위험성이 더 높아진다.

 

namespace는 이러한 문제를 해결할 수 있다.

namespace 역시 kubernetes의 리소스로 관리되기 때문에 kubectl 명령어를 통해 생성/삭제를 할 수 있다.

 

kubernetes의 control plane POD는 kube-system namespace에 존재하고 해당 namespace의 POD를 조회할 때는

-n kube-system option을 사용하면 된다.

$ kubectl get pods -n kube-system
NAME                                      READY   STATUS    RESTARTS   AGE
coredns-66bff467f8-8wk9d                  1/1     Running   0          49m
coredns-66bff467f8-hwqm2                  1/1     Running   0          49m
etcd-master                               1/1     Running   0          49m
katacoda-cloud-provider-58f89f7d9-v79q2   1/1     Running   0          49m
kube-apiserver-master                     1/1     Running   0          49m
kube-controller-manager-master            1/1     Running   0          49m
kube-flannel-ds-amd64-9f4js               1/1     Running   0          48m
kube-flannel-ds-amd64-9v7l9               1/1     Running   0          49m
kube-keepalived-vip-7fn2r                 1/1     Running   0          48m
kube-proxy-ccm7p                          1/1     Running   0          49m
kube-proxy-ttfhw                          1/1     Running   0          48m
kube-scheduler-master                     1/1     Running   0          49m

 

namespace 생성

$ kubectl create namespace dev
namespace/dev created

 

namespace 삭제

$ kubectl delete namespace dev
namespace "dev" deleted

 

namespace 조회

$ kubectl get namespace
NAME              STATUS   AGE
default           Active   54m
kube-node-lease   Active   54m
kube-public       Active   54m
kube-system       Active   54m

 

cluster context

유저가 kubectl을 사용하기 위한 kubernetes의 정보는 ~/.kube/config에 있는데 해당 configuration을 통해 여러 k8s cluster와 namespace를 조회/변경할 수 있다.

 

context 조회

$ kubectl config get-contexts
CURRENT   NAME                          CLUSTER      AUTHINFO           NAMESPACE
*         kubernetes-admin@kubernetes   kubernetes   kubernetes-admin

 

context 추가

$ kubectl config set-context cluster1_kube-system --cluster=cluster1 --namespace=kube-system --user=cluster1
Context "cluster1_kube-system" created.
$ kubectl config get-contexts
CURRENT   NAME                          CLUSTER      AUTHINFO           NAMESPACE
          cluster1_kube-system          cluster1     cluster1           kube-system
*         kubernetes-admin@kubernetes   kubernetes   kubernetes-admin

 

현재 context 변경

$ kubectl config use-context cluster1_kube-system
Switched to context "cluster1_kube-system".
$ kubectl config get-contexts
CURRENT   NAME                          CLUSTER      AUTHINFO           NAMESPACE
*         cluster1_kube-system          cluster1     cluster1           kube-system
          kubernetes-admin@kubernetes   kubernetes   kubernetes-admin

 

앞에서 namespace는 논리적 단위의 isolation이라고 했는데 이 뜻은 dev와 prod namespace에 있는 POD가 실제로 다른 worker node에 존재하지 않고 같은 node에 있을 수 있다는 의미다.

실제 물리적으로 다른 worker node에 배치하고 싶다면 뒤에서 배울 taint and toleration 과 node affinity 등을 사용하면 된다.

 

추가로 namespace에는 resource quota를 지정할 수 있다.

resource quota는 CPU/Memeory/Storage 등 컴퓨팅/스토리지 리소스를 나타내는데 각 namespace에 resource quota를 지정해 namespace 별로 최대 사용할 수 있는 컴퓨팅/스토리지 리소스 량을 제한할 수 있다.

 

service

kubernetes에서 POD를 생성하면 POD는 k8s cluster 내부 IP를 가진다.

이 내부 IP를 이용하면 POD 간의 통신에는 문제가 없다.

하지만 웹페이지와 같이 외부에서 브라우저를 통해 접근해야 하는 POD는 어떻게 제공해줄 수 있을까?

 

그리고 Deployment, ReplicaSet등을 통해 POD의 replicas를 2개 이상 만들었을 때,

이들을 load balacing을 어떻게 제공해줄 수 있을까?

POD를 사용하는 client에서 다수 POD의 IP를 모두 알고 load balancing 로직을 직접 구현해야할까?

 

이러한 문제를 해결할 수 있는 방법을 kubernetes는 service를 통해 제공하고 있다.

위의 예시는 Service의 타입 중 NodePort를 이용한 예시다.

POD는 kubernetes node 중 IP 192.168.1.2를 사용하는 Worker Node에서 실행 중이다.

그리고 Service는 Worker Node의 30008번 포트를 통해 listen을 하고 있고 10.244.0.2 IP를 사용하는 POD에 연결되어 있다.

개발자는 Desktop(192.168.1.5)에서 192.168.1.2:30008를 통해 해당 POD에 접근할 수 있게된다.

 

참고로 NodePort는 Node의 30000~32767 사이의 포트 중 하나를 배정받아 Service를 외부로 노출시킬 수 있게 해준다.

 

Deployment 또는 ReplicaSet을 통해 배포된 POD는 Label과 Selector를 통해 Service가 연결할 수 있고 이를 통해 Loadbalancing을 할 수 있게 된다.

apiVersion: v1
kind: Service
metadata:
  name: my-service
spec:
  selector:
    app: MyApp
  ports:
    - protocol: TCP
      port: 80
      targetPort: 8080

위와 같이 selector를 사용해 POD의 Label을 지정할 수 있다.

 

Service Type

Service의 Type은 아래와 같이 3가지 방식이 있다.

Type 내용
ClusterIP Cluster 내부 IP만 배정받음, 외부로 노출 X
Default Service Type
NodePort Node의 Port를 배정받음, 외부로 노출
LoadBalancing Cloud Provider의 리소스를 제공받음(eg,. AWS ELB)

 

POD에서 Service를 통해 다른 POD에 요청할 때는 Service name을 DNS 네임으로 이용할 수 있다.

동일 namespace의 Service port에 접근 : {SERVICE_NAME}:{PORT} (eg,. nginx:80)

다른 namespace의 Service port에 접근 : {SERVICE_NAME}.{namespace}.svc.cluster.local:{PORT}

(eg,. nginx.dev.svc.cluster.local:80)

 

정리

kubernetes의 service 기능에 대해 정리할 수 있었다

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

 

Kubernetes는 Container를 직접적으로 다루지 않는다.

Container를 Pod라는 리소스로 감싸고

Pod를 ReplicationController 또는 ReplicaSet으로 감싸고

ReplicaSet은 최종적으로 Deployment로 깜싸진다.

 

이들의 관계와 역할에 관해 알아보자.

 

Pod

Kubernetes는 Container를 Pod 라는 리소스 단위로 감싼다.

Container와 Pod의 관계는 N:1 이다.

Pod라는 개념을 만든 이유를 살펴보자

Docker와 같은 Runtime Container Engine을 사용해 container를 실행하고 관리하는 경우를 살펴보자

 

1. Backend Container를 실행하고 Frontend로부터 요청을 받는다.

요청 결과는 host와 volumn mount를 통해 log로 쌓는다.

 

 

backend container 실행을 위한 docker 명령어는 아래와 같을 것이다.

$ docker run backend -v /log:/log

2. Backend에서 발생하는 Log를 S3로 전송하는 container를 구동한다.

일반적으로 특정 일 수가 지난 log file은 지워지기 때문에 주기적으로 S3로 file을 업로드하는 container를 실행한다.

logCrawler까지 실행하는 docker 명령어는 아래와 같을 것이다.

$ docker run backend -v /log:/log
$ docker run logCrawler -v /log:/log

3. Backend의 상태를 확인하기 위한 health check container를 구동한다.

 

$ docker run backend -v /log:/log
$ docker run logCrawler -v /log:/log
$ docker run healCheck --link backend:backend

4. Frontend로부터 요청이 많아 Backend Container의 갯수를 늘려 LoadBalancing을 한다.

$ docker run backend1 -v /log:/log1
$ docker run logCrawler1 -v /log:/log1
$ docker run healCheck1 --link backend1:backend

$ docker run backend2 -v /log:/log2
$ docker run logCrawler2 -v /log:/log2
$ docker run healCheck2 --link backend2:backend

Container의 Replication의 갯수가 많아질 수록 연관이 있는 Container의 관리가 복잡해지게 된다.

 

따라서 Kubernetes에서는 위와 같은 현상을 해결하기 위해 Pod라는 리소스 제공해 N개의 Container를 포함하고

같은 Pod로 묶은 Container는 localhost로 네트워크 통신이 가능하며 같은 volume을 공유할 수 있도록 해준다.

나중에 설명될 Kubernetes의 Service를 이용하여 다수의 Pod의 Loadbalancing이 가능하게 된다.

 

Kubernetes의 Resource는 모두 YAML File을 입력으로 받아 생성된다.

따라서 POD 역시 YAML 형식으로 구성된다.

apiVersion: v1
kind: Pod
metadata:
  name: memory-demo
  namespace: mem-example
  labels:
    app: stress
    tier: develop
spec:
  containers:
  - name: memory-demo-ctr
    image: polinux/stress

metadata.labels은 dictionary 구조를 가지고 있어 key/value 쌍을 입력할 수 있다.

spec.containers는 배열 형식으로 다수의 container를 설정할 수 있다.

 

ReplicaSet

Kubernetes에서는 동일한 Container를 가지고 동일한 일을 하는 다수의 POD를 통합해 관리하기 위해 ReplicaSet이라는 개념이 나왔다.

apiVersion: apps/v1
kind: ReplicaSet
metadata:
  name: frontend
  labels:
    app: guestbook
    tier: frontend
spec:
  replicas: 3
  selector:
    matchLabels:
      tier: frontend
  template:
    metadata:
      labels:
        tier: frontend
    spec:
      containers:
      - name: php-redis
        image: gcr.io/google_samples/gb-frontend:v3

기본 골격은 POD와 같이 ROOT path에는 apiVersion, kind, metadata, spec이 존재한다.

spec.template에는 apiVersion, kind를 제외한 POD의 구조가 입력된다.

spec.replicas는 생성하고 유지할 POD의 갯수를 지정한다.

spec.selector.matchLabels은 POD를 관리하기 위한 요소다.

selector.matchLabels에는 spec.template.metadata.labels에 있는 key/value를 입력해야만 하며 다를 경우 에러가 발생하면서 생성되지 않는다.

(selector.matchLabels를 명시하지 않으면 spec.template.metadata.labels의 app을 기본값으로 사용한다.)

 

Label & Selector

kubernetes cluster에는 다수의 ReplicaSet이 존재할 수 있다.

kubectl의 option --selector <key=value> 을 이용하면 해당 Label을 가진 POD의 목록만을 가져올 수 있다.

$ kubectl get pod --selector tier=frontend
$ kubectl delete pod --selector tier=frontend

또한 나중에 배우게 될 Service에서도 Selector를 이용해 Labels을 가지고 있는 POD을 연결할 수 있다.

apiVersion: v1
kind: Service
metadata:
  name: my-service
spec:
  selector:
    app: MyApp
  ports:
    - protocol: TCP
      port: 80
      targetPort: 9376

위 Service는 metadata.labels 중 app:MyApp 인 field를 가진 POD에 요청을 전달하게 된다.

 

 

Deployment

ReplicaSet으로 배포된 POD의 배포 전략을 구성하고 싶을 때 Deployment 사용할 수 있다.

공식문서에서는 ReplicaSet보단 Deployment를 사용해 POD를 배포할 것을 권장한다.

(Deployment를 생성하면 ReplicaSet도 생성됨)

 

배포 전략이란 새로운 이미지를 배포할 때 Rolling Update/Recreate/Canary 등의 전략을 선택하고

배포가 실패했을 시 Rollback 전략을 말한다.

 

더 자세한 내용은 다음에 해준다고 한다.

기본적인 YAML 형식은 ReplicaSet과 같으며 kind가 Deployment가 변경된다.

apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-deployment
  labels:
    app: nginx
spec:
  replicas: 3
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: nginx:1.14.2
        ports:
        - containerPort: 80

 

시험 Tip!

CKA 시험을 볼 때 YAML을 처음부터 작성하는 건 큰 부담이 된다.

따라서 아래와 같은 명령어를 통해 deployment의 기본 골격 YAML을 얻을 수 있다.

$ kubectl create deployment nginx --image=nginx --dry-run -o yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  creationTimestamp: null
  labels:
    app: nginx
  name: nginx
spec:
  replicas: 1
  selector:
    matchLabels:
      app: nginx
  strategy: {}
  template:
    metadata:
      creationTimestamp: null
      labels:
        app: nginx
    spec:
      containers:
      - image: nginx
        name: nginx
        resources: {}
status: {}

--dry-run option은 deployment를 실제 생성하지 않음을 명시한다.

 

정리

POD에 대한 개념을 다시 정리할 수 있었다.

Label과 Selector의 의미를 정확히 알 수 있었다.

Pod, ReplicaSet, Deployment의 관계를 알 수 있었다.

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

Kubernetes Data Plane 이란

Kubernetes를 설치했다면 거기에서 동작시킬 어플리케이션은 어디서 어떻게 동작이 될까?

Data Plane은 사용자의 application이 돌아갈 수 있도록 도와주는 component를 나타낸다.

Data Plane의 component는 모든 Minion(Worker) Node에 위치해 있으며 Control Plane에 응답하거나 Data Plane 간에 요청/응답 한다.

구성요소로는 kubelet, kube-proxy, runtime container등이 있다.

 

 

Runtime Container

실제 Container를 동작/정지시킬 수 있는 daemon을 의미한다.

주로 Docker를 많이 사용하며 이 밖에도 rocket, CRI-O 등이 있다.

역할

  • Container lifecycle 관리

 

Kubelet

각 Worker Node를 배(ship)라고 생각했을 때 kubelet은 이 배의 선장을 의미한다.

 

역할

  • Control Plane의 Scheduler(kube api-server)로부터 배정된 Container를 실행시키거나 종료한다.
  • 주기적으로 Container의 상태를 kube api-server에 report한다.

 

예시 1. Container 생성 요청을 받았을 경우 kubelet은 Container Runtime 데몬에게 명령한다.

예시 2. 주기적으로 Container의 상태를 Control Plane에 report 한다.

 

kubelet은 다른 component와는 다르게 kubeadm을 통해 설치할 수 없고 오직 manual 적으로만 설치가 가능하다고 한다.

역시 설치 부분은 다음에 한다고 한다.

kube-proxy

같은 Kubernetes Cluster에 있는 WAS Pod이 Database Pod에 접근해 SQL을 사용하고 싶다면 어떻게 해야할까?

WAS Pod이 Database Pod의 IP를 알고 있으면 된다.

Worker Node의 Pod은 실행될 때 k8s cluster 내부 IP를 할당받는다.

그런데 Pod은 언제든 다른 node로 옮겨질 수 있고 그 때 다른 IP가 할당될 것이다.

이를 위해 kubernetes에서는 Service를 통해 Pod 간의 네트워크 통신을 한다.

Service는 특정 IP를 배정받아 가지고 있고 또한 연결시켜줄 Pod의 IP를 알고 있다.

그러면 WAS Pod는 Database Pod이 연결되어 있는 Service의 IP를 알면 된다.

Service IP를 매번 알아야 하고 Service IP 역시 변경될 수 있기 때문에 Service Name을 이용해 통신을 할 수 있다.

즉, WAS Pod는 Database Pod이 연결된 Service의 이름을 알면 된다.

 

이러한 통신을 할 수 있게 해주는게 kube-proxy의 역할이다.

kube-proxy는 모든 node에 설치되며 Service와 연결된 Pod IP에 대한 일종의 IPTABLE을 공유하고 있다.

Kubernetes Cluster에서 IP가 변경되는 특정 이벤트가 발생하게 되면 모든 node의 kube-proxy가 자신의 Table을 업데이트 한다.

 

간략하게 아래와 같은 Table 정보를 모든 kube-proxy가 가지고 있게되며 이를 통해 Pod 간의 네트워크 통신을 도와준다.

Service Name Service IP Pod IP
WAS 10.96.0.12 10.32.0.15, 10.32.0.16
Database 10.96.1.12 10.32.1.15, 10.32.1.16

Kubernetes의 네트워크 부분은 이보다 더 많은 내용이 있기 때문에 다음 시간에 더 깊게 살펴본다고 한다.

 

kube-proxy는 kubeadm으로 설치가 가능하며 설치하게 되면 kube-proxy pod를 daemon-set으로 설치한다

정리

Data Plane의 구성요소에 대해 간략하게 살펴보았다.

kubelet과 kube-proxy의 역할에 대해 정리할 수 있었고 Control Plane과의 관계도 자세히 알 수 있었다.

 

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

 

Kuberentes Control Plane 이란

Kubernetes에 배포되는 Container를 관리하기 위한 구성요소들을 Kubernetes Control Plane이라고 한다.

주로 master node에 배포되며 minion node의 kubelet과 통신하면서 Container를 관리한다.

 

Control Plane의 구성 요소

etcd : K-V 저장소이며 kubernetes cluster 정보를 저장한다.

kube-api-server : kubernetes의 API server이며 minion node를 관리하는 kubelet, 사용자 client kubectl 등과 통신한다.

kube-controller-manager : kubernetes의 resource(node, replicaset, pod, service account, secret 등)을 모니터링하며 관리한다.

kube-scheduler : pod가 어느 minion node에 배치될지를 결정한다.

 

kube-api-server

kubernetes의 API server

역할 :

  • Control Plane의 구성요소 및 minion node의 kubelet 사이를 통신해주는 역할을 한다.
  • 사용자가 kubectl을 사용할 때 요청을 받아 처리하는 역할을 한다.

 

예시. kubectl create pod Process

  1. Authenticate User
    - 사용자가 인증된 사용자인지 확인한다. (cert key 또는 oidc)
  2. Validate Request
    - 요청이 올바른 요청인지 확인한다.
  3. Retrieve data
    - 요청에 대한 결과를 응답한다. (Pod 생성 요청 완료)
  4. Updatae ETCD
    - 생성 요청된 POD 정보를 ETCD에 업데이트 한다.
  5. Scheduler
    - Scheduler에게 POD이 배치될 Minion Node를 요청한다.
  6. Kubelet
    - Schduler에게 받은 Minion Node의 Kubelet에게 POD 생성을 요청한다.

 

kube-controller-manager

Kubernetes의 resource를 모니터링 및 관리

하나의 프로세스지만 각 resource를 담당하는 모듈이 다 별도로 존재함

 

역할 :

  • Kubernetes Resource 상태 모니터링(Watch Status)
  • Resource에 이슈 발생 시 조치(Remediate Situation)

예시. node-controller monitoring

  1. Node Monitor Period (5s)
    - kube-api-server --> kublet을 통해 5초마다 node의 상태를 요청한다.
  2. Node Monitor Grace Period (40s)
    - kubelet이 응답하지 않았을 시, unreachable 상태를 마킹하고 40초를 더 대기한다.
  3. POD eviction TImeout (5m)
    - 40초 이후에도 응답하지 않을 시, 해당 node에 떠있는 POD를 다른 node로 이전시킨다.
    - POD이 replicaset이라면 replicaset-controller를 통해 POD을 이전시킨다.

예시. replicaset-controller monitoring

  1. Replicaset의 POD 갯수가 유지되고 있는지 확인한다.
  2. 갯수가 맞지 않을 경우 새로운 POD를 생성한다.

kube-scheduler

POD를 어느 node에 배정될지 결정

직접 POD를 실행시키지 않음, POD를 실행시키는 역할은 각 minion node의 kubelet

 

역할:

  • 정해진 Scheduling 방식에 의해 POD이 배정될 node를 결정한다.

예시. POD Scheduling

  1. 4개의 minion node가 각기 다른 가용 CPU resource를 가지고 존재한다.
    - minion 1 : CPU 4 core
    - minion 2 : CPU 6 core
    - minion 3 : CPU 12 core
    - minion 4 : CPU 16 core
  2. CPU를 10 core 사용하는 POD 생성 요청이 들어와 이를 Sheduleing 함.
  3. POD가 들어갈 수 없는 minion 1, minion 2를 제외함
  4. POD가 생성되었을 경우 minion node의 가용 CPU를 계산함
    - minion 3 : CPU 2 core
    - mnion 4 : CPU 6 core
  5. minion 4의 가용 CPU가 많기 때문에 minion 4에 POD를 배정할 것을 결정함.
  6. minion 4의 Kubelet이 POD를 생성함.

Affinity, taint-and-toleration 등에 의해 스케쥴링 방식이 위 예시처럼 간단하진 않다고 한다.

더 자세한 내용은 다음에 해준다고함.

정리

control plane 구성요소를 간단하게 살펴볼 수 있었음

각 구성요소 간의 통신을 위한 certificate는 다음에 살펴본다고 함

kubeadm 또는 manaual deploy 방법 또한 다음에 살펴본다고 함

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

 

ETCD란?

Distributed Reliable Key Value Store

일반적인 Key/Value Store보다 높은 안정성과 분산 환경을 제공해줄 수 있어 위와 같이 설명된 것 같다.

또한 Key/Value Store이다보다 사용이 굉장히 쉽고 적은 데이터 환경에서는 아주 빠르게 이용할 수 있다.

하지만 많은 데이터가 저장되는 용도로는 좋지 않다고 한다.

 

ETCD 기본 명령어

ETCD는 일반 K-V Store 처럼 Key/Value에 대한 operation을 지원한다.

ETCD 설치는 아래 링크 참조

https://github.com/etcd-io/etcd/releases

 

etcd-io/etcd

Distributed reliable key-value store for the most critical data of a distributed system - etcd-io/etcd

github.com

$ ./etcdctl version
etcdctl version: 3.4.9
API version: 3.4

# K-V 생성
$ ./etcdctl put key value
OK

# K-V 조회
$ ./etcdctl get key
key
value

# K-V 삭제
$ ./etcdctl del key
1

일반적인 K-V operation 이외에 지원되는 기능으로는 Key를 Directory 구조로 사용할 수 있다.

$ ./etcdctl put parent/child1 value1
OK

$ ./etcdctl put parent/child2 value2
OK

$ ./etcdctl get parent/ --prefix
parent/child1
value1
parent/child2
value2

$ ./etcdctl del parent/ --prefix
2

위처럼 --prefix option을 통해 parent의 하위 directory를 조회/삭제하는 것과 유사한 결과가 나오는 것을 확인할 수 있다.

 

Kubernetes의 ETCD

Kubernetes에서 ETCD이 역할은 k8s cluster의 정보를 저장하는 역할이다.

k8s cluster 정보라는 것은 nodes, pods, configs, secrets 등 kubectl get 을 사용했을 때 조회할 수 있는 resource type 등을 의미한다.

 

kubernetes를 위한 ETCD를 배포하기 위한 방법은 Manual 적으로 배포하는 방법과 kubeadm 이용하는 방법이 있다.

kubeadm를 사용할 경우 kube-system namespace(master node)에 etcd-master가 pod 형태로 배포된다.

 

kubernetes는 각 resource type을 key로 가지고 생성된 resource를 value 형태로 ETCD에 저장한다.

$ etcdctl --cacert=/etc/kubernetes/pki/etcd/ca.crt \
--cert=/etc/kubernetes/pki/etcd/server.crt \
--key=/etc/kubernetes/pki/etcd/server.key \
get / --prefix --keys-only
/registry/apiregistration.k8s.io/apiservices/v1.

/registry/apiregistration.k8s.io/apiservices/v1.admissionregistration.k8s.io

/registry/apiregistration.k8s.io/apiservices/v1.apiextensions.k8s.io

/registry/apiregistration.k8s.io/apiservices/v1.apps

/registry/apiregistration.k8s.io/apiservices/v1.authentication.k8s.io

/registry/apiregistration.k8s.io/apiservices/v1.authorization.k8s.io

/registry/apiregistration.k8s.io/apiservices/v1.autoscaling

/registry/apiregistration.k8s.io/apiservices/v1.batch

/registry/apiregistration.k8s.io/apiservices/v1.coordination.k8s.io
...

$ etcdctl --cacert=/etc/kubernetes/pki/etcd/ca.crt \
--cert=/etc/kubernetes/pki/etcd/server.crt \
--key=/etc/kubernetes/pki/etcd/server.key \
get /registry/pods/kube-system --prefix --keys-only
/registry/pods/kube-system/coredns-5644d7b6d9-49pb2

/registry/pods/kube-system/coredns-5644d7b6d9-98tmz

/registry/pods/kube-system/etcd-master

/registry/pods/kube-system/kube-apiserver-master

/registry/pods/kube-system/kube-controller-manager-master

/registry/pods/kube-system/kube-proxy-972kw

/registry/pods/kube-system/kube-proxy-blr9v

/registry/pods/kube-system/kube-scheduler-master

/registry/pods/kube-system/weave-net-9pzcd

/registry/pods/kube-system/weave-net-ls2gv

 

ETCD High Availability

 

ETCD는 안정성이 뛰어나다고 했는데 그 얘기는 당연히 하나가 죽어도 다른 ETCD에 의해 그 역할이 대체된다는 의미를 나타낸다.

ETCD를 실행할 때 --initial-cluster option으로 clustering할 다른 ETCD의 IP와 Port를 입력하게 되면 아래 그림처럼 ETCD Cluster가 구성된다.

 

정리

kubeadm을 이용한 ETCD 배포, ETCD certification 등 많은 부분이 추후에 설명될거라고 한다.

기본 개념을 정리하는 단계라 비교적 쉬운 내용이었다.

ETCD의 leader election 방법이나 data replication은 ETCD를 깊게 볼 일이 있으면 그때 확인 해 봐야겠다.

+ Recent posts