공부하고 기록하는, 경제학과 출신 개발자의 노트

학습일지/Service Mesh

istio 개념 정리 (2) - VirtualService / DestinationRule / Gateway

inspirit941 2022. 10. 10. 14:32
반응형

Canaries

스크린샷 2022-09-25 오전 11 02 32

  • 애플리케이션의 사용량이 많을수록 유용한 기능. 사용자가 많은 애플리케이션에서 새로운 버전을 100% 바로 배포할 경우 발생할 수 있는 리스크를 줄일 수 있음.

istio 없이 Kubernetes component로 Canary 적용하기

스크린샷 2022-09-25 오전 11 15 13

  • Single Service에 multiple pod를 연결하는 것이 가능함. deployment에 Label 붙이고, service에서 matchLabel로 pod 선택하는 게 가능하기 때문
  • k8s의 load balancing은 기본적으로 round robin.
    • 따라서 위 그림과 같은 상황이라면, new version (staff-service:6)은 전체 트래픽의 33%를 점유하게 됨
  • 대신, 이 방법은 traffic의 %를 변경하려면 그만큼 pod 비율을 늘려야 함. 33% -> 10%로 변경하고 싶으면, new version : old version 비율이 1:9가 되어야 함

deployment에서 label에 version을 키값으로 입력할 경우, kiali의 versioned app graph를 입력하면 version에 입력한 value값이 표시됨.

스크린샷 2022-09-25 오후 12 01 16

kiali에서 Service -> Details -> Create Weighted Routing 선택하면 traffic spliting이 가능함. VirtualService + DestinationRules object를 생성해야 하는 작업이지만, kiali에서 ui로 처리할 수 있음.

Istio VirtualService?

스크린샷 2022-09-27 오후 1 16 03

Traffic Customization을 가능하게 해 주는 istio의 CRD.

  • Routing Configuration 또는 Custom Routing이 더 직관적인 리소스 이름이라고 생각함.

istio VirtualService가 적용된 Architecture 구조란?

스크린샷 2022-09-27 오후 1 59 48

k8s의 Service는 외부로부터의 요청을 받아서, labels / Selector 기준으로 내부의 여러 pod에 트래픽 routing을 수행함.

대략 동작방식을 요약하자면

  1. Client pod이 다른 pod를 호출하려 하면, 먼저 k8s 내부에서 DNS lookup을 수행한다.
    1. DNS 작업은 kubernetes built-in pod인 kube-system이 수행함.
  2. pod는 각각 private IP주소를 가지고 있고, Service에서 IP table + load balancing을 수행함.

istio를 적용하면, 대략 아래처럼 구조가 바뀐다.

스크린샷 2022-09-27 오후 2 10 31

  1. virtualService를 클러스터에 배포하면, istio의 control plane(istiod) (그림에서는 pilot) 으로 전달된다.
    1. control plane은 실시간으로 proxy configuring을 담당하는 컴포넌트.
    2. virtualService에 정의된 configuration이 실시간으로 istio proxy (envoy proxy) 옵션을 변경한다. custom Routing 로직이 주로 반영됨
  2. 따라서 k8s pod가 다른 Pod로 요청을 보내면, custom routing이 적용된 proxy를 통해서 요청이 전달된다.

스크린샷 2022-09-27 오후 2 19 16

envoy proxy가 traffic management 기능을 제공하고, istio는 여러 pod에 배포된 envoy proxy에 routing configuration을 실시간으로 변경할 수 있게 만들어준다.

따라서 kubernetes의 Service와 istio의 VirtualService는 이름과 달리 완전히 별개의 기능이라고 이해해도 됨.

  • k8s Service는 individual pods의 private IP주소를 관리하는 게 주된 역할
    • istio의 envoy proxy가 요청을 다른 pod의 proxy로 전달할 때 반드시 필요로 함.
  • virtualService는 Canary Deployment같은 traffic management 기능을 on-the-fly로 반영할 수 있게 해 줌.

스크린샷 2022-09-27 오후 2 26 34

DestinationRules

스크린샷 2022-09-27 오후 3 10 24

