Cilium
개요
실리움은 eBPF 기반의 CNI 프로바이더이다.
칼리코와 더불어 가장 많이 쓰이는 CNI 중 하나이며, CNCF 졸업 프로젝트이다.
ebpf 관련 툴을 활발하게 만들고 있는 Isovalent 주도로 만들어지고 있다.
CNI로 출발했지만, 점차 발전하며 훨씬 다양한 기능을 제공하고 있다.
실리움으로 쿠버 로드밸런서를 세팅하는 것도 가능하고, 서비스 메시를 구축할 수도 있다..
그래서 실질적으로 실리움을 쓴다고 하면 별도의 네트워크 솔루션을 도입하지 않고 실리움의 기능들을 백번 활용하여 구축하는 케이스가 많다.
허블이란 UI 툴을 통해 모니터링을 매우 이쁘게 할 수 있다.[1]
특징
ebpf 를 사용한다는 게 그냥 특징이다.
물론 이 특징이 매우 큰 것이긴 하다.
소켓에서 인터페이스로 가는 기본적인 리눅스의 네트워크 스택을 거치지 않고, 실리움의 ebpf로 구현한 자체 경로를 통해 네트워크 기능을 수행할 수 있다.[2]
원래는 복잡하게 거쳐서 이뤄져야 했던 iptables 와 같은 영역을 전부 우회하고 실리움이 모든 기능을 커널에 임베디드된 코드를 통해 제공할 수 있다.
기능
- CNI로서 파드 네트워킹 - 당연 가장 기본적인 기능.
- 오버레이 - 흔한 cni 방식으로 최소한의 전제조건으로 구축 가능
- 멀티 노드 간 파드 통신에서 오버레이 네트워크 구성을 하여 통신시킨다.
- VXLAN, Geneve 두 가지 캡슐 방식으로 지원된다.
- 네이티브 라우팅 - 호스트 노드의 라우팅 테이블을 활용하며, 추가적인 설정이 필요할 수도 있다.
- 라우팅 데몬이 돌고 있거나, 다른 라우터들과의 호환이 필요할 때 유용하다.
- 오버레이 - 흔한 cni 방식으로 최소한의 전제조건으로 구축 가능
- api 방화벽
- L3,4에서 기능하는 방화벽처럼 기능하는 것을 넘어 L7처럼 특정 HTTP 엔드포인트나 헤더, 특정 서비스에 대한 방화벽 기능도 수행할 수 있다.
- 서비스 신원에 기반한 트래픽 암호화
- 흔한 서비스 메시로서의 기능 가능!
- 외부 트래픽 처리(로드밸런싱) 및 접근 정책 설정
- 로드 밸런서로서 기능 가능!
- 대역폭 관리
- EDT(Earliest Departure Time) 기반 속도 제한 지원
- 어플리케이션 간 전송 대기 시간을 크게 줄일 수 있다.
- HTB(Hierachy Token Bucket), TBF(Token Bucket Filter) 등의 고전적인 방식을 사용하는 다른 대역폭 CNI 플러그인과 차별화된다.
- 간편한 모니터링과 트러블슈팅
- 허블 UI로 직관적인 모니터링 가능
- 자체 제공 cli 툴로 tcpdump, ping 동작을 제공한다.
구조
아래 그림은 실리움이 커널에 bpf로 접근해 각종 조작을 수행하는 전반 구조를 담고 있다.
구체적으로 실리움의 구성 요소는 다음과 같다.
- 실리움
- 에이전트 - 데몬셋
- kube-apiserver의 설정을 감시하며 노드 워크로드에 대해 각종 기능을 수행한다.
cilium-dbg
라는 이름의 cli 툴이 디버그 클라이언트로 내부에 같이 들어있다.- rest api 통신으로 각종 디버깅 요청 처리
- 에이전트의 상태, 노드의 ebpf 맵 상태 조회
- 오퍼레이터 - 실리움 관련 커스텀 리소스를 추적하여 설정 반영
- 각종 실리움의 기능을 활용하는데 있어서는 필수적이나, CNI로서 필수는 아니다.
- 에이전트 - 데몬셋
- 허블
- 데이터 스토어
- 이건 뭐 별도로 존재한다는 건 아니고 실리움이 동작하는데 있어 에이전트 설정 및 상태 전파에 활용되는 데이터 객체를 말한다.
- 당연히 커스텀 리소스가 기본인데, 키값쌍 방식으로 설정을 저장하는 게 가능해서 그냥 Etcd에 바로 접촉해 필요한 데이터를 저장하는 방식도 지원한다.
이제부터는 실리움이 제공할 수 있는 각종 기능들을 알아본다.
CNI 모드
클러스터 내 파드 간 네트워킹을 전담하는 cni로서, 실리움은 두 가지 모드를 제공한다.[3]
캡슐 모드와 네이티브 라우팅 모드인데, 이름은 여기저기 다르지만 결국 뜻은 같다.
캡슐화(encapsulation) 모드
기본 설정으로, 네트워크 인프라 단의 별도의 설정 없이 동작할 수 있는 모드이다.
이 모드에서는 UDP 기반의 캡슐 터널링 프로토콜이 사용된다.
각 프로토콜을 잘 몰라 조금 더 자세히 정리했다.
- VXLAN - Virtual eXtensible Local Area Network
- VLAN(Virtual Local Area Network)의 한계를 극복하기 위해 등장
- L2 이더넷 프레임을 L3 UDP 패킷으로 캡슐화(Encapsulation)하여 기존 IP 네트워크 위에서 터널링
- 구조
- VTEP (VXLAN Tunnel End Point) - VXLAN 터널의 시작점과 끝점 역할을 하는 장치(주로 물리 스위치 또는 서버의 가상 스위치)
- VNI (VXLAN Network Identifier) - 각 VXLAN 가상 네트워크를 고유하게 식별하는 24비트 식별자
- 약 1,600만 개의 논리적 네트워크 세그먼트를 생성할 수 있어 VLAN의 4096개 한계 초월!
- 과정
- 원본 L2 이더넷 프레임이 VTEP에 도착
- VTEP이 VXLAN 헤더(VNI 포함) 추가
- UDP 헤더(기본 포트 4789)와 외부 IP 헤더(Source IP: 송신 VTEP, Destination IP: 수신 VTEP) 추가
- 캡슐화된 IP 패킷 언더레이 물리 네트워크를 통해 목적지 VTEP로 전달
- 목적지 VTEP는 패킷에서 외부 IP, UDP, VXLAN 헤더를 제거(디캡슐화)
- 원래의 Layer 2 이더넷 프레임을 목적지 호스트에게 전달
- Geneve - Generic Network Virtualization Encapsulation
- VXLAN 및 NVGRE(Network Virtualization using Generic Routing Encapsulation)와 같은 기존 오버레이 프로토콜의 장점을 결합, 확장성 있게 설계된 프로토콜
- 과정 자체는 비슷하며, TLV(Type-Length-Value) 가변 길이 필드를 붙여 여러 옵션 데이터를 추가할 수 있음
이 방식의 장점은 다음과 같다.
- 단순함 - 별도의 설정이 요구되지 않는다.
- vxlan에서 8472, geneve에서 6081 udp 포트만 모든 노드가 오픈하고 있으면 된다.
- 파드나 노드의 cidr을 일절 신경쓸 필요가 없다.
- 주소 공간 확보 - 별도의 캡슐링을 하니 다른 주소를 침범하지 않는다.
- 신원 컨텍스트 - 캡슐화 시 신원 메타데이터를 추가하기에 서비스간 신원을 식별하여 트래픽 전달이 최적화된다.
단점으로는 MTU 부하가 존재할 수 있다.
아무래도 패킷의 양이 늘어나고 자체 크기도 커질 수 있으며, 이는 처리량의 저하를 유발할 수 있다.
점보 프레임을 활성화해 이를 완화할 수는 있다.
네이티브 라우팅(native routing) 모드
routing-mode: native
를 통해 활성화할 수 있는 모드로, 노드의 라우팅 테이블을 활용한다.
보다시피 파드마다 lxc라는 인터페이스를 부착시키고, 노드의 라우팅 테이블에 관련한 경로를 설정한다.
그래서 내부로 들어가는 주소만 아니라면 모든 패킷을 라우팅 테이블에 의해 라우팅되며, 패킷은 로컬 프로세스가 보내는 패킷처럼 다뤄진다.
그래서 패킷 포워딩 커널 파라미터가 설정돼야 하는데 이 모드를 설정하면 실리움이 알아서 세팅도 해준다. 위험한 놈
라우팅 테이블을 사용한다는 것은 노드 별로 다른 PodCIDR이 있기에 가능한 것이다.
몇 가지 제약이 있다.
- 자신 노드가 아닌 파드 주소에 대한 라우팅 시 대상 노드가 트래픽을 받을 수 있어야 함
- 노드 자신이 라우팅 경로를 알 수 없다면 별도의 라우터가 요구된다.
- 또는 각 노드가 전부 온전히 모든 노드가 가진 PodCIDR를 아는 상태로 라우팅 테이블에 반영해야 한다.
- 모든 노드가 L2에서 공유된다면,
auto-direct-node-routes: true
로 가능하다. - 그렇지 않다면 BGP로 각 주소를 전파해줘야만 한다.
- 모든 노드가 L2에서 공유된다면,
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을 세팅하는 것도 가능하다.
다른 모드들도 많으니 문서 참고.
큐브 프록시 대체
실리움은 서비스에서 파드로 가는 트래픽 경로를 제공하는 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]
파란색이 커스텀 리소스로 직접 사용자가 설정해주면 된다.
- CiliumBGPClusterConfig - 어떤 노드에 BGP 설정할 건지 지정
- CiliumBGPPeerConfig - BGP 피어 세부 설정
- CiliumBGPAdvertisement - 어떤 걸 광고할지 BGP 라우팅 테이블 설정
- CiliumBGPNodeConfiguration - 노드별 세밀 컨트롤을 할 때 설정
결과적으로 우측 하단처럼 파드 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"
여기에서 피어 관련 설정 세부를 지정한다.
구체적으로 다음의 기능들을 설정할 수 있다.
- MD5 암호
- 피어 간 보안을 위해 인증을 요구하는 기능으로, 동일한 비밀키를 세팅하면 이를 MD5 해싱에 활용한다.
authSecretRef: bgp-auth-secret
와 같은 식으로 시크릿을 지정하며, 해당 시크릿에는password
키를 넣어주면 된다.
- 타이머 - bgp 연결 간 시간 변수
connectRetryTimeSeconds: 120
- 최소 5holdTimeSeconds: 90
- 최소는 9keepAliveTimeSeconds: 30
- 최소 3
- EBGP Multihop
- 기본으로 eBGP에서 뛰는 최대 홉은 1이다.
ebgpMultihop
으로 이 제한을 올릴 수 있다.
- 우아한 재시작
- bgp 세션에 오픈 메시지로 재시작 가능함을 피어에 알린다.
- 이를 통해 순간 노드가 죽어도 바로 라우팅 테이블을 수정하지 않고 재시작을 기대하며 트래픽 전달을 중단하지 않는다.
gracefulRestart
필드를 통해 설정한다.
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줄 되기 때문에.. 조금이라도 전반적인 부분을 파악해두면 좋다.
헬름 설정을 넣으면 컨피그맵에 해당 정보가 들어가는데, 이를 에이전트와 오퍼레이터가 받아 설정을 반영한다.
에이전트에서 해당 설정을 받아서 처리하는 방식은 다음과 같다.
viper(아마 이건 고랭 라이브러리 말하는듯?)이 설정을 받고, ebpf 설정 파일을 작성한 후 이를 커널 프로그램으로 넣는다.
설치
이 부분은 당장 내가 활용한 방법만 정리한다.
헬름 설치 당빠 가능
투닝
https://docs.cilium.io/en/stable/operations/performance/tuning/
관련 문서
EXPLAIN - 파생 문서
이름0 | related | 생성 일자 |
---|
기타 문서
Z0-연관 knowledge, Z1-트러블슈팅 Z2-디자인,설계, Z3-임시, Z5-프로젝트,아카이브, Z8,9-미분류,미완이름0 | 코드 | 타입 | 생성 일자 |
---|
참고
https://docs.cilium.io/en/stable/observability/hubble/#hubble-intro ↩︎
https://cilium.io/blog/2020/11/10/ebpf-future-of-networking/ ↩︎
https://docs.cilium.io/en/stable/network/concepts/routing/ ↩︎
https://docs.cilium.io/en/stable/network/concepts/ipam/cluster-pool/ ↩︎
https://docs.cilium.io/en/stable/network/concepts/ipam/kubernetes/#k8s-hostscope ↩︎
https://docs.cilium.io/en/stable/network/kubernetes/kubeproxy-free/ ↩︎
http://static.googleusercontent.com/media/research.google.com/en//pubs/archive/44824.pdf ↩︎
https://docs.cilium.io/en/stable/network/bgp-control-plane/bgp-control-plane/ ↩︎
https://docs.cilium.io/en/stable/network/bgp-control-plane/bgp-control-plane-v2/ ↩︎
https://docs.cilium.io/en/stable/network/l2-announcements/ ↩︎
https://docs.cilium.io/en/stable/installation/k8s-install-kubespray/ ↩︎