Cloud Tech

Cilium Service Mesh on EKS

Hanhorang31 2024. 10. 26. 21:27
 

Overview

AWS 블로그 글 Getting Started with Cilium Service Mesh on Amazon EKS 를 기반으로 EKS에서 Clium 기반의 Service Mesh를 구성하고 구성 요소를 확인하겠습니다.

Clium 기반의 Service Mesh 구성 아키텍처

Sidecarless Service Mesh for Cilium?

Cilium 은 eBPF(Extended Berkeley Packet Filter)를 기반으로 하는 K8s CNI Plugin 입니다.

BPF는 Kernel에 Snadbox 형태로 설치되어 패킷을 필터링하고 제어할 수 있습니다. 기존 CNI 로 Iptables를 통해 패킷을 제어하게 되면 네임스페이스 간의 통신에서 오버헤드가 발생하지만, BPF는 커널에 내장되어 있어 성능향상과 패킷 추적을 기대할 수 있습니다.

  • 컨테이너 네트워킹 내 불필요한 오버헤드 제거

성능 향상 벤치마크 결과로 eBPF 기반 CNI 네트워킹(Cilium)은 기존 Iptables 사용 CNI(calico, baseline) 대비 10%에서 최대 40%까지 성능이 향상됩니다. 주요 수치적인 결과로는 다음과 같이 정리할 수 있습니다. (그래프 참고 : cni-benchmakr)

 

CNI Benchmark: Understanding Cilium Network Performance

As more crucial workloads are being migrated to Kubernetes, network performance benchmarks are becoming an important selection criter...

cilium.io

 

단일 TCP 연결당 최대 처리량(100Gbit/s)

  • 처리량: Cilium eBPF 호스트 라우팅을 통해 최대 40 Gbit/s 이상의 처리량 처리함

100Gbit/s 전송에 필요한 CPU 리소스

  • CPU 효율성: 100 Gbit/s의 네트워크 대역폭에서 CPU 사용량이 10-20% 줄임

 

새로운 연결 비율
이 부분이 핵심입니다. Iptables 이 대량으로 관리되는 경우 새로운 룰 추가에 따라 성능에 영향이 갈 수 있기 때문입니다.

아래 32 프로세스에 따른 TCP CRR 벤치마킹 결과를 확인하면, 약 30~40 %의 성능향상과 CPU 소비량은 오히려 5%적은 것을 확인할 수 있습니다 .

다만, 서비스 메시에서 사이드카 성능으로 일부 지연을 확인할 수 있습니다.

이는 사이드카 형태의 istio 구성으로 각 파드당 사이드카가 필요함을 의미합니다. 프로식 구현이 아무리 작고 효율적이더라고 파드가 늘어남에 따라 사이드카가 그 만큼 필요합니다.

 

예를 들어 일부 사례에서 3개 노드에 100개 프록시가 있는 경우 노드당 메모리 리소스 2GB가 필요하다고 합니다.

 

Watch Out for This Istio Proxy Sidecar Memory Pitfall

We encountered many perplexing issues throughout our ongoing service mesh journey with Istio at my company, many of which left us wishing…

medium.com

 

Cilium service mesh 는 Envoy 관리를 사이드카 주입이 아닌 데이터 플레인에 구성하여 서비스 매쉬 환경을 구성합니다.

eBPF의 장점뿐만 아니라, Pod에 사이드카가 필요 없어 오버헤드와 복잡성이 크게 줄어듭니다.

 

EKS 배포

실습 환경 구성은 위 AWS 블로그 글(깃허브) 참고하였습니다.

EKS version 1.29, m.large 서버 2대로 EKS 클러스터 구성
git clone https://github.com/aws-samples/cilium-service-mesh-on-eks.git 

cd cilium-mesh-on-eks/terraform
terraform init
terraform apply --auto-approve 
  • 배포에 약 15분정도 소요됩니다.
# kubeconfig 등록
aws eks --region us-west-2 update-kubeconfig --name terraform

# 노드, 파드 확인 
kubectl get nodes
kubectl get pods -A 

