System/Container & Kubernetes

On-Premise 환경에서 Kubernetes LoadBalancer 구현

마늘김 2025. 4. 27. 03:57

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

 CSP에서 제공하는 Kubernetes 서비스는 클라우드 인프라에 잘 통합되어 있어 있습니다. 그래서 간단한 명령어나 웹 UI를 통해 쉽게 클러스터를 생성할 수 있고 사용할 수 있습니다. 특히 Load Balancer 서비스에 대한 구현체라던가, CSI 뒤에서 작동하는 스토리지 프로바이더와 같은 Kubernetes에서는 직접적으로 그 기능을 제공하지는 않지만 꼭 필요한 요소들까지 함께 제공되어 사용의 편의성이 아주 높습니다.

 하지만 Kubernetes를 On-premise 환경에서 구현한다면 그 이야기가 달라집니다. 이런 요소 하나하나가 모두 관리의 포인트가 되기 때문입니다. 게다가 이를 Seamless 하게 통합하고 운영하는 것은 보통 일이 아닙니다. 그럼에도 불구하고 여러 가지 요인으로 인해 퍼블릭 클라우드의 Kubernetes 서비스를 사용할 수 없다면 이를 직접 구현할 수 있어야 합니다.

 이번 포스팅에서는 Kubernetes의 Service 타입 중 Load Balancer를 Cilium BGP Control Plane 기능을 통해 구현하는 방법에 대해서 소개하고자 합니다.

시작하기 전에

 On-Premise환경에 베어메탈, 또는 가상머신으로 구성된 Kubernetes 클러스터가 있어야 합니다. 또한 BGP 기능을 하는 라우터도 필요합니다. 이번 포스팅에서는 가상머신으로 구성된 Kubernetes 클러스터와 OPNsense의 BGP 기능을 기준으로 설명하도록 하겠습니다.

1. Kubernetes 클러스터

Host Name 역할 IP Address
bgp-k8s-ctrl-01 Kubernetes Control Plane Node 192.168.200.21
bgp-k8s-ctrl-02 Kubernetes Control Plane Node 192.168.200.22
bgp-k8s-ctrl-03 Kubernetes Control Plane Node 192.168.200.23
bgp-k8s-wkr-01 Kubernetes Worker Node / BGP Peer(AS 65000) 192.168.200.31
bgp-k8s-wkr-02 Kubernetes Worker Node / BGP Peer(AS 65000) 192.168.200.32
bgp-k8s-wkr-03 Kubernetes Worker Node / BGP Peer(AS 65000) 192.168.200.33
  • Kubernetes Version: 1.33.0
  • OS: Ubuntu 22.04
  • Control Plane Node 3대와 Worker Node 3대로 이루어진 구성
  • HAProxy를 통해 Kubernetes API 단일 진입점을 제공하고 있음(본 포스팅에서는 중요하지 않아 다루지 않음, 해당 내용 링크)
 

[Kubernetes] HAProxy와 Keepalived를 활용한 Kubernetes API 클러스터 HA 구현 - 1편

0. 들어가며 Kubernetes 클러스터는 크게 Master 노드(Control Plane 역할)와 Worker 노드(워크로드 구동 역할)로 나뉩니다. 프로덕션 환경에서는 고가용성(HA) 및 로드 밸런싱을 위해 Master 노드를 여러대로

tech-recipe.tistory.com

 

2. Cilium

  • Image version: 1.17.2
  • Helm chart version: 1.17.3
  • Cli version: 0.18.3

3. OPNsense

  • Version: OPNsense 25.1.5_5-amd64
  • OPNsense 설치 및 설정 포스팅 링크
  • BGP Peer IP(AS 65551): 192.168.200.1 (Kubernetes Node들의 Gateway IP 주소로 OPNsense의 인터페이스)
 

OPNsense로 방화벽 구축하기 [1편]

구축 배경 홈 랩을 확장하면서 가정용 Wi-Fi 네트워크와 홉 랩 인프라용 네트워크 간의 분리 및 격리, 라우팅 및 접근 제어 그리고 그 외 서비스에 필요한 기능 구현이 필요했습니다. 그래서 네트

