RR DNS 를 통한 kube-apiserver 앞단의 LB 대체 테스트

이 글에서는 Round Robin DNS 를 통해 HA k8s 클러스터 앞단의 kube-apiserver를 위한 로드밸런서를 대체 할 수 있는지 테스트를 해보는 글입니다. 결론부터 이야기 하면 어느정도는 가능하다입니다. DNS 로는 Route53 을 사용하고, 상태검사등의 기능까지 사용해서 어느정도 구현이 가능합니다.

RR DNS 를 통한 kube-apiserver 앞단의 LB 대체 테스트
💡
HA Kubernetes 클러스터에서, kube-apiserver 앞단에 LB 가 있어야 단일 엔드포인트로 여러 kube-apiserver를 접근 할 수 있고. 보통은 그 LB 또한 HA 구성을 하기 위해서 보통 HAProxy + Keepalived 조합을 통해 LB 자체는 Active-Standby 로. kube-apiserver 들은 모두 Active-Active 상태로 구축하는게 보편적인 구축 방식입니다. 이 문서에서는 LB 없이 RR DNS 를 통해서 HA Kubernetes 클러스터 구축, 특히 kube-apiserver 와 kubelet 사이에 문제가 생기지 않을지 등을 염두에 두고 내용을 작성했습니다.

쿠버네티스 고가용성 토폴리지 (etcd)

고가용성 토폴로지 선택
이 페이지는 고가용성(HA) 쿠버네티스 클러스터의 토플로지를 구성하는 두 가지 선택 사항을 설명한다. 다음과 같이 HA 클러스터를 구성할 수 있다. etcd 노드와 컨트롤 플레인 노드를 함께 위치시키는 중첩된(stacked) 컨트롤 플레인 노드 방식 etcd와 컨트롤 플레인이 분리된 노드에서 운영되는 외부 etcd 노드 방식 HA 클러스터를 구성하기 전에 각 토플로지의 장단점을 주의 깊게 고려해야 한다. 참고: kubeadm은 etcd 클러스터를 정적으로 부트스트랩한다. 자세한 내용은 etcd 클러스터 구성 가이드 를 읽는다. 중첩된 etcd 토플로지 중첩된 HA 클러스터는 etcd에서 제공하는 분산 데이터 저장소 클러스터를, 컨트롤 플레인 구성 요소를 실행하는 kubeadm으로 관리되는 노드에 의해서 형성된 클러스터 상단에 중첩하는 토플로지이다.

위 문서는 쿠버네티스 공식 문서에서 고가용성이 확보된 쿠버네티스 클러스터를 구축하는 두가지 토폴로지에 대해서 서술하고 있습니다. 위 문서에서의 초점은 etcd 이며. etcd 를 컨트롤 플레인 노드(머신)과 중첩시켜서 구축할지, 아니면 외부 다른 노드(머신)에 구축할지에 대한 내용입니다.

이 문서는 etcd 를 초점으로 한 문서는 아니지만 간략하게 요약하자면.

Stacked etcd 토폴로지

Stacked etcd 토폴로지

Stacked etcd 토폴로지의 경우 컨트롤 플레인 노드(머신)에 etcd 를 같이 설치하는 형태로.

kube-apiserveretcd 간에 Network I/O 가 없다는 장점이 있지만. RAFT 알고리즘을 사용하는 etcd 의 특성, 그리고 그 etcd 를 컨트롤 플레인 노드에 같이 설치한다는 구성상의 특징으로 컨트롤 플레인 노드의 개수는 etcd 클러스터 멤버의 개수와 동일하며. 그에 따라서 고가용성 유지를 위한 Quorum 개수를 맞추려고 할 때 컨트롤 플레인 노드의 개수도 etcd 와 동일하게 맞춰줘야 한다는 단점이 있을 수 있습니다.

etcd 클러스터는 etcd 멤버를 3대 이상 유지했을 때 1개의 etcd 멤버가 다운되더라도 정상 동작이 가능하고, 5대로 구성하는경우 최대 2개의 멤버가 다운되더라도 정상 동작이 가능합니다.

