Study/CS

[Cilium Study] 4주차 - Service와 LoadBalancer로 세상과 소통하기

마늘김 2025. 8. 9. 22:21

지난 3주차 스터디에서는 IPAM, 라우팅, 마스커레이딩 등 파드 간 통신을 가능하게 하는 쿠버네티스 네트워킹의 내부 동작 원리를 깊이 있게 살펴보았습니다. 이제 클러스터라는 우리만의 세상 안에서 통신하는 법을 익혔으니, 드디어 클러스터 외부의 사용자들이 우리 애플리케이션을 만날 수 있도록 문을 열어줄 시간입니다.

이번 포스팅에서는 쿠버네티스에서 외부 트래픽을 처리하는 핵심 관문인 Service의 동작 원리를 알아보고, Cilium이 제공하는 강력한 LoadBalancer 기능을 통해 온프레미스 환경에서도 클라우드처럼 서비스를 외부에 노출하는 방법을 자세히 다뤄보겠습니다.

이미지 출처: https://cilium.io/use-cases/load-balancer/


1. 쿠버네티스 Service 다시 보기: 왜 필요할까?

쿠버네티스에서 파드는 언제든지 사라지고 다시 생성될 수 있는 '임시적인' 존재입니다. 파드가 재시작되면 IP 주소도 바뀌어 버리죠. 만약 우리가 이 파드의 IP 주소를 코드에 직접 기록해두었다면, 파드가 재시작될 때마다 코드를 수정해야 하는 끔찍한 상황이 발생할 겁니다.

바로 이 문제를 해결하기 위해 서비스(Service) 가 등장했습니다. 서비스는 여러 개의 파드에 대한 고정된 단일 진입점(Single, Stable Entrypoint) 을 제공합니다. 우리는 변하기 쉬운 파드의 IP 대신, 변하지 않는 서비스의 고유한 IP(ClusterIP)나 도메인 이름을 바라보면 됩니다. 서비스는 마치 똑똑한 중간 관리자처럼, 자신에게 온 요청을 현재 실행 중인 건강한 파드들에게 알아서 분배(로드 밸런싱)해주는 역할을 합니다.

kube-proxy: 서비스의 마법을 현실로 만드는 일꾼

이러한 서비스의 마법은 각 노드에서 실행되는 kube-proxy라는 컴포넌트 덕분에 가능합니다. kube-proxy는 API 서버를 지켜보다가 서비스나 엔드포인트(서비스에 연결된 파드들)에 변경이 생기면, 각 노드의 네트워크 규칙을 업데이트하여 서비스로 가는 트래픽이 실제 파드로 전달되도록 설정합니다. 이 kube-proxy가 동작하는 방식은 역사적으로 발전해 왔습니다.

  1. Userspace 모드 (초기 방식): 모든 서비스 트래픽이 커널 공간과 사용자 공간을 넘나들며 kube-proxy 프로세스를 직접 거쳐 파드로 전달되었습니다. 구조는 간단했지만, 잦은 컨텍스트 스위칭으로 인한 성능 저하가 심해 현재는 거의 사용되지 않습니다.
  2. iptables 모드 (오랜 기간 기본값): kube-proxy가 트래픽을 직접 처리하지 않고, 커널의 netfilter 모듈(iptables)에 서비스 IP를 실제 파드 IP로 변환(DNAT)하는 규칙을 기록해둡니다. 트래픽은 커널 공간에서 바로 처리되므로 성능이 크게 향상되었습니다. 하지만 서비스와 파드가 수천, 수만 개로 늘어나면 iptables 규칙의 양이 방대해져 규칙 업데이트와 패킷 처리에 지연이 발생하는 확장성 문제가 있었습니다.
  3. IPVS 모드: iptables의 확장성 문제를 해결하기 위해 등장했습니다. IPVS(IP Virtual Server)는 해시 테이블을 사용하는 고성능 L4 로드 밸런서로, 훨씬 더 많은 수의 서비스를 효율적으로 처리할 수 있습니다. 현재 많은 환경에서 권장되는 방식입니다.
  4. eBPF (Cilium의 방식): Cilium은 kube-proxy를 완전히 대체하고 eBPF를 사용하여 서비스 로드 밸런싱을 구현합니다. 패킷이 커널의 네트워크 스택을 거치는 복잡한 과정을 우회하고, 네트워크 인터페이스(NIC)에 도착하는 시점에서 eBPF 프로그램이 직접 패킷을 처리하여 목적지 파드로 전달합니다. 이는 가장 높은 성능과 유연성을 제공하는 최신 방식입니다.

2. 서비스를 외부에 노출하는 방법들

