2024년 8월 15일 목요일

난 단지 k8s 클러스터를 클라우드 비용 안 내고 구축해보고 싶었을 뿐이었다

업무 환경에서 AWS EKS로 k8s를 접했고 그 앞에 CI/CD도 구성되어서 꽤 편리한 경험이었다. 이걸 직접 만들어보고 싶었다. 돈 들이지 않고.

 

GCP, Azure, AWS 다 써봤고 무료 단계라는 것도 다 손대봤지만 OCI에서 제공하는 무료 VM만큼 제약 없이 완전 무료인 경우는 없었다.

그래서 생각했다. VM을 4개나 쓸 수 있고 집에도 서버를 켜놓고 있는데 여러 노드짜리 k8s 클러스터로 묶어 쓰면 되지 않을까? 이게 기나긴 모험의 시작이었다.

k8s 구성요소를 패키징해서 설치해주는 게 몇 가지 있다는 건 알고 있었다. k8s 사이트에서 minikube라는 걸 안내하는 것도 봤었고, 우분투에 쉘로 들어가면 microk8s를 써보라고 안내가 나오기도 했으니까.

 

microk8s를 우분투가 기본으로 지원하기 때문에 첫 타자로 써봤지만 여러 노드에 설치했을 때 서로 연동이 잘 되지 않았다. microk8s 자체가 snap 기반으로 돌아가기도 했기 때문에 어렴풋하게 네트워크 문제인 것 같다고 의심했다. 하지만 로그를 확인하기도 쉽지 않았다. 몇 번의 시도를 해보다가 결국 포기했다.


다음은 k3s였다. Rancher에서 k3s와 rke2를 제공하는데 https://www.suse.com/c/rancher_blog/when-to-use-k3s-and-rke2/ 비교에 따르면 rke2는 보안에 좀 더 힘을 줬고, k3s는 가볍게 성능을 챙기고 싶으면 쓰라고 되어 있다.

이것도 여러차례 삽질을 했다. k8s가 이런저런 부품으로 이루어졌다는 건 알고 있었지만 각 역할을 충분히 이해하지 않은 상태에서 https://github.com/superseb/multipass-k3s/blob/master/multipass-k3s.sh 이런 성공사례가 있고 공식문서에도 착착 따라하면 다 된다고 적혀 있는데 왜 나는 안 되는지 다양한 시도를 했다.

로그를 살펴봐도 단순히 결과로만 CA 인증이 실패했다거나 접속이 안 됐다거나 하는 식으로만 나올 경우도 많아서, 도대체 뭘 잘못 넣었길래 그런 결과가 나오는지 여러 부분을 의심하면서 옵션을 빼고 넣고를 반복해야 했다.

 

그리고 마침내 뭐가 문제였는지 충분히 이해했다.

  • 일반 OS라면 /etc 경로에 파일로 저장했을 각종 상태들을 etcd라는 서비스로 저장하는데, HA를 유지하기 위해 3개 노드에 etcd를 사용하길 권장한다. 여러 노드에 있는 각각의 etcd는 서로 통신을 해야 하고, k8s 안에서 운용되는 다른 부품들도 etcd에 접속해서 내용을 읽고 쓸 수 있어야 한다.
  • k3s 설치 과정에서 서버와 에이전트로 노드가 접속하는 것만이 아니라 각 노드 안에 흩어져서 실행되는 pod 등의 요소들도 이런저런 이유로 서로 통신할 수 있어야 한다. 다시 말하면 10.x.y.z로 부여된 k8s 내부 IP를 라우터를 거쳐서 어느 노드로 들어가야 하는지 파악할 수 있어야 한다는 것. 오라클 VM과 홈서버로 구성된 이질적인 네트워크에서는 이게 뚝딱 되는 게 아니었다.
  • 오라클 VM에서 x86_64와 aarch64 서버를 모두 제공하는데 x86 쪽은 성능이 k8s의 모든 부품을 다 띄울 수 있는 상황이 되지 않는다. 무료로 쓰면서 불평하는 건 아니지만 처음에 성능 한계가 어느 정도인지 잘 인지하지 못해서 고부하를 일으키고 그것 때문에 k8s 클러스터에 노드가 들어왔다 끊기다가 하는 것처럼 보이는 상황이 생긴 건 문제였다.

 

해결은 이렇게 했다.

  • k3s에서 각 노드의 역할(role)을 제어할 수 있게 되어 있어서, etcd는 오라클 VM 안에서만 뜨게 했다. k3s 구동 자체가 etcd는 내부IP 값을 따르게 되어 있었고, 실제로도 빈번하게 접속을 하는 성격상 외부 네트워크를 경유하게 만드는 건 적절하지 않기 때문이다. 특히 x86 VM에는 다른 부품 없이 etcd만 들어가게 해서 부하가 높아지지 않게 했다.
    • multipass-k3s.sh 같은 간단한 성공사례는 애초에 동일 네트워크 안에 VM을 잔뜩 만들어서 구성하는 방식이기 때문에 이런 네트워크 부분에 특이사항이 나오지 않았던 것이다.
  • 노드 내부의 pod 등 통신을 위해서는 tailscale을 도입했다. https://docs.k3s.io/kr/networking/distributed-multicloud#integration-with-the-tailscale-vpn-provider-experimental 에서 안내하는 --vpn-auth 옵션을 붙이고 나면 kubectl get nodes -o wide 명령으로 나오는 노드의 내부IP가 모두 tailscale로 잡히면서 실제 노드가 서로 다른 네트워크에 속하는 상황을 해결해준다. 이미 tailscale을 사용하고 있었기 때문에 도입에 문제는 없었고, 오라클 VM 같이 자체 내부망이 있는 경우에는 tailscale status를 해보면 direct라는 표시가 따로 붙었기 때문에 tailscale IP를 쓴다고 통신에 지연이 생기지는 않을 것으로 생각한다.

 

결과물은 이렇게 나왔다.

  • k3s로 5개 호스트에 etcd는 3개를 띄우고 에이전트 전용은 2개 노드인 클러스터를 만들었다.
  • openfaas를 helm로 설치해서 nodeinfo라는 함수를 모든 노드에 다 퍼트린 다음, 반복해서 실행했을 때 각 노드의 정보가 번갈아 나오는 것을 확인했다.