참조 : https://etcd.io/docs/v3.5/faq/#what-is-failure-tolerance

따라서 고가용성을 유지하기 위해 etcd 를 5개를 설치하기로 선택 했을 때, 컨트롤 플레인의 개수 자체도 5개가 되어야 한다는것이 단점이 될 수 있습니다.

External etcd 토폴로지

External etcd 토폴로지

External etcd 토폴로지의 경우 컨트롤 플레인 노드와 별도로 etcd 를 설치해서 사용하는 형태로. kube-apiserveretcd 가 물리적으로 다른 머신에 설치되어있는 경우 Network I/O 를 염두해서 사용해야 합니다. 다만 이 케이스에서는 컨트롤 플레인의 개수는 etcd 의 개수와 전혀 상관없이 구성이 가능하다는 점이 장점입니다.

컨트롤 플레인 노드는 2대, etcd 멤버는 5개로 설치하거나, 컨트롤 플레인은 3대 , etcd 멤버는 3개 등으로도 설치 가능합니다. 이렇게 컨트롤 플레인의 개수와 etcd 개수간의 디펜던시가 없는것이 장점이 될 수 있습니다. 다만 etcd 멤버 수 만큼 추가적인 노드(머신)이 필요 한 것이 단점이 될 수 있습니다.

kube-apiserver 앞단의 로드밸런서 (software LB)

앞서서 HA 쿠버네티스를 구성하는 두가지 토폴로지를 보면서 확인 가능했겠지만. 워커노드들이 kube-apiserver 에 접근 할 때 로드밸런서를 통해 접근하는것은 동일하다는 것을 볼 수 있었습니다.

kubeadm 을 통해 HA 쿠버네티스 클러스터를 구성하는 아래와 같은 문서에서도 kube-apsierver 용 로드밸런서를 생성하라는 이야기가 나와있습니다.

Creating Highly Available Clusters with kubeadm

또한 위 문서에서 소프트웨어를 통한 부하 분산에 대한 내용이 아래와 같이 링크 되어있는데요.

https://github.com/kubernetes/kubeadm/blob/main/docs/ha-considerations.md#options-for-software-load-balancing

위 문서에서는 VIP(Virtual IP)를 통해 로드밸런싱을 제공하는 keepalived + haproxy 조합이 오래동안 사용되어왔고, 충분한 테스트를 거친 조합이라고 설명하고있습니다.

Keepalived + HAProxy 조합의 HA 쿠버네티스 클러스터 구성

위 그림에서의 IP 주소는 이해를 돕기위해 임의로 설정한 주소입니다.

아래쪽의 각 컨트롤 플레인 노드는 각각 192.168.100.2 , 192.168.100.3 , 192.168.100.4 주소를 갖고 있다고 할 때. kube-apiserver 는 각 컨트롤 플레인 노드에서 6443(기본적으로 설정되는 포트이며 변경될 수 있음) 포트로 listen 하고 있는 상태일 것입니다.

이러한 kube-apiserver 들에 부하를 분산해서, 그리고 문제가 생겼을때 문제가 없는 서버로 트래픽을 틀어주기 위해서 HAProxy 를 통해 각 kube-apiserver 에 round-robin 형태로 트래픽을 틀어주도록 설정하고. 이 때 각 HAProxy192.168.100.10 , 192.168.100.20 주소를 갖고 있다고 가정합니다.

이때 각 LB(HAProxy) 간의 HA 구성을 위해 Keepalived 를 두고. VIP192.168.100.100 으로 두고. 설정을 통해 한쪽 LB를 마스터, 한쪽 LB 를 백업으로 두는 Active-Standby 조합으로 구성하면

클라이언트나 워커노드는 192.168.100.100:6443 혹은 도메인을 192.168.100.100 에 연결했다고 하고, 그 도메인이 foo.bar.com 이라고 하면 https://foo.bar.com:6443 으로 요청을 보낼 수 있습니다.