VirtualService이 정상적으로 동작하기 위해 필요한 object. istio의 Load balancing에 적용하는 Rule이라고 생각하면 된다.

  • Subset: 트래픽을 전달할 단위 (단일 pod이거나, label selector로 pod 그룹을 지정하거나)
  • label의 key를 'version'으로 지정할 경우 kiali에서 "versioned app group" 설정을 활용할 수 있다.
    • same Service, different Deployment

Configuration

VirtualService yaml 파일은 kiali UI에서 생성되는 걸 사용할 수도 있으나, 잘 쓰이지는 않음.

kind: VirtualService
apiVersion: networking.istio.io/v1alpha3
metadata:
  name: a-set-of-routing-rules-we-can-call-this-anything  # "just" a name for this virtualservice
  namespace: default
spec:
  hosts:
    - fleetman-staff-service.default.svc.cluster.local  # The Service DNS (i.e the regular K8S Service) name that we're applying routing rules to.
    # 예시의 경우 routing에 사용할 DNS Lookup이 필요했으므로 k8s service가 매핑됨.
  http:
    - route:
        - destination:
            host: fleetman-staff-service.default.svc.cluster.local # The Target DNS name
            subset: safe-group  # The name defined in the DestinationRule
          weight: 90
        - destination:
            host: fleetman-staff-service.default.svc.cluster.local # The Target DNS name
            subset: risky-group  # The name defined in the DestinationRule
          weight: 10
---
kind: DestinationRule       # Defining which pods should be part of each subset
apiVersion: networking.istio.io/v1alpha3
metadata:
  name: grouping-rules-for-our-photograph-canary-release # This can be anything you like.
  namespace: default
spec:
  host: fleetman-staff-service # Service
  subsets:
    - labels:   # SELECTOR.
        version: safe # find pods with label "safe"
      name: safe-group
    - labels:
        version: risky
      name: risky-group

Load Balancing

istio에서 적용할 수 있는 load balancing 알고리즘. - stickyness.

istio의 traffic management 기능을 그대로 적용하면, 매번 request를 보낼 때마다 traffic split이 이루어진다.

한 번 connection + session이 생성되면, 해당 세션에 한 번 설정된 traffic split이 그대로 유지되도록 설정할 수도 있음.

  • request의 특정 값을 hash key로 사용한 sticky session 기능이 있다.

예컨대 ip주소값을 hash key로 사용하는 sticky session의 경우

스크린샷 2022-09-27 오후 3 25 16

Load balancer 옵션은 아래 docs에서 확인할 수 있다.

Consistent Hash를 사용할 경우, hash key로 사용할 수 있는 값은 아래에서 확인할 수 있다.

cf. Weighted Traffic Management 기능에 sticky session을 쓰면 제대로 동작하지 않는 버그가 있다고 함.

  • https://github.com/istio/istio/issues/9764
  • envoy에서 기능지원을 하지 않았기에 생긴 버그이고, istio에서 제대로 해결되지 않은 듯.
    • istio 내부적으로는 load balancing을 적용하기 전에 weight가 먼저 적용되기 때문이라고 하는데..

스크린샷 2022-09-27 오후 3 41 54

  • 이슈에 사람들이 남긴 workaround를 참고해서 적용하면 될 거 같다.

httpHeaderName 값을 hash해서 load balancing에 사용하는 예시 yaml 파일.

kind: VirtualService
apiVersion: networking.istio.io/v1alpha3
metadata:
  name: a-set-of-routing-rules-we-can-call-this-anything  # "just" a name for this virtualservice
  namespace: default
spec:
  hosts:
    - fleetman-staff-service.default.svc.cluster.local  # The Service DNS (ie the regular K8S Service) name that we're applying routing rules to.
  http:
    - route:
        - destination:
            host: fleetman-staff-service.default.svc.cluster.local # The Target DNS name
            subset: all-staff-service-pods  # The name defined in the DestinationRule
          # weight: 100 not needed if there's only one.
---
kind: DestinationRule       # Defining which pods should be part of each subset
apiVersion: networking.istio.io/v1alpha3
metadata:
  name: grouping-rules-for-our-photograph-canary-release # This can be anything you like.
  namespace: default
spec:
  host: fleetman-staff-service # Service
  trafficPolicy:
    loadBalancer:
      consistentHash:
        httpHeaderName: "x-myval"
  subsets:
    - labels:   # SELECTOR.
        app: staff-service # find pods with label "safe"
      name: all-staff-service-pods

