본 포스팅은 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의 관계를 알 수 있었다.

+ Recent posts