스케줄러 — Pod이 어떤 노드에 배치되는지 결정하는 과정
kubectl apply로 Pod을 배포하면 "어떤 노드"에서 실행될지 누가, 어떤 기준으로 결정할까요?
Kubernetes 스케줄러(kube-scheduler)는 새로 생성된 Pod을 적절한 노드에 배치하는 역할을 합니다. 단순히 빈 노드를 찾는 것이 아니라, 리소스 상황, 제약 조건, 분산 정책 등을 종합적으로 고려합니다.
스케줄링 과정
스케줄러는 두 단계를 거칩니다.
1단계: 필터링 (Filtering)
Pod을 실행할 수 없는 노드를 제외합니다.
- 리소스 부족: CPU/메모리 requests를 만족하지 못하는 노드
- nodeSelector/Affinity: 조건에 맞지 않는 노드
- Taints: 해당 Taint를 Tolerate하지 않는 Pod은 배제
- PV 토폴로지: 볼륨이 특정 AZ에 있으면 해당 AZ 노드만 통과
- Port 충돌: hostPort가 이미 사용 중인 노드
2단계: 스코어링 (Scoring)
필터링을 통과한 노드들에 점수를 매깁니다.
- LeastRequestedPriority: 리소스 여유가 많은 노드 선호
- BalancedResourceAllocation: CPU/메모리 사용 비율이 균형 잡힌 노드 선호
- InterPodAffinity: Pod Affinity 규칙에 부합하는 노드에 가산점
- NodeAffinity: preferredDuringScheduling 규칙에 따른 가산점
- ImageLocality: 이미 필요한 이미지를 가진 노드 선호
Pod 생성 → 필터링 (부적합 노드 제외) → 스코어링 (점수 부여) → 최고 점수 노드에 바인딩
nodeSelector — 가장 단순한 노드 선택
spec:
nodeSelector:
disk-type: ssd
region: ap-northeast-2
# 노드에 레이블 추가
kubectl label nodes node-1 disk-type=ssd
nodeSelector는 AND 조건의 정확한 매칭만 가능합니다. 더 유연한 조건이 필요하면 nodeAffinity를 사용합니다.
Node Affinity — 유연한 노드 선택
spec:
affinity:
nodeAffinity:
# 필수 조건: 반드시 충족해야 스케줄링
requiredDuringSchedulingIgnoredDuringExecution:
nodeSelectorTerms:
- matchExpressions:
- key: topology.kubernetes.io/zone
operator: In
values:
- ap-northeast-2a
- ap-northeast-2b
# 선호 조건: 충족하면 가산점
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 80
preference:
matchExpressions:
- key: disk-type
operator: In
values:
- ssd
- weight: 20
preference:
matchExpressions:
- key: instance-type
operator: In
values:
- compute-optimized
연산자 종류
| 연산자 | 설명 | 예시 |
|---|---|---|
In | 값이 목록에 포함 | zone In [a, b] |
NotIn | 값이 목록에 미포함 | zone NotIn [c] |
Exists | 키가 존재 | gpu Exists |
DoesNotExist | 키가 없음 | spot DoesNotExist |
Gt | 값이 초과 | cpu-count Gt 4 |
Lt | 값이 미만 | memory-gb Lt 32 |
Pod Affinity / Anti-Affinity
Pod 간의 관계를 기반으로 스케줄링합니다.
Pod Affinity — 함께 배치
spec:
affinity:
podAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
- labelSelector:
matchExpressions:
- key: app
operator: In
values:
- cache
topologyKey: kubernetes.io/hostname # 같은 노드에
캐시 서버와 같은 노드에 배치하여 네트워크 지연을 최소화하는 경우에 사용합니다.
Pod Anti-Affinity — 분산 배치
spec:
affinity:
podAntiAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
- labelSelector:
matchExpressions:
- key: app
operator: In
values:
- web
topologyKey: kubernetes.io/hostname # 서로 다른 노드에
같은 앱의 Pod을 서로 다른 노드에 분산시켜 한 노드 장애 시 전체 서비스가 중단되지 않도록 합니다.
topologyKey
분산의 기준이 되는 토폴로지를 지정합니다.
| topologyKey | 의미 |
|---|---|
kubernetes.io/hostname | 노드 단위 분산 |
topology.kubernetes.io/zone | AZ 단위 분산 |
topology.kubernetes.io/region | 리전 단위 분산 |
Topology Spread Constraints
Pod을 토폴로지에 걸쳐 균등하게 분산합니다. Anti-Affinity보다 세밀한 제어가 가능합니다.
spec:
topologySpreadConstraints:
- maxSkew: 1 # AZ 간 최대 편차
topologyKey: topology.kubernetes.io/zone
whenUnsatisfiable: DoNotSchedule # 불만족 시 스케줄 거부
labelSelector:
matchLabels:
app: web
- maxSkew: 1 # 노드 간 최대 편차
topologyKey: kubernetes.io/hostname
whenUnsatisfiable: ScheduleAnyway # 불만족이어도 스케줄
labelSelector:
matchLabels:
app: web
- maxSkew: 토폴로지 간 Pod 수 최대 차이 (1이면 균등 분배)
- whenUnsatisfiable:
DoNotSchedule(거부) 또는ScheduleAnyway(최선 노력)
예를 들어 3개의 AZ에 6개 Pod을 배포하면 각 AZ에 2개씩 배치됩니다.
커스텀 스케줄러
기본 스케줄러 외에 추가 스케줄러를 배포할 수 있습니다.
spec:
schedulerName: my-custom-scheduler # 기본: default-scheduler
containers:
- name: app
image: my-app:1.0
머신러닝 워크로드의 GPU 최적화, 특수한 배치 전략 등이 필요할 때 사용합니다.
스케줄링 디버깅
# Pod이 Pending인 이유 확인
kubectl describe pod my-pod
# Events:
# Warning FailedScheduling 0/3 nodes are available:
# 1 node(s) had taint {gpu: true}, that the pod didn't tolerate,
# 2 Insufficient memory.
# 노드 리소스 상황 확인
kubectl describe node node-1 | grep -A 5 "Allocated resources"
# 스케줄러 이벤트 확인
kubectl get events --field-selector source=default-scheduler
자주 보는 스케줄링 실패 원인
| 메시지 | 원인 | 해결 |
|---|---|---|
| Insufficient cpu/memory | 리소스 부족 | 노드 추가 또는 requests 조정 |
| node(s) didn't match selector | nodeSelector 불일치 | 레이블 확인 |
| node(s) had taints | Taint 미허용 | Toleration 추가 |
| no persistent volumes available | PV 없음 | PV/StorageClass 확인 |
실무 권장 설정
apiVersion: apps/v1
kind: Deployment
metadata:
name: production-web
spec:
replicas: 6
template:
spec:
affinity:
# AZ 분산
podAntiAffinity:
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 100
podAffinityTerm:
labelSelector:
matchLabels:
app: web
topologyKey: topology.kubernetes.io/zone
topologySpreadConstraints:
- maxSkew: 1
topologyKey: topology.kubernetes.io/zone
whenUnsatisfiable: ScheduleAnyway
labelSelector:
matchLabels:
app: web
containers:
- name: web
image: web-app:1.0
resources:
requests:
cpu: 200m
memory: 256Mi
정리
Kubernetes 스케줄러는 필터링으로 부적합한 노드를 제외하고, 스코어링으로 최적의 노드를 선택합니다. nodeSelector는 단순하지만 제한적이고, nodeAffinity와 podAffinity/AntiAffinity로 더 세밀한 배치 전략을 구현할 수 있습니다. 프로덕션에서는 topologySpreadConstraints로 AZ 분산을 설정하여 고가용성을 확보하는 것이 중요합니다.