Cilium

개요


실리움은 eBPF 기반의 CNI 프로바이더이다.
칼리코와 더불어 가장 많이 쓰이는 CNI 중 하나이며, CNCF 졸업 프로젝트이다.
ebpf 관련 툴을 활발하게 만들고 있는 Isovalent 주도로 만들어지고 있다.

CNI로 출발했지만, 점차 발전하며 훨씬 다양한 기능을 제공하고 있다.
image.png
실리움으로 쿠버 로드밸런서를 세팅하는 것도 가능하고, 서비스 메시를 구축할 수도 있다..
그래서 실질적으로 실리움을 쓴다고 하면 별도의 네트워크 솔루션을 도입하지 않고 실리움의 기능들을 백번 활용하여 구축하는 케이스가 많다.

허블이란 UI 툴을 통해 모니터링을 매우 이쁘게 할 수 있다.[1]

특징

ebpf 를 사용한다는 게 그냥 특징이다.

물론 이 특징이 매우 큰 것이긴 하다.
소켓에서 인터페이스로 가는 기본적인 리눅스의 네트워크 스택을 거치지 않고, 실리움의 ebpf로 구현한 자체 경로를 통해 네트워크 기능을 수행할 수 있다.[2]
원래는 복잡하게 거쳐서 이뤄져야 했던 iptables 와 같은 영역을 전부 우회하고 실리움이 모든 기능을 커널에 임베디드된 코드를 통해 제공할 수 있다.

기능

구조

아래 그림은 실리움이 커널에 bpf로 접근해 각종 조작을 수행하는 전반 구조를 담고 있다.

구체적으로 실리움의 구성 요소는 다음과 같다.

이제부터는 실리움이 제공할 수 있는 각종 기능들을 알아본다.

CNI 모드

클러스터 내 파드 간 네트워킹을 전담하는 cni로서, 실리움은 두 가지 모드를 제공한다.[3]
image.png
캡슐 모드와 네이티브 라우팅 모드인데, 이름은 여기저기 다르지만 결국 뜻은 같다.

캡슐화(encapsulation) 모드

기본 설정으로, 네트워크 인프라 단의 별도의 설정 없이 동작할 수 있는 모드이다.
이 모드에서는 UDP 기반의 캡슐 터널링 프로토콜이 사용된다.
각 프로토콜을 잘 몰라 조금 더 자세히 정리했다.

이 방식의 장점은 다음과 같다.

단점으로는 MTU 부하가 존재할 수 있다.
아무래도 패킷의 양이 늘어나고 자체 크기도 커질 수 있으며, 이는 처리량의 저하를 유발할 수 있다.
점보 프레임을 활성화해 이를 완화할 수는 있다.

네이티브 라우팅(native routing) 모드

routing-mode: native를 통해 활성화할 수 있는 모드로, 노드의 라우팅 테이블을 활용한다.

보다시피 파드마다 lxc라는 인터페이스를 부착시키고, 노드의 라우팅 테이블에 관련한 경로를 설정한다.
그래서 내부로 들어가는 주소만 아니라면 모든 패킷을 라우팅 테이블에 의해 라우팅되며, 패킷은 로컬 프로세스가 보내는 패킷처럼 다뤄진다.
그래서 패킷 포워딩 커널 파라미터가 설정돼야 하는데 이 모드를 설정하면 실리움이 알아서 세팅도 해준다. 위험한 놈

라우팅 테이블을 사용한다는 것은 노드 별로 다른 PodCIDR이 있기에 가능한 것이다.
몇 가지 제약이 있다.

IP 주소 관리(IPAM)

실리움은 파드의 IP 주소를 관리하는 기능도 수행할 수 있다. (사실 다른 cni들도 대체로 그렇다)
ipam 필드을 설정해 각 모드를 사용할 수 있다.
전체 제어 흐름을 보자면..

cri가 컨테이너 띄울 때 cni 플러그인을 활용한다.
이때 에이전트가 각 모드에 따라 추가적인 동작을 하며 풀에서 ip를 할당하는 원리이다.
복잡해보이나 각 설정 모드의 흐름을 한 구조도에 담아서 그럴 뿐이다.

cluster scope


기본 모드로, CiliumNode라는 커스텀 리소스에 설정된 cidr를 기반으로 노드 별 cidr을 설정한다.[4]
이 리소스는 오퍼레이터가 초반에 알아서 만들어주는데 이 상태에서 알아서 조금씩 커스텀하면 된다.

이때 한번 설정된 풀 자체를 수정하는 것은 권장되지 않고, 대신 그냥 새로운 대역을 할당하는 것이 추천된다.
노드 자체, 브로드 캐스트를 위해 cidr에 두 주소를 예약해버리기에 추천되는 범위는 /29 이상이다.
또한 기본으로 설정되는 파드 cidr은 10.0.0.0/8 이기에 노드 간 네트워크 주소가 해당 범위에 들어간다면 주의하자.
clusterPoolIPv4PodCIDRList를 명시적으로 세팅하여 이 문제를 회피할 수 있다고 한다.

