Kubernetes

[ Kans 3 Study - 8w ] 3. Cilium Egress Gateway

su''@ 2024. 10. 27. 08:26
CloudNetaStudy - Kubernets Networtk 3기 실습 스터디 게시글입니다.
10. Cilium Egress Gateway
  • Egress Gateway
    • Egress Gateway - Link
    • Egress Gateway Advanced Troubleshooting - Link
    • [카카오] 기존 100여대희 gateway 역할 서버를 k8s cliium egress gateway 를 통해서 40여대의 서버로 줄여서, 서버 비용과 운영 비용 감소 - Blog
  • Egress IP Gateway 소개
    • 특정 파드들을 특정 Egress GW(특정 IP)를 고정으로 외부(혹은 특정 대역)와 통신을 설정할 수 있다
    • Egress IP Gateway 를 활성화하고 Egress SNAT 정책 설정
      https://isovalent.com/blog/post/2021-12-release-111#egress-gateway-improvements
  • 설정 - 링크
    • 요구조건 : The egress gateway requires BPF masquerading, which itself requires BPF NodePort to be enabled.
  • 테스트를 위한 웹서버와 파드 생성
    • k8s-pc, k8s-rtr 에 웹서버 정보 확인
      k8s-pc, k8s-rtr
      
      # 웹서버 접근 테스트
      curl localhost
      <h1>Web Server : k8s-pc</h1>
      
      # 웹 접속 로그 실시간 출력
      tail -f /var/log/apache2/access.log
      192.168.10.10 - - [01/Dec/2021:23:35:24 +0000] "GET / HTTP/1.1" 200 264 "-" "curl/7.74.0"
    • 파드 생성 및 웹 접속 테스트 ⇒ mediabot 파드는 CRI(containerd) 사용 시 별도의 파일시스템 설치가 필요하여, CRI(docker)로 실습을 권장한다 

    • # 파드 생성
      kubectl create -f https://raw.githubusercontent.com/cilium/cilium/1.11.2/examples/kubernetes-dns/dns-sw-app.yaml # 파드 확인 kubectl get pod -o wide # 파드에서 nginx 접속 >> k8s-pc 웹서버 접속 시 클라이언트 IP가 어떻게 찍히나요? kubectl exec mediabot -- curl -s 192.168.20.100 kubectl exec mediabot -- curl -s 192.168.10.254 kubectl exec mediabot -- curl -s wttr.in/seoul # (옵션) 외부 웹서버 접속 테스트 kubectl exec mediabot -- curl -s wttr.in/seoul?format=3 kubectl exec mediabot -- curl -s wttr.in/seoul?format=4 kubectl exec mediabot -- curl -s 'wttr.in/{London,Busan}' kubectl exec mediabot -- curl -s ipinfo.io kubectl exec mediabot -- curl -s ipinfo.io/city kubectl exec mediabot -- curl -s ipinfo.io/loc kubectl exec mediabot -- curl -s ipinfo.io/org
  • Configure Egress IPs
    • 인터페이스 모니터링 걸어두기 (Egress GW 가 생성된 워커노드에)
      # k8s-w1 , k8s-w2
      watch -d 'ip -4 addr show enp0s8'
    • Egress GW 배포 - EGRESS_IPS 범위 지정 : 예시) 192.168.10.240/24 192.168.10.241/24
      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
      # 설정 후 k8s-w2 확인
      ip -c -4 addr show enp0s8
      3: enp0s8: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP group default qlen 1000
          inet 192.168.10.102/24 brd 192.168.10.255 scope global enp0s8
             valid_lft forever preferred_lft forever
          inet 192.168.10.240/24 scope global secondary enp0s8
             valid_lft forever preferred_lft forever
          inet 192.168.10.241/24 scope global secondary enp0s8
             valid_lft forever preferred_lft forever
      
      # iptables nat 에 특별한 chain/rule 이 없다
      iptables -t nat -S


  • Create Egress NAT Policy
    • 192.168.20.100/32 목적지와 통신 할 때는 egressSourceIP 를 192.168.10.240 를 사용!
    • 참고로 destinationCIDRs: 가 0.0.0.0/0 일 경우 모든 네트워크를 의미한다
      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
       
    • 통신 확인 및 추가 테스트
      # 파드에서 nginx 접속 : policy 에 맞게 지정된 ip가 출력
      kubectl exec mediabot -- curl -s 192.168.20.100
      ## 로그 확인
      root@k8s-pc:~# tail -f /var/log/apache2/access.log
      172.16.2.242 - - [28/Feb/2022:12:32:37 +0000] "GET / HTTP/1.1" 200 256 "-" "curl/7.52.1"
      192.168.10.200 - - [29/Nov/2021:17:48:26 +0000] "GET / HTTP/1.1" 200 612 "-" "curl/7.52.1"
      
      # 파드에서 nginx 접속 : policy 에 정의된 목적지가 아니여서 파드 ip 출력
      kubectl exec mediabot -- curl -s 192.168.10.254
      ## 로그 확인
      root@k8s-rtr:~# tail -f /var/log/apache2/access.log
      127.0.0.1 - - [28/Feb/2022:12:30:27 +0000] "GET / HTTP/1.1" 200 257 "-" "curl/7.74.0"
      172.16.2.238 - - [29/Nov/2021:17:45:06 +0000] "GET / HTTP/1.1" 200 612 "-" "curl/7.52.1"
      172.16.2.238 - - [29/Nov/2021:17:45:06 +0000] "GET / HTTP/1.1" 200 612 "-" "curl/7.52.1"
      
      # 파드에서 외부 접속
      kubectl exec mediabot -- curl -s wttr.in/busan
      kubectl exec mediabot -- curl -s wttr.in/busan?format=4
    • 신규 추가 파드로 테스트
      cat <<EOF | kubectl create -f -
      apiVersion: v1
      kind: Pod
      metadata:
        name: net-pod
        labels:
          org: empire
          class: mediabot
      spec:
        nodeName: k8s-w1
        containers:
        - name: netshoot-pod
          image: nicolaka/netshoot
          command: ["tail"]
          args: ["-f", "/dev/null"]
        terminationGracePeriodSeconds: 0
      EOF
    • 신규 파드(워커노드1번 배치, labes 설정)로 접속 테스트
      # 파드에서 nginx 접속 : policy 에 맞게 지정된 ip가 출력
      kubectl exec net-pod -- curl -s 192.168.20.100
      
      # 파드에서 nginx 접속 : policy 에 정의된 목적지가 아니여서 파드 ip 그래도 출력
      kubectl exec net-pod -- curl -s 192.168.10.254
      
      # (옵션) egress 파드가 있는 노드(예시. k8s-w2)에서 패킷덤프 >> 트래픽이 egress 를 항상 경유하는지, 아니면 파드에서 바로 목적지로 가는지 확인해보자!
      tcpdump -eni any tcp port 80 -q
      tcpdump -eni any tcp port 80 -q -v
      
      # 파드에서 외부 접속
      kubectl exec net-pod -- curl -s wttr.in/busan?format=4 # (옵션) 외부 웹서버 접속 테스트
    • 기존정책을 삭제하고 destinationCIDRs: 가 0.0.0.0/0 정책으로 생성
      # 기존정책을 삭제
      kubectl delete ciliumegressnatpolicies egress-sample
      
      # destinationCIDRs: 가 0.0.0.0/0 정책으로 생성
      cat <<EOF | kubectl create -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:
        - 0.0.0.0/0
        egressSourceIP: "192.168.10.241"
      EOF
    • 접속 테스트
      # 파드에서 nginx 접속 : egressSourceIP 가 찍임!
      kubectl exec net-pod -- curl -s 192.168.20.100
      
      # 파드에서 nginx 접속 : egressSourceIP 가 찍임!
      kubectl exec net-pod -- curl -s 192.168.10.254
      kubectl exec mediabot -- curl -s 192.168.10.254
      
      # 파드에서 외부 접속
      kubectl exec mediabot -- curl -s http://ipinfo.io/
      kubectl exec mediabot -- curl -s http://ipinfo.io/city
      
      # (옵션) egress 파드가 있는 노드(예시. k8s-w2)에서 패킷덤프 >> 트래픽이 egress 를 항상 경유하는지, 아니면 파드에서 바로 목적지로 가는지 확인해보자!
      tcpdump -eni any tcp port 80 -q
      tcpdump -eni any tcp port 80 -q -v
      - 예를 들면 0.0.0.0/0 설정 시, 특정 파드는 무조건 egressSourceIP 경유하는 통신을 하게 되므로 통신 트래픽 경로와 대역폭 등을 잘 고려하자. (심지어, 동일 노드 내의 파드 끼리도 모든 통신이 egressSourceIP 경유하게 될까요? 아닐까요?)
    • 삭제
      kubectl delete ciliumegressnatpolicies egress-sample
      kubectl delete deploy egress-ip-assign 
      kubectl delete pod --all