본문 바로가기
카테고리 없음

istio traffic 관리 (3) [Egress Gateway]

by 비어원 2025. 1. 18.
728x90

이전 시간에는 ingress gateway를 위주로 알아봤었는데 이번 시간에는 egress gateway를 위주로 알아보자.

 

ingress gateway가 외부에서 mesh에 대한 인바운드 트래픽을 관리하는 것이라면 mesh에서 바깥으로 나가는 트래픽을 관리하기 위해서 사용하는 것이 egress gateway이다.

 

사용하는 이유

mesh에서 바깥으로 나가는 트래픽을 관리하기 위해 egressgateway를 사용하는 이유는 매우 다양하다.

 

1. ACL 관리

특정 기능을 제공하기 위해 외부 서비스의 기능을 사용하는 경우, 외부 서비스에서는 특정 IP만 해당 서버와 요청할 수 있도록 ACL 제한을 두는 경우가 있다. 하지만 모든 쿠버네티스 워커노드에 공인 IP가 있는 것은 아닐 것이다. 워커노드에서 인터넷이 되는 경우라면 워커노드에서 인터넷으로 통신할 때는 NAT(Network Address Translation)를 통해 특정 공인 IP로 IP 변환한 후 인터넷으로 패킷이 전달된다. 그런데 여기서 전달되는 공인 IP가 인프라 설정에 따라 주기적으로 변경이 될 수도 있다.

 

NAT IP를 확인하는 방법은 다음과 같다.

curl ifconfig.me

 

만약 외부 서비스에서 IP ACL 검사를 하는 경우라면, NAT IP가 변경되면 더 이상 통신을 할 수 없게 되어 장애가 발생할 것이다. 그렇다고 워커노드 각각에 대해 고정된 공인 IP를 부여할 수도 없다. 고정된 공인 IP를 구입하는 비용도 있으며 워커노드는 언제든지 늘어날 수 있으며 공인 IP가 추가되면 ACL에 추가된 IP도 넣어야 해서 관리의 어려움이 있다.

 

egressgateway를 사용한다면 특정 외부 서비스로 나가는 트래픽을 egressgateway를 통해 나가도록 구성할 수 있다. 노드 어피니티를 통해 egressgateway를 특정 노드에만 배포되게 함과 동시에 특정 노드에 공인 IP를 부여하면 위의 문제를 해결할 수 있다.

 

 

2. 인터넷이 안되는 환경인 경우

워커노드에서 인터넷으로 접근이 안되는 경우에도 마찬가지로 egressgateway를 활용할 수 있다. 이 때도 마찬가지로 특정 노드에 공인 IP를 할당하여 인터넷이 되게끔 한 다음 허용된 외부 서비스에 한해서 egressgateway를 통해 트래픽이 나가도록 구성할 수 있다.

 

3. 로깅 및 권한 제어

외부로 나가는 모든 트래픽을 egressgateway를 통하게끔 구성한다면 외부로 나가는 모든 트래픽에 대한 접근 로그는 egressgateway에 쌓일 것이다. 그러면 어떤 요청이 밖으로 나갔는지를 로그를 통해 쉽게 확인이 가능하며, 특정 서비스로 나가는 트래픽을 egressgateway 수준에서 차단 시키는 것도 가능하다. 즉, mesh에서 나가는 트래픽을 egressgateway를 통해 모두 제어할 수도 있다.

 

Egressgateway 설치

 

위의 그림처럼 트래픽이 전달되도록 egressgateway를 구성해보자. 먼저 egressgateway가 설치되어 있지 않다면 설치를 해야한다. Helm chart를 사용하여 egressgateway를 설치하자.

 

설치하기에 앞서 egressgateway를 배치할 노드에다가 특정 레이블을 넣자.

$ kubectl label no k8s-wn1 schedule/istio=egress

 

그 다음에는 values.yaml파일을 다음과 같이 구성한 후에 helm을 통해 egressgateway를 설치한다.

 

egressgateway.yaml