ingress Gateway

kubernetes ingress : HW load balancer 없이도 여러 service를 외부로 노출할 수 있게 해주는 k8s Object.

 

스크린샷 2022-09-27 오후 9 09 12

만약 위와 같은 Canary Deployment를 해야 한다면, istio의 traffic management 옵션만으로도 충분히 가능할 것처럼 보이지만 실제로는 그렇지 않다.

  • 정확히는, pod에서 다른 pod으로 요청을 보낼 때의 traffic management는 정상 작동.
  • browser (외부)에서 pod으로 요청을 보냈을 경우에는 traffic management가 동작하지 않음.

원인은 istio Proxy를 타고 요청이 전달되는지 여부.

스크린샷 2022-09-27 오후 10 20 12스크린샷 2022-09-27 오후 10 23 19

  • pod에서 다른 pod으로 요청을 보낼 경우에는 istio proxy를 거쳐서 전달되므로 traffic management 옵션이 반영된 채 request가 전달된다.
  • cluster 외부로부터 들어오는 요청은 Cloud provider's Load balancer이거나, NodePort로부터 직접 트래픽이 전달됨. 별다른 lb 알고리즘 설정이 없을 경우 round robin 방식으로 pod에 트래픽이 전달됨.\
    • 즉, container가 요청을 받은 후에야 proxy 로직이 동작하기 때문.

 

istio에서 제시하는 해결방안: Edge Proxy (= istio Gateway).

 

스크린샷 2022-09-29 오후 12 56 57

  • cluster 외부로부터 pod 요청이 들어오면, istio proxy에 우선 트래픽이 전달됨.
  • proxy를 거친 후에야 pod로 요청이 전달되는 식.

 

스크린샷 2022-09-29 오후 12 58 45

Istio ingress Gateway: 외부로부터 오는 트래픽에도 istio Proxy 로직을 적용하기 위한 object (edge Proxy)

  • [container + istio proxy]로 이루어져 있는 일반 pod.
  • 따라서 configuration 생성 / 수정 방식이 일반적인 istio proxy configuration과 동일함.

istio-ingressgateway pod과 istio-ingressgateway Service가 존재함.

  • istio-ingressgateway Service의 타입은 LoadBalancer / NodePort 등 외부로부터 트래픽을 받을 수 있는 타입으로 되어 있음.

 

스크린샷 2022-09-29 오후 2 14 45

 

  • 위 예시의 경우 NodePort 번호는 31119. 따라서 외부에서 ingress-gateway pod로 요청을 직접 보내려 할 경우 : 로 요청을 보내면 됨. 내부적으로는 80 포트로 변환한다.
  • 하지만 ingress-gateway pod는 기본적으로 deny all traffic. 따라서 ingress-gateway pod의 80포트를 열어야 외부로부터 트래픽을 받을 수 있다.
  • ingress-gateway pod가 외부 트래픽을 받을 수 있도록 설정하려면 istio의 Gateway라는 Object를 사용해야 함. 아래 예시는 http + 80포트로 들어오는 요청을 허용하는 configuration.
apiVersion: networking.istio.io/v1alpha3
kind: Gateway
metadata:
  name: ingress-gateway-configuration
spec:
  selector:
    istio: ingressgateway # use Istio default gateway implementation
  servers:
  - port:
      number: 80
      name: http
      protocol: HTTP
    hosts:
    - "*"   # Domain name of the external website. gateway가 적용될 domain scope를 결정함.
    # 특정 도메인으로 들어오는 트래픽에만 80포트를 열도록 허용할 수 있음. *는 모든 도메인을 허용한다는 뜻

 

스크린샷 2022-09-29 오후 12 56 57

외부 트래픽이 ingress gateway로 들어오는 설정을 완료했으니, ingress gateway pod에 있는 Proxy 설정은 아래와 같이 진행하면 된다.

  • ingress gateway의 proxy에서 적용할 로직이므로
    • virtualService의 host값은 Gateway object에서 정의한 host값과 동일하다.
    • hosts가 리스트이므로, 만약 내부의 특정 서비스에는 별도의 로직을 적용하겠다면 다르게 설정할 수 있음
kind: VirtualService
apiVersion: networking.istio.io/v1alpha3
metadata:
  name: fleetman-webapp
  namespace: default
