콘텐츠로 이동
Study NoteCKA

HA와 etcd 멤버 관리

결론부터
HA는 컨트롤 플레인을 다중화하고 etcd의 과반수를 유지해 일부 장애에도 API를 사용할 수 있게 한다.

컨트롤 플레인 한 대가 멈춰도 클러스터를 관리하려면 무엇이 필요할까? HA(High Availability, 고가용성) 토폴로지, 장애 검증, etcd 멤버 변경을 살펴본다.

HA 컨트롤 플레인 — 두 가지 토폴로지개념 위주

섹션 제목: “HA 컨트롤 플레인 — 두 가지 토폴로지”

HA를 실제로 구축하는 문제는 로드밸런서까지 준비해야 해서 시험 시간 안에 내기 어렵다. 토폴로지 두 가지의 차이와 아래의 “공통 요구사항” 세 줄 정도를 이해해 두고, 구축 절차 자체를 외우는 데 시간을 쓰지는 않는 편이 낫다.

etcd가 컨트롤 플레인 노드 안에 있다.

스택드 etcd HA — 컨트롤 플레인 3대가 각자 etcd를 품고 Raft로 묶이는 구성
  • 노드 3대면 끝. 구성이 단순
  • kubeadm의 기본
  • 노드 하나를 잃으면 API 서버와 etcd 멤버를 함께 잃는다

공통 요구사항과 quorum 경계

  • 컨트롤 플레인은 최소 3대가 필요하다. stacked topology에서는 각 노드가 etcd 멤버이기도 하므로 3, 5처럼 홀수로 둔다
  • external topology에서는 control plane 수와 별개로 etcd 멤버를 홀수(3, 5) 로 둔다
  • 앞단에 로드밸런서가 필요하다 (haproxy + keepalived 등)
  • kubeadm init --control-plane-endpoint=<LB주소>:6443 으로 시작해야 한다

로드밸런서가 필요한 이유는 단순하다 — apiserver가 세 대가 되어도 kubelet과 kubectl은 주소 하나만 알고 있다. 그 하나의 주소를 살아 있는 apiserver로 넘겨주는 것이 앞단의 haproxy이고, keepalived는 그 haproxy마저 죽었을 때 가상 IP를 옆 서버로 넘겨 준다. 둘 다 쿠버네티스 컴포넌트가 아니라 리눅스 도구이니 이름과 역할만 알아두면 된다.

터미널 창
kubeadm token create --print-join-command --certificate-key $(kubeadm init phase upload-certs --upload-certs | tail -1)
# → 여기에 --control-plane 을 붙이면 컨트롤 플레인으로 조인한다

--certificate-key가 더 붙는 이유 — 컨트롤 플레인으로 조인하는 노드는 워커와 달리 컨트롤 플레인끼리 공유해야 하는 인증서와 키를 받아야 한다. upload-certs가 그 파일들을 kubeadm-certs Secret에 암호화해 잠시 올려두고, 이 키가 그것을 푸는 열쇠다. 노드별 SAN(Subject Alternative Name)이 필요한 인증서는 조인할 때 kubeadm이 따로 만든다.

Secret과 복호화 키는 기본 2시간 뒤 만료된다. 조인이 늦어졌다면 기존 키를 재사용하려 하지 말고 kubeadm init phase upload-certs --upload-certs로 다시 올려 새 키를 받는다. 키는 클러스터의 민감한 인증서에 접근할 수 있으므로 토큰처럼 로그나 저장소에 남기지 않는다.

HA를 장애 전·중·후로 검증하기

섹션 제목: “HA를 장애 전·중·후로 검증하기”

