Kubernetes 마스터 노드 재기동 시 사용자 파드 네트워크가 중단되지 않는 이유

Kubernetes 마스터 노드 재기동 시 사용자 파드 네트워크가 중단되지 않는 이유

마스터 노드 1대 재기동이 사용자 트래픽에 영향을 주지 않는 이유. 컨트롤 플레인 HA와 데이터 플레인 자립 구조 정리.

Kubernetes 마스터 노드 재기동 시 사용자 파드 네트워크가 중단되지 않는 이유

TL;DR
마스터 3대 HA 구성에서 1대 재기동은 컨트롤 플레인이 2/3 정족수로 동작 중이라 무중단입니다.
컨트롤 플레인이 전부 정지해도 워커 노드의 데이터 플레인(kubelet, kube-proxy, 커널 규칙)이 자립해서 기존 파드와 트래픽은 흐릅니다.
kube-proxy는 Service 추상화를 노드 커널 iptables/IPVS 규칙으로 번역해서 기재하는 노드별 DaemonSet입니다.
중단이 발생하는 시점: apiserver 부재 + kube-proxy 재기동이 겹칠 때, 노드 재부팅 시점, 마스터 노드에 워크로드를 올린 경우.

배경

클라우드 KVM 인스턴스 마이그레이션으로 마스터 노드 1대 재기동이 필요한 상황을 만났습니다. 영향도 점검 답변은 “큰 이슈 없음”이었습니다. 결론은 같았지만 그렇다면 사용자 파드 네트워크는 왜 중단되지 않는지가 궁금해졌습니다. 마스터가 일시 정지하면 클러스터 전체 동작이 멈출 것 같지만 실제로는 그렇지 않습니다. 그 이유를 정리합니다.

컨트롤 플레인 vs 데이터 플레인

쿠버네티스는 두 층으로 분리됩니다.

구성 요소역할
컨트롤 플레인apiserver, etcd, 스케줄러, 컨트롤러매니저“무엇이 되어야 하는가”를 결정·저장
데이터 플레인kubelet, kube-proxy, CNI, 워커 노드 커널결정된 상태를 실제로 실행·라우팅

컨트롤 플레인이 다운되어도 “이미 결정된 상태”는 데이터 플레인이 자체적으로 유지합니다. 변경은 멈추지만 동작은 계속됩니다.

1차 방어: 컨트롤 플레인 HA

마스터 3대 구성에서 1대만 재기동될 때 컨트롤 플레인 자체가 안 멈춥니다.

etcd quorum

etcd는 Raft 합의로 동작합니다. 3대 중 2대가 동작 중이면 정족수(quorum) 충족이라 읽기·쓰기 모두 정상입니다.

마스터 수정족수허용 장애
3대2대1대
5대3대2대

apiserver

apiserver는 stateless라서 LB 뒤에 N대 배치됩니다. 1대 빠져도 나머지가 받습니다.

컨트롤러매니저·스케줄러

leader election으로 1대만 활성 상태로 동작합니다. 리더가 다운되면 남은 노드 중 하나가 즉시 리더로 올라옵니다. 페일오버 지연은 보통 수 초입니다.

결과: 1대 재기동은 “1/3 용량 감소” 정도이지 컨트롤 플레인 다운이 아닙니다. 인프라 영향도 평가의 “큰 이슈 없음”이 여기서 나옵니다.

2차 방어: 데이터 플레인 자립

그렇다면 마스터 3대가 동시에 다운되면 어떻게 될까요. 이 경우에도 사용자 파드 트래픽은 흐릅니다. 데이터 플레인이 독립적으로 동작하기 때문입니다.

kubelet

각 워커 노드에서 자기 노드의 컨테이너 라이프사이클을 직접 관리합니다. apiserver와 연결이 끊겨도 이미 실행 중인 파드는 그대로 유지합니다.

kube-proxy: Service 번역기

여기가 핵심입니다. kube-proxy는 쿠버네티스 Service 추상화를 각 노드의 커널이 알아들을 수 있는 실제 네트워크 규칙으로 번역하는 에이전트입니다.

Service(ClusterIP 10.0.0.1) ──[셀렉터]──► Endpoints(파드 IP 10.1.0.5, 10.1.0.6, ...)
                                              │
                                              ▼ kube-proxy가 watch
                                              │
                                              ▼ 노드 커널에 기재
                                    iptables/IPVS 규칙
                                    "10.0.0.1로 오는 패킷은 10.1.0.5 또는 10.1.0.6으로 DNAT"