defaults:
  revision: "1-23-4"

  replicaCount: 1

  autoscaling:
    enabled: false

  service:
    type: ClusterIP

  resources:
    requests:
      cpu: 100m
      memory: 128Mi
    limits:
      cpu: 1000m
      memory: 128Mi

  affinity:
    nodeAffinity:
      requiredDuringSchedulingIgnoredDuringExecution:
        nodeSelectorTerms:
          - matchExpressions:
              - key: schedule/istio
                operator: In
                values:
                  - egress
  • schedule/istio=egress 레이블을 가지는 노드에만 스케줄링 하도록 제한시켰다.
  • service.type이 기본적으로 LoadBalancer로 지정이 되는데 egressgateway는 외부로 노출시킬 필요가 없기 때문에 ClusterIP로 지정해야 함.

 

$ helm install egressgateway istio/gateway -n istio-system --version 1.23.4 -f egressgateway.yaml

 

 

실제로 k8s-wn1 노드에 스케줄링 되었는지 확인해보자.

$ kubectl get po -o wide
NAME                              READY   STATUS    RESTARTS       AGE     IP             NODE      NOMINATED NODE   READINESS GATES
egressgateway-6f88757c54-dg5nz    1/1     Running   0              40s     10.244.2.140   k8s-wn1   <none>           <none>
ingressgateway-64d96fccbb-mvvh2   1/1     Running   2 (170m ago)   6d22h   10.244.2.130   k8s-wn1   <none>           <none>
ingressgateway-64d96fccbb-s5pxm   1/1     Running   2 (169m ago)   6d22h   10.244.1.20    k8s-wn2   <none>           <none>
istiod-1-23-4-567f9c879-vkrpk     1/1     Running   2 (170m ago)   6d22h   10.244.2.136   k8s-wn1   <none>           <none>

 

egressgateway를 설치만 하면 다음 그림과 같은 형식으로 통신이 될 것이다. 설치만 하고 아무 설정이 없다면 당연히 외부 트래픽을 제어하지 못한다. Ingressgateway에서 내부로 들어오는 트래픽을 제어하기 위해 특정 도메인에 대한 VirtualService와 Gateway를 추가한 것 처럼 Egressgateway에서도 특정 도메인에 대해 나가는 트래픽을 제어하기 위한 설정이 필요한 것이다.

 

실제로 istio proxy가 들어있는 파드에서 요청을 하고 egressgateway 로그를 확인해보면 아무 로그도 찍혀있지 않을 것이다.

 

HTTP 트래픽 제어하기

1. mesh VirtualService 추가

일단 mesh 내에서 edition.cnn.com 으로 요청할 경우 egressgateway로 트래픽을 보내야 한다. mesh 내의 트래픽을 제어하려면 VirtualService를 추가하면 된다.

 

apiVersion: networking.istio.io/v1
kind: VirtualService
metadata:
  name: external-services
  namespace: istio-system
spec:
  hosts:
  ## 해당 호스트로 요청할 경우 egressgateway로 트래픽이 이동된다.
  ## egressgateway의 제어를 받을 호스트를 여기에 여러개 추가가 가능함.
  - edition.cnn.com
  gateways:
  - mesh
  http:
  - route:
      - destination:
          ## egressgateway 서비스의 FQDN
          host: egressgateway.istio-system.svc.cluster.local

 

VirtualService를 추가한 후 istio-proxy가 있는 아무 파드에서 통신을 해보자. HTTP 통신을 했을 경우 에러가 발생한다.

 

$ kubectl exec -it reviews-v3-5d8f7bf4c8-vttlf -n bookinfo -- curl -sSL -o /dev/null -D - http://edition.cnn.com
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
  0     0    0     0    0     0      0      0 --:--:-- --:--:-- --:--:--     0HTTP/1.1 503 Service Unavailable
content-length: 190
content-type: text/plain
date: Sat, 18 Jan 2025 11:53:43 GMT
server: envoy

100   190  100   190    0     0    223      0 --:--:-- --:--:-- --:--:--   223

 

istio-proxy 로그를 보면 다음과 같이 나오는데 일단 http의 경우에는 edition.cnn.com으로 통신하면 egressgateway까지는 잘 갔지만 egressgateway용 Gateway가 없어서 503 에러가 발생한다.

$ kubectl logs reviews-v3-5d8f7bf4c8-vttlf -c istio-proxy

 

 

 

