클러스터를 만들 때 이름을 쓸 수 있는 자리와 그렇지 않은 자리가 나뉜다.
| 대상 | DNS 이름 | 비고 |
|---|---|---|
| API 서버 진입점 | 쓸 수 있고 권장된다 | --control-plane-endpoint |
| 노드 등록 | 호스트명으로 등록되지만 주소는 IP 로 고정된다 | 바꾸면 재조인 |
| etcd 피어 | 권하지 않는다 | 이름 해석이 흔들리면 클러스터가 갈라진다 |
| 파드·서비스 사이 통신 | 이미 이름 기반이다 | CoreDNS |
컨트롤 플레인을 하나만 두더라도 진입점은 이름으로 잡아 두는 편이 낫다. 나중에 컨트롤 플레인을 늘리거나 앞단에 로드밸런서를 둘 때 이 값을 바꿀 수 없기 때문이다.
kubeadm init --control-plane-endpoint "k8s-api.example.com:6443" --upload-certs
kubeadm join k8s-api.example.com:6443 --token ... --discovery-token-ca-cert-hash sha256:...
이 이름이 API 서버 인증서의 SAN 에 들어가야 한다. --control-plane-endpoint 를 주면 kubeadm 이 넣어 준다. 나중에 이름을 추가하려면 인증서를 다시 발급해야 한다.
openssl x509 -in /etc/kubernetes/pki/apiserver.crt -noout -text | grep -A1 'Subject Alternative Name'
모든 노드에서 같은 결과로 풀려야 하고, 값이 자주 바뀌지 않아야 한다. /etc/hosts 로 때우면 노드마다 내용이 어긋나 추적하기 어려운 장애가 된다. 폐쇄망이라 DNS 를 둘 수 없다면 차라리 IP 로 구성하고, 앞단에 VIP(keepalived 등)를 두어 그 주소를 고정하는 편이 낫다.
이름을 쓰고 있었다면 영향이 작다. DNS 레코드를 고치면 새 연결은 새 주소로 간다. 인증서 SAN 에 그 이름이 들어 있어야 하고, 캐시된 TTL 동안 옛 주소로 가는 요청은 실패한다.
IP 를 그대로 kubeconfig 와 join 명령에 박아 뒀다면 그 값들을 모두 고쳐야 한다.
kubelet 은 자신의 주소로 등록되므로 노드가 NotReady 로 떨어진다. 새 주소로 다시 등록되지 않고 옛 노드 객체가 남는다. 정리 후 재조인이 사실상 유일한 방법이다.
kubectl drain <노드> --ignore-daemonsets --delete-emptydir-data
kubectl delete node <노드>
# 해당 노드에서
kubeadm reset -f
kubeadm join k8s-api.example.com:6443 --token ... --discovery-token-ca-cert-hash sha256:...
더 나쁘다. kubelet 클라이언트 인증서의 주체가 CN=system:node:<호스트명> 이라 인증이 어긋난다. 인증서를 다시 받고 노드도 다시 등록해야 하므로 재조인이 답이다. 클러스터를 세운 뒤에는 호스트명을 바꾸지 않는다.
가장 복잡하다. 인증서 SAN, etcd 피어 주소, 정적 파드 매니페스트의 광고 주소가 모두 옛 값을 담고 있다. 컨트롤 플레인이 하나뿐이라면 백업에서 복구하거나 다시 세우는 편이 빠르다. 여럿이라면 문제 노드를 클러스터에서 빼고 새 주소로 다시 합류시킨다.
노드에는 고정 주소를 준다. DHCP 로 받은 주소로 클러스터를 세우면 임대가 갱신될 때 위 문제가 그대로 일어난다.
애플리케이션 설정에는 노드 주소나 파드 주소를 넣지 않는다. 서비스 이름과 Ingress 호스트만 쓴다. 그러면 파드가 옮겨다니고 노드가 바뀌어도 설정을 고칠 일이 없다.
바깥에서 들어오는 경로는 DNS → 로드밸런서 → Ingress → Service 로 둔다. 노드 주소를 DNS 에 직접 매핑해 두면 그 노드가 바뀔 때마다 레코드를 고쳐야 한다.