반응형

LoadBalancer 서비스 타입

Kubernetes 의 NodePort 서비스는 각 서비스마다 고유한 IP 를 가질 수 있지만, 클라이언트가 어느 호스트로 트래픽을 보낼지 결정해야 하는 단점이 있습니다. 또한 노드가 다운되면 IP + Port 조합 사용이 불가합니다. LoadBalncer 서비스 타입은 이러한 단점을 해결하고자 외부에서 IP 를 할당해주고, 어떤 IP 로 트래픽을 보내야 할지 알려줍니다. 클라이언트는 LoadBalancer 서비스 타입의 IP 만 알고 있으면 됩니다. 아래와 같이 Kubernetes에서 LoadBalancer 타입 서비스를 만들면,

apiVersion: v1
kind: Service
metadata:
  name: my-app
spec:
  type: LoadBalancer
  ports:
  - port: 80
  selector:
    app: my-app

 

클라우드에서는 AWS, GCP, Azure 같은 클라우드 제공업체가 자동으로 로드밸런서를 만들어주고 IP를 할당해주지만, 온프레미스 환경에서는 아무도 IP 를 할당해주지 않아서 아래처럼 계속 <pending> 상태로 남아있게 됩니다.

NAME     TYPE           EXTERNAL-IP   PORT(S)
my-app   LoadBalancer   <pending>     80:30080/TCP

 

Cilium 에서는 이러한 역할을 LoadBalancer IPAM 이 담당합니다. LoadBalancer 서비스 유형에서 IP 를 할당하고, 로드밸런싱 역할을 수행합니다. 하지만 IP 를 각 노드에 할당을 해준다고 해서 통신이 가능할까요? 자신의 주소는 받았지만, 경로는 설정되지 않아서 도달할 수 없습니다. 누군가(라우터 or 스위치)가 경로를 안내해주어야 하지만, 자신이 어디있다고 말하지 않으면 네트워크 관리자가 매번 라우터 or 스위치에 수동적으로 경로 테이블을 변경해야 할 것입니다. 따라서 어떻게 자신의 주소를 홍보할 것인지 방법이 필요합니다.

 

같은 네트워크 대역이라면 ARP 프로토콜을 통해 자신의 주소를 알려줄 수 있고, 다른 네트워크 대역이라면 BGP 프로토콜을 통해 자신의 주소를 홍보할 수 있습니다. cilium 에서 어떻게 설정할 수 있는지 아래에서 살펴보겠습니다.

 

Cilium LoadBalncer IPAM (서비스의 IP 분배 방법)

Cilium 에서 LoadBalancer IPAM 은 IP 를 Pool 형태로 관리하고, 필요 시 Pool 에서 할당하고 해제 시 Pool 에 반납합니다.

apiVersion: "cilium.io/v2"
kind: CiliumLoadBalancerIPPool
metadata:
  name: "cilium-lb-ippool"
spec:
  blocks:
  - start: "192.168.10.211"
    stop: "192.168.10.215"

 

blocks 문법 형태로 start 와 stop 사이의 연속된 범위를 지정할 수 있고,

apiVersion: "cilium.io/v2"
kind: CiliumLoadBalancerIPPool
metadata:
  name: "cilium-lb-ippool"
spec:
  cidrs:
  - "192.168.10.0/24"

 

cidrs 문법 형태로 서브넷 마스크를 활용하여 CIDR 대역을 지정할 수도 있습니다. 혼용하여 사용도 가능합니다.

보통 blocks 문법은 불연속적인 IP 범위를 각각 지정하여 정확한 IP 를 분배할 때 사용하며, CIDR 문법은 CIDR 대역으로 유연하게 관리할 때 사용합니다.

 

 

위와 같은 환경에서 아래와 같이 ip pool 을 생성 후, LoadBalancer 타입의 서비스를 생성해보면,

ippool 중 하나의 ip 가 할당되는 것을 볼 수 있습니다.

 

내부 클러스터끼리도 정상 통신하고 있습니다.

 

