Terry Cho 2019. 12. 12. 00:15

Istio Traffic management

조대협 (http://bcho.tistory.com)


Istio의 기능중의 하나가, 들어오는 트래픽을 여러 서비스로 라우팅을 하거나, 또는 트래픽에 테스트를 위해서 인위적으로 장애를 만들어 내는 것과 같은 트래픽 관리 기능이 있다. 

이러한 기능을 사용하려면, Istio에서 트래픽 관리에 사용되는 컴포넌트들의 컨셉을 알아야 한다. 초기에 Kubernetes의 트래픽 관리 기능인 Service, Ingress와 개념이 헷갈렸는데,  잘 정리해놓은 문서가 있어서 개념을 잘 정리할 수 있었다. 

Istio 트래픽 관리 컴포넌트는 크게 Gateway, VirtualService, DestinationRule 3가지로 정의된다.

Gateway

Gateway는 외부로부터 트래픽을 받는 최앞단으로, 트래픽을 받을 호스트명과 포트, 프로토콜을 정의한다. 웹서버에서 버추얼 호스트를 정의하는 것과 같이 정의 된다.  

아래는 Gateway의 예제이다. 


apiVersion: networking.istio.io/v1alpha3

kind: Gateway

metadata:

  name: ext-host-gwy

spec:

  selector:

    app: my-gateway-controller

  servers:

  - port:

      number: 443

      name: https

      protocol: HTTPS

    hosts:

    - ext-host.example.com

    tls:

      mode: SIMPLE

      serverCertificate: /tmp/tls.crt

      privateKey: /tmp/tls.key



이 설정은 ext-host.example.com 로 HTTPS 프로토콜로 443 포트를 통해서 트래픽을 받겠다고 정의한것이다. 

Virtual Service

들어온 트래픽을 서비스로 라우팅 하는 기능이다. 쿠버네티스의 Service가 목적지가 되는 것이고, 실제로 Virtual Service의 기능은 URI 기반으로 라우팅을 하는 Ingress와 유사하다고 보면 된다.


클라이언트가 쿠버네티스의 Service를 호출할때 라우팅 규칙에 따라서 적절한 쿠버네티스 Service로 라우팅 해주는 기능을 한다. 

아래 예제를 보자


apiVersion: networking.istio.io/v1alpha3

kind: VirtualService

metadata:

  name: bookinfo

spec:

  hosts:

  - "*"

  gateways:

  - bookinfo-gateway

  http:

  - match:

    - uri:

        exact: /productpage

    - uri:

        prefix: /static

    - uri:

        exact: /login

    - uri:

        exact: /logout

    - uri:

        prefix: /api/v1/products

    route:

    - destination:

        host: productpage

        port:

          number: 9080


예제 출처: https://github.com/istio/istio/blob/master/samples/bookinfo/networking/bookinfo-gateway.yaml

hosts는 클라이언트가 요청한 request에 대해서 어떤 host를 호출한것을 처리할것인지를 결정한다. 여기서는 hosts: “*”로 하였기 때문에, 모든 서비스 호출에 대해서 이 Virtual Host가 라우팅 처리를 하는데, 모든 서비스 호출은 gateways: bookinfo-gateway를 이용하여, 어느 gateway에서 들어온 요청을 처리하는지 지정할 수 있다. 

즉 이 VirtualService는 bookinfo-gateway 를 통해서 들어온 모든 요청에 대해서 처리하게 된다. 


그 다음 http: 부분에서 URL 별로 라우팅을 하도록 정의하였다.

http: match: 부분을 보면 /product, /login,/logout은 exact로 정의가 되어 있기 때문에 이 URL로 매칭 되는 요청이나 또는 /static, /api/v1/products는 prefix로 정의되어 있기 때문에, /static, /api/v1/products URI로 시작하는 요청에 대해서 라우팅을 처리한다.


라우팅 Destination은 route: destination에 정의하는데 productpage 쿠버네티스 Service의 9080, 즉 products.{namespace}.svc.cluster.local 서비스로 포워딩이 된다. 


앞의 예제는 gateway를 통해서 들어온 트래픽을 처리하는 방법이었다. 다른 예제를 보자. 아래 Virtual Service는 gateway와 연동되어 있지 않고 reviews.prod.svc.cluster.local 서비스를 호출하는 요청에 대한 라우팅 규칙을 정의하였다.


apiVersion: networking.istio.io/v1alpha3

kind: VirtualService

metadata:

  name: reviews-route

spec:

  hosts:

  - reviews.prod.svc.cluster.local

  http:

  - name: "reviews-v2-routes"

    match:

    - uri:

        prefix: "/wpcatalog"

    - uri:

        prefix: "/consumercatalog"

    rewrite:

      uri: "/newcatalog"

    route:

    - destination:

        host: reviews.prod.svc.cluster.local

        subset: v2

  - name: "reviews-v1-route"

    route:

    - destination:

        host: reviews.prod.svc.cluster.local

        subset: v1

예제 출처 :https://istio.io/docs/reference/config/networking/virtual-service/ 


reviews.prod.svc.cluster.local을 호출하였을때 URI가 /wpcatalog 나 /consumercatalog로 시작한다면 이를 /newcatalog로 바꾼 후에, reviews.prod.svc.cluster.local 쿠버네티스 서비스를 호출하는데, subset이 v2로 되어 있기 때문에, reviews.prod.svc.cluster.local pod 중에 v2 subset으로만 라우팅 한다.  (subset이라는 부분이 있는데, 이 부분은 추후에 설명한다.)

만약 이 URI와 매칭되는 것이 없다면, 

  - name: "reviews-v1-route"

    route:

    - destination:

        host: reviews.prod.svc.cluster.local

        subset: v1

에 의해서 다른 모든 트래픽은 reviews.prod.svc.cluster.local pod 중에 v1 subset으로만 라우팅 된다.


다른 예제를 하나 더 보자

apiVersion: networking.istio.io/v1alpha3

kind: VirtualService

metadata:

  name: my-productpage-rule

  namespace: istio-system

spec:

  hosts:

  - productpage.prod.svc.cluster.local # ignores rule namespace

  http:

  - timeout: 5s

    route:

    - destination:

        host: productpage.prod.svc.cluster.local

예제 출처 : https://istio.io/docs/reference/config/networking/virtual-service/


이 예제는 productpage.prod.svc.cluster.local 쿠버네티스 서비스를 호출 하는 Virtual Service 인데, 별도의 라우팅 없이 productpage.prod.svc.cluster.local 를 그대로 호출하도록 정의하였는데, timeout: 5s 로 줘서, 해당 서비스가 5초 동안 응답이 없으면 timeout 처리를 하도록 하였다. 


Virtual Service의 개념을 이해할때 자칫 헷갈릴 수 있는데, 아래 두가지를 기억하면 이해에 도움이 된다. 

  • 쿠버네티스의 Ingress 처럼 들어오는 트래픽에 대해서 URI 나 Header 등 요청에 따른 라우팅을 한다

  • Virtual Service는 Gateway를 통해서 들어오는 트래픽을 받아서 Service로 라우팅 시킬 수 도 있고 또는 쿠버네티스 Service로 가는 요청에 대해서 라우팅을 할 수 도 있다. 

DestinationRule

Virtual Service가 쿠버네티스 Service로 트래픽을 보내도록 라우팅을 했다면, 그 다음 Destination Rule은 그 서비스로 어떻게 트래픽을 보낼것인가를 정의한다. (하나의 Detination Rule은 하나의 Service에 대해서만 정의한다)


Virtual Service가 라우팅을 해서 Service로 트래픽을 보내면, 실제로는 그 안에 있는 Pod 들에 라우팅이 되는데, 어느 Pod 들로 트래픽을 보낼지, 그리고, 어떻게 보낼지를 정의한다. 


아래 예제는 my-svc.{namespace}.svc.cluster.local 서비스로 보내는 트래픽을 어떻게 보낼지를 정의하였는데, my-svc.{namespace}.svc.cluster.local  를 version이라는 pod 의 label에 따라서 v1,v2,v3 로 그룹핑을 하였다. 이를 subset이라고 한다. 


apiVersion: networking.istio.io/v1alpha3

kind: DestinationRule

metadata:

  name: my-destination-rule

spec:

  host: my-svc

  trafficPolicy:

    loadBalancer:

      simple: RANDOM

  subsets:

  - name: v1

    labels:

      version: v1

  - name: v2

    labels:

      version: v2

    trafficPolicy:

      loadBalancer:

        simple: ROUND_ROBIN

  - name: v3

    labels:

      version: v3

예제 출처 : https://istio.io/docs/concepts/traffic-management/#destination-rules


그 후에 pod 간 로드 밸런싱 정책은 

  host: my-svc

  trafficPolicy:

    loadBalancer:

      simple: RANDOM

와 같이 디폴트는 RANDOM 로드 밸런싱을 정의하였지만 subset v2의 경우에는

  - name: v2

    labels:

      version: v2

    trafficPolicy:

      loadBalancer:

        simple: ROUND_ROBIN


와 같이 ROUND_ROBIN을 사용하도록 하였다. 

Destination Rule을 사용하면, 서비스로 트래픽을 보내더라도, pod의 label에 따라서 서비스내의 pod들에 골라서 트래픽을 보낼 수 있고, pod간의 로드밸런싱이나 여러 정책을 정의하는 것이 가능하다.


대략적으로 이 개념들을 정리해서 하나의 그림에 그려보면 다음과 같다



읽어볼만 한글


참고할 사항

Ingress-gateway라는 용어가 종종 나오는데, 이는 Kubernetes에서 Gateway 를 구현하기 위한 실제 Pod이다. Service + Deployment로 되어 있고, 스케일을 위해서 HPA를 적용할 수 있다.


Istio를 사용한다고 해서 외부에서 들어오는 트래픽을 반드시 Istio gateway를 사용해야 하는 것은 아니다. 기존의 Kubernetes Ingress를 그대로 사용할 수 도 있고, Kong 과 같은 API gateway를 사용하는 것도 가능하다.


다른 Service간의 통신은 아래 그림과 같이 pod → 다른 Service의 Pod를 호출할때, 중간에 Load balancer를 거치거나 하지 않고 아래 그림과 같이 Envoy Proxy를 통해서 바로 이루어진다. 생각해보니, ClusterIP는 원래 Load balancer를 거치지 않았어..


그림 원본 https://medium.com/@zhaohuabing/which-one-is-the-right-choice-for-the-ingress-gateway-of-your-service-mesh-21a280d4a29c


예제 코드 참고 https://istio.io/docs/concepts/traffic-management/