kubeadm join 작업하다 삽질한 이야기

· Kubernetes

쿠버네티스 클러스터에 노드를 추가해야 하는 상황이 왔다. 최대한 비용을 줄이려고 4GB RAM 단일 인스턴스에 쿠버네티스 + MYSQL + Redis + Spring 등 여러 파드를 띄워놓고 돌린 결과… 인스턴스가 메모리 사용률 평균 90%를 찍고 차라리 죽여달라고 울부짖고 있었다.

성능을 올리기 위해 리소스를 추가할 때는 항상 선택지가 2가지 정도 있다. 스케일업과 스케일아웃. 나는 스케일 아웃하는 방법을 골랐다. 그것이 쿠버네티스다움이니까 쿠버네티스는 스케일아웃에 용이한 도구기도 하고, 쿠버네티스 클러스터에 워커 노드를 조인하는 작업은 몇 번 해보긴 했지만 주기적으로 한 번씩 해줘야 트러블 슈팅 능력도 올라 갈 것 같아서 스케일아웃으로 결정했다.

인스턴스를 하나 생성하고 뚝딱뚝딱 워커 노드 조인 준비를 마쳤다. 스왑 메모리도 꺼주고, 컨테이너 런타임도 설치하고, kubelet도 설치하고, kubeadm도 설치했다. 이제 kubeadm join 작업만 남았다.

첫번째 난관, 인스턴스가 kubeadm에 join되지 않아!

kubeadm join 명령어를 이용해서 마스터 노드에 조인을 시도했는데, Preflight에서 넘어가지 않는 모습이였다. 뭔가 이상함을 느끼고 여러 실험을 해보았다.

  1. 쿠버네티스 apiserver가 외부로 열려 있는지 확인하기 - 공인IP 주소로 외부로 노출되어 있는 상태였다. 공인IP로 apiserver를 노출한 것은 의도한 일이였다. 사실 내부 아이피로 처리를 하는게 가장 베스트지만, AWS나 GCP가 아닌 Vultr를 쓰고 있었고, 단일 인스턴스 하나로 운영하다가 인스턴스 하나를 추가하는 작업이였기 때문에 별도의 VPC도 구성하지 않은 상태여서 외부IP로 클러스터를 노출한 상태였다.
  2. worker-node의 Outbound가 문제인가? - 확인해보았지만 정상적으로 외부로 트래픽이 나가고 있었다.
  3. master-node의 Inbound가 문제인가? - 그랬다. ufw가 켜져있었고, ufw를 끄니 바로 정상적으로 join이 이루어졌다. 생각보다 간단한 문제였다.

해치웠나?

두번째 난관, Join된 직후부터 워커 노드-> 마스터 노드로의 네트워크 연결이 끊김

분명 마스터노드IP:6443으로 연결이 되었기 때문에 클러스터로 Join이 된건데, Join하고 나니 마스터노드IP:6443으로 Connection Refused가 발생하기 시작했다. 워커 노드에서 마스터 노드로 ping조차 보내지지 않았다. ping 날리면 Unreachable이 발생했다. 새 인스턴스를 만들어서 join을 시켜보아도 같은 문제가 발생했다.

처음에는 정상적으로 올라오지 않는 CNI를 의심했다. CNI가 Worker 노드에 제대로 설치되지 않아서 네트워크 연결이 안되는거 아닐까? 그런데 이상했다. CNI가 문제라면 ping되지 않는 것은 설명이 안됐다. CNI는 Overlay Network를 지원하는 도구이기 때문에 공인IP를 대상으로 쏘는, 실제 네트워크를 타는 ping 명령어는 CNI와 관계 없이 동작해야 하기 때문이다. 일단 CNI 문제가 아닐 확률이 높다고 생각했지만 내가 CNI에 대해 잘 몰라서 그런 것일 수도 있다는 생각이 들어 CNI에 대해 좀 더 공부를 해보았다. CNI에 대해 개략적으로만 알고 있었기에 이번 트러블슈팅을 통해 CNI에 대해 조금 더 세부적으로 공부할 수 있었다. 하지만 당연히 CNI 문제가 아니였으니 CNI를 더 공부한다고 해서 문제가 해결되지는 않았다.

계속 고민하면서 길을 걷던 중에 갑자기 metallb가 떠올랐다. 나는 metallb를 L2로 동작하게끔 설정해놨었다. metallb에 공인IP를 물려놓고 트래픽을 받고 있었다. 아마도 내 생각에는 metallb가 공인IP를 물고 모든 프레임을 L2 레벨에서 쓱-싹 해간 것이 아닐까 싶었다. 워커노드 입장에서는 kube-apiserver의 주소로 연결해도 metallb가 트래픽을 받으니 제대로 통신이 안되었던 것이라는 생각이 들었다.

metallb가 범인?

