연재 시리즈

쿠버네티스 네트워크 스터디 7주차 - Cilium CNI service 통신

악분 2022. 3. 2. 16:04
반응형

스터디 목차

 

안녕하세요. 이 글은 facebook 쿠버네티스 그룹에서 올라온 "쿠버네티스 네트워크 스터디" 5주차 내용을 정리했습니다.

스터디 모집글: https://www.facebook.com/groups/k8skr/posts/3202691746679143

 

1. 7주차 네번째 주제

7주차 네번째 주제는 cilium CNI의 service통신과정입니다.

 

2. iptables 규칙과 차이점

iptables는 KUBE-SVC라는 iptables 규칙(rule)을 생성합니다. 하지만, Cilium은 생성하지 않습니다. iptables규칙 없이 어떻게 통신할 수 있을까요?

 

3. 서비스 통신:  socket-based loadbalancing

정답은 파드 내에서 바로 (D)NAT처리를 실행합니다. 

 

iptables(왼쪽 그림)을 경유하여 통신하게 되면, 일치하는 iptables규칙을 찾고 NAT변환과정이 일어납니다. 하지만, Cilium CNI(오른쪽 그림)는 파드 내에서 바로 목적지 파드 IP로 통신합니다.

파드 내에서 바로 목적지 파드 주소로 변환하는 과정을 "Socket-Based LoadBalaning"이라고 합니다. eBPF가 시스템 콜 이벤트가 발생하면 소켓의 목적지 주소를 변환해서 "socket-based"이름이 붙었습니다.

출처: https://velog.io/@haruband/K8SCilium-Socket-Based-LoadBalancing-기법

 

파드가 목적지 IP로 변경할 수 있는 이유는 cilium agent(데몬셋)이 host의 cgroup(socket-based Loadbalancing에 관련된 정보)을 마운트하기 때문입니다. 마운트 과정은 Init Container에서 실행됩니다.

 

4. 실습

4.1 준비

이전 글(https://malwareanalysis.tistory.com/290) [챕터 3.1]에서 준비한 환경과 파드를 그대로 사용했습니다. 그리고 실습에 자주 사용하는 명령어는 alias로 설정했습니다.

# CILIUMPOD 이름 조회
CILIUMPOD0=$(kubectl get -l k8s-app=cilium pods -n kube-system --field-selector spec.nodeName=k8s-m  -o jsonpath='{.items[0].metadata.name}')
CILIUMPOD2=$(kubectl get -l k8s-app=cilium pods -n kube-system --field-selector spec.nodeName=k8s-w2 -o jsonpath='{.items[0].metadata.name}')

# cilium client alias
alias c0="kubectl exec -it $CILIUMPOD0 -n kube-system -- cilium"
alias c2="kubectl exec -it $CILIUMPOD2 -n kube-system -- cilium"

# btftool alias
alias c0bpf="kubectl exec -it $CILIUMPOD0 -n kube-system -- bpftool"
alias c2bpf="kubectl exec -it $CILIUMPOD2 -n kube-system -- bpftool"

# 실습에 사용하는 pod kubectl alias
alias p0="kubectl exec -it netpod  -- "
alias p2="kubectl exec -it webpod2 -- "

# 실습에 사용하는 파드 IP
NETPODIP=$(kubectl get pods netpod -o jsonpath='{.status.podIP}')
WEBPOD2IP=$(kubectl get pods webpod2 -o jsonpath='{.status.podIP}')

 

4.2 서비스 생성

webpod라벨을 갖는 파드에 연결하는 서비스를 생성했습니다.

cat <<EOF | kubectl create -f -
apiVersion: v1
kind: Service
metadata:
  name: svc
spec:
  ports:
    - name: svc-webport
      port: 80
      targetPort: 80
  selector:
    app: webpod
  type: ClusterIP
EOF

 

실습 편의를 위해 서비스 ClustesrIP를 alias로 설정했습니다.

SVCIP=$(kubectl get svc svc -o jsonpath='{.spec.clusterIP}')

 

4.3 서비스로 http get요청

netpod에서 서비스 clustesrIP로 http get요청을 실행합니다. 통신이 성공하면 아래와 같이 http 응답이 보여야 합니다.

kubectl exec netpod -- curl -s $SVCIP

 

4.4 iptables 확인

서비스 생성 후, 서비스 iptables(KUBE-SVC)규칙이 생성되지 않는 것을 확인합니다.

 iptables-save | grep KUBE-SVC

 

4.5 cilium agent(데몬셋)이 관리하는 서비스 목록 확인

service list로 cilium agent가 관리하는 서비스 목록을 조회할 수 있습니다. 서비스에 연결된 파드가 2개여서 Backend IP가 2개 조회되었습니다.

# 서비스 clusterIP출력
echo $SVCIP

# cilium이 관리하는 서비스 목록 출력
c0 service list

 

4.6 파드 내에서 목적지 IP변환과정 디버깅

strace를 이용하여 시스템 콜을 디버깅하여 변환과정을 추적할 수 있습니다. 

 

서비스 clusterIP통신 과정을 표로 출력하면 소켓정보를 조회, 수정하는 시스템 콜 등이 보입니다.

kubectl exec netpod -- strace -c curl -s $SVCIP

 

자세하게 과정을 보려면 strace "-c" 인자를 제거합니다. strace로그 결과를 보면 분명히 전송할 때는 서비스 clusterIP였지만, curl 응답에서는 도착지IP로 출력됩니다. 중간과정에서 eBPF가 이벤트를 후킹하여 변경했기 때문입니다.

kubectl exec netpod -- strace -s 65535 -f -tt curl -s $SVCIP

 

반응형