그렇게 요청을 보내면 Keepalived 를 통해 한쪽 LB 로 요청이 넘어가고. 해당 LB 는 round-robin 형태로 세 컨트롤 플레인 노드의 kube-apiserver 중 하나로 트래픽을 틀어주게 됩니다.

이것이 일반적인 Keepalived + HAProxy 조합의 HA 쿠버네티스 클러스터 구성입니다.

이렇게 하게 되면 kube-apiserver , LB(HAProxy) 중 하나가 장애가 생기더라도 정상적인 kube-apiserver, LB 로 트래픽을 틀어 줄 수 있기 때문에 고가용성이 확보되는 것입니다.

물론 뒷단의 Control Plane NodeStacked etcd 토폴로지 로 했냐, External etcd 토폴로지 로 했냐에 따라서 고가용성 확보 여부가 달라질 수 있고. Keepalived 에 장애가 생겼냐에 따라서도 고가용성 확보 여부가 달라 질 수는 있습니다. 여기서는 단편적인 케이스에 대해서만 설명하였습니다.

위 구성에서 VIP(Virtual IP) 그리고 Keepalived 를 사용해서 LB(HAProxy) 에 대한 고가용성을 확보했기 때문에 두 LB(HAProxy) 중 하나는 유휴상태(Standby) 상태로 남아있게 되는 것 단점으로 남습니다.

위 부분을 해결하기 위해서는 별도의 Keepalived 를 하나 더 추가하고. 2개의 VIP(Virtual IP) 를 구성하고. Round Robin DNS , 그리고 Health Check 까지 겸해서 사용하거나, 두개의 LB 에 대해서 Round Robin DNS 그리고 Health Check 를 겸해서 사용하는 등의 추가적인 방법을 사용해야 할 것입니다.

Round-Robin DNS 로 해보자!

💡
라운드 로빈 DNS는 구현하기는 쉽지만 DNS 계층 구조 자체의 레코드 캐싱과 클라이언트 측 주소 캐싱 및 재사용으로 인해 발생하는 여러 가지 단점이 있으며, 이러한 조합은 관리하기 어려울 수 있습니다. 서비스 가용성을 위해 라운드 로빈 DNS에만 의존해서는 안 됩니다. 목록에 있는 주소 중 하나의 서비스에 장애가 발생하면 DNS는 해당 주소를 계속 전달하고 클라이언트는 여전히 작동하지 않는 서비스에 연결하려고 시도합니다.
💡
라운드 로빈 DNS는 네임 서버를 쿼리할 때마다 주소 레코드의 순서를 번갈아 가며 쿼리하기 때문에 그 자체로는 부하 분산에 가장 적합한 선택이 아닐 수 있습니다. 트랜잭션 시간, 서버 부하, 네트워크 혼잡을 고려하지 않기 때문에 동일한 용량의 서버에 균일하게 분산된 많은 수의 연결이 있는 서비스에 가장 적합합니다. 그렇지 않으면 부하 분산만 수행합니다.[9]

Round-robin DNS 는 DNS 요청에 대한 IP 주소들의 응답을 여러 주소를 반환하고 그와 더불어서 첫번째 레코드를 순회(round-robin)하면서 DNS 응답을 하는 방법입니다.

일반적인 상황에서 S/W , H/W LB 를 두지 않고 가장 간단하게 부하 분산을 할 수 있는 방법에 그 이점이 있습니다. 다만 고려해야할 아래와 같은 단점이 있습니다.

  1. DNS 쿼리 정보를 캐싱하는 주체가 다양하다. (OS, 애플리케이션, 기타 클라이언트 등등)
  2. DNS 쿼리 캐싱 문제로 인해 문제가 발생했을 때 바로 반영이 되지 않을 수 있다. 기존 쿼리해서 받은 IP 주소로 계속해서 애플리케이션/클라이언트 등이 호출을 하려 할 것이다.
  3. DNS 서버에 Health Check 기능이 존재하지 않으면 비정상적인 IP 주소를 계속 담아서 보내줄 수도 있다.

