올릴 수 있다. 다만 기본값으로는 올라가지 않는다. kubeadm 이 컨트롤 플레인 노드에 테인트를 걸어 두기 때문이다.
kubectl describe node <control-plane-node> | grep -A2 Taints
Taints: node-role.kubernetes.io/control-plane:NoSchedule
Kubernetes 1.24 까지는 node-role.kubernetes.io/master 테인트가 함께 붙었고, 1.25 에서 없어졌다. 오래된 문서의 명령을 그대로 쓰면 지워지지 않는 테인트를 지우려다 오류가 난다.
테인트를 걷으면 일반 워크로드가 스케줄된다.
kubectl taint nodes <control-plane-node> node-role.kubernetes.io/control-plane:NoSchedule-
특정 파드만 올리고 싶으면 테인트는 그대로 두고 파드 쪽에 톨러레이션을 단다. 이쪽이 범위가 좁아 안전하다.
tolerations:
- key: node-role.kubernetes.io/control-plane
operator: Exists
effect: NoSchedule
nodeSelector:
node-role.kubernetes.io/control-plane: ""
컨트롤 플레인이 흔들리면 클러스터 전체의 제어가 흔들린다. 특히 etcd 는 디스크 지연에 민감해서, 같은 노드에서 I/O 를 많이 쓰는 파드가 돌면 리더 선출이 흔들리고 API 응답이 느려진다.
| 성격 | 컨트롤 플레인 배치 |
|---|---|
| 모니터링 에이전트, 로그 수집기, CNI·CSI 데몬셋 | 올린다. 애초에 데몬셋은 톨러레이션을 갖고 있다 |
| 인그레스 컨트롤러 | 노드 수가 아주 적을 때만. 트래픽이 컨트롤 플레인을 통과한다 |
| 애플리케이션 워크로드, 배치 작업, 빌드 러너 | 올리지 않는다 |
노드가 셋뿐인 실습·개발 클러스터라면 테인트를 걷고 쓰는 것이 현실적이다. 운영 클러스터에서 자원이 아까워 테인트를 걷는 것은 대체로 손해다.
가능하지만, 처음 kubeadm init 을 어떻게 했는지에 따라 난이도가 갈린다.
--control-plane-endpoint 를 주지 않고 초기화했다면 kube-apiserver 의 접점이 그 노드의 IP 로 굳어 있다. 이 상태에서는 두 번째 컨트롤 플레인을 join 할 수 없다. 늘리려면 먼저 접점을 VIP 나 DNS 이름으로 바꿔야 한다.
k8s-api.example.internal:6443.kubeadm-config 컨피그맵의 ClusterConfiguration.controlPlaneEndpoint 를 그 이름으로 바꾼다.apiserver.crt·apiserver.key 를 치우고 kubeadm init phase certs apiserver 를 다시 돌린다.kubelet.conf·admin.conf·controller-manager.conf·scheduler.conf 의 서버 주소를 새 접점으로 바꾼다.kubeadm init phase upload-certs --upload-certs 로 인증서를 올리고, 새 노드를 kubeadm join --control-plane 으로 붙인다.각 단계 사이에 API 서버가 잠깐씩 끊긴다. etcd 스냅숏을 먼저 떠 두고, 되돌릴 계획을 세운 뒤에 시작한다.
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 \
snapshot save /var/backups/etcd-$(date +%F).db
처음부터 --control-plane-endpoint 를 주고 초기화하는 것이 정답이다. 노드가 하나뿐이더라도 이름만 미리 잡아 두면 나중에 늘리는 일이 join 한 번으로 끝난다.
etcd 는 과반으로 동작하므로 컨트롤 플레인은 홀수로 둔다. 셋이면 하나가 죽어도 버티고, 둘이면 하나만 죽어도 과반이 깨진다.
세 가지가 후보에 오른다.
| 방식 | 장점 | 걸리는 점 |
|---|---|---|
| 하드웨어 L4 스위치·어플라이언스 | 클러스터 밖에 있어 클러스터 장애와 무관하다. 성능과 지원이 확실하다 | 네트워크 팀에 요청해야 하고 변경이 느리다. 장비 비용이 든다 |
| keepalived + HAProxy (노드에 직접 설치) | 장비가 필요 없다. 헬스 체크와 로그를 직접 본다 | 설치·설정 대상이 늘어난다. VRRP 가 도는 네트워크 조건을 맞춰야 한다 |
| kube-vip | 정적 파드 하나로 끝난다. 설정이 간단하다 | 클러스터 부트스트랩 시점의 닭과 달걀 문제를 다뤄야 한다. 컨트롤 플레인이 멀쩡해야 동작한다 |
판단 기준은 "누가 운영하는가" 다.
멀티캐스트·VRRP 가 막힌 네트워크에서는 keepalived 가 동작하지 않는다. 클라우드나 일부 사내망이 여기에 해당한다. 그런 곳에서는 kube-vip 의 BGP 모드나 제공되는 부하 분산기를 쓴다.
워커 노드의 kubelet 은 /etc/kubernetes/kubelet.conf 의 server: 로 API 서버에 붙는다. 여기에 VIP 가 들어가 있어야 컨트롤 플레인 한 대가 빠져도 워커가 끊기지 않는다. L2 스위치는 이 그림에서 별도 기능을 하지 않는다. 노드들을 같은 브로드캐스트 도메인에 묶어 주는 역할이고, keepalived 의 VRRP 나 kube-vip 의 ARP 가 그 도메인 안에서 동작한다.