CKA #2 Core Concepts, #3 Scheduling

· Infra, Kubernetes, CKA

Certified Kubernetes Administrator (CKA) with Practice Tests 강의를 듣고 몰랐던 내용, 알고 있어도 중요하다고 생각하는 내용 등을 추가적인 공부와 함께 간략히 정리한 글입니다.

Index

Introduction

섹션2는 기본적인 내용들이라 대체로 쉬웠다. 대부분 기본적인 골자를 설명하는 것이기도 하고, 기본적인 리소스를 생성하는 방법에 대해 다룬다. 알고 있던 기본적인 내용은 생략하고, 들으면서 내가 써볼 일이 없어서 몰랐던 부분들 혹은 알고 있었지만 꽤 중요하거나, CKA 시험을 위해서라도 외워두면 좋을 팁들 위주로 기록했다.

특히 파일을 만들어서 delcarative하게 관리하는 작업은 많이 해보았지만 imperative하게 동작해본 적은 거의 없어서… 특히 kubectl run이나 kubectl create 같은 명령어를 잘 안쓰다보니 익숙하지가 않아 기록해두기로 했다.

apiVersion - v1 vs apps/v1

v1 - 쿠버네티스에서 발행한 첫 stable release API (대부분의 기본적인 API)
apps/v1 - 쿠버네티스 Common API들. Deployment, RollingUpdate, ReplicaSet 등
리소스 별로 다양한 apiVersion이 있다. (rbac.authorization.k8s.io/v1, networking.k8s.io/v1 등)
그래서 이것을 확인하려면, kubectl explain [리소스명] 명령어를 쳐보는 것이 마음이 편하다.

ReplicaSet vs ReplicationController

대충 yaml 파일 쓰는 법이 다르다는 것은 안다. 그런데 본질적으로 뭐가 다른걸까? ReplicationController는 쿠버네티스 프로젝트 초기부터 존재했다. 근데 나는 현업에서 ReplcationController를 쓴 기억이 없다. 나는 ReplicaSet만 썼다. 실제로도 Deployment와 ReplicaSet을 사용하는게 추세라고 한다.

셀렉터

롤링 업데이트 유무

레플리카셋은 yaml에 selector를 정의하고, template도 정의해두어야 한다. 왜? 만약 이미 desired state에 해당하는 개수가 배포되어 있다고 하더라도, 이 파드가 죽을 경우, 레플리카셋이 template을 참고해 새 파드를 생성해 desired state를 충족시켜야 하기 때문이다.

TIP: 파일 없이 replicaset의 replicas 조정하기

현업에서는 yaml 파일들로 관리하거나 오토스케일을 걸어버리니 쓸 일이 없었는데…

kubectl scale --replicas=6 replicaset myapp-replicaset

TIP: 파일없이 리소스 수정하기

얘도 현업에서는 yaml 파일들로 관리하다보니 잘안쓰는 기능이였는데, CKA 시험에서 yaml 파일을 모두 작성하면 시간이 너무 오래 걸리니 edit 유용한 기능일 듯

kubectl edit 리소스명 리소스이름

Deployment와 ReplicaSet의 차이점

Deployment: 파드와 레플리카셋(ReplicaSet)에 대한 선언적 업데이트를 제공

Dployment And ReplicaSet이미지 출처: ReplicaSet과 Deployment

  1. Rolling Update : 새 버전 파드를 하나 생성하고, 이전 버전 파드를 하나 삭제하는 방식을 반복하며 동작
  2. Recreate : 기존 버전 파드 삭제 후 재생성 (파드가 존재하지 않는 순간이 생김)
  3. Blue/Green : 기존 버전 파드를 유지한 후 새 버전 파드를 생성하고, Service가 한 번에 트래픽 전달 대상을 교체함. 이전 버전과 새 버전이 공존하지 않지만, 순간적으로 파드가 2배가 되는 단점 존재
  4. Canary : 구버전 신버전 파드를 모두 만들고, 트래픽 양을 조절한 뒤 교체함

수동 스케줄링

POD가 떠있는 상태로 노드에서 다른 노드로 옮길 수 없다. → 파드도 결국 컨테이너고, 컨테이너는 프로세스다. 노드들은 서로 다른 컴퓨터다. 아무런 변동 없이 한 컴퓨터의 프로세스를 다른 컴퓨터의 프로세스로 옮기는 것은 불가능하기 때문이다.

yaml 파일에서 nodeName이라는 키를 이용해서 노드를 지정해줄 수 있음

셀렉터

이건 잘 안쓰던 옵션이긴한데, 알아두면 편하게 쓸 것 같아서 기록

kubectl get pods --selector "app=App1"

Node Label 보기

--show-labels=true

Tolerations & Taints

Taints and Tolerations이미지 출처: Kubernetes Pod 배치전략, Taint와 Toleration에 대해 이해하고 실습해보기

