트래픽 미러링

트래픽 미러링 (Mirroring)

이 태스크는 Istio의 트래픽 미러링(shadowing) 기능을 보여줘요. 미러링은 실시간 트래픽의 사본을 미러링된 서비스로 보내서, 프로덕션 변경을 최대한 위험을 줄여 도입할 수 있게 해주는 강력한 개념이에요.

출처: Istio 문서

본문

참고: Istio 웹사이트 코드의 템플릿 버그 때문에 이 페이지의 예제는 제대로 렌더링되지 않아요. 올바른 매니페스트(manifest)는 페이지 소스에서 확인할 수 있어요.

이 태스크는 Istio의 트래픽 미러링(shadowing) 기능을 보여줘요.

트래픽 미러링은 섀도잉(shadowing)이라고도 불리는데, 피처 팀이 최대한 위험을 줄이면서 프로덕션에 변경 사항을 도입할 수 있게 해주는 강력한 개념이에요. 미러링은 실시간 트래픽의 사본을 미러링된 서비스로 보내요. 미러링된 트래픽은 primary 서비스의 중요 요청 경로(critical request path) 밖에서(out of band) 발생해요.

이 태스크에서는 먼저 모든 트래픽을 테스트 서비스의 v1로 보내도록 강제할 거예요. 그런 다음, 트래픽의 일부를 v2로 미러링하는 규칙을 적용할 거예요.

Istio는 Kubernetes Gateway API를 지원하며, 향후 트래픽 관리의 기본 API로 만들려고 하고 있어요. 아래 지침을 통해 메시에서 트래픽 관리를 설정할 때 Gateway API를 쓸지 Istio 설정 API를 쓸지 선택할 수 있어요. 선호에 따라 Gateway API 탭이나 Istio APIs 탭 중 하나의 지침을 따르면 돼요.

참고로 Kubernetes Gateway API CRD는 대부분의 Kubernetes 클러스터에 기본으로 설치되지 않으므로, Gateway API를 사용하기 전에 해당 CRD가 설치되어 있는지 확인해야 해요:

$ kubectl get crd gateways.gateway.networking.k8s.io &> /dev/null || \
  kubectl apply --server-side -f https://github.com/kubernetes-sigs/gateway-api/releases/download/v1.6.0/experimental-install.yaml

시작하기 전에

  1. 설치 가이드를 따라 Istio를 설정해요.

  2. 액세스 로깅(access logging)을 활성화한 두 가지 버전의 httpbin 서비스를 배포하는 것으로 시작해요:

    1. httpbin-v1을 배포해요:

      $ kubectl create -f - <<EOF
      apiVersion: apps/v1
      kind: Deployment
      metadata:
        name: httpbin-v1
      spec:
        replicas: 1
        selector:
          matchLabels:
            app: httpbin
            version: v1
        template:
          metadata:
            labels:
              app: httpbin
              version: v1
          spec:
            containers:
            - image: docker.io/kennethreitz/httpbin
              imagePullPolicy: IfNotPresent
              name: httpbin
              command: ["gunicorn", "--access-logfile", "-", "-b", "[::]:80", "httpbin:app"]
              ports:
              - containerPort: 80
      EOF
      
    2. httpbin-v2를 배포해요:

      $ kubectl create -f - <<EOF
      apiVersion: apps/v1
      kind: Deployment
      metadata:
        name: httpbin-v2
      spec:
        replicas: 1
        selector:
          matchLabels:
            app: httpbin
            version: v2
        template:
          metadata:
            labels:
              app: httpbin
              version: v2
          spec:
            containers:
            - image: docker.io/kennethreitz/httpbin
              imagePullPolicy: IfNotPresent
              name: httpbin
              command: ["gunicorn", "--access-logfile", "-", "-b", "[::]:80", "httpbin:app"]
              ports:
              - containerPort: 80
      EOF
      
    3. httpbin Kubernetes 서비스를 배포해요:

      $ kubectl create -f - <<EOF
      apiVersion: v1
      kind: Service
      metadata:
        name: httpbin
        labels:
          app: httpbin
      spec:
        ports:
        - name: http
          port: 8000
          targetPort: 80
        selector:
          app: httpbin
      EOF
      
  3. httpbin 서비스로 요청을 보내는 데 사용할 curl 워크로드를 배포해요:

    $ cat <<EOF | kubectl create -f -
    apiVersion: apps/v1
    kind: Deployment
    metadata:
      name: curl
    spec:
      replicas: 1
      selector:
        matchLabels:
          app: curl
      template:
        metadata:
          labels:
            app: curl
      spec:
        containers:
        - name: curl
          image: curlimages/curl
          command: ["/bin/sleep","3650d"]
          imagePullPolicy: IfNotPresent
    EOF
    