그림으로 표현하면 다음과 같다.

2. Egress용 Gateway 추가

그 다음에 해야할 일은 Egress용 Gateway를 추가해야 한다. 다음과 같이 Gateway를 만들어보자.

apiVersion: networking.istio.io/v1
kind: Gateway
metadata:
  name: egressgateway
  namespace: istio-system
spec:
  selector:
    istio: egressgateway
  servers:
  - hosts:
    - edition.cnn.com
    port:
      number: 80
      name: http
      protocol: HTTP
  • spec.selector 에는 egressgateway만 가지고 있는 레이블을 넣으면 된다.
  • egressgateway에서 다룰 트래픽을 spec.servers.hosts에 넣으면 된다.

 

Egressgateway를 추가하고 통신하면 404에러가 발생할 것이다.

$ kubectl exec -it reviews-v3-5d8f7bf4c8-vttlf -n bookinfo -- curl -sSL -o /dev/null -D - http://edition.cnn.com
HTTP/1.1 404 Not Found
date: Sat, 18 Jan 2025 12:01:26 GMT
server: envoy
x-envoy-upstream-service-time: 5
content-length: 0

 

Gateway를 추가함으로써 http://edition.cnn.com:80 으로 들어오는 요청을 egressgateway가 받지만 해당 트래픽을 어디로 보낼지 정하지 않아서 404 에러가 발생한다. egressgateway 로그를 확인해보면 route_not_found가 뜬 것을 확인할 수 있다.

$ kubectl logs egressgateway-65566c4579-zf94w

 

 

현재 설정을 그림으로 표현하면 다음과 같다.

 

 

Egress Gateway에서는 외부 트래픽을 외부에 있는 원래 서비스로 전달만 해주면 된다. 해당 트래픽을 어디로 보낼지 정의하는 것은 VirtualService에서 하는 것이기 때문에 Egressgateway용 VirtualService를 만들어야 한다.

 

3. EgressGateway용 VirtualService 추가

실제로 egressgateway에서 http://edition.cnn.com 로 요청이 들어온 경우에는 실제 서버로 전달해주기만 하면 된다. 그렇게 하기 위해서는 다음과 같이 설정하면 된다.

apiVersion: networking.istio.io/v1
kind: VirtualService
metadata:
  name: egressgateway
  namespace: istio-system
spec:
  hosts:
  - edition.cnn.com
  gateways:
  - egressgateway
  http:
  - route:
      - destination:
          host: edition.cnn.com

 

하지만 VirtualService를 추가하고 난 후 통신을 하면 503 에러가 발생한다.

$ kubectl exec -it reviews-v3-5d8f7bf4c8-vttlf -n bookinfo -- curl -sSL -o /dev/null -D - http://edition.cnn.com
HTTP/1.1 503 Service Unavailable
date: Sat, 18 Jan 2025 12:15:54 GMT
server: envoy
x-envoy-upstream-service-time: 74
content-length: 0

 

503에러가 발생하는 이유는 istio는 destination.host 에 있는 도메인의 위치 (IP)를 모른다. istio는 내장 서비스 레지스트리가 존재하는데, 해당 서비스 레지스트리에 있는 서비스로만 트래픽 제어가 가능하다. istio는 내장 서비스 레지스트리에 edition.cnn.com 를 추가해줘야 실제로 통신이 가능하다.

 

외부 서비스를 서비스 레지스트리에 추가하려면 ServiceEntry를 등록하면 된다.

 

4. ServiceEntry

 

istio 내장 서비스 레지스트리는 자동으로 쿠버네티스 서비스 엔드포인트를 저장해둔다. 그래서 ingressgateway를 등록할 때나 mesh에서 edition.cnn.com에 대한 요청을 egressgateway로 보내는 것을 설정할 때는 위와 같은 이슈가 없었다. 하지만 쿠버네티스 바깥에 있는 서비스나 도메인에 대해 트래픽 제어를 하고싶다면 ServiceEntry를 통해 직접 추가해줘야 한다.

apiVersion: networking.istio.io/v1
kind: ServiceEntry
metadata:
  name: external-svc
  namespace: istio-system