기존 인프라 구성 확인시 VPC CNI 가 구성됨을 확인할 수 있습니다.

VPC CNI 대신 Cilium을 CNI로 설정하도록 cilium을 배포하겠습니다.

helm repo add cilium https://helm.cilium.io/
helm upgrade --install cilium cilium/cilium --version 1.14.7 \
--namespace kube-system \
--reuse-values -f ../values_cilium.yaml \
--set hubble.enabled=true \
--set hubble.tls.auto.enabled=true \
--set hubble.metrics.enabled="{dns,drop,tcp,flow,icmp,http}" \
--set hubble.relay.enabled=true \
--set hubble.ui.enabled=true \
--set hubble.ui.service.type=NodePort \
--set hubble.relay.service.type=NodePort \
--set kubeProxyReplacement=strict \
--set encryption.enabled=false \
--set encryption.nodeEncryption=false \
--set routingMode=native \
--set ipv4NativeRoutingCIDR="0.0.0.0/0" \
--set bpf.masquerade=true \ # ebpf 활성화 
--set nodePort.enabled=true \
--set autoDirectNodeRoutes=true \
--set hostLegacyRouting=false \
--set ingressController.enabled=true \
--set ingressController.loadbalancerMode=shared \
--set cni.chainingMode=aws-cni \
--set cni.install=true
  • kubeProxyReplacement=strict: kube-proxy의 기능을 Cilium의 자체 eBPF 기반 구현으로 대체합니다.
  • ingressController.enabled=true: Cilium Ingress Controller를 활성화합니다.
  • reuse-values -f ../values_cilium.yaml: values_cilium.yaml 파일의 특정 주석(annotation)을 사용하여 Cilium Ingress를 AWS 네트워크 로드 밸런서를 통해 노출할 수 있도록 합니다.
# values_cilium.yaml 
ingressController:
  service:
   annotations:
     service.beta.kubernetes.io/aws-load-balancer-scheme: "internet-facing"
  • hubble.enabled=true: 네트워크/보안 모니터링 툴인 Hubble을 활성화합니다.

cilium 배포시 coreDNS 및 addon이 재배포됩니다.

이는 cilium을 통해 모든 파드가 네트워킹되도록 다시 시작함을 의미합니다.

cilium 배포가 확인되었으면 Kube-proxy(VPC CNI)를 비활성화합니다.

# VPC CNI addon 제거
aws eks delete-addon --cluster-name terraform --addon-name kube-proxy --region us-west-2

# 파드 확인
kubectl get pods -A 

cilium 아키텍처는 다음과 같습니다.

  • cilium(cilium agent) : 데몬셋으로 실행되며, k8s api 설정으로 부터 네트워크 설정, 정책, 서비스 부하분산, 모니터링 등을 수행하며, eBPF를 관리합니다.
  • cilium Operator : Cilium 연산자는 클러스터 작업을 중앙에서 관리하여 노드별로가 아니라 집합적으로 처리합니다. Pod 스케줄링 또는 종료 중에 Kubernetes에서 호출하는 CNI 플러그인은 노드의 Cilium API와 상호 작용하여 네트워킹, 부하 분산 및 네트워크 정책에 필요한 데이터 경로를 구성합니다.
  • Hubble : 네트워크와 보안 모니터링 플랫폼

쿠버네티스에서 cilium 구성 정보를 확인할 수 있습니다.

kubectl get ciliumnodes 
kubectl get ciliumendpoints -A
kubectl get cm -n kube-system cilium-config -o json | jq

노드에 접근해서 iptables를 확인하면 AWS 관련 NAT 처리, 호환등의 이유로 일부 룰이 있음을 확인할 수 있습니다.

ebpf 로 처리되는 NOTRACK 관련 규칙들은 특정 트래픽(예: 프록시 트래픽, L7 트래픽 등)을 추적하지 않도록 설정되어, Cilium의 eBPF 데이터 경로와 함께 최적화된 처리 방식을 제공합니다.