tech-recipe.tistory.com

 

BGP의 기본 개념

 

BGP란 무엇인가? - 네트워킹의 Border Gateway Protocol 설명 - AWS

Border Gateway Protocol(BGP)은 인터넷에서 데이터를 전송하는 데 가장 적합한 네트워크 경로를 결정하는 일련의 규칙입니다. 인터넷은 표준화된 프로토콜, 디바이스 및 통신 기술을 통해 서로 연결된

aws.amazon.com

본격적인 실습에 들어가기 전에 BGP에 대해서 알아봅시다. BGP를 완벽하게 이해할 필요는 없지만 이번 실습에서 진행할 여러 과정에 꼭 필요한 개념 정도는 알고 가는것이 좋을 것 같습니다.

 BGP(Border Gateway Protocol)은 동적 라우팅 프로토콜의 한 종류입니다. 라우팅 정보를 공유하는 라우터를 그룹화 하고, 그룹 간에 게이트웨이를 두어 라우팅 정보를 교환하여 네트워크 연결을 만듭니다. 이 과정에서 라우터들은 최적의 경로에 대한 정보를 업데이트합니다.

1. AS(Autonomous System)

 BGP를 이야기하면 항상 등장하는 것이 바로 AS, 자율 시스템입니다. 이는 라우터의 그룹이라고 보면 되는데, 이 AS 안의 라우터들은 모두 서로의 라우팅 정보를 공유합니다. AS내의 각각의 라우터들은 서로의 라우팅 정보를 교환하여 네트워크 토폴로지를 인식합니다. 이를 통해 라우터 각자는 특정 네트워크로 향하는 최적의 경로를 계산합니다.

2. Border Gateway

 AS 간 경계에 있고 라우팅 경로를 교환하는 라우터를 Border Gateway라 합니다. 이를 통해 격리된 AS간에도 라우팅 정보를 교환할 수 있습니다. AS 내의 라우터들은 Border Gateway 라우터로 부터 전파되는 다른 AS의 라우팅 정보를 수신하고 네트워크 토폴로지를 업데이트 합니다. AS간 라우팅 정보 교환을 위해서 Border Gateway는 TCP/179 포트를 사용합니다.

네트워크 토폴로지

 이번 포스팅에서는 OPNsense와 Cilium이 각각 AS를 형성할 것입니다. Cilium은 Kubernetes에서 LoadBlancer Service가 생성되면 자신이 가지고 있는 LB IP Pool에서 IP를 하나 할당합니다. 그리고 이를 BGP를 통해 광고하고, OPNsense는 이것을 수신하여 다른 클라이언트가 접근할 수 있는 라우팅 정보를 제공합니다. 간략히 나타내면 아래와 같습니다.

Cilium BGP 토폴로지

  • Kubernetes에서 LoadBlancer Service 타입이 생성
  • Cilium이 이를 감지하고 CiliumLoadBalancerIPPool에서 IP를 할당
  • CiliumBGPAdvertisement가 생성된 LB IP를 AS 65000에 전파
  • AS 65000의 BGP Peer들이 목적지가 172.16.200.1인 패킷은 192.168.200.31, 192.168.200.32, 192.168.200.33으로 보내면 된다고 광고
  • 이를 수신한 AS 65551의 BGP Peer의 라우터(OPNsense)는 이 중 최적의 경로를 선정하여 라우팅 테이블을 업데이트
  • Client가 172.16.200.1에 대한 요청을 보내면 OPNsense에서 라우팅 정보 제공

Kubernetes Load Balancer 구현

 아래의 내용은 앞서 언급한 데로 Cilium이 배포된 Kubernetes Cluster와 kubectl 명령어, OPNsense가 이미 구성되어 있다는 가정하에 작성합니다.

1. Cilium CLI 설치