kubernetes host scope

기본적으로 쿠버네티스에서는 노드 별로 파드 IP 주소를 할당한다.[5]

ipam: kubernetes로 설정하면 기본 노드에 할당된 cidr 값을 통해 파드의 ip를 관리해준다.(그럼 있으나 없으나..)
주의사항으로 kube-controller-manager 인자로 --allocate-node-cidrs 인자를 넣어 노드에 꼭 cidr이 설정되도록 해야 한다.
아니면 이렇게 노드에 어노테이션을 달아 직접 노드 별 cidr을 세팅하는 것도 가능하다.
image.png

다른 모드들도 많으니 문서 참고.

큐브 프록시 대체

실리움은 서비스에서 파드로 가는 트래픽 경로를 제공하는 kube-proxy를 대체할 수 있다.[6]

    --set kubeProxyReplacement=true \
    --set k8sServiceHost=${API_SERVER_IP} \
    --set k8sServicePort=${API_SERVER_PORT}

헬름 기준으로는 위와 같이 세팅해주면 된다.
이때 실리움은 bpf cgroup을 노드 /run/cilium/cgroupv2에 마운팅하는데, 이미 노드가 cgroupv2를 사용하고 있다면 아래와 같이 작성해주면 된다.

--set cgroup.autoMount.enabled=false
--set cgroup.hostRoot=/sys/fs/cgroup

확인 방법

kubectl -n kube-system exec ds/cilium -- cilium-dbg status | grep KubeProxyReplacement
kubectl -n kube-system exec ds/cilium -- cilium-dbg service list
iptables-save | grep KUBE-SVC # 이거 하면 아무것도 없어야 정상

클라이언트 IP를 보존하기 위해서는 서비스에 externalTrafficPolicy를 Local로 설정해야 한다.

마글레브 해시 로드밸런싱 모드를 설정할 수 있다.
마글레브 방식은 룩업 테이블에 각 노드가 동등한 순서로 배치되도록 보장해서 가급적 균등한 분산이 가능하게 한다.
특징으로, 노드가 제거되는 변화에 대해서는 기존 테이블이 변화되지 않고[7], 추가되는 상황에서만 기존 라우팅 경로가 변경될 가능성이 생기기에 상대적으로 커넥션에 대해 안정성이 큰 알고리즘이다.[8]

BGP

실리움에서는 BGP 컨트롤 플레인을 설정할 수 있다.[9]
이 bgp 설정을 함께 하는 클러스터 외부 라우터는 클러스터 내부의 각종 IP 주소로 트래픽을 라우팅할 수 있게 된다.
다시 말해, 클러스터의 파드 IP, 서비스 IP 만으로 외부에서 클러스터에 접근이 가능하게 된다는 말이다.

  --set bgpControlPlane.enabled=true

관련 리소스

아래 그림이 BGP 설정을 넣는 리소스오 이로부터 실리움이 동작하는 전체 구조를 담고 있다.[10]

파란색이 커스텀 리소스로 직접 사용자가 설정해주면 된다.

결과적으로 우측 하단처럼 파드 cidr, 서비스, 실리움의 ip풀 등의 주소를 bgp 피어들이 받아 라우팅할 수 있게 된다.
각 리소스 양식을 조금 더 상세히 파보자.

CiliumBGPClusterConfig

apiVersion: cilium.io/v2alpha1
kind: CiliumBGPClusterConfig
metadata:
  name: cilium-bgp
spec:
  nodeSelector:
    matchLabels:
      rack: rack0
  bgpInstances:
  - name: "instance-65000"
    localASN: 65000
    peers:
    - name: "peer-65000-tor1"
      peerASN: 65000
      peerAddress: fd00:10:0:0::1
      peerConfigRef:
        name: "cilium-peer"
    - name: "peer-65000-tor2"
      peerASN: 65000
      peerAddress: fd00:11:0:0::1
      peerConfigRef:
        name: "cilium-peer"

먼저 노드를 고르고, 해당 노드에서 외부 BGP 동료를 세팅한다.
peerConfigRef는 아래 리소스를 뜻한다.

CiliumBGPPeerConfig

apiVersion: cilium.io/v2alpha1
kind: CiliumBGPPeerConfig
metadata:
  name: cilium-peer
spec:
  timers:
    holdTimeSeconds: 9
    keepAliveTimeSeconds: 3
  authSecretRef: bgp-auth-secret
  ebgpMultihop: 4
  gracefulRestart:
    enabled: true
    restartTimeSeconds: 15
  families:
    - afi: ipv4
      safi: unicast
      advertisements:
        matchLabels:
          advertise: "bgp"