기본 라우팅 정책 만들기

기본적으로 Kubernetes는 httpbin 서비스의 두 버전에 걸쳐 로드 밸런싱을 해요. 이 단계에서는 모든 트래픽이 v1로 가도록 그 동작을 바꿀 거예요.

  1. 모든 트래픽을 서비스의 v1로 라우팅하는 기본 라우트 규칙을 만들어요:

    Istio APIs

    $ kubectl apply -f - <<EOF
    apiVersion: networking.istio.io/v1
    kind: VirtualService
    metadata:
      name: httpbin
    spec:
      hosts:
        - httpbin
      http:
      - route:
        - destination:
            host: httpbin
            subset: v1
          weight: 100
    ---
    apiVersion: networking.istio.io/v1
    kind: DestinationRule
    metadata:
      name: httpbin
    spec:
      host: httpbin
      subsets:
      - name: v1
        labels:
          version: v1
      - name: v2
        labels:
          version: v2
    EOF
    

    Gateway API

    $ kubectl apply -f - <<EOF
    apiVersion: v1
    kind: Service
    metadata:
      name: httpbin-v1
    spec:
      ports:
      - port: 80
        name: http
      selector:
        app: httpbin
        version: v1
    ---
    apiVersion: v1
    kind: Service
    metadata:
      name: httpbin-v2
    spec:
      ports:
      - port: 80
        name: http
      selector:
        app: httpbin
        version: v2
    ---
    apiVersion: gateway.networking.k8s.io/v1
    kind: HTTPRoute
    metadata:
      name: httpbin
    spec:
      parentRefs:
      - group: ""
        kind: Service
        name: httpbin
        port: 8000
      rules:
      - backendRefs:
        - name: httpbin-v1
          port: 80
    EOF
    
  2. 이제 모든 트래픽이 httpbin:v1로 향하게 됐으니, 서비스에 요청을 보내 볼게요:

    $ kubectl exec deploy/curl -c curl -- curl -sS http://httpbin:8000/headers
    {
      "headers": {
        "Accept": "*/*",
        "Content-Length": "0",
        "Host": "httpbin:8000",
        "User-Agent": "curl/7.35.0",
        "X-B3-Parentspanid": "57784f8bff90ae0b",
        "X-B3-Sampled": "1",
        "X-B3-Spanid": "3289ae7257c3f159",
        "X-B3-Traceid": "b56eebd279a76f0b57784f8bff90ae0b",
        "X-Envoy-Attempt-Count": "1",
        "X-Forwarded-Client-Cert": "By=spiffe://cluster.local/ns/default/sa/default;Hash=20afebed6da091c850264cc751b8c9306abac02993f80bdb76282237422bd098;Subject=\"\";URI=spiffe://cluster.local/ns/default/sa/default"
      }
    }
    
  3. httpbin-v1와 httpbin-v2 pod의 로그를 확인해요. v1에 대한 액세스 로그 항목이 보이고 v2에는 아무것도 없어야 해요:

    $ kubectl logs deploy/httpbin-v1 -c httpbin
    127.0.0.1 - - [07/Mar/2018:19:02:43 +0000] "GET /headers HTTP/1.1" 200 321 "-" "curl/7.35.0"
    
    $ kubectl logs deploy/httpbin-v2 -c httpbin
    <none>
    