Taint된 노드에 Toleration된 팟은 “배치될 수 있다”이지 배치가 보장되는 것이 아니다.

Taints

kubectl describe node node01 | grep Taint

kubectl taint nodes node01 spray=mortein:NoSchedule # Key=Value:Effect

# Taint 삭제
kubectl taint nodes controlplane node-role.kubernetes.io/control-plane:NoSchedule-

Tolerations

NodeSelectors

kubectl label nodes <node-name> <label-key>=<label-value>
kubectl label nodes <node-name> size=Large
# Pod Yaml
...
nodeSelector:
  size: Large

노드 셀렉터를 이용해 파드가 배치될 노드를 선택할 수 있게끔 만들수 있다.

하지만 명시적으로 배치될 노드를 선택하는 경우는 가능하지만, 조건이나 범위 등이 주어지는 상황에서 처리할 수 없다는 문제가 있다. 그래서 Affinity가 생겼다.

# Pod Yaml
affinity:
  nodeAffinity:
    requiredDuringSchedulingIgnoreDuringExecution:
      nodeSelctorTerms:
      - matchExpressions:
        - key: size
          operator: In
          values:
          - Large
          
    preferredDuringSchedulingIgnoredDuringExecution:
    - weight: 10
      preference:
      - matchExpressions:
        - key: disktype
          operator: In
          values:
          - hdd

참고자료 - [k8s] 파드 스케쥴링 - Node Affinity(노드 어피니티)

Taint/Toleration과 NodeAffinity 함께 사용하기

Taint/Toleration 만으로는 특정 팟이 특정 노드에 꽂힌다는 보장을 할 수 없다.
Taint된 노드에 Toleration된 팟은 “배치될 수 있다”이지 배치가 보장되는 것이 아니기 때문. 따라서, NodeAffinity까지 적절히 사용하여 특정 파드를 특정 노드에 배치시켜야 한다.

컨테이너는 pod memory limit을 걸어도 메모리를 더 쓸 수 있다.

하지만 memory limit보다 더 많은 메모리를 계속해서 쓰면, 파드가 터미네이트 당한다. OOM으로 종료된다.

리소스 사용량 제한 → 네임스페이스 단위로 쿼터를 제공할 수 있다.

Static Pod 개념

Multiple Scheduler

apiVersion: v1
kind: Pod
metadata:
  name: nginx
spec:
  containers:
  - image: nginx
    name: nginx
  schedulerName: my-custom-controller

스케줄러가 잘 동작하는지 보고 싶다면, 아래 명령어를 통해 확인할 수 있다.

kubectl get events -o wide
kubectl logs my-custom-scheduler --namespace=kube-system

Custom Scheduler yaml

apiVersion: v1
kind: Pod
metadata:
  labels:
    run: my-scheduler
  name: my-scheduler
  namespace: kube-system
spec:
  serviceAccountName: my-scheduler
  containers:
  - command:
    - /usr/local/bin/kube-scheduler
    - --config=/etc/kubernetes/my-scheduler/my-scheduler-config.yaml
    image: <use-correct-image>
    livenessProbe:
      httpGet:
        path: /healthz
        port: 10259
        scheme: HTTPS
      initialDelaySeconds: 15
    name: kube-second-scheduler
    readinessProbe:
      httpGet:
        path: /healthz
        port: 10259
        scheme: HTTPS
    resources:
      requests:
        cpu: '0.1'
    securityContext:
      privileged: false
    volumeMounts:
      - name: config-volume
        mountPath: /etc/kubernetes/my-scheduler
  hostNetwork: false
  hostPID: false
  volumes:
    - name: config-volume
      configMap:
        name: my-scheduler-config

POD Scheduling

apiVersion: v1
kind: Pod
metadata:
  name: nginx
spec:
  priorityClassName: high-priority
  containers:
  - image: nginx
    name: nginx
apiVersion: scheduling.k8s.io/v1
kind: PriorityClass
metadata:
  name: high-priority
value: 1000000 # Priority Value
globalDefault: false
description: "This priority class should be used for XYZ service pods only."

각 단계는 플러그인으로 이루어져 있다. 자신만의 플러그인을 작성해서 붙일 수도 있다. Extension을 지원한다.

Scheduler 동작 과정이미지 출처: Certified Kubernetes Administrator (CKA) with Practice Tests(Udemy)

apiVersion: kubescheduler.config.k8s.io/v1
kind: KubeSchedulerConfiguration
profiles:
- schedulerName: my-scheduler-2
  plugins:
    score:
      disabled:
      - name: TaintToleration
      enabled:
      - name: MyCustomPluginA
      - name: MyCustomPluginB

여러 스케줄러를 쓸 때는 RaceCondition이 발생하는 것에 주의해야한다. 서로 다른 두 개 이상의 스케줄러가 동시에 스케줄링을 하게 될 때 동시에 같은 노드에 파드를 스케줄링할 때 문제가 발생할 수 있음에 유의하자.