spec:
  hosts:
  - edition.cnn.com
  ports:
  - number: 80
    name: http-port
    protocol: HTTP
  resolution: DNS
  • 80 포트로 들어오는 edition.cnn.com 을 서비스 레지스트리에 넣는다.
  • edition.cnn.com IP를 찾는 방식을 DNS로 하려면 spec.resolutionsDNS로 넣으면 된다.

 

이를 적용한 후에 통신하면 통신이 잘 되는 것을 확인할 수 있다.

$ kubectl exec -it reviews-v3-5d8f7bf4c8-vttlf -n bookinfo -- curl -sSL -o /dev/null -D - http://edition.cnn.com
HTTP/1.1 301 Moved Permanently
content-length: 0
...

HTTP/2 200
x-content-type-options: nosniff
content-type: text/html; charset=utf-8
...

 

하지만 실제로 egressgateway 로그를 보면 약간 이상함을 느끼게 된다. 301 코드로는 로그가 쌓였는데 HTTPS 통신에 대한 트래픽은 egressgateway를 거치지 않았다.

 

 

HTTPS 트래픽을 제어하려면 추가 설정이 필요하다.

HTTPS 트래픽 제어하기

1. mesh VirtualService HTTPS 추가

이전에 설정했던 mesh VirtualService에서 HTTPS에 대한 설정을 추가해야 한다.

 

apiVersion: networking.istio.io/v1
kind: VirtualService
metadata:
  name: external-services
  namespace: istio-system
spec:
  hosts:
  - edition.cnn.com
  gateways:
  - mesh
  http:
  - name: default
    route:
      - destination:
          host: egressgateway.istio-system.svc.cluster.local
          subset: external-services
​
  tls:
  - match:
      - port: 443
        sniHosts:
          - edition.cnn.com
    route:
      - destination:
          host: egressgateway.istio-system.svc.cluster.local
          subset: external-services
          port:
            number: 443

 

 

해당 설정을 추가한 후에 https 통신을 해보자.

$ kubectl exec -it reviews-v3-5d8f7bf4c8-vttlf -n bookinfo -- curl -sSL -o /dev/null -D - https://edition.cnn.com
HTTP/2 200
x-content-type-options: nosniff
content-type: text/html; charset=utf-8
...

 

통신은 잘 되지만 istio-proxy 로그를 확인해보면 egressgateway로 가지 않는 것을 확인할 수 있다.

 

443포트에 대한 라우팅룰 설정을 해도 egressgateway로 가지 않은 이유는 tls.match.sniHosts 에 있는 도메인을 istio에서 알지 못해서일 수도 있다. sniHost 일치를 확인할 때 edition.cnn.com:443 이 서비스 레지스트리에 있는지 확인하는 것 같다. 그래서 ServiceEntry를 추가하면 해결된다.

 

2. ServiceEntry 443 추가하기

 

이전에 만들어 둔 ServiceEntry에서 443 포트에 대한 설정을 추가하자.

apiVersion: networking.istio.io/v1
kind: ServiceEntry
metadata:
  name: external-svc
  namespace: istio-system
spec:
  hosts:
  - edition.cnn.com
  ports:
  - number: 80
    name: http-port
    protocol: HTTP
  - number: 443
    name: tls
    protocol: TLS
  resolution: DNS

 

ServiceEntry에 443 포트를 추가한 후에 통신을 해보면 TLS 에러가 발생한다.

$ kubectl exec -it reviews-v3-5d8f7bf4c8-vttlf -n bookinfo -- curl -sSL -o /dev/null -D - https://edition.cnn.com
curl: (35) OpenSSL SSL_connect: Connection reset by peer in connection to edition.cnn.com:443
command terminated with exit code 35

 

이유는 마찬가지로 egressgateway까지는 갔지만 egressgateway에서 edition.cnn.com:443 에 대한 처리 방식을 설정하지 않았기 때문이다.

 

3. egress용 Gateway & VirtualService 443 설정 추가

egressgateway용 Gateway와 VirtualService에 대해 각각 해당 설정을 추가하자.

apiVersion: networking.istio.io/v1
kind: Gateway
metadata:
  name: egressgateway
  namespace: istio-system