우선 1, 2번 문제를 제외하고 3번 부분을 처리하기 위해서 AWS Route53 의 다중 응답 기능 + 상태 검사 기능을 사용해서 테스트를 해보기로 하였습니다.

테스트 환경

테스트를 하고자 하는 환경 구성은 아래와 같습니다.

2개의 Control Plane Node (172.xx.xx.31 , 172.xx.xx.32) 와 1개의 Worker Node (172.xx.xx.33) 이 존재하고. 이 노드들은 모두 사설 대역대에 존재합니다. 이 사설망과 연결된 게이트웨이는 xx.xx.xx.xx 라는 공인 ip 주소를 갖고 있고 Route53 의 헬스체크 요청이 도달할 수 있도록 포트포워딩을 아래와 같이 하였습니다.

xx.xx.xx.xx:16443172.xx.xx.31:6443 (kube-apiserver)

xx.xx.xx.xx:26443172.xx.xx.32:6443 (kube-apiserver)

xx.xx.xx.xxpublic_ip 라고 지칭하겠습니다.

Route53 설정

 

위와 같이 public_ip:16443 , public_ip:26443 에 대해서 상태검사를 생성합니다.

이후 A Record 에 위와 같이 다중값 응답 및 상태확인을 연결하여 생성합니다.

 

172.xx.xx.31:6443 , 172.xx.xx.32:6443 으로 떠있는 각 kube-apiserver 가 정상 상태라면 두 상태 검사도 정상이고, 이에 따라서 DNS 쿼리시 두 IP 주소를 번갈아가면서 응답해주게 됩니다.

DNS 테스트

맨 위쪽이 172.xx.xx.31 , 중간이 172.xx.xx.32 , 아래는 로컬 머신(맥북)

kube-apiserver 는 static pod 로 띄워져있기때문에 ,한번 172.xx.xx.31 쪽에 있는 kube-apiserver 를 제거해보고 DNS 응답이 어떻게 나오는지 확인해봅니다.

 

$ mv /etc/kubernetes/manifests/kube-apiserver.yaml /root/kube-apiserver.yaml

위와 같이 kube-apiserver.yaml 을 잠시 /etc/kubernetes/manifests 경로에서 빼내줍니다.

💡
kubelet은 static pod yaml 파일 경로를 지정해놓고, 해당 경로에 있는 yaml 파일을 static pod로 띄웁니다.
cat /etc/kubernetes/kubelet-config.yaml | grep staticPodPath
staticPodPath: /etc/kubernetes/manifests

잠시 기다리면 kubeletkube-apiserver.yaml 파일이 제거됨을 인지하고, kube-apsierver static pod 를 제거합니다. 위 사진처럼 node1 (172.xx.xx.31) 에는 kube-apiserver 가 제거되었습니다.

kube1 상태 검사가 비정상 상태로 보임
dig 결과 비정상적인 172.xx.xx.31 은 반환되지 않고 172.xx.xx.32 만 반환 됨을 확인 할 수 있다.

이렇게 상태 검사 + Rotue53 다중 응답(Round Robin DNS)을 동작하도록 했다. 172.xx.xx.31 에서 잠깐 제거한 kube-apiserver.yaml 을 다시 아래 명령어를 통해 정상화 한다.

$ mv /root/kube-apiserver.yaml /etc/kubernetes/manifests/

 kube-apiserver 인증서 재발급

이제 kube.xxx.com 과 같은 도메인으로(host header) kube-apiserver를 요청하더라도 처리가 되도록 기존에 프로비저닝 된 kube-apiserver 인증서를 재발급 해줘야한다.

💡
이 테스트에서의 쿠버네티스 클러스터는 kubespray 를 통해 프로비저닝 되었고, kubespray는 내부적으로 kubeadm 을 통해 인증서 생성 , 클러스터 조인 등의 작업을 진행합니다. 따라서 인증서 재발급등의 작업도 kubeadm 을 통해 수행합니다.