아직 풀리지 않는 의문 2가지가 남아있었다.

일단 이 시점에서 워커 노드에서 마스터 노드로 ping이 가지 않는 이유가 kubeadm join을 하면서 네트워크 인터페이스나 iptables 설정이 변경되었기 때문일 것이라는 생각이 들긴 했다. 근데 일단 join을 할 때 이러한 문제가 발생하는 이유가 metallb가 공인IP를 물고 있어서일 것이란 생각이 들어, 이 부분부터 해결하기로 했다. 만약 이게 해결되지 않은 채로 네트워크 인터페이스만 수정한다고 해도 rejoin을 할 때 동일한 문제가 발생할 수 있을 것 같았기 때문이다.

metallb 수정하기

metallb 문제를 해결하기 위해서는 2가지 방법이 있었다.

1안은 추가 공인IP를 하나 할당 받아오면 되는데, Vultr에서 추가 IP를 할당 받는데는 월 2천원 정도가 청구된다. 2안은 VPC를 만들고 BGP 모드로 바꾸고 라우터를 만들고 노드에 연결하고… 등등의 작업들을 수행하는 방법이였다. 특히 라우터의 역할을 할 컴포넌트가 필요했다. 게다가 vultr 단일 노드로 운영하고 있었으니 VPC도 별도로 셋업해두지 않아서 이 작업을 하게 될 경우 Best Practice로 구성하려면 VPC도 만들어주고, 전체적인 네트워크 구조를 세팅해야 했다.

현실적으로 빠르게 문제를 해결할 수 있는 방법은 1안이라는 생각이 들어 1안으로 선택했다. metallb를 BGP 모드로 사용해보고 싶긴 했지만, 시간이 얼마나 걸릴지 알 수 없어 확실하고 빠르게 해결이 가능할 것으로 보이는 1안을 선택하게 되었다.

추가 공인IP를 할당 받고, 우분투에 이를 설정해준 뒤에 서버를 재부팅했다. 서버를 재부팅하고 나니 클러스터에 떠있던 파드들이 다 Unknown으로 바뀌었다. CNI가 죽고 다시 뜨지 않고 있었다. 당연히 CNI가 없으면 모든 파드들은 IP를 할당받지 못해 Running되지 못한다. 이게 무슨 날벼락이지 하면서 CNI의 로그를 살펴보니 다음과 같은 로그가 눈에 띄었다.

ipam.go 136: Error assigning IPV6 addresses: cannot find a qualified ippool
plugin.go 163: Final result of CNI ADD was an error. error=cannot find a qualified ippool

IPv6가 왜 켜져있지? 나는 CNI에 IPv6 대역을 할당해놓지 않았기 때문에 당연히 IPv6 아이피가 할당되지 않는다. 근데 IPv6 옵션이 왜인지 모르게 켜져있어서 할당해달라고 오류가 나고 있던 것이다. calico-config에서 assign_ipv6가 true로 되어있었고, 이걸 false로 바꿔주고 calico-controller를 재실행하니 정상적으로 파드가 올라오는 것을 확인할 수 있었다.

이제 다시 metallb 설정으로 돌아와 metallb의 공인IP 할당 설정을 새로운 공인IP로 변경하고 도메인 DNS Record도 새로운 공인IP를 바라보게 수정해주었다. 이제 기존의 공인IP는 kube-apiserver를 바라보고 있고, 새로운 공인IP는 쿠버네티스 내부 서비스들을 위해 metallb에 연결된 구조가 되었다. 마지막 작업, 워커 노드의 클러스터 조인만 남았다

기존 워커 노드로 사용하려던 인스턴스를 kubeadm reset해주고 다시 kubeadm join하는 작업을 해주었는데, 여전히 Preflight에서 안넘어가는 문제가 있었다. 다시 마스터 노드로 ping을 찍어보았는데 여전히 Unreachable이 발생했다. 아까 예상했던대로 네트워크 인터페이스의 문제였다. ip 명령어를 통해 네트워크 인터페이스를 살펴보니 kube-ipvs0이라는 이름을 가진 인터페이스가 보였다. kubeadm reset는 대부분의 리소스를 삭제시켜주긴 하지만, iptables 같은 리눅스 설정들은 직접 없애야 한다. 그래서 네트워크 인터페이스 설정도 남아있었는데, 이 인터페이스가 범인일 것 같다는 생각이 들었다. ip link delete kube-ipvs0 명령어를 이용해 지워주고 나니 ping 명령어도 잘되었고, kubeadm join도 성공적으로 이루어졌다.

뭘 배웠나?

kubeadm으로 워커 노드를 추가하는 작업을 처음해보는 것도 아니였는데 예상치 못한 문제를 만나게 되어 조금 당황스러웠지만, 결과적으로 네트워크에 대해 공부해볼 수 있는 기회가 되었던 것 같다.