httpbin-v2로 트래픽 미러링

  1. httpbin-v2로 트래픽을 미러링하도록 라우트 규칙을 변경해요:

    Istio APIs

    $ kubectl apply -f - <<EOF
    apiVersion: networking.istio.io/v1
    kind: VirtualService
    metadata:
      name: httpbin
    spec:
      hosts:
        - httpbin
      http:
      - route:
        - destination:
            host: httpbin
            subset: v1
          weight: 100
        mirror:
          host: httpbin
          subset: v2
        mirrorPercentage:
          value: 100.0
    EOF
    

    이 라우트 규칙은 트래픽의 100%를 v1로 보내요. 마지막 부분(stanza)은 같은 트래픽의 100%를 httpbin:v2 서비스로 미러링(즉, 동시에 전송)하고 싶다는 것을 지정해요. 트래픽이 미러링되면, 요청은 Host/Authority 헤더에 -shadow가 붙어서 미러링된 서비스로 전송돼요. 예를 들어 cluster-1은 cluster-1-shadow가 돼요.

    또한, 이 요청들은 "fire and forget" 방식으로 미러링된다는 점이 중요해요. 즉 응답은 버려져요.

    mirrorPercentage 필드 아래의 value 필드를 사용하면 모든 요청을 미러링하는 대신 트래픽의 일부만 미러링할 수 있어요. 이 필드가 없으면 모든 트래픽이 미러링돼요.

    Gateway API

    $ kubectl apply -f - <<EOF
    apiVersion: gateway.networking.k8s.io/v1
    kind: HTTPRoute
    metadata:
      name: httpbin
    spec:
      parentRefs:
      - group: ""
        kind: Service
        name: httpbin
        port: 8000
      rules:
      - filters:
        - type: RequestMirror
          requestMirror:
            backendRef:
              name: httpbin-v2
              port: 80
        backendRefs:
        - name: httpbin-v1
          port: 80
    EOF
    

    이 라우트 규칙은 트래픽의 100%를 v1로 보내요. RequestMirror 필터는 같은 트래픽의 100%를 httpbin:v2 서비스로 미러링(즉, 동시에 전송)하고 싶다는 것을 지정해요. 트래픽이 미러링되면, 요청은 Host/Authority 헤더에 -shadow가 붙어서 미러링된 서비스로 전송돼요. 예를 들어 cluster-1은 cluster-1-shadow가 돼요.

    또한, 이 요청들은 "fire and forget" 방식으로 미러링된다는 점이 중요해요. 즉 응답은 버려져요.

  2. 트래픽을 보내요:

    $ kubectl exec deploy/curl -c curl -- curl -sS http://httpbin:8000/headers
    

    이제 v1과 v2 양쪽 모두에서 액세스 로깅이 보여야 해요. v2에 생성된 액세스 로그는 실제로 v1로 향하는 미러링된 요청들이에요.

    $ kubectl logs deploy/httpbin-v1 -c httpbin
    127.0.0.1 - - [07/Mar/2018:19:02:43 +0000] "GET /headers HTTP/1.1" 200 321 "-" "curl/7.35.0"
    127.0.0.1 - - [07/Mar/2018:19:26:44 +0000] "GET /headers HTTP/1.1" 200 321 "-" "curl/7.35.0"
    
    $ kubectl logs deploy/httpbin-v2 -c httpbin
    127.0.0.1 - - [07/Mar/2018:19:26:44 +0000] "GET /headers HTTP/1.1" 200 361 "-" "curl/7.35.0"
    

정리하기

  1. 규칙을 제거해요:

    Istio APIs

    $ kubectl delete virtualservice httpbin
    $ kubectl delete destinationrule httpbin
    

    Gateway API

    $ kubectl delete httproute httpbin
    $ kubectl delete svc httpbin-v1 httpbin-v2
    
  2. httpbin과 curl 배포 및 httpbin 서비스를 삭제해요:

    $ kubectl delete deploy httpbin-v1 httpbin-v2 curl
    $ kubectl delete svc httpbin
    

더 알아보기 (Learn more)