172.xx.xx.31 서버에서 아래와 같이 kube-apiserver 가 사용하는 기존 인증서를 확인해보자.

$ openssl x509 -text -noout -in /etc/kubernetes/ssl/apiserver.crt

노란색 박스 부분을 잘 보자(Subject Alternative Names). 여기에 우리가 사용하려는 도메인 주소가 없다면 kube-apsierver 에서 ssl termination 을 할 때 오류가 발생한다.

kubeadm 을 통해 다시 인증서를 발급하기 위해서 우선 /etc/kubernetes/kubeadm-config.yaml 파일의 certSANs 항목에 도메인을 추가해야 한다.

$ vi /etc/kubernetes/kubeadm-config.yaml

위는 kubeadm-config.yaml 파일의 certSANS 항목의 일부이다. 해당 부분의 아무 라인에서 kube.xxx.com 과 같은 도메인을 추가해줘야 한다.

이후 기존 kube-apiserver 인증서, 키파일을 제거한다.

$ rm -rf /etc/kubernetes/ssl/{apiserver.crt,apiserver.key}

이후 kubeadm 을 통해 kube-apiserver 인증서를 업데이트된 kubeadm-config.yaml 파일 기반으로 재생성한다.

⚠️
kubeadm 바이너리를 호출해서 실행하면 된다. kubespray로 프로비저닝 한 경우 kubeadm/usr/local/bin 에 설치되어있다.

$ /usr/local/bin/kubeadm init phase certs apiserver --config=/etc/kubernetes/kubeadm-config.yaml

위 명령어를 수행하면 apiserver.crt , apiserver.key 가 새롭게 /etc/kubernetes/ssl 디렉터리에 생성된다.

이후 제대로 생성이 되었는지 $ openssl x509 -text -noout -in /etc/kubernetes/ssl/apiserver.crt 명령어를 통해 확인한다.

kube.xxx.com 과 같은 도메인이 SAN 에 추가된것을 볼 수 있다.

이후 172.xx.xx.31 에서 생성된 인증서를 다른 모든 컨트롤플레인(현재 테스트 환경에서는 172.xx.xx.32)에도 반영해준다. (기존 인증서 제거 및 172.xx.xx.31 에서 생성한 인증서 복사)

워커노드에서의 kubelet 설정

이제 172.xx.xx.33 (워커노드) 에서 본격적인 테스트를 진행해보자.

워커노드에서 $ vi /etc/kubernetes/kubelet.conf 을 통해 kubelet 이 사용할 kubeconfig 파일을 열어봅니다.

💡
kubelet , kube-scheduler , kube-controller-manager 와 같은 쿠버네티스 컴포넌트들은 kubeconfig 형태의 파일을 통해 어떤 서버 주소로 접근하고, 어떤 인증서로 API 서버에 인증할지 등을 설정할 수 있습니다. 우리가 로컬 머신에서 사용하는 kubectl 의 경우도 동일한 파일 포맷을 사용합니다.

위와 같이 server 항목에 도메인(kube.xxx.com:6443) 을 입력해줍니다.

이로써 워커노드의kubeletkube.xxx.com 도메인 네임을 resolve 하기 위해 DNS Query를 하게 되고. 그 응답으로 돌아오는 IP 주소를 통해 kube-apiserver 에 호출하게됩니다.

kubelet 의 동작을 자세하게 확인하기 위해서 kubelet 의 로그레벨을 잠시 올려봅니다. kubeletsystemd 서비스로 설치되었고. 로그는 journald 에 기록되기때문에 로그를 확인하기 위해서는

$ journalctl -xeu kubelet -f 형태로 확인 가능합니다.

로그 레벨 변경은 $ vi /etc/kubernetes/kubelet.env 을 통해 kubelet.env 파일을 수정해야합니다.

