자격증 학원 › CKA › 워크로드 · 스케줄링

강의 11 / 28 · 약 30분

스케줄링 — 파드를 원하는 노드에

nodeName · nodeSelector · nodeAffinity, taint 와 toleration, 파드 affinity, DaemonSet, static pod, PriorityClass.

이 강의를 마치면

  • nodeSelector 와 nodeAffinity 로 특정 노드에 파드를 배치할 수 있다
  • taint 와 toleration 의 관계("들어갈 수 있다" ≠ "반드시 간다")를 설명할 수 있다
  • DaemonSet 과 static pod 을 만들 수 있다
  • FailedScheduling 메시지를 읽고 원인을 찾을 수 있다

스케줄러가 노드를 고르는 과정

  1. 거르기(Filter) — 조건에 안 맞는 노드를 뺀다: 자원 부족, nodeSelector 불일치, 견디지 못하는 taint, cordon, 볼륨 제약 …
  2. 점수(Score) — 남은 노드에 점수를 매겨 가장 좋은 곳에 둔다(선호 affinity, 고른 분산 등).

남는 노드가 없으면 파드는 Pending 이고 이벤트에 이유가 나옵니다.

0/3 nodes are available: 1 node(s) had untolerated taint {node-role.kubernetes.io/control-plane: },
2 node(s) didn't match Pod's node affinity/selector.

이 문장을 끝까지 읽는 것이 스케줄링 트러블슈팅의 전부입니다.

노드 고르기 네 가지

① nodeName — 스케줄러를 건너뛰고 노드를 직접 지정(가장 강제적, 조건 검사 없음).

② nodeSelector — 노드 레이블이 일치하는 곳에만.

kubectl label node node02 disktype=ssd
kubectl get nodes -L disktype          # 레이블을 열로
spec:
  nodeSelector:
    disktype: ssd

③ nodeAffinity — 더 풍부한 조건(In, NotIn, Exists, Gt …)과 “꼭”/“되도록”.

spec:
  affinity:
    nodeAffinity:
      requiredDuringSchedulingIgnoredDuringExecution:      # 꼭
        nodeSelectorTerms:
        - matchExpressions:
          - key: disktype
            operator: In
            values: [ssd, nvme]
      preferredDuringSchedulingIgnoredDuringExecution:     # 되도록
      - weight: 50
        preference:
          matchExpressions:
          - key: zone
            operator: In
            values: [a]

문서에서 “Assign Pods to Nodes using Node Affinity” 를 검색하면 이 예제가 그대로 있습니다. 외우지 말고 가져다 고치세요.

④ podAffinity / podAntiAffinity — 다른 파드의 위치 기준. “캐시 파드와 같은 노드에”, “같은 앱 파드끼리 다른 노드에”.

spec:
  affinity:
    podAntiAffinity:
      requiredDuringSchedulingIgnoredDuringExecution:
      - labelSelector:
          matchLabels:
            app: web
        topologyKey: kubernetes.io/hostname     # '노드' 단위로 떨어뜨려라

taint 와 toleration

노드 쪽의 밀어내기(taint) + 파드 쪽의 견디기(toleration).

kubectl taint node node01 dedicated=gpu:NoSchedule        # 걸기
kubectl taint node node01 dedicated=gpu:NoSchedule-       # 풀기(끝에 -)
kubectl describe node node01 | grep -i taint
effect새 파드이미 있는 파드
NoSchedule못 들어옴그대로
PreferNoSchedule되도록 안 들어옴그대로
NoExecute못 들어옴쫓겨남
spec:
  tolerations:
  - key: dedicated
    operator: Equal       # 값까지 일치(기본)
    value: gpu
    effect: NoSchedule
  - key: node-role.kubernetes.io/control-plane
    operator: Exists      # 값 무관
    effect: NoSchedule

toleration 은 입장권일 뿐 배정표가 아닙니다. “GPU 노드에서만 돌게 하라”는 문제는 toleration(들어갈 수 있게) + nodeSelector/affinity(거기로 가게) 둘 다 필요합니다.

DaemonSet — 노드마다 하나

로그 수집기, 모니터링 에이전트, CNI 처럼 모든(또는 특정) 노드에 하나씩 떠야 하는 것.

kubectl create daemonset 은 없습니다. 디플로이먼트 뼈대에서 바꿉니다.

kubectl create deployment agent --image=busybox:1.37 $do > ds.yaml
# kind: Deployment → DaemonSet, replicas · strategy · status 줄 삭제

컨트롤 플레인 노드에도 띄우려면 control-plane taint 를 견디는 toleration 을 넣습니다.

static pod

API 서버가 아니라 노드의 kubelet 이 직접 띄우는 파드. 컨트롤 플레인 부품이 이렇게 돕니다(아키텍처 강의).

ssh node01
grep staticPodPath /var/lib/kubelet/config.yaml      # 보통 /etc/kubernetes/manifests
kubectl run static-web --image=nginx:1.27 $do > /etc/kubernetes/manifests/static-web.yaml
exit
kubectl get pods -o wide        # static-web-node01 (이름 뒤에 노드 이름이 붙는다)

지우려면 그 노드에서 파일을 지웁니다. kubectl delete pod 로 지워도 곧 다시 생깁니다.

cordon · drain

kubectl cordon node01      # 새 파드 금지(기존 파드는 그대로)
kubectl uncordon node01
kubectl drain node01 --ignore-daemonsets --delete-emptydir-data   # 비우기(유지보수)

drain 은 자세히 노드 유지보수에서 다룹니다.

PriorityClass

자원이 모자랄 때 누구를 먼저 배치할지(높은 우선순위는 낮은 것을 밀어낼 수 있음).

kubectl create priorityclass high-priority --value=100000 --description="중요 서비스"
spec:
  priorityClassName: high-priority

⌨️ 직접 해 보기

브라우저 실습 터미널에서 풀고 자동 채점을 받습니다.

✅ 확인 문제

과정 목차