간단하게 egress gateway 사용해보기

 

cilium에서는 egress gateway를 지원하는데 별도의 파드가 뜨는 방식이 아닌 노드를 egress 노드로 만들어서 해당 노드에 설정되어있는 아이피로 변환시키는 방법을 사용합니다. 

 

한번 직접 테스트를 해보도록 하겠습니다.

 

 

kind: Cluster
apiVersion: kind.x-k8s.io/v1alpha4
nodes:
- role: control-plane
- role: worker
- role: worker
- role: worker

networking:
  disableDefaultCNI: true # kind cni 설치 안하기
  kubeProxyMode: "none" # kube-proxy 설치 안하게 하기

 

 

바로 kind로 클러스터 생성하고 cilium도 설치해 줍니다. 

API_SERVER=$( kubectl get nodes -l node-role.kubernetes.io/control-plane -o yaml | yq '.items[0].status.addresses[] | select(.type=="InternalIP").address' )


helm install cilium cilium/cilium --version 1.17.1 \
   --namespace kube-system \
   --set k8sServiceHost=${API_SERVER} \
   --set k8sServicePort=6443 \
   --set egressGateway.enabled=true \
   --set bpf.masquerade=true \
   --set kubeProxyReplacement=true

 

cilium 상태를 확인해주겠습니다.

# 정상이 될때까지 기다리기
cilium status --wait

 

상태가 정상이 되었다면  이제 테스트 용도의 파드를 생성하겠습니다.

이 파드가 뜨지 않은 곳을 egress 노드로 만들기 위해 테스트 파드 부터 띄우겠습니다.

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

 

 

이제 egress gateway노드로 만들 노드를 설정합니다.

파드가 뜨지 않은 노드에 egress gateway라는 라벨을 붙여줍니다.

kubectl label nodes aya-worker egress-node=true

 

이제 이 테스트 파드에서 외부로 호출을 테스트하기 위해 테스트용 컨테이너를 하나 띄우겠습니다.

kind로 만든 클러스터와 통신이 되게 하기 위해 network를 kind로 설정하여 생성합니다. 

docker run -d --rm --name nginx --network kind nginx:latest

 

 

이제 어디서(출발지) 어떤 곳으로 가려고 할때 (목적지) egress gateway 노드의 아이피로 변환할 것인지 정책을 추가합니다.

apiVersion: cilium.io/v2
kind: CiliumEgressGatewayPolicy
metadata:
  name: egress-sample
spec:
  selectors:
  - podSelector:
      matchLabels:
         # default 네임스페이스에 있는 
        io.kubernetes.pod.namespace: default
        # 아래의 라벨이 붙은 파드들로부터 
        org: empire
        class: mediabot
    # (옵션) 위의 조건에 맞는 파드들 중에서도 특정 노드에 뜬 파드를 고를 수 있음 
    # 선택하지 않는 경우 모든 노드의 뜬 파드들이 골라짐
    nodeSelector: 
      matchLabels:
        node.kubernetes.io/name: node1 
  # 어떤 곳으로 갈 때 egress gateway를 거치게 된다.
  destinationCIDRs:
  - "0.0.0.0/0"

  # egress gateway는 아래의 라벨에 매칭 되는 곳을 egress gateway 노드로 사용한다
  egressGateway:
    nodeSelector:
      matchLabels:
        node.kubernetes.io/name: node2

    # 정책에 의해 매칭된 트래픽의 출발지 주소를 변경할 아이피를 고정한다. 
    # 이 아이피는 노드의 인터페이스에 설정되어있어야 한다. 
    egressIP: 10.168.60.100

    # 혹은 인터페이스를 설정할 수도 있는데 
    # 이렇게 되는경우 이 인터페이스에 할당된 첫 번째 ip로 변환된다. 
    # interface: enp0s8

 

위의 설정에서 destinationCIDRs를 먼저 생성한 nginx컨테이너의 아이피로 변경하고 egressIP에 egress노드의 아이피로 변경하여 생성합니다.

docker inspect nginx | jq '.[0].NetworkSettings.Networks.kind.IPAddress' -r

 

 

환경에 맞춰서 구성한 설정의 예시입니다. 

apiVersion: cilium.io/v2
kind: CiliumEgressGatewayPolicy
metadata:
  name: egress-sample
spec:
  selectors:
  - podSelector:
      matchLabels:
        io.kubernetes.pod.namespace: default
        org: empire
        class: mediabot
  destinationCIDRs:
  - "192.168.97.6/32"
  egressGateway:
    nodeSelector:
      matchLabels:
        egress-node: "true"
    egressIP: 192.168.97.3

 

잘 구성이 되었습니다. 

 

cilium 파드에들어가서 룰을 확인해보면 egress 노드가 설정된 곳에서는 egress IP가 보이고 egresss 노드가 설정되어잇지 않은 곳에서는 gateway로 가서 나가도록 설정이 되는 것을 확인할 수 있습니다. 

# egress 노드에서 확인
root@aya-worker:/home/cilium# cilium bpf egress list
Source IP   Destination CIDR   Egress IP    Gateway IP
10.0.0.56   192.168.97.6/32    192.168.97.3 192.168.97.3

# egress 노드가 아닌 곳에서 확인
root@aya-worker2:/home/cilium# cilium bpf egress list
Source IP   Destination CIDR   Egress IP   Gateway IP
10.0.0.56   192.168.97.6/32    0.0.0.0     192.168.97.3

 

 

실제로 nginx를 호출했을 때 다음과 같이 egressIP로 변경되어 로그에 찍히는것을 확인할 수 있습니다. 

 

192.168.97.3은 aya-worker로 egress노드의 아이피입니다. 

 

hubble로 확인해보기

마지막으로 hubble로도 확인해보도록 하겠습니다.

helm upgrade cilium cilium/cilium --version 1.17.1 \
   --namespace kube-system \
   --reuse-values \
   --set hubble.relay.enabled=true \
   --set hubble.ui.enabled=true

 

여기 UI에 나오는 값은 identity로 정해진게 보이는 듯 한데 world를 다른걸로 바꿀 방법이 안보인다. ㅠㅠ