특징:

  • 노드별 DaemonSet: 노드당 정확히 1개 파드, ReplicaSet 아님
  • 데이터 패스 비참여: kube-proxy는 규칙을 “기재”만 하고, 실제 패킷 처리는 리눅스 커널이 수행
  • 번역 결과는 노드 커널에 남음: kube-proxy 프로세스가 멈춰도 기재된 규칙은 그대로 동작

이 구조 덕분에 apiserver가 다운되어도 다음이 성립합니다.

상태이유
기존 파드 → 기존 파드 통신노드 커널 규칙 잔류
외부 → NodePort/LoadBalancer → 파드동일
클러스터 DNS (CoreDNS)파드로 떠있어 정상

CNI

파드 간 네트워킹도 노드 로컬 데이터 패스라 컨트롤 플레인 부재와 무관합니다.

kube-proxy 업데이트는 어떻게 되는가

DaemonSet의 기본 updateStrategy는 RollingUpdate, maxUnavailable=1입니다.

updateStrategy:
  type: RollingUpdate
  rollingUpdate:
    maxUnavailable: 1

노드 순차로 종료 → 신규 기동을 반복합니다. 종료에서 기동 사이의 짧은 공백 동안에도 커널 규칙은 잔류하므로 트래픽은 흐릅니다. 새 kube-proxy가 apiserver와 동기화 후 규칙을 재계산합니다.

중단이 발생하는 시점

“마스터 다운 = 무관”이 항상 성립하는 것은 아닙니다. 다음 조건이 겹치면 중단이 발생합니다.

apiserver 부재 + kube-proxy 재기동

kube-proxy가 종료된 뒤 다시 올라올 때 apiserver에서 Service/Endpoints 상태를 받아 규칙을 재계산합니다. 이때 apiserver가 다운이면 동기화 실패로 그 노드는 빈 규칙으로 시작하거나 캐시로 출발해서 일부 라우팅이 중단됩니다.

백엔드 파드 다운

apiserver가 동작해야 Endpoints 컨트롤러가 다운된 파드를 Service에서 제외합니다. apiserver 다운 중에는 그 갱신이 안 됩니다. 다운된 파드 IP로 트래픽이 계속 흘러 일부 요청 실패가 발생합니다.

노드 자체 재부팅

워커 노드가 재부팅되면 kube-proxy도 다시 올라옵니다. 위 “apiserver 부재 + kube-proxy 재기동” 케이스와 같습니다.

마스터 노드에 워크로드를 올린 경우

기본 설정에서 마스터 노드는 NoSchedule taint로 워크로드를 거부합니다. 그러나 taint를 해제하거나 toleration을 추가해서 마스터에 사용자 파드를 띄운 경우, 그 마스터 재기동 시 해당 파드는 함께 다운됩니다. 영향도 점검 시 별도로 확인해야 하는 항목입니다.

영향도 점검 체크리스트

마스터 노드 1대 재기동 전 점검 항목:

항목확인 방법
마스터 HA 구성kubectl get nodes -l node-role.kubernetes.io/control-plane 으로 3대 이상 확인
etcd quorumetcdctl endpoint health 로 멤버 상태 점검
마스터 노드 워크로드kubectl get pods -A -o wide 로 그 노드 파드 목록 확인
단일 인스턴스 워크로드replicas=1 + 그 노드에 묶인 파드는 일시 다운 예상
PodDisruptionBudget회수 가능한 파드 수 제약 확인

정리

쿠버네티스는 “선언적 상태 관리”와 “데이터 패스 실행”을 분리합니다. 이 분리 덕분에 컨트롤 플레인 일시 부재가 사용자 트래픽으로 전파되지 않습니다.

  • 마스터 1대 재기동(HA 3대): 컨트롤 플레인 무중단, 데이터 플레인 무관
  • 마스터 전체 다운: 컨트롤 플레인 정지, 데이터 플레인은 “기존 상태”로 유지
  • 중단이 발생하는 시점: 파드 라이프사이클이 바뀌거나 kube-proxy가 재기동될 때
  • 마스터에 워크로드를 올린 환경에서는 그 노드 단위 영향을 별도 점검

쿠버네티스 영향도 분석에서 “컨트롤 플레인 의존 동작”과 “데이터 플레인 자립 동작”을 분리해서 보는 시각이 출발점입니다.

이 글은 Claude와 함께 작업했습니다.

This post is licensed under CC BY 4.0 by the author.