spec:
  selector:
    istio: egressgateway
  servers:

  - hosts:
    - edition.cnn.com
    port:
      number: 80
      name: http
      protocol: HTTP

  - hosts:
    - edition.cnn.com
    port:
      number: 443
      name: https
      protocol: HTTPS
    tls:
      mode: PASSTHROUGH
  • TLS는 외부 서비스에 대한 인증서가 없기 때문에 PASSTHROUGH로 외부 서비스에다가 위임한다.
apiVersion: networking.istio.io/v1
kind: VirtualService
metadata:
  name: egressgateway
  namespace: istio-system
spec:
  hosts:
  - edition.cnn.com
  gateways:
  - egressgateway
  http:
  - route:
      - destination:
          host: edition.cnn.com
  tls: 
    - match:
        - port: 443
          sniHosts:
            - edition.cnn.com
      route:
        - destination:
            host: edition.cnn.com

 

이렇게 추가하고 나면 https에 대한 통신도 egressgateway쪽으로 간 후 처리가 잘 될 것이다.

$ kubectl exec -it reviews-v3-5d8f7bf4c8-vttlf -n bookinfo -- curl -sSL -o /dev/null -D - https://edition.cnn.com
HTTP/2 200
x-content-type-options: nosniff
content-type: text/html; charset=utf-8

 

 

 

추가 외부 서비스 등록하기

위와 같이 하나의 외부 서비스를 등록해두면 다른 추가 서비스를 등록하는 것은 단순하다. 파일 4개만 등록해놓고 관리할 외부 서비스를 기계적으로 등록만 하면 된다.

 

serviceentry.yaml

apiVersion: networking.istio.io/v1
kind: ServiceEntry
metadata:
  name: external-svc
  namespace: istio-system
spec:
  hosts:
  # 서비스 추가
  - edition.cnn.com
  - www.naver.com
  ports:
  - number: 80
    name: http-port
    protocol: HTTP
  - number: 443
    name: tls
    protocol: TLS
  resolution: DNS

 

mesh-virtualservice.yaml

apiVersion: networking.istio.io/v1
kind: VirtualService
metadata:
  name: external-services
  namespace: istio-system
spec:
  hosts:
  # 서비스 추가
  - edition.cnn.com
  - www.naver.com
  gateways:
  - mesh
  http:
  - name: default
    route:
      - destination:
          host: egressgateway.istio-system.svc.cluster.local
          subset: external-services
​
  tls:
  - match:
      - port: 443
        sniHosts:
          # 서비스 추가
          - edition.cnn.com
          - www.naver.com
    route:
      - destination:
          host: egressgateway.istio-system.svc.cluster.local
          subset: external-services
          port:
            number: 443
​

 

egressgateway.yaml

apiVersion: networking.istio.io/v1
kind: Gateway
metadata:
  name: egressgateway
  namespace: istio-system
spec:
  selector:
    istio: egressgateway
  servers:
  - hosts:
    # 서비스 추가
    - edition.cnn.com
    - www.naver.com
    port:
      number: 80
      name: http
      protocol: HTTP
  - hosts:
    # 서비스 추가
    - edition.cnn.com
    - www.naver.com
    port:
      number: 443
      name: https
      protocol: HTTPS
    tls:
      mode: PASSTHROUGH

 

egressgateway-vs.yaml

apiVersion: networking.istio.io/v1
kind: VirtualService
metadata:
  name: egressgateway
  namespace: istio-system
spec:
  hosts:
  # 서비스 추가
  - edition.cnn.com
  - www.naver.com
  gateways:
  - egressgateway
  http:
    # 서비스별로 추가
    - match:
        - authority:
            exact: edition.cnn.com
      route:  
        - destination:
            host: edition.cnn.com
    - match:
        - authority:
            exact: www.naver.com
      route:  
        - destination:
            host: www.naver.com
  tls: 
    # 서비스별로 추가
    - match:
        - port: 443
          sniHosts:
            - edition.cnn.com
      route:
        - destination:
            host: edition.cnn.com
    - match:
        - port: 443
          sniHosts:
            - www.naver.com
      route:
        - destination:
            host: www.naver.com

 

 

728x90

댓글