ClusterIP 타입의 서비스는 클러스터 내부에서만 유효합니다. 외부 사용자가 서비스에 접근하게 하려면 NodePortLoadBalancer 타입을 사용해야 합니다.

  • NodePort: 모든 노드에 특정 포트(기본 30000-32767)를 열고, http://<아무노드IP>:<NodePort>로 들어온 요청을 해당 서비스로 전달합니다. 간단하지만, 노드 IP가 변경될 수 있고 3만번대 포트를 사용해야 하는 등의 제약이 있습니다.
  • LoadBalancer: 클라우드 환경에서 가장 이상적인 방식입니다. 이 타입으로 서비스를 생성하면, AWS의 ELB나 GCP의 Cloud Load Balancer와 같은 클라우드 제공사의 로드 밸런서가 자동으로 생성되고, 이 로드 밸런서의 고유한 외부 IP 주소를 통해 서비스에 접근할 수 있게 됩니다.

하지만 온프레미스(On-premise) 환경에서는 이런 외부 로드 밸런서를 자동으로 생성해주는 주체가 없습니다. 과거에는 MetalLB와 같은 별도의 솔루션을 설치해야만 LoadBalancer 타입의 서비스를 사용할 수 있었죠. 하지만 이제 Cilium이 이 기능을 자체적으로 품게 되었습니다.


3. Cilium의 LoadBalancer IPAM과 L2 Announcement

Cilium은 온프레미스 환경에서도 클라우드와 유사한 경험을 제공하기 위해 LoadBalancer IPAM(LB-IPAM)L2 Announcement 기능을 제공합니다.

LoadBalancer IPAM (LB-IPAM)

LB-IPAM은 LoadBalancer 타입의 서비스에 할당할 외부 IP 주소 풀(Pool)을 사용자가 직접 정의하고 관리할 수 있게 해주는 기능입니다.

# LoadBalancer 서비스에 할당할 IP 주소 풀을 정의하는 CRD
apiVersion: "cilium.io/v2"
kind: CiliumLoadBalancerIPPool
metadata:
  name: "cilium-lb-ippool"
spec:
  blocks:
    - start: "192.168.10.211"
      stop: "192.168.10.215"

위와 같이 IP 풀을 생성해두고, webpod 서비스를 LoadBalancer 타입으로 변경하면, Cilium은 정의된 풀에서 사용 가능한 IP(192.168.10.211)를 자동으로 할당하여 서비스의 EXTERNAL-IP로 설정해줍니다.

# webpod 서비스를 LoadBalancer 타입으로 변경
kubectl patch svc webpod -p '{"spec":{"type": "LoadBalancer"}}'

# 할당된 EXTERNAL-IP 확인
kubectl get svc webpod
# NAME     TYPE           CLUSTER-IP     EXTERNAL-IP      PORT(S)        AGE
# webpod   LoadBalancer   10.96.32.212   192.168.10.211   80:32039/TCP   150m

L2 Announcement: "192.168.10.211은 바로 나야!"

이제 서비스에 외부 IP가 할당되었지만, 아직 한 가지 문제가 남았습니다. 같은 네트워크 대역에 있는 다른 장비들(예: router)은 192.168.10.211이라는 IP가 실제로 어디에 있는지 알지 못합니다. 이 IP 주소에 대한 MAC 주소를 모르기 때문에 ARP 요청을 보내도 아무도 응답하지 않아 통신이 불가능하죠.

이때 필요한 것이 L2 Announcement 기능입니다. 이 기능이 활성화되면, LoadBalancer 서비스의 트래픽을 처리하게 될 노드(일반적으로 서비스의 엔드포인트 파드가 실행 중인 노드 중 하나)가 "192.168.10.211 주소의 주인은 바로 나(의 MAC 주소)야!" 라고 네트워크 전체에 알리기 위해 Gratuitous ARP (GARP) 패킷을 주기적으로 브로드캐스트합니다.

이 GARP 응답을 받은 스위치나 라우터는 자신의 ARP 테이블에 192.168.10.211 IP와 해당 노드의 MAC 주소를 매핑하여 기록합니다. 그 결과, 외부 클라이언트가 192.168.10.211로 보내는 트래픽은 L2 레벨에서 정확히 해당 노드로 전달될 수 있게 됩니다. 이는 MetalLB의 L2 모드와 동일한 원리로 동작합니다.

결론

쿠버네티스 서비스는 변덕스러운 파드들의 세상에 안정성이라는 질서를 부여하는 핵심 개념입니다. 그리고 kube-proxy의 발전사와 Cilium의 eBPF 기반 서비스 구현은 쿠버네티스 네트워킹이 어떻게 성능과 확장성을 향해 진화해왔는지를 보여주는 좋은 예시입니다.

특히 Cilium의 LB-IPAM과 L2 Announcement 기능은 온프레미스 환경의 제약을 뛰어넘어, 클라우드 환경과 거의 동일한 수준의 편리한 서비스 노출을 가능하게 합니다. 더 이상 외부 노출을 위해 복잡한 솔루션을 고민할 필요 없이, Cilium 하나로 클러스터 내부와 외부 네트워킹을 모두 해결할 수 있게 된 것입니다. 다음 스터디에서는 BGP를 연동하여 더욱 정교하고 확장성 있는 외부 라우팅을 구성하는 방법을 알아보겠습니다.