기본적으로 위와 같이 KUBE_LOG_LEVEL--v=2 로 되어있습니다. 이것을 잠시 9로 올려봅니다.

위 환경변수를 반영하기 위해서 $ systemctl restart kubelet 으로 kubelet 을 재실행합니다.

워커노드에서의 테스트

현재 환경에서는 2개의 kube-apiserver 가 있는데. 두 kube-apiserver 를 죽여봅니다. 각각의 컨트롤 플레인에서

$ mv /etc/kubernetes/manifests/kube-apiserver.yaml /root/kube-apiserver.yaml 를 통해 kube-apiserver static pod 를 제거해봅니다.

static pod manifest 경로에서 kube-apiserver.yaml 을 제거하고. 프로세스가 떠있지 않고 6443 포트가 열려있지 않음을 확인
Route53 상태 검사에서도 모두 비정상으로 확인

위와 같은 상황일때. Route53kube.xxx.com 에 대한 DNS 쿼리 응답으로. kube.xxx.com 에 등록한 모든 A Record 를 반환합니다.

모든 레코드를 반환

현재 172.xx.xx.31 , 172.xx.xx.32 두 컨트롤 플레인 노드에 떠있는 kube-apiserver 가 모두 없는 상황입니다.

이 때 워커노드의 kubelet 은 어떤지 로그를 살펴보면 ($ journalctl -xeu kubelet -f)

kubelet 의 로그.

노란색 박스 친 부분을 보면 round_trippers 코드 부분에서 DNS Loookup 을 진행해서 두개의 IP 주소를 받아오고. 이후 앞쪽에 있는 IP 주소에 먼저 TCP 커넥션 맺기를 시도해보고, 이후 다른 IP 주소로도 TCP 커넥션을 맺어보려고 한다. 이 round_trippers 는 DNS 쿼리 결과에 따른 IP 주소들에 대해서 성공하는 IP 주소로 DNS Query 결과를 kubelet 이 캐싱해서 해당 IP 주소로 계속해서 요청하기 위한 로직입니다.

https://github.com/kubernetes/client-go/blob/7ebe0ea60e0a6bea3e8280871c1241d0405cdb32/transport/round_trippers.go#L459-L586

실제 kubernetes 컴포넌트들의 클라이언트 모듈인 client-go 레포 내의 round_trippers 코드를 보면 도메인을 DNS Query 하고, IP 주소로 커넥션을 맺어보려고 시도하는 로직들이 들어있습니다.  

여기서 172.xx.xx.31 서버의 kube-apiserver 를 복구하고 kubelet 로그를 다시 확인해보겠습니다.

172.xx.xx.31 의 kube-apiserver를 복구한 이후의 kubelet 로그

172.xx.xx.31 에 떠있는 kube-apiserver 로의 TCP 커넥션 맺기가 성공했다는 로그를 확인 할 수 있습니다. 이러한 로그가 생긴 시점 이후로는 kube.xxx.com 도메인으로 kube-apiserver 접근 요청을 하더라도 클라이언트 내부적으로 172.xx.xx.31 로 계속해서 요청을 보내게 됩니다.

추가적으로172.xx.xx.32kube-apiserver 를 복구하더라도 더이상 DNS Query 를 하지 않고 계속해서 기존에 정상적으로 호출했던 172.xx.xx.31 쪽으로 요청을 하는것도 확인 할 수 있었습니다.

그럼 잘 동작하는거 아닌가요? kube-apiserver 앞단에 로드밸런서 이제 걷어내도 되나요?

위 테스트를 간단하게 했을때는 kube-apiserver의 일부가 장애가 발생했더라도, 정상적인 kube-apiserver 로 쿠버네티스 컴포넌트들(kubelet 포함한 kube-scheduler, kube-controller-manager 등)이 Round Robin DNS 를 통해서 정상적으로 동작하는 것 처럼 보입니다.