여기에서 피어 관련 설정 세부를 지정한다.
구체적으로 다음의 기능들을 설정할 수 있다.

CiliumBGPAdvertisement

apiVersion: cilium.io/v2alpha1
kind: CiliumBGPAdvertisement
metadata:
  name: bgp-advertisements
  labels:
    advertise: bgp
spec:
  advertisements:
    - advertisementType: "PodCIDR"
      attributes:
        communities:
          standard: [ "65000:99" ]
        # 가까운 경로 가중치
        localPreference: 99

피어에게 무엇을 광고할지 지정하는 리소스이다.
advertisementType에는 PodCIDR, CiliumIPAMPool, Service를 넣을 수 있다.
서비스를 설정할 경우, 아래와 같이 어떤 타입의 서비스를 광고할 건지도 세부 지정 가능하다.

    - advertisementType: "Service"
      service:
        addresses:          # <-- specify the service types to advertise
          - LoadBalancerIP

L2 announcement

MetalLB가 사용하는 방식 그대로, garp를 날려서 ip 대역을 알리는 기능을 지원한다.[11]
그래서 위에 bgp도 지원하고 이것도 지원하니 metalLB를 같이 쓰일 이유가 전혀 없다..
이 기능은 큐브 프록시를 대체할 때 사용 가능하다.

   --set l2announcements.enabled=true \
   --set k8sClientRateLimit.qps={QPS} \
   --set k8sClientRateLimit.burst={BURST} \
   --set kubeProxyReplacement=true \
   --set k8sServiceHost=${API_SERVER_IP} \
   --set k8sServicePort=${API_SERVER_PORT} \
   --set externalIPs.enabled=true
apiVersion: "cilium.io/v2alpha1"
kind: CiliumL2AnnouncementPolicy
metadata:
  name: policy1
spec:
  serviceSelector:
    matchLabels:
      color: blue
  nodeSelector:
    matchExpressions:
      - key: node-role.kubernetes.io/control-plane
        operator: DoesNotExist
  interfaces:
  - ^eth[0-9]+
  externalIPs: true
  loadBalancerIPs: true

노드를 선택하고 해당 노드에 인터페이스를 지정한다.
그럼 매칭된 서비스는 해당 인터페이스로 garp된다.

헬름 설정

헬름 관련 설정들을 조금 알아보자.[12]
헬름 설정 가능한 필드 길이가 대충 3000줄 되기 때문에.. 조금이라도 전반적인 부분을 파악해두면 좋다.
헬름 설정을 넣으면 컨피그맵에 해당 정보가 들어가는데, 이를 에이전트와 오퍼레이터가 받아 설정을 반영한다.
image.png
에이전트에서 해당 설정을 받아서 처리하는 방식은 다음과 같다.
image.png
viper(아마 이건 고랭 라이브러리 말하는듯?)이 설정을 받고, ebpf 설정 파일을 작성한 후 이를 커널 프로그램으로 넣는다.

설치

이 부분은 당장 내가 활용한 방법만 정리한다.
헬름 설치 당빠 가능

큐브스프레이를 이용하는 방식[13]

투닝

https://docs.cilium.io/en/stable/operations/performance/tuning/

관련 문서

EXPLAIN - 파생 문서

이름0related생성 일자

Dataview: No results to show for table query.

기타 문서

Z0-연관 knowledge, Z1-트러블슈팅 Z2-디자인,설계, Z3-임시, Z5-프로젝트,아카이브, Z8,9-미분류,미완
이름0코드타입생성 일자

Dataview: No results to show for table query.

참고


  1. https://docs.cilium.io/en/stable/observability/hubble/#hubble-intro ↩︎

  2. https://cilium.io/blog/2020/11/10/ebpf-future-of-networking/ ↩︎

  3. https://docs.cilium.io/en/stable/network/concepts/routing/ ↩︎

  4. https://docs.cilium.io/en/stable/network/concepts/ipam/cluster-pool/ ↩︎

  5. https://docs.cilium.io/en/stable/network/concepts/ipam/kubernetes/#k8s-hostscope ↩︎

  6. https://docs.cilium.io/en/stable/network/kubernetes/kubeproxy-free/ ↩︎

  7. https://cla9.tistory.com/217 ↩︎

  8. http://static.googleusercontent.com/media/research.google.com/en//pubs/archive/44824.pdf ↩︎

  9. https://docs.cilium.io/en/stable/network/bgp-control-plane/bgp-control-plane/ ↩︎

  10. https://docs.cilium.io/en/stable/network/bgp-control-plane/bgp-control-plane-v2/ ↩︎

  11. https://docs.cilium.io/en/stable/network/l2-announcements/ ↩︎

  12. https://www.youtube.com/watch?v=ni0Uw4WLHYo ↩︎

  13. https://docs.cilium.io/en/stable/installation/k8s-install-kubespray/ ↩︎