Theme:

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: 이미 필요한 이미지를 가진 노드 선호
PLAINTEXT
Pod 생성 → 필터링 (부적합 노드 제외) → 스코어링 (점수 부여) → 최고 점수 노드에 바인딩

nodeSelector — 가장 단순한 노드 선택

YAML
spec:
  nodeSelector:
    disk-type: ssd
    region: ap-northeast-2
SHELL
# 노드에 레이블 추가
kubectl label nodes node-1 disk-type=ssd

nodeSelector는 AND 조건의 정확한 매칭만 가능합니다. 더 유연한 조건이 필요하면 nodeAffinity를 사용합니다.

Node Affinity — 유연한 노드 선택

YAML
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 — 함께 배치

YAML
spec:
  affinity:
    podAffinity:
      requiredDuringSchedulingIgnoredDuringExecution:
        - labelSelector:
            matchExpressions:
              - key: app
                operator: In
                values:
                  - cache
          topologyKey: kubernetes.io/hostname  # 같은 노드에

캐시 서버와 같은 노드에 배치하여 네트워크 지연을 최소화하는 경우에 사용합니다.

Pod Anti-Affinity — 분산 배치

YAML
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/zoneAZ 단위 분산
topology.kubernetes.io/region리전 단위 분산

Topology Spread Constraints

Pod을 토폴로지에 걸쳐 균등하게 분산합니다. Anti-Affinity보다 세밀한 제어가 가능합니다.

YAML
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개씩 배치됩니다.

커스텀 스케줄러

기본 스케줄러 외에 추가 스케줄러를 배포할 수 있습니다.

YAML
spec:
  schedulerName: my-custom-scheduler  # 기본: default-scheduler
  containers:
    - name: app
      image: my-app:1.0

머신러닝 워크로드의 GPU 최적화, 특수한 배치 전략 등이 필요할 때 사용합니다.

스케줄링 디버깅

SHELL
# 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 selectornodeSelector 불일치레이블 확인
node(s) had taintsTaint 미허용Toleration 추가
no persistent volumes availablePV 없음PV/StorageClass 확인

실무 권장 설정

YAML
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 분산을 설정하여 고가용성을 확보하는 것이 중요합니다.

댓글 로딩 중...