여기서 중요한 점은, kubelet 을 포함한 쿠버네티스 컴포넌트들은 아까 kubelet 이 사용했던 kubernetes/client-go 를 사용하는데. 이 클라이언트 코드는 DNS 쿼리를 하고, 반환된 IP 주소에 TCP 커넥션을 맺어보고 성공한 IP:Port 조합으로 계속해서 요청을 보내는 코드입니다.

따라서 172.xx.xx.31 , 172.xx.xx.32 에 존재하는 두개의 kube-apiserver 가 모두 장애가 발생했다가, 한쪽이 복구되었을 때, 복구된 한 쪽으로 kubelet 같은 쿠버네티스 컴포넌트들, 그리고 클라이언트들이 잘 호출 할 수 있겠지만, 클라이언트 코드쪽의 DNS 캐싱정책으로 인해서, 장애가 있었던 나머지 한쪽이 복구가 되더라도 트래픽이 Load Balancing 되는 것이 아니라, 여전히 한쪽으로만 흘러가게 되는 현상을 겪을 수 밖에 없습니다.

이는 Round Robin DNS 자체의 문제점으로, 불특정 다수의 다양한, 수많은 클라이언트가 동일 도메인으로 접속하는 경우에 대해서는 어느정도의 Load Balancing 이 가능하지만. 소수의 일부 클라이언트가 지속해서 접속해야하는 케이스에서는 적절한 Load Balancing 을 기대하긴 어렵습니다.

 

Wrapping up

Round Robin DNS 를 통해 kube-apiserver 앞단의 로드밸런서를 대체 할 수 있는지 테스트해보았는데 결론적으로는 반은 되고 반은 안되는 구성이라고 볼 수 있습니다.

kube-apiserver 의 앞단 LB 를 대체 할 수 있는 부분은 일부 kube-apiserver 장애시 장애에 대한 복구(fail over)는 어느정도 가능하지만 대체 할 수 없는 부분이 일부 존재해 정말 진정한 Load Balancing 을 수행하기는 어렵다는 부분이 있습니다.

일반적인 LB의 경우 들어온 요청을 자신이 관리하는 뒷단의 백엔드 서버들로 로드밸런싱 정책에 맞춰 부하를 분산해줍니다.

하지만 지금과 같은 설정 케이스(RR-DNS)는 별도의 서버가 트래픽을 중계(proxy)해서 처리하는 형태가 아닌. 각각의 클라이언트들이 Domain 에 대한 IP 주소를 Resolve하고. 그 IP 주소가 여러대의 백엔드 서버(머신)주소 일 때 진정한 부하 분산이 되는 케이스라고 볼 수 있습니다.

여기서 클라이언트의 DNS Query 캐싱 동작으로 인해, 여러 클라이언트들이 여러 IP주소의 백엔드로 호출하고 있다가, 장애가 발생해서 일부 백엔드로 호출하고, 다시 모든 백엔드 서버가 정상이 되어서 여러 IP 주소의 백엔드로 호출하는 (부하분산) 형태가 아닌점이 문제가 됩니다.

대부분의 클라이언트들은 한번 DNS Query 를 하고 정상 동작중이라면 계속해서 동일한 IP주소의 백엔드에 요청을 보낸다는 점이 문제가 됩니다. 따라서 장애 발생 후 복구가 되었더라도, 장애 발생시 정상적으로 처리했던 백엔드에만 클라이언트가 요청을 하게 되는것입니다.

이렇게 되면 일부 서버에만 부하가 몰리게 됩니다.

물론 kubelet 같은 것들을 임의로 재시작해서(kubelet 프로세스의 재시작에 의미가 있는 건 아니고 DNS 캐싱을 초기화 하는 부분에 의미가 있음) Round Robin DNS 를 통해 어느정도의 강제적인 로드밸런싱을 수행 할 수도 있겠지만 그렇게 하는것이 과연 H/W, S/W LB 를 kube-apiserver 앞단에 두는것보다 얼마나 더 장점이 있을지를 고려해보고, 사용 자체는 해볼 수 있을 것으로 판단은 됩니다.