하지만 k8s 클러스터 외부인 라우터에서 접속하면 아래와 같이 통신이 되지 않는 것을 볼 수 있습니다.

 

앞서 살펴보았듯이, 다른 네트워크 대역과 통신하려면 서비스의 IP 를 홍보해야 합니다.

 

Cilium 의 IP 홍보방법

cilium 의 LoadBalancer IPAM 에서 Cilium BGP Control Plane 사용하여 BGP 프로토콜을 통해 IP 를 홍보하고, L2 Announcements/L2 Aware LB(베타)를 통해 로컬에 자신의 주소를 홍보합니다.

Cilium L2 Announcement

로컬 영역 네트워크(LAN) 에서 External IP 나 LoadBalancer 유형의 IP 요청이 왔을 때 자신의 ARP 주소를 응답 쿼리로 보냄으로써 통신이 수행됩니다. 가장 빠르게 ARP 요청을 보낸 노드가 리더 노드가 되어 라우팅 역할을 수행하게 됩니다. 나머지 노드는 백업 상태로 유지되다가 리더 노드가 죽으면 다시 빠르게 ARP 요청을 보낸 노드를 리더로 선출합니다. 이처럼 항상 리더 노드를 거쳐 트래픽이 이동해야 된다는 단점이 있다는 것을 알 수 있습니다.

helm upgrade cilium cilium/cilium --namespace kube-system --version 1.18.0 --reuse-values \
 --set l2announcements.enabled=true
   
kubectl rollout restart -n kube-system ds/cilium

 

l2announcements.enabled 옵션을 활성화하고 rollout 하여 announce 기능을 활성화합니다.

Cilium L2 Announcement Policy 정책으로 특정 서비스와 특정 노드에 필터링을 걸어 조금 더 세밀하게 제어할 수 있습니다.

apiVersion: "cilium.io/v2alpha1"
kind: CiliumL2AnnouncementPolicy
metadata:
  name: policy1
spec:
  serviceSelector:
    matchLabels:
      app: webpod
  nodeSelector:
    matchExpressions:
      - key: kubernetes.io/hostname
        operator: NotIn
        values:
          - k8s-w0
  interfaces:
  - ^eth[1-9]+
  externalIPs: true
  loadBalancerIPs: true

 

webpod 서비스를 노출하는 externalIP 와 loadbalncerIP 유형들에 대해 k8s-w0 을 제외한 노드가 ARP 응답을 보낼 수 있도록 정책을 구성하였습니다. 앞서 본 테스트 환경에서 k8s-w0 노드는 k8s 클러스터 내에 있지 않고 같은 네트워크 영역에 있지 않으므로 ARP 를 광고할 수 있는 노드에서 배제해야 합니다.

 

 

정책을 적용하고 나서 현재 상태를 확인해보니 k8s-w1 노드가 리더로 선출된 것을 확인할 수 있습니다.

 

 

cilium-dbg shell -- db/show l2-announce  명령어를 통해서도 현재 서비스 IP 가 어떤 노드에게 할당되어 있는지 확인 가능합니다.

 

 

아까와 다르게 router 노드와 k8s-cluster 간 통신이 정상적으로 수행되는 것을 볼 수 있습니다.

 

 

router 노드의 ARP 테이블도 확인해보면 서비스(192.168.10.211)의 ARP 가 현재 리더 노드(192.168.10.101)의 ARP 와 같습니다.

리더 노드가 모든 트래픽을 받아 원래 도착지 주소로 보냄으로써 통신한게 됩니다.

 

Cilium LoadBalncer IPAM 추가 기능

cilium <-> router 간 노드가 정상적으로 통신된 상태에서 cilium LoadBalancer IPAM 의 추가 기능을 살펴보겠습니다.

추가 기능을 살펴보기 앞서 netshoot 을 개량하여 만든 웹 서비스를 하나 더 띄워 보겠습니다.

 

cat << EOF | kubectl apply -f -
apiVersion: apps/v1
kind: Deployment
metadata:
  name: netshoot-web
  labels:
    app: netshoot-web