spec:
  hosts:      # which incoming host are we applying the proxy rules to???
    - "*" # Copy the value in the gateway hosts - usually a Domain Name
  gateways:
    - ingress-gateway-configuration # 위에 정의한 Gateway object 이름을 넣어준다.
  http:
    - route:
        - destination:
            host: fleetman-webapp
            subset: original
          weight: 90
        - destination:
            host: fleetman-webapp
            subset: experimental
          weight: 10
---
kind: DestinationRule
apiVersion: networking.istio.io/v1alpha3
metadata:
  name: fleetman-webapp
  namespace: default
spec:
  host: fleetman-webapp
  subsets:
    - labels:
        version: original
      name: original
    - labels:
        version: experimental
      name: experimental

ingress Gateway가 적용된 이후 kiali에서 Graph를 확인해보면

 

스크린샷 2022-09-30 오전 11 02 22

  • default Namespace로 정의한 그래프임에도 불구하고 incoming traffic의 맨 앞단이 istio-system Namespace의 ingressgateway로 되어 있다.

요약하자면

  • ingress gateway가 istio에서 반드시 필요한 옵션은 아니다.
  • ingress gateway는 custom traffic management를 service 맨 앞단 (edge) 에서부터 적용하고자 할 때 사용함.
    • 예컨대 사용자가 직접 request를 요청하는 webApp.

Prefix-based Routing

https://istio.io/latest/docs/reference/config/networking/virtual-service/#VirtualService 문서에서 확인할 수 있음.

문법은 대략 아래와 같음.

  • route의 indentation 유의.
  • 문서에는 header based routing 기능이 있다.
kind: VirtualService
apiVersion: networking.istio.io/v1alpha3
metadata:
  name: fleetman-webapp
  namespace: default
spec:
  hosts:      # which incoming host are we applying the proxy rules to???
    - "*"
  gateways:
    - ingress-gateway-configuration
  http:
    - match:
      - uri:  # IF
          prefix: "/experimental"
      - uri:  # OR
          prefix: "/canary"
      route: # THEN
      - destination:
          host: fleetman-webapp
          subset: experimental
    - match:
      - uri :
          prefix: "/"
      route:
      - destination:
          host: fleetman-webapp
          subset: original
---
kind: DestinationRule
apiVersion: networking.istio.io/v1alpha3
metadata:
  name: fleetman-webapp
  namespace: default
spec:
  host: fleetman-webapp
  subsets:
    - labels:
        version: original
      name: original
    - labels:
        version: experimental
      name: experimental

Subdomain Routing

prefix 방식은 별개의 URL을 생성하는 방법으로는 그다지 좋지 못함. 여러 개의 Url 자체를 생성한다면 subdomain 방식이 낫다.

  • subdomain을 생성했을 때 routing 방법은 아래 yaml에서 확인 가능.
  • prefix 방식보다 virtualService의 필드가 간결함
apiVersion: networking.istio.io/v1alpha3
kind: Gateway
metadata:
  name: ingress-gateway-configuration
spec:
  selector:
    istio: ingressgateway # use Istio default gateway implementation
  servers:
  - port:
      number: 80
      name: http
      protocol: HTTP
    hosts:
    - "*.fleetman.com"   # Domain name of the external website
    - "fleetman.com"
---
kind: VirtualService
apiVersion: networking.istio.io/v1alpha3
metadata:
  name: fleetman-webapp
  namespace: default
spec:
  hosts:      # which incoming host are we applying the proxy rules to???
    - "fleetman.com"
  gateways:
    - ingress-gateway-configuration
  http:
    - route:
      - destination:
          host: fleetman-webapp
          subset: original
---
kind: VirtualService
apiVersion: networking.istio.io/v1alpha3
metadata:
  name: fleetman-webapp-experiment
  namespace: default
spec:
  hosts:      # which incoming host are we applying the proxy rules to???
    - "experimental.fleetman.com"
  gateways:
    - ingress-gateway-configuration
  http:
      - route:
        - destination:
            host: fleetman-webapp
            subset: experimental
---
kind: DestinationRule
apiVersion: networking.istio.io/v1alpha3
metadata:
  name: fleetman-webapp
  namespace: default
spec:
  host: fleetman-webapp
  subsets:
    - labels:
        version: original
      name: original
    - labels:
        version: experimental
      name: experimental