#리눅스 기준 아래 명령어 모두 복사하여 붙여넣고 실행
CILIUM_CLI_VERSION=$(curl -s https://raw.githubusercontent.com/cilium/cilium-cli/main/stable.txt)
CLI_ARCH=amd64
if [ "$(uname -m)" = "aarch64" ]; then CLI_ARCH=arm64; fi
curl -L --fail --remote-name-all https://github.com/cilium/cilium-cli/releases/download/${CILIUM_CLI_VERSION}/cilium-linux-${CLI_ARCH}.tar.gz{,.sha256sum}
sha256sum --check cilium-linux-${CLI_ARCH}.tar.gz.sha256sum
sudo tar xzvfC cilium-linux-${CLI_ARCH}.tar.gz /usr/local/bin
rm cilium-linux-${CLI_ARCH}.tar.gz{,.sha256sum}\

#설치 후 명령어가 잘 작동하는지 확인
cilium version

 이번 포스팅에서는 Cilium CLI를 통해 Cilium을 제어할 예정으로 CLI 설치가 필수입니다.

2. Cilium BGP 설정

#BPG Control Plane 활성화
cilium config set enable-bgp-control-plane true

#L2 announcements 활성화
cilium config set enable-l2-announcements true

#LB IPAM 활성화
cilium config set enable-lb-ipam true

 위 설정들을 통해 Cilium의 BGP 사용을 준비합니다. 다음은 Cilium BGP Control Plane Resource에 관한 설정입니다. 이 리소스들을 통해 Cilium BGP control plane을 유연하게 제어할 수 있습니다.

Cilium Control Plane Resource Diagram

2.1. BGP Cluster configuration

#bgp-cluster.yaml

apiVersion: cilium.io/v2alpha1
kind: CiliumBGPClusterConfig
metadata:
  name: cilium-bgp
spec:
  nodeSelector:
    matchLabels:
      cilium.io/bgp-instance: enable     #BGP instance가 될 노드를 Label로 지정
  bgpInstances:
    - name: instance-65000               #Cilium BGP의 AS 정보 설정
      localASN: 65000
      peers:
        - name: peer-opnsense
          peerASN: 65551                 #외부 AS 번호(이후 단계에서 OPNsense AS로 설정)
          peerAddress: 192.168.200.1     #외부 AS Peer(Border Gateway Router)의 IP 주소
          peerConfigRef:
            name: cilium-peer            #참조할 CiliumBGPPeerConfigf를 지정
  • CiliumBGPClusterConfig는 BGP Peer가 될 Kubernetes Node에 대한 명세
  • spec.nodeSelector.matchLabels의 Kye/Value의 값으로 BGP Peer가 될 Kubernetes Node 선택

2.2. Node Labels

#Kubernetes 워커노드에 Label 부여
kubectl label nodes bgp-k8s-wkr-01 bgp-k8s-wkr-02 bgp-k8s-wkr-03 cilium.io/bgp-instance=enable

2.3. BGP Peer Configuration

#bgp-peer.yaml

apiVersion: cilium.io/v2alpha1
kind: CiliumBGPPeerConfig
metadata:
  name: cilium-peer        #CiliumBGPClusterConfig에서 참조할 이름
spec:                      #BGP 관련 설정
  timers:
    connectRetryTimeSeconds: 5
    holdTimeSeconds: 9
    keepAliveTimeSeconds: 3
  ebgpMultihop: 1
  gracefulRestart:
    enabled: true
    restartTimeSeconds: 15
  families:                 #Peer가 광고할 네트워크 명세
    - afi: ipv4
      safi: unicast
      advertisements:       #참조할 CiliumBGPAdvertisement 이름
        matchLabels:
          advertise: bgp
  • spec.families는 Peer가 광고할 네트워크에 대한 정보가 명세됨
  • AFI: Address Family Identifier / SAFI: Subsequent Adress Family Identifier
  • 현재는 AFI/SAFI 옵션으로 {afi: ipv4, safi: unicast}{afi: ipv6, safi: unicast}만을 지원

2.4. BGP Advertisements

#bgp-advert.yaml

apiVersion: cilium.io/v2alpha1
kind: CiliumBGPAdvertisement
metadata:
  name: bgp-advertisements
  labels:
    advertise: bgp
spec:
  advertisements:
    - advertisementType: Service
      service:
        addresses:
          - LoadBalancerIP
      selector:
        matchLabels:
          color: blue     #Service 중에서 Label이 color=blue인 것 만을 BGP로 광고
  • BGP에 광고할 Kubernetes 네트워크에 대한 명세로 BGP Peer에서 이것을 참조
  • Pod CIDR이나 Service IP를 광고할 수 있음

2.5. LB IPAM

#blue-pool.yaml

apiVersion: cilium.io/v2alpha1
kind: CiliumLoadBalancerIPPool
metadata:
  name: blue-pool
spec:
  blocks:
    - cidr: "172.16.200.0/24"
  allowFirstLastIPs: "No"     #IP의 CIDR의 처음과 마지막 IP 주소를 사용하지 않는 옵션
  serviceSelector:
    matchLabels:
      color: blue             #Service의 Label이 color=blue이면서 LoadBalancer Type이면 Pool에서 IP를 할당
  • LoadBalancer 타입의 Service가 사용할 IP Pool을 관리
  • spec.allowFirstLastIPs 기본값은 Yes로 네트워크 혼란을 막기 위해 "No" 옵션을 권장

2.6. 설정 확인

 위에서 작성한 YAML을 모두 apply 한 후 잘 설정이 되었는지 확인이 필요합니다. 다음 명령어를 통해 확인할 수 있습니다.

#bgp peer 설정 확인
cilium bgp peers

#아래 처럼 출력
Node             Local AS   Peer AS   Peer Address    Session State   Uptime   Family         Received   Advertised
bgp-k8s-wkr-01   65000      65551     192.168.200.1   active          0s       ipv4/unicast   0          0    
bgp-k8s-wkr-02   65000      65551     192.168.200.1   active          0s       ipv4/unicast   0          0    
bgp-k8s-wkr-03   65000      65551     192.168.200.1   active          0s       ipv4/unicast   0          0

 우선 여기서 Session State가 active인 것은 현재 Cilium의 BGP가 작동하고 있다는 뜻입니다. 아직 OPNsense 쪽 BGP 설정을 하지 않았기 때문에 세션이 연결된 상태는 아닙니다.

3. OPNsense BGP 설정

3.1. FRR Plugin 설치

frr 플러그인 설치

  • OPNsense Web 콘솔 > System > Firmware > Plugins
  • frr 검색하여 설치

3.2. Routing 기능 설정

Routing 기능 활성화

  • Routing > General
  • 상단 Enable 체크 박스 활성화
  • Routing: General이 활성화되어야 라우팅 프로토콜이 작동됨

3.3. BGP 설정

BGP 기능 활성화

  • Routing > BGP > General 탭
  • enable 체크 박스 활성화 > BGP AS Number 65551(앞선 Cilium BGP Cluster Configuration에서 sepc.bgpInstances.peers.peerASN 값) 입력 > Apply

Neighbor 설정

  • Route > BGP > Neighbors 탭
  • [+] 버튼으로 Peer 추가
  • Description: Peer에 대한 설명으로 사람이 식별하기 좋은 정보 입력
  • Peer-IP: Kubernetes Worker 노드 IP 주소 입력
  • Rmote AS: CIlium BGP AS 번호 입력
  • Update-Source Interface: Kubernetes Worker 노드의 게이트웨이 인터페이스 선택
  • Kubernetes Worker 노드 모두 반복 작업
  • 우측 상단 서비스 재시작 버튼 클릭하여 BGP 서비스 재시작

 이렇게 BGP의 모든 설정을 마무리하였습니다. 이제 테스트 서비스를 만들어 잘 작동하는지 확인해 보도록 하겠습니다.


Load Balancer 테스트

 Nginx Deployment와 이를 노출할 LoadBlancer Service를 생성해 보겠습니다. 아래 YAML을 작성하여 apply 합니다.

#bgp-lb-test.yaml

#deployment
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: test-app
  namespace: default
spec:
  replicas: 2
  selector:
    matchLabels:
      app: test-app
  template:
    metadata:
      labels:
        app: test-app
    spec:
      containers:
      - name: nginx
        image: nginx:latest
        
#service
---
apiVersion: v1
kind: Service
metadata:
  name: test-service
  namespace: default
  labels:
    color: blue        #color=blue Label로 Cilium BGP에 적용 될 수 있도록 함
spec:
  type: LoadBalancer
  externalTrafficPolicy: Cluster
  loadBalancerClass: io.cilium/bgp-control-plane
  ports:
    - port: 80
      targetPort: 80
  selector:
    app: test-app

1. Kubernetes 상태 확인

#Service 생성 확인
kubectl get svc

NAME           TYPE           CLUSTER-IP       EXTERNAL-IP    PORT(S)        AGE
kubernetes     ClusterIP      10.96.0.1        <none>         443/TCP        25h
test-service   LoadBalancer   10.109.208.124   172.16.200.1   80:30199/TCP   2m

#endpoint 확인
kubectl get endpointslice

NAME                 ADDRESSTYPE   PORTS   ENDPOINTS                                      AGE
kubernetes           IPv4          6443    192.168.200.21,192.168.200.22,192.168.200.23   25h
test-service-25qq7   IPv4          80      10.0.0.184,10.0.5.16                           2m
  • test-service의 EXTERNAL-IP에 172.16.200.1이 할당됨
  • CiliumLoadBalancerIPPool의 설정의 내용(spec.blocks.cidr: 172.16.200.0/24, spec.allowFirstLastIPs: "No")과 일치하는 것 확인할 수 있음

2. 브라우저에서 LB IP 접근 테스트

LB IP(172.16.200.1)에 접근 확인

3. Cilium CLI 확인

#Peer 확인
cilium bgp peers

Node             Local AS   Peer AS   Peer Address    Session State   Uptime     Family         Received   Advertised
bgp-k8s-wkr-01   65000      65551     192.168.200.1   established     7h46m36s   ipv4/unicast   1          2    
bgp-k8s-wkr-02   65000      65551     192.168.200.1   established     7h46m34s   ipv4/unicast   1          2    
bgp-k8s-wkr-03   65000      65551     192.168.200.1   established     7h46m35s   ipv4/unicast   1          2  

#Route 확인

cilium bgp routes available ipv4 unicast vrouter 65000
Node             VRouter   Prefix            NextHop   Age        Attrs
bgp-k8s-wkr-01   65000     172.16.200.1/32   0.0.0.0   7h49m21s   [{Origin: i} {Nexthop: 0.0.0.0}]   
bgp-k8s-wkr-02   65000     172.16.200.1/32   0.0.0.0   7h49m19s   [{Origin: i} {Nexthop: 0.0.0.0}]   
bgp-k8s-wkr-03   65000     172.16.200.1/32   0.0.0.0   7h49m20s   [{Origin: i} {Nexthop: 0.0.0.0}]
  • BGP Peer의 Session State가 active에서 established가 된 것을 확인할 수 있음

4. OPNsense 확인

BGP Route Diagnostics
Route Status

 이렇게 해서, Cilium BGP와 OPNsense를 활용하여 Kubernetes의 Load Balancer Service를 생성해 보았습니다. On-Premise 환경에서 특별한 솔루션을 사용하지 않는 경우에 Load Balancer를 사용할 수 있는 유용한 방법이라고 생각합니다.


참고 자료

1. Cilium BGP 공식 문서

2. OPNsense BGP 공식 문서

 

Dynamic Routing - BGP Tutorials — OPNsense documentation

Note Rules allowing traffic from LAN Router A to LAN Router B must be created in their respective LAN rulesets. Since traffic from LAN A to LAN B will use the peering connection, additional rules must be created in the Peering ruleset. Create rules to allo

docs.opnsense.org


문제 발생! BGP의 한계?

 

Cilium BGP로 구성된 LoadBalancer의 한계

이전 포스팅에서 이어집니다. 'On-Premise 환경에서 Kubernetes LoadBalancer 구현'을 읽고 오시길 권장드립니다. On-Premise 환경에서 Kubernetes LoadBalancer 구현CSP에서 제공하는 Kubernetes 서비스는 클라우드 인

tech-recipe.tistory.com