Step 13 연습 과제 — 스케줄링 제어
모든 실습은 step13 네임스페이스에서. 노드 테인트는 워커에만, 고유 키로, 실습 직후 원복한다.
과제 1. required nodeAffinity로 Pending 만들고 진단하기
kubernetes.io/os: windows를 required nodeAffinity로 요구하는 파드를 만들어라(우리 노드는 모두 linux). 파드가 Pending이 되는 것을 확인하고, describe로 사유를 읽어라.
해답
apiVersion: v1
kind: Pod
metadata: { name: q1-pending, namespace: step13 }
spec:
affinity:
nodeAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
nodeSelectorTerms:
- matchExpressions:
- { key: kubernetes.io/os, operator: In, values: ["windows"] }
containers:
- { name: c, image: nginx:1.27-alpine, resources: { requests: { cpu: 10m, memory: 16Mi } } }
kubectl apply -f q1.yaml
kubectl get pod q1-pending -n step13 # STATUS: Pending
kubectl describe pod q1-pending -n step13 | grep -A3 Events
# -> "didn't match Pod's node affinity/selector"
required는 만족 노드가 없으면 영원히 Pending. preferred였다면 그냥 아무 노드에나 떴을 것이다.
과제 2. soft anti-affinity로 3개 흩기 (Pending 없이)
replicas=3인 Deployment를 만들되, 워커가 2개뿐이라 hard antiAffinity면 1개가 Pending이 된다. soft(preferred) podAntiAffinity로 바꿔 3개 모두 Running이면서 최대한 흩어지게 하라.
해답
spec:
affinity:
podAntiAffinity:
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 100
podAffinityTerm:
labelSelector: { matchLabels: { app: q2 } }
topologyKey: kubernetes.io/hostname
kubectl get pod -n step13 -l app=q2 -o wide
# 3개 모두 Running. 2개는 각기 다른 워커, 3번째는 둘 중 하나에 얹힌다.
soft라서 "못 흩어도 배치는 한다". hard였다면 3번째가 Pending. 이것이 12-5 함정의 해법이다.
과제 3. 전용 노드 시뮬레이션 (taint + toleration + nodeSelector)
learn-worker2를 "배치 전용 노드"로 만들어라. 테인트 batch=true:NoSchedule를 걸고, toleration이 있는 파드만, 그리고 그 파드는 실제로 worker2에 뜨게 하라. 실습 후 반드시 테인트 제거.
해답
kubectl taint nodes learn-worker2 batch=true:NoSchedule
apiVersion: v1
kind: Pod
metadata: { name: q3-batch, namespace: step13 }
spec:
nodeSelector: { kubernetes.io/hostname: learn-worker2 }
tolerations:
- { key: batch, operator: Equal, value: "true", effect: NoSchedule }
containers:
- { name: c, image: busybox:1.36, command: ["sleep","3600"], resources: { requests: { cpu: 10m, memory: 16Mi } } }
kubectl apply -f q3.yaml
kubectl get pod q3-batch -n step13 -o wide # NODE: learn-worker2
# ★ 원복
kubectl taint nodes learn-worker2 batch=true:NoSchedule-
kubectl describe node learn-worker2 | grep -i taint # <none> 확인
toleration만 있고 nodeSelector가 없으면 worker2로 "갈 수도" 있을 뿐 보장되진 않는다. toleration은 "허용"이지 "유인"이 아니다.
과제 4. topologySpread를 soft로 완화하기
13-7의 topo Deployment를 replicas=5로 늘리면 어떻게 되는가? whenUnsatisfiable을 DoNotSchedule과 ScheduleAnyway로 각각 두고 결과를 비교하라.
해답
DoNotSchedule(하드): 워커 2개에 2/2까지 채운 뒤 5번째는 skew 위반이라 Pending.
ScheduleAnyway(소프트): 5개 모두 Running. 최대한 균등(3/2)하게 배치하지만 Pending은 없다.
kubectl get pod -n step13 -l app=topo -o wide
kubectl describe pod <pending-pod> -n step13 | grep -A3 Events
# 하드일 때: "didn't match pod topology spread constraints"
"절대 Pending은 안 된다"는 서비스면 소프트, "무조건 고르게"가 중요하면 하드.
과제 5. podAffinity의 "닭이 먼저냐" 함정 재현
기준이 될 app: db 파드가 아직 없는 상태에서, required podAffinity로 app: db를 요구하는 파드를 먼저 만들면? 그 뒤 db를 띄우면?
해답
# db 없이 required podAffinity 파드부터 apply
kubectl get pod q5-client -n step13 # Pending
kubectl describe pod q5-client -n step13 | grep -A3 Events
# -> "didn't match pod affinity rules" (매칭할 db 파드가 없음)
# 이제 db 를 띄우면
kubectl run q5-db --image=nginx:1.27-alpine -n step13 --labels=app=db
# 잠시 후 q5-client 가 db 와 같은 노드에서 Running 으로 전환된다
required podAffinity는 기준 파드가 나타날 때까지 기다린다. 그래서 초기 배포 순서가 중요하거나, soft로 완화하는 편이 안전하다.