header based Routing

브라우저에서 custom header를 추가하려면 chrome extension의 ModHeader 를 사용하면 된다.

문서: https://istio.io/latest/docs/reference/config/networking/virtual-service/#HTTPMatchRequest

  • prefix와 마찬가지로 virtualService의 indentation에 유의.
apiVersion: networking.istio.io/v1alpha3
kind: Gateway
metadata:
  name: ingress-gateway-configuration
spec:
  selector:
    istio: ingressgateway # use Istio default gateway implementation
  servers:
  - port:
      number: 80
      name: http
      protocol: HTTP
    hosts:
    - "*"   # Domain name of the external website
---
kind: VirtualService
apiVersion: networking.istio.io/v1alpha3
metadata:
  name: fleetman-webapp
  namespace: default
spec:
  hosts:      # which incoming host are we applying the proxy rules to???
    - "*"
  gateways:
    - ingress-gateway-configuration
  http:
    - match:
      - headers:  # IF
          my-header:
            exact: canary
      route: # THEN
      - destination:
          host: fleetman-webapp
          subset: experimental
    - route: # CATCH ALL. matching rule이 없을 경우 default로 적용되는 옵션.
      - destination:
          host: fleetman-webapp
          subset: original
---
kind: DestinationRule
apiVersion: networking.istio.io/v1alpha3
metadata:
  name: fleetman-webapp
  namespace: default
spec:
  host: fleetman-webapp
  subsets:
    - labels:
        version: original
      name: original
    - labels:
        version: experimental
      name: experimental

응용하기: Dark Release

header based routing이 가능하다는 건, 동일한 Url에서 header만 변경하는 것으로 New Release를 Live Cluster에서 띄울 수 있다는 뜻.

일반적으로 Live cluster에서 테스트하기 위한 환경으로 staging을 구축하지만, staging을 제대로 구축하려면 실제 prod 환경을 그대로 복제해야 한다. Running Cost가 두 배인 셈.

따라서 prod Live cluster에 new release를 배포하고, 테스트하는 것.

  • risk-averse 프로젝트에서는 받아들여지지 않을 수 있다. 충분히 이해함.
  • 다만 istio를 사용해서 얻을 수 있는 이점을 활용한 응용방법 중 하나임.

스크린샷 2022-10-01 오전 10 40 28

microService 형태로 수많은 pod가 live cluster에 있을 경우, 모든 microservices를 staging 환경에 동일하게 배포 / 관리하는 작업은 힘들 수 있음.

  • live test용 microservice 앱을 dark release로 배포하고, header based routing으로 다른 microservice 컴포넌트와 연결하고 문제여부를 확인하는 테스트 진행.
  • 문제가 없을 경우, header based routing 옵션을 변경해서 새로운 버전이 트래픽을 받을 수 있도록 변경.

istio configuration의 변경만으로도 가능함. 단, jaeger에서 언급한 header propagation 이슈 있음.

 

스크린샷 2022-10-01 오전 10 53 24

  • 브라우저 / frontend webapp에서 header를 받았다고 해도, 로직상 microservice의 다른 pod를 호출하는 과정에서 header의 Propagation은 자동으로 만들어지지 않음
  • istio에서 header propagation 기능을 제공하지 않고 있으므로, 모든 application code에 custom header 파라미터를 전달하고 받을 수 있도록 설정해야 함.



kiali ui로도 Matching Routing 설정이 가능해졌다.

스크린샷 2022-10-01 오전 10 50 43

# Dark release용 virtualService / DestinationRulekind: VirtualService

apiVersion: networking.istio.io/v1alpha3
metadata:
  name: fleetman-staff-service
  namespace: default
spec:
  hosts:
    - fleetman-staff-service
  http:
    - match:
        - headers:
            x-my-header:
              exact: canary
      route:
        - destination:
            host: fleetman-staff-service
            subset: risky
    - route:
        - destination:
            host: fleetman-staff-service
            subset: safe
---
kind: DestinationRule
apiVersion: networking.istio.io/v1alpha3
metadata:
  name: fleetman-staff-service
  namespace: default
spec:
  host: fleetman-staff-service
  subsets:
    - labels:
        version: safe
      name: safe
    - labels:
        version: risky
      name: risky
반응형