spec:
  replicas: 3
  selector:
    matchLabels:
      app: netshoot-web
  template:
    metadata:
      labels:
        app: netshoot-web
    spec:
      terminationGracePeriodSeconds: 0
      containers:
        - name: netshoot
          image: nicolaka/netshoot
          ports:
            - containerPort: 8080
          env:
            - name: POD_NAME
              valueFrom:
                fieldRef:
                  fieldPath: metadata.name
          command: ["sh", "-c"]
          args:
            - |
              while true; do 
                { echo -e "HTTP/1.1 200 OK\r\nContent-Type: text/plain\r\n\r\nOK from \$POD_NAME"; } | nc -l -p 8080 -q 1;
              done
EOF

cat << EOF | kubectl apply -f -
apiVersion: v1
kind: Service
metadata:
  name: netshoot-web
  labels:
    app: netshoot-web
spec:
  type: LoadBalancer
  selector:
    app: netshoot-web
  ports:
    - name: http
      port: 80      
      targetPort: 8080
EOF

 

서비스 생성 이후, CiliumL2AnnouncementPolicy 정책을 하나 더 생성하면

 

cat << EOF | kubectl apply -f -
apiVersion: "cilium.io/v2alpha1"  # not v2
kind: CiliumL2AnnouncementPolicy
metadata:
  name: policy2
spec:
  serviceSelector:
    matchLabels:
      app: netshoot-web
  nodeSelector:
    matchExpressions:
      - key: kubernetes.io/hostname
        operator: NotIn
        values:
          - k8s-w0
  interfaces:
  - ^eth[1-9]+
  externalIPs: true
  loadBalancerIPs: true
EOF

 

 

Requesting IPs

Service 에 External IP 를 직접 설정할 수 있는 기능으로 CiliumLoadBalancerIPPool 으로 선언한 IP Pool 에 해당하는 범위 내에 특정 IP 들을 직접 선택할 수 있습니다.

 

## metadata.annotations 아래에 추가
  annotations:
    "lbipam.cilium.io/ips": "192.168.10.215"

 

Service 객체의 "lbipam.cilium.io/ips" 어노테이션으로 활성화할 수 있습니다.

 

 

현재는 192.168.10.212 IP 를 사용하고 있지만,

 

 

annotation 적용 후 192.168.10.215 IP 로 변경한 것을 볼 수 있습니다.

 

Sharing Keys

Service 간 External IP 를 같이 사용하고자 할 때 사용합니다. Service 객체의 "lbipam.cilium.io/sharing-key" 어노테이션으로 활성화할 수 있습니다.

# metadata.annotations 아래 아래 추가
  annotations:
  	# "lbipam.cilium.io/ips": "192.168.10.215" 같은 IP 로 세팅
    "lbipam.cilium.io/sharing-key": "1234"

 

포트만 다른 동일한 서비스를 하나 더 만들어 보고 같은 IP 를 공유할 수 있는지 체크해보겠습니다.

 

cat << EOF | kubectl apply -f -
apiVersion: v1
kind: Service
metadata:
  name: netshoot-web2
  labels:
    app: netshoot-web
spec:
  type: LoadBalancer
  selector:
    app: netshoot-web
  ports:
    - name: http
      port: 8080      
      targetPort: 8080
EOF

 

위에서 생성한 netshoot-web 서비스에도 같은 sharing-key 를 넣어주어야 합니다.

 

 

netshoot-web 서비스와 netshoot-web2 서비스의 IP 가 같아진 것을 볼 수 있습니다.

반응형

'인프라 > 쿠버네티스' 카테고리의 다른 글

[cilium] cluster mesh  (2) 2025.08.15
[cilium] BGP 설정 방법  (1) 2025.08.15
[cilium] Overlay Network  (1) 2025.08.09
[cilium] Routing & Masquerading  (1) 2025.07.31
[cilium] IPAM  (3) 2025.07.31

+ Recent posts