노드 세 대가 Ready인 것만으로 HA가 증명되지는 않는다. 클라이언트가 로드밸런서의 안정된 endpoint를 보고 있는지, 로드밸런서 뒤의 API 서버와 etcd 과반이 실제 요청을 계속 처리하는지를 장애 전·중·후의 같은 명령으로 비교해야 한다. 기준 흐름은 Kubernetes v1.35의 kubeadm HA 구성과 API health endpoint 문서에서 다시 확인한다.

  1. 장애 전 기준선을 남긴다

    터미널 창
    # kubeconfig가 개별 노드가 아니라 control-plane endpoint를 보는가
    kubectl config view --minify \
    -o jsonpath='{.clusters[0].cluster.server}{"\n"}'
    kubectl get nodes
    kubectl get pods -n kube-system -o wide | grep -E 'apiserver|etcd'
    kubectl get --raw='/readyz?verbose'
    # 인증서 옵션은 위의 etcd 접근 준비 절처럼 실제 etcd.yaml에서 읽는다
    sudo etcdctl endpoint status --cluster -w table ...
    sudo etcdctl endpoint health --cluster ...

    readyz의 etcd 검사까지 성공하고, etcd 표에 기대한 멤버 수와 리더가 보여야 다음 단계로 간다.

  2. 같은 endpoint를 계속 호출하며 control plane 한 대만 중단한다

    터미널 창
    while true; do
    date '+%H:%M:%S'
    kubectl get --raw='/readyz' >/dev/null \
    && echo API_READY || echo API_FAILED
    sleep 1
    done

    별도 터미널에서 control plane 한 대를 중단한다. 로드밸런서에서는 그 backend만 빠지고, 위 루프는 남은 API 서버로 이어져야 한다. kubeconfig가 중단한 노드의 주소를 직접 보고 있었다면 여기서 실패한다. 그것은 etcd 장애가 아니라 안정된 endpoint를 쓰지 않은 실패다.

  3. 조회뿐 아니라 쓰기와 quorum을 확인한다

    터미널 창
    kubectl create configmap ha-write-check \
    --from-literal=checked-at="$(date -Iseconds)" \
    --dry-run=client -o yaml | kubectl apply -f -
    kubectl get configmap ha-write-check \
    -o jsonpath='{.metadata.resourceVersion}{"\n"}'
    sudo etcdctl endpoint status --cluster -w table ...
    sudo etcdctl endpoint health --cluster ...

    세 멤버 중 하나가 내려가도 두 멤버가 건강하고 새 resourceVersion이 생기면 API 경로와 etcd 쓰기 quorum이 함께 유지된 것이다. readyz의 etcd가 실패하거나 쓰기가 멈추면 로드밸런서보다 etcd peer 통신·인증서·과반부터 확인한다.

  4. 복귀까지 닫는다

    터미널 창
    kubectl get nodes
    kubectl get --raw='/readyz?verbose'
    sudo etcdctl endpoint health --cluster ...
    kubectl delete configmap ha-write-check

    중단했던 노드를 되살린 뒤 모든 control plane과 etcd 멤버가 건강해졌는지 확인하고 검증용 객체를 지운다. 장애 중 API가 잠시 유지됐어도 멤버가 복귀하지 않았다면 복구는 끝난 것이 아니다.

터미널 창
sudo ETCDCTL_API=3 etcdctl --endpoints=https://127.0.0.1:2379 \
--cacert=/etc/kubernetes/pki/etcd/ca.crt \
--cert=/etc/kubernetes/pki/etcd/server.crt \
--key=/etc/kubernetes/pki/etcd/server.key \
member list -w table
# 상태 확인 (누가 리더인가)
... endpoint status --cluster -w table
... endpoint health --cluster
멤버 수과반견딜 수 있는 장애
110
321
431 (4대는 3대보다 낫지 않다)
532

이 표의 근거가 Raft다. etcd 멤버 여러 대가 같은 데이터를 갖게 하려면 “무엇을 썼는가”에 대해 서로 합의해야 하는데, Raft는 그 합의 방식으로 과반(quorum)이 동의한 쓰기만 확정한다. 과반을 못 채우면 etcd는 틀린 값을 주느니 쓰기를 거부하고 멈춘다 — 클러스터가 읽기 전용처럼 굳는 상황이 이것이다.

짝수는 의미가 없다. 4대는 3대와 내결함성이 같으면서 쓰기 지연만 늘어난다. 그래서 항상 3 또는 5다.

  • HA는 컨트롤 플레인을 다중화하고 etcd의 과반수를 유지해 일부 장애에도 API를 사용할 수 있게 한다.
  • stacked etcd와 external etcd는 장애 범위와 운영 구성이 다르다.
  • 멤버 변경 전후 endpoint health·status와 API 경로를 검증한다.