iptables -t nat -S
iptables -t filter -S
iptables -t raw -S
iptables -t mangle -S

# notrack 확인 
iptables -t raw -S | grep notrack

ebpf 설정은 sidecarless service mesh로 데몬셋으로 배포된 cilium agent을 통해 확인이 가능합니다.

export CILIUMPOD0=$(kubectl get -l k8s-app=cilium pods -n kube-system --field-selector spec.nodeName=ip-10-0-1-140.us-west-2.compute.internal   -o jsonpath='{.items[0].metadata.name}')
alias c0="kubectl exec -it $CILIUMPOD0 -n kube-system -c cilium-agent -- cilium"
c0 status --verbose

 

 

예제 서비스매쉬 애플리케이션(workshop)을 배포하여 구성 네트워킹 동작을 확인하겠습니다.

사용자가 Frontend로 액세스하면 Product Catalog 가 Catalog Detail 을 호출합니다. 이때 Catalog Detail 는 2개의 버전으로 배포되는데, 서비스 메시의 기능을 위해 배포하였습니다.

# 예제 네임스페이스 배포
kubectl create namespace workshop

# 에제 애플리케이션 배포 
cd cilium-service-mesh-on-eks/productapp
helm install productapp . -n workshop

# 오브젝트 확인 
kubectl get deployment,pod,service  -n workshop

# 사이드카 확인
kubectl describe pod/frontend-78f696695b-hrlw6   -n workshop
  • 컨테이너가 하나만 존재함을 확인할 수 있습니다.

이제 Frontend 서비스에 Ingress를 붙이겠습니다.

Ingress 는 cilium operator 를 거쳐 aws load balancer controller 가 생성합니다.

cat <<EOF | kubectl apply -f -
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  namespace : workshop
  name: productappingress # name given to the ingress
spec:
  ingressClassName: cilium
  rules:
  - http:
      paths:
      - path: / # this rule applies to all requests that specifies this path
        pathType: Prefix
        backend:
          service:
            name: frontend # route all these requests to this service
            port:
              number: 9000 # route the requests to this port of the frontend service
EOF

다음의 명령어를 통해 애플리케이션과 연결한 ELB의 URL을 알 수 있습니다.

Copy
# URL 확인
CILIUM_INGRESS_URL=$(kubectl get svc cilium-ingress -n kube-system -o jsonpath='{.status.loadBalancer.ingress[*].hostname}')
echo "http://$CILIUM_INGRESS_URL"
  • upstream connect error or disconnect/reset before headers. reset reason: connection failure 라는 문구가 확인되는 경우 브라우저를 시크릿 모드로 접속해주세요.

Cilium CLI 를 설치하여 모니터링 툴인 Hubble UI에 접근하겠습니다.

아래 링크에서 사용자의 OS에 맞게 CLI를 다운받고 명령어를 실행해주세요.

sudo tar xzvfC cilium-linux-${CLI_ARCH}.tar.gz /usr/local/bin
rm cilium-linux-${CLI_ARCH}.tar.gz{,.sha256sum}

# 확인
cilium status --wait
cilium config view
cilium hubble ui
  • 네임스페이스를 workshop 으로 설정하고, 애플리케이션을 새로 고침하면 파드별 통신 과정이 시각화됩니다.

Traffic Shifting

다음은 서비스매쉬 기능인 Traffic Shifting을 활용해보겠습니다.

서비스 파드 확인시 catalog Detail이 두개인 것을 확인할 수 있는데, front에서 접근 시 파드 두개로 접근하여 아래와 같이 Detail 정보가 다르게 확인되는 것을 알 수 있습니다.

여기서 서비스와 CiliumEnvoyConfig를 생성하여 트래픽 비율을 설정하겠습니다.

# 서비스 생성
cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: Service
metadata:
  labels:
    app: catalogdetail
  name: catalogdetailv1
  namespace: workshop
spec:
  ports:
  - name: http
    port: 3000
    protocol: TCP
    targetPort: 3000
  selector:
    app: catalogdetail
    version: v1
