Overview
AWS 블로그 글 Getting Started with Cilium Service Mesh on Amazon EKS 를 기반으로 EKS에서 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)
단일 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가 필요하다고 합니다.
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을 알 수 있습니다.
# 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
'Cloud Tech' 카테고리의 다른 글
도커 기반 애플리케이션 CI/CD 구성 (0) | 2024.12.08 |
---|---|
DownTime Zero를 통한 EKS 운영 안정성 높이기 (1) | 2024.11.02 |
EKS VPC CNI 네트워크 최적화 설정과 Kubeflow에서의 istio 구성 (2) | 2024.10.20 |
AWS Load Balancer Controller 기능 분석 (4) | 2024.10.13 |
k8s node-shell 원리이해 (0) | 2024.10.04 |