Mirroring

    Traffic mirroring, also called shadowing, is a powerful concept that allows feature teams to bring changes to production with as little risk as possible. Mirroring sends a copy of live traffic to a mirrored service. The mirrored traffic happens out of band of the critical request path for the primary service.

    In this task, you will first force all traffic to of a test service. Then, you will apply a rule to mirror a portion of traffic to v2.

    Istio includes beta support for the Kubernetes Gateway API and intends to make it the default API for traffic management . The following instructions allow you to choose to use either the Gateway API or the Istio configuration API when configuring traffic management in the mesh. Follow instructions under either the Gateway API or Istio classic tab, according to your preference.

    Note that this document uses the Gateway API to configure internal mesh (east-west) traffic, i.e., not just ingress (north-south) traffic. Configuring internal mesh traffic is an experimental feature of the Gateway API, currently under development and pending . Make sure to install the experimental CRDs before using the Gateway API:

    • Set up Istio by following the instructions in the .

    • Start by deploying two versions of the httpbin service that have access logging enabled:

      1. $ cat <<EOF | istioctl kube-inject -f - | kubectl create -f -
      2. apiVersion: apps/v1
      3. kind: Deployment
      4. metadata:
      5. name: httpbin-v1
      6. spec:
      7. replicas: 1
      8. selector:
      9. matchLabels:
      10. app: httpbin
      11. version: v1
      12. template:
      13. metadata:
      14. labels:
      15. app: httpbin
      16. version: v1
      17. spec:
      18. containers:
      19. - image: docker.io/kennethreitz/httpbin
      20. imagePullPolicy: IfNotPresent
      21. name: httpbin
      22. command: ["gunicorn", "--access-logfile", "-", "-b", "0.0.0.0:80", "httpbin:app"]
      23. ports:
      24. - containerPort: 80
      25. EOF

      httpbin-v2:

      1. $ cat <<EOF | istioctl kube-inject -f - | kubectl create -f -
      2. apiVersion: apps/v1
      3. kind: Deployment
      4. metadata:
      5. name: httpbin-v2
      6. spec:
      7. replicas: 1
      8. selector:
      9. matchLabels:
      10. app: httpbin
      11. version: v2
      12. template:
      13. metadata:
      14. labels:
      15. app: httpbin
      16. version: v2
      17. spec:
      18. containers:
      19. - image: docker.io/kennethreitz/httpbin
      20. imagePullPolicy: IfNotPresent
      21. name: httpbin
      22. command: ["gunicorn", "--access-logfile", "-", "-b", "0.0.0.0:80", "httpbin:app"]
      23. ports:
      24. - containerPort: 80
      25. EOF

      httpbin Kubernetes service:

      1. $ kubectl create -f - <<EOF
      2. apiVersion: v1
      3. kind: Service
      4. metadata:
      5. name: httpbin
      6. labels:
      7. app: httpbin
      8. spec:
      9. ports:
      10. - name: http
      11. port: 8000
      12. selector:
      13. app: httpbin
      14. EOF
    • Start the sleep service so you can use curl to provide load:

      sleep service:

      1. $ cat <<EOF | istioctl kube-inject -f - | kubectl create -f -
      2. apiVersion: apps/v1
      3. metadata:
      4. name: sleep
      5. spec:
      6. replicas: 1
      7. selector:
      8. matchLabels:
      9. app: sleep
      10. template:
      11. metadata:
      12. labels:
      13. app: sleep
      14. spec:
      15. containers:
      16. - name: sleep
      17. image: curlimages/curl
      18. command: ["/bin/sleep","3650d"]
      19. imagePullPolicy: IfNotPresent
      20. EOF

    By default Kubernetes load balances across both versions of the httpbin service. In this step, you will change that behavior so that all traffic goes to v1.

    1. Create a default route rule to route all traffic to v1 of the service:
    1. $ kubectl apply -f - <<EOF
    2. apiVersion: networking.istio.io/v1alpha3
    3. kind: VirtualService
    4. metadata:
    5. name: httpbin
    6. spec:
    7. hosts:
    8. - httpbin
    9. http:
    10. - route:
    11. - destination:
    12. host: httpbin
    13. subset: v1
    14. weight: 100
    15. ---
    16. apiVersion: networking.istio.io/v1alpha3
    17. kind: DestinationRule
    18. metadata:
    19. name: httpbin
    20. spec:
    21. host: httpbin
    22. subsets:
    23. - name: v1
    24. labels:
    25. version: v1
    26. - name: v2
    27. labels:
    28. version: v2
    29. EOF
    1. Now, with all traffic directed to httpbin:v1, send a request to the service:

      1. $ export SLEEP_POD=$(kubectl get pod -l app=sleep -o jsonpath={.items..metadata.name})
      2. $ kubectl exec "${SLEEP_POD}" -c sleep -- curl -sS http://httpbin:8000/headers
      3. {
      4. "headers": {
      5. "Accept": "*/*",
      6. "Content-Length": "0",
      7. "Host": "httpbin:8000",
      8. "User-Agent": "curl/7.35.0",
      9. "X-B3-Parentspanid": "57784f8bff90ae0b",
      10. "X-B3-Sampled": "1",
      11. "X-B3-Spanid": "3289ae7257c3f159",
      12. "X-Envoy-Attempt-Count": "1",
      13. "X-Forwarded-Client-Cert": "By=spiffe://cluster.local/ns/default/sa/default;Hash=20afebed6da091c850264cc751b8c9306abac02993f80bdb76282237422bd098;Subject=\"\";URI=spiffe://cluster.local/ns/default/sa/default"
      14. }
      15. }
      1. $ kubectl logs "$V1_POD" -c httpbin
      2. 127.0.0.1 - - [07/Mar/2018:19:02:43 +0000] "GET /headers HTTP/1.1" 200 321 "-" "curl/7.35.0"
      1. $ export V2_POD=$(kubectl get pod -l app=httpbin,version=v2 -o jsonpath={.items..metadata.name})
      2. $ kubectl logs "$V2_POD" -c httpbin
      3. <none>
    1. Change the route rule to mirror traffic to v2:
    1. $ kubectl apply -f - <<EOF
    2. apiVersion: networking.istio.io/v1alpha3
    3. kind: VirtualService
    4. metadata:
    5. name: httpbin
    6. spec:
    7. hosts:
    8. - httpbin
    9. http:
    10. - route:
    11. - destination:
    12. host: httpbin
    13. subset: v1
    14. weight: 100
    15. mirror:
    16. host: httpbin
    17. subset: v2
    18. mirrorPercentage:
    19. value: 100.0
    20. EOF

    This route rule sends 100% of the traffic to v1. The last stanza specifies that you want to mirror (i.e., also send) 100% of the same traffic to the httpbin:v2 service. When traffic gets mirrored, the requests are sent to the mirrored service with their Host/Authority headers appended with -shadow. For example, cluster-1 becomes cluster-1-shadow.

    Also, it is important to note that these requests are mirrored as “fire and forget”, which means that the responses are discarded.

    You can use the value field under the mirrorPercentage field to mirror a fraction of the traffic, instead of mirroring all requests. If this field is absent, all traffic will be mirrored.

    1. $ kubectl apply -f - <<EOF
    2. apiVersion: gateway.networking.k8s.io/v1beta1
    3. kind: HTTPRoute
    4. metadata:
    5. name: httpbin
    6. spec:
    7. parentRefs:
    8. - kind: Service
    9. name: httpbin
    10. port: 8000
    11. rules:
    12. - filters:
    13. - type: RequestMirror
    14. requestMirror:
    15. backendRef:
    16. name: httpbin-v2
    17. port: 80
    18. backendRefs:
    19. - name: httpbin-v1
    20. port: 80
    21. EOF

    This route rule sends 100% of the traffic to v1. The RequestMirror filter specifies that you want to mirror (i.e., also send) 100% of the same traffic to the httpbin:v2 service. When traffic gets mirrored, the requests are sent to the mirrored service with their Host/Authority headers appended with -shadow. For example, cluster-1 becomes cluster-1-shadow.

    Also, it is important to note that these requests are mirrored as “fire and forget”, which means that the responses are discarded.

    1. Send in traffic:

      1. $ kubectl logs "$V1_POD" -c httpbin
      2. 127.0.0.1 - - [07/Mar/2018:19:02:43 +0000] "GET /headers HTTP/1.1" 200 321 "-" "curl/7.35.0"
      3. 127.0.0.1 - - [07/Mar/2018:19:26:44 +0000] "GET /headers HTTP/1.1" 200 321 "-" "curl/7.35.0"
      1. $ kubectl logs "$V2_POD" -c httpbin
      2. 127.0.0.1 - - [07/Mar/2018:19:26:44 +0000] "GET /headers HTTP/1.1" 200 361 "-" "curl/7.35.0"
    1. Remove the rules:
    1. $ kubectl delete virtualservice httpbin
    2. $ kubectl delete destinationrule httpbin
    1. $ kubectl delete httproute httpbin
    2. $ kubectl delete svc httpbin-v1 httpbin-v2
    1. Shutdown the httpbin service and client:

      1. $ kubectl delete svc httpbin