연재 시리즈

쿠버네티스 네트워크 스터디 7주차 - 7주차 과제

악분 2022. 3. 2. 18:19
반응형

스터디 목차


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

 

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

 

1. 주제

7주차 과제를 수행한 내용을 정리했습니다. 이 글을 읽기 위해서 cillium 선수지식이 필요합니다. 각 과정의 상세내용은 설명하지 않습니다.

  • 서비스 통신 디버깅
  • DSR 확인
  • Egress gateway
  • BGP for LoadBalancer VIP
  • Bandwith Manager

 

2. 공통설정

2.1 인프라

스터디에서 제공하는 vagrantfile로 환경을 구축했습니다. 쿠버네티스 클러스터는 cilium CNI가 설치되어 있습니다. 쿠버네티스 외부 PC, 외부 라우터 서버가 있습니다.

출처: 스터디 공유자료ㅑ

 

2.2 alias설정

자주 사용하는 명령어는 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}')

 

3. 서비스 통신 디버깅

strace를 이용하여 eBPF동작을 추측합니다. 

자세한 과정은 Cilium CNI service 통신 글(https://malwareanalysis.tistory.com/292)을 참고하시길 바랍니다.

 

3.1 deployment, 서비스 배포

deployment와 서비스를 배포합니다.

cat <<EOF | kubectl create -f -
apiVersion: apps/v1
kind: Deployment
metadata:
  name: mario
  labels:
    app: mario
spec:
  replicas: 1
  selector:
    matchLabels:
      app: mario
  template:
    metadata:
      labels:
        app: mario
    spec:
      containers:
      - name: mario
        image: pengbai/docker-supermario
---
apiVersion: v1
kind: Service
metadata:
   name: mario
spec:
  selector:
    app: mario
  ports:
  - port: 80
    protocol: TCP
    targetPort: 8080
  type: NodePort
  externalTrafficPolicy: Local
EOF

 

배포가 성공했는지 확인합니다.

kubectl get deploy,svc mario

 

3.2 외부에서 서비스 접속

<노드>:<nodeport>로 접속하면 마리오게임이 보입니다.

 

3.3 cilium 서비스 라우팅 정보 확인

워커노드에서 cilium 클라이언트 명령어로 서비스 목록을 확인했습니다. 

c2 service list

 

3.4 strace 디버깅

192.168.10.102IP를 갖는 호스트에서 strace 디버깅을 수행했습니다. sendto -> recvfrom 중간단계에서 eBPF가 실행되어 도착지 IP가 변경됩니다. 

strace -c curl -s 192.168.10.102:30784

 

3.5 hubble 대시보드 확인

hubble 대시보드에서 통신과정 시각화 결과를 확인할 수 있었습니다. netpod는 테스트용도 입니다.

 

4. DSR(Direct Server Return)확인

 

4.1 cilium 모드 확인

DSR이 활성화 되어 있는지 확인합니다. cilium config view 또는 status로 확인할 수 있습니다.

 

- cilium config view 확인방법

cilium config view | grep dsr

 

- cilium 클라이언트 status 확인방법

c0 status --verbose | grep 'KubeProxyReplacement Details:' -A7

 

4.2 파드, 서비스 배포

apache 도커 이미지(버전 2.4.52)를 이용하여 파드와 서비스를 배포합니다. 서비스는 외부에서 접속할 수 있도록 nodeport타입입니다.

apiVersion: v1
kind: Pod
metadata:
  name: demo-apache
  labels:
    app: demo-apache
spec:
  containers:
  - name: demo-apache
    image: httpd:2.4.52
    resources:
      limits:
        memory: "128Mi"
        cpu: "500m"
    ports:
      - containerPort: 80
---
apiVersion: v1
kind: Service
metadata:
  name: demo-apache
spec:
  type: NodePort
  selector:
    app: demo-apache
  ports:
  - port: 80
    targetPort: 80

 

파드와 서비스가 잘 실행되는지 확인합니다. 쿠버네티스 클러스터 외부 k8s-pc호스트에서 테스트했습니다.

CLUSTERNODE=192.168.10.10
NODEPORT=$(kubectl get svc demo-apache -o jsonpath='{.spec.ports[0].nodePort}')
echo $NODEPORT
curl $CLUSTERNODE:$NODEPORT

 

4.3 DSR확인

DSR을 쉽게 확인하기 위해 k8s-pc에서 1초마다 http GET요청을 수행합니다.

while true; do curl -s $CLUSTERNODE:$NODEPORT;echo "-----";sleep 1;done

 

cilium 통신을 모니터링 하면, orig-ip가 k8s-pc ip입니다. 즉, DSR이 잘적용되고 있습니다.

# apache 파드가 워커노드2번에서 실행 중
c2 monitor -vv

 

4.4 패킷 덤프

패킷을 덤프하기 위해 apache 파드가 실행 중인 노드로 이동합니다. 그리고 tcpdump로 패킷덤프를 수행합니다.

NODEPORT=$(kubectl get svc demo-apache -o jsonpath='{.spec.ports[0].nodePort}')
tcpdump -eni any tcp port 80 or tcp port $NODEPORT -q -v -w /tmp/subject.pcap

 

와이어샤크로 패킷파일을 열면 ip헤더 options필드에 8byte가 있습니다.

 

8바이트 일부를 추출하면 클라이언트가 최초 접근한 노드 IP를 얻을 수 있습니다. (그런데 ... 왜 클라이언트 IP가 options에 없지!?..)

 

5. Egress gateway

 

5.1 클라이언트 파드 생성

공식문서(https://docs.cilium.io/en/stable/gettingstarted/egress-gateway/#create-client-pods)에서 제공하는 mediabot 파드를 생성합니다.

kubectl create -f https://raw.githubusercontent.com/cilium/cilium/1.11.2/examples/kubernetes-dns/dns-sw-app.yaml

 

음... mediabot으로 실습이 잘 진행이 안되어 netshoot 도커이미지를 사용하여 파드를 생성했습니다.

 

5.2 외부 웹서버 설치

쿠버네티스 클러스터 외부에 웹 서버를 설치합니다. 저는 apache를 설치했습니다. 웹서버 IP는 192.168.20.100입니다.

 

5.3 클라이언트 파드에서 웹서버 호출

egress gateway를 설정하기 전 클라이언트 파드에서 웹서버로 HTTP GET요청을 수행합니다.

kubectl exec netpod -- curl 192.168.20.100

 

apache로그를 확인하면 파드 IP가 보입니다.

 

5.4 egress IP설정

공식문서(https://docs.cilium.io/en/stable/gettingstarted/egress-gateway/#configure-egress-ips)를 참고하여 egress IP를 설정합니다.

cat <<EOF | kubectl create -f -
apiVersion: apps/v1
kind: Deployment
metadata:
  name: "egress-ip-assign"
  labels:
    name: "egress-ip-assign"
spec:
  replicas: 1
  selector:
    matchLabels:
      name: "egress-ip-assign"
  template:
    metadata:
      labels:
        name: "egress-ip-assign"
    spec:
      affinity:
        # the following pod affinity ensures that the "egress-ip-assign" pod
        # runs on the same node as the mediabot pod
        podAffinity:
          requiredDuringSchedulingIgnoredDuringExecution:
            - labelSelector:
                matchExpressions:
                  - key: class
                    operator: In
                    values:
                      - mediabot
                  - key: org
                    operator: In
                    values:
                      - empire
              topologyKey: "kubernetes.io/hostname"
      hostNetwork: true
      containers:
      - name: egress-ip
        image: docker.io/library/busybox:1.31.1
        command: ["/bin/sh","-c"]
        securityContext:
          privileged: true
        env:
        - name: EGRESS_IPS
          value: "192.168.10.240/24 192.168.10.241/24"
        args:
        - "for i in \$EGRESS_IPS; do ip address add \$i dev enp0s8; done; sleep 10000000"
        lifecycle:
          preStop:
            exec:
              command:
              - "/bin/sh"
              - "-c"
              - "for i in \$EGRESS_IPS; do ip address del \$i dev enp0s8; done"
EOF

 

파드가 설치된 노드의 인터페이스를 확인하면 설정한 egress ip목록이 보입니다.

 

5.5 egress policy 설정

egress 정책을 설정합니다. 192.168.20.100이 목적지이고 파드 라벨이 일치하다면 정책이 활성화됩니다.

cat <<EOF | kubectl apply -f -
apiVersion: cilium.io/v2alpha1
kind: CiliumEgressNATPolicy
metadata:
  name: egress-sample
spec:
  egress:
  - podSelector:
      matchLabels:
        org: empire
        class: mediabot
        io.kubernetes.pod.namespace: default
  destinationCIDRs:
  - 192.168.20.100/32
  egressSourceIP: "192.168.10.240"
EOF

 

5.6 egress gateway 테스트

[챕터 5.3]에서 했던 http 요청을 똑같이 실행합니다. [챕터 5.3]결과와 다르게 apache로그에서는 egress gateway IP가 출력됩니다.

kubectl exec netpod -- curl 192.168.20.100

 

6. BGP for LoadBalancer VIP

6.1 BGP 설정

공식문서(https://cilium.io/blog/2021/05/20/cilium-110)를 참고하여 BGP를 설정합니다.

cat <<EOF | kubectl create -f -
apiVersion: v1
kind: ConfigMap
metadata:
  name: bgp-config
  namespace: kube-system
data:
  config.yaml: |
    peers:
      - peer-address: 192.168.10.254
        peer-asn: 64513
        my-asn: 64512
    address-pools:
      - name: default
        protocol: bgp
        avoid-buggy-ips: true
        addresses:
          - 172.20.1.0/24
EOF

 

kubectl get cm -n kube-system bgp-config

 

helm upgrade를 수행하여 loadbalancer 옵션을 활성화합니다.

VERSION=1.11.2
helm upgrade cilium cilium/cilium --version $VERSION --namespace kube-system --reuse-values --set bgp.enabled=true --set bgp.announce.loadbalancerIP=true

 

그리고, cilium 파드를 재실행합니다.

kubectl -n kube-system rollout restart ds/cilium

 

bgp설정이 잘 되었는지 확인합니다.(podCIDR도 표시되어야 하는데 아마도? 버그로 설정X)

cilium config view | grep bgp

 

179 포트에서 loadbalancer광고 패킷을 캡처할 수 있습니다.

 tcpdump -i enp0s8 -nn tcp port 179 -q

 

6.2 파드와 로드밸런서타입 서비스 생성

nginx파드와 로드배런서타입 서비스를 생성합니다.

cat <<EOF | kubectl create -f -
apiVersion: v1
kind: Service
metadata:
  name: test-lb
spec:
  type: LoadBalancer
  ports:
  - port: 80
    targetPort: 80
    protocol: TCP
    name: http
  selector:
    svc: test-lb
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx
spec:
  selector:
    matchLabels:
      svc: test-lb
  template:
    metadata:
      labels:
        svc: test-lb
    spec:
      containers:
      - name: web
        image: nginx
        imagePullPolicy: IfNotPresent
        ports:
        - containerPort: 80
        readinessProbe:
          httpGet:
            path: /
            port: 80
EOF

 

6.3 External IP확인

로드밸런서타입 서비스를 확인하면 External IP가 할당되었습니다. 

kubectl get svc test-lb

 

외부 라우터에서도 neigthbor가 추가되었습니다.

vtysh -c 'show ip route bgp'

 

6.4 External IP접근

쿠버네티스 클러스터에서 접근이 되는지 확인합니다.

curl 172.20.1.1

 

그리고 쿠버네티스 클러스터가 아닌 외부에서도 접속이 되는지 확인합니다.

 

6.6 externalTrafficPolicy 설정

로드밸런서타입 서비스에 externalTrafficPolicy를 설정합니다.

kubectl patch svc test-lb -p '{"spec":{"externalTrafficPolicy": "Local"}}'

 

외부 라우터에서 라우팅 정보를 확인하면, [챕터 6.3]와 다르게 External IP가 한 노드로 1:1 매칭되었습니다.

ip -c route | grep 172.20.1.1

 

7. Bandwidth Manager

7.1 Bandwidth 설정 확인

cilium config view | grep bandwidth

 

c0 status | grep  BandwidthManager

 

7.2 파드 생성

서버와 클라이언트 역할하는 파드 2개를 생성합니다.  netperf-server는 bandwidth가 10M로 설정됩니다.

cat <<EOF | kubectl create -f -
---
apiVersion: v1
kind: Pod
metadata:
  annotations:
    # Limits egress bandwidth to 10Mbit/s.
    kubernetes.io/egress-bandwidth: "10M"
  labels:
    # This pod will act as server.
    app.kubernetes.io/name: netperf-server
  name: netperf-server
spec:
  containers:
  - name: netperf
    image: cilium/netperf
    ports:
    - containerPort: 12865
---
apiVersion: v1
kind: Pod
metadata:
  # This Pod will act as client.
  name: netperf-client
spec:
  affinity:
    # Prevents the client from being scheduled to the
    # same node as the server.
    podAntiAffinity:
      requiredDuringSchedulingIgnoredDuringExecution:
      - labelSelector:
          matchExpressions:
          - key: app.kubernetes.io/name
            operator: In
            values:
            - netperf-server
        topologyKey: kubernetes.io/hostname
  containers:
  - name: netperf
    args:
    - sleep
    - infinity
    image: cilium/netperf
EOF

 

7.3 bandwidth설정 확인

netperf-server파드가 bandwidth가 잘 설정되었는지 확인합니다.

 kubectl describe pod netperf-server | grep Annotations:

 

netperf-server파드를 실행 중인 노드에서 cilium agent(데몬셋) bandwith 목록을 출력합니다.

c2 bpf bandwidth list

 

7.4 bandwidth 테스트

netperf-server파드 IP를 변수로 설정합니다.

NETPERF_SERVER_IP=$(kubectl get pod netperf-server -o jsonpath='{.status.podIP}')

 

bandwidth 10M설정이 잘되는지 확인합니다.

kubectl exec netperf-client -- netperf -t TCP_MAERTS -H "${NETPERF_SERVER_IP}"

 

bandwidth를 5M으로 변경 후 테스트합니다.

kubectl get pod netperf-server -o json | sed -e 's|10M|5M|g' | kubectl apply -f -
kubectl exec netperf-client -- netperf -t TCP_MAERTS -H "${NETPERF_SERVER_IP}"

 

bandwidth를 20M으로 변경 후 테스트합니다.

kubectl get pod netperf-server -o json | sed -e 's|5M|20M|g' | kubectl apply -f -
kubectl exec netperf-client -- netperf -t TCP_MAERTS -H "${NETPERF_SERVER_IP}"

반응형