---
apiVersion: v1
kind: Service
metadata:
  labels:
    app: catalogdetail
  name: catalogdetailv2
  namespace: workshop
spec:
  ports:
  - name: http
    port: 3000
    protocol: TCP
    targetPort: 3000
  selector:
    app: catalogdetail
    version: v2
EOF



# CiliumEnvoyConfig 생성 
cat <<EOF | kubectl apply -f -
apiVersion: cilium.io/v2
kind: CiliumEnvoyConfig
metadata:
  name: traffic-shifting-test
  namespace: workshop
spec:
  services:
    - name: catalogdetail
      namespace: workshop
  backendServices:
    - name: catalogdetailv1
      namespace: workshop
    - name: catalogdetailv2
      namespace: workshop
  resources:
    - "@type": type.googleapis.com/envoy.config.listener.v3.Listener
      name: traffic-shifting-test
      filter_chains:
        - filters:
            - name: envoy.filters.network.http_connection_manager
              typed_config:
                "@type": type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager
                stat_prefix: traffic-shifting-test
                rds:
                  route_config_name: lb_route
                http_filters:
                  - name: envoy.filters.http.router
                    typed_config:
                      "@type": type.googleapis.com/envoy.extensions.filters.http.router.v3.Router
    - "@type": type.googleapis.com/envoy.config.route.v3.RouteConfiguration
      name: lb_route
      virtual_hosts:
        - name: "lb_route"
          domains: [ "*" ]
          routes:
            - match:
                prefix: "/"
              route:
                weighted_clusters: # 해당 부분 
                  clusters:
                    - name: "workshop/catalogdetailv1"
                      weight: 50
                    - name: "workshop/catalogdetailv2"
                      weight: 50
                retry_policy:
                  retry_on: 5xx
                  num_retries: 3
                  per_try_timeout: 1s
    - "@type": type.googleapis.com/envoy.config.cluster.v3.Cluster
      name: "workshop/catalogdetailv1"
      connect_timeout: 2s
      lb_policy: ROUND_ROBIN
      type: EDS
      outlier_detection:
        split_external_local_origin_errors: true
        consecutive_local_origin_failure: 2
    - "@type": type.googleapis.com/envoy.config.cluster.v3.Cluster
      name: "workshop/catalogdetailv2"
      connect_timeout: 2s
      lb_policy: ROUND_ROBIN
      type: EDS
      outlier_detection:
        split_external_local_origin_errors: true
        consecutive_local_origin_failure: 2
EOF
  • weighted_clusters 부분에서 트래픽을 전환합니다.
  • Cilium이 제공하는 서비스매쉬 기능은 공식 문서를 참고해주세요.

아래 명령어를 통해 트래픽을 확인할 수 있습니다.

productcatalogpod=$(kubectl get pods -n workshop | awk '{print $1}' | grep -e "productcatalog")
for i in {1..6}; do echo "Output $i:"; kubectl -n workshop exec -it $productcatalogpod -- curl catalogdetail:3000/catalogDetail; echo ""; done

 

리소스 제거

# 예제 제거 
helm uninstall productapp -n workshop
kubectl delete ingress productappingress -n workshop
kubectl delete svc catalogdetailv1 -n workshop
kubectl delete svc catalogdetailv2 -n workshop
helm uninstall cilium -n kube-system

# 인프라 제거 
cd terraform

terraform state rm 'module.eks.aws_eks_access_entry.this["cluster_creator"]' || true
terraform state rm 'module.eks.aws_eks_access_policy_association.this["cluster_creator_admin"]' || true

terraform destroy -target="module.eks_blueprints_addons" -auto-approve
terraform destroy -target="module.eks" -auto-approve
terraform destroy -auto-approve

 

참고

KANS 3기 스터디

https://aws.amazon.com/ko/blogs/opensource/getting-started-with-cilium-service-mesh-on-amazon-eks/ https://thenewstack.io/how-ebpf-streamlines-the-service-mesh/ https://thenewstack.io/how-ebpf-streamlines-the-service-mesh/ https://blog.naver.com/kangdorr/222593265958