镜像

    流量镜像,也称为影子流量,是一个以尽可能低的风险为生产带来变化的强大的功能。镜像会将实时流量的副本发送到镜像服务。镜像流量发生在主服务的关键请求路径之外。

    在此任务中,首先把流量全部路由到 版本的测试服务。然后,执行规则将一部分流量镜像到 v2 版本。

    • 按照安装指南中的说明设置 Istio。

    • 首先部署两个版本的 服务,httpbin 服务已开启访问日志:

    httpbin-v1:

    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. template:
    9. metadata:
    10. labels:
    11. app: httpbin
    12. version: v2
    13. spec:
    14. containers:
    15. - image: docker.io/kennethreitz/httpbin
    16. imagePullPolicy: IfNotPresent
    17. name: httpbin
    18. command: ["gunicorn", "--access-logfile", "-", "-b", "0.0.0.0:80", "httpbin:app"]
    19. ports:
    20. - containerPort: 80
    21. 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. targetPort: 80
    13. selector:
    14. app: httpbin
    15. EOF
    • 启动 sleep 服务,这样就可以使用 curl 来提供负载了:

    sleep service:

    1. $ cat <<EOF | istioctl kube-inject -f - | kubectl create -f -
    2. apiVersion: apps/v1
    3. kind: Deployment
    4. metadata:
    5. name: sleep
    6. spec:
    7. replicas: 1
    8. template:
    9. metadata:
    10. labels:
    11. app: sleep
    12. spec:
    13. containers:
    14. - name: sleep
    15. image: tutum/curl
    16. command: ["/bin/sleep","infinity"]
    17. imagePullPolicy: IfNotPresent
    18. EOF
    • 创建一个默认路由规则,将所有流量路由到服务的 v1

    如果安装/配置 Istio 的时候开启了 TLS 认证,在应用 DestinationRule 之前必须将 TLS 流量策略 mode: ISTIO_MUTUAL 添加到 DestinationRule。否则,请求将发生 503 错误,如所述。

    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. kind: DestinationRule
    17. metadata:
    18. spec:
    19. host: httpbin
    20. subsets:
    21. - name: v1
    22. labels:
    23. version: v1
    24. - name: v2
    25. labels:
    26. version: v2
    27. EOF

    现在所有流量都转到httpbin:v1服务。

    • 向服务发送一下流量:
    • 分别查看 httpbin 服务 v1v2 两个 pods 的日志,您可以看到访问日志进入 v1,而 v2 中没有日志,显示为 <none>
    1. $ export V1_POD=$(kubectl get pod -l app=httpbin,version=v1 -o jsonpath={.items..metadata.name})
    2. $ kubectl logs -f $V1_POD -c httpbin
    3. 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 -f $V2_POD -c httpbin
    3. <none>
    • 改变流量规则将流量镜像到 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. mirror_percent: 100
    19. EOF

    这个路由规则发送 100% 流量到 v1。最后一段表示你将镜像流量到 httpbin:v2 服务。当流量被镜像时,请求将发送到镜像服务中,并在 headers 中的 Host/Authority 属性值上追加 -shadow。例如 cluster-1 变为 cluster-1-shadow

    此外,重点注意这些被镜像的流量是『即发即弃』的,就是说镜像请求的响应会被丢弃。

    您可以使用 mirror_percent 属性来设置镜像流量的百分比,而不是镜像全部请求。为了兼容老版本,如果这个属性不存在,将镜像所有流量。

    • 发送流量:
    1. $ kubectl exec -it $SLEEP_POD -c sleep -- sh -c 'curl http://httpbin:8000/headers' | python -m json.tool

    现在就可以看到 v1v2 中都有了访问日志。v2 中的访问日志就是由镜像流量产生的,这些请求的实际目标是 v1。

    1. $ kubectl logs -f $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. $ export SLEEP_POD=$(kubectl get pod -l app=sleep -o jsonpath={.items..metadata.name})
    2. $ export V1_POD_IP=$(kubectl get pod -l app=httpbin -l version=v1 -o jsonpath={.items..status.podIP})
    3. $ export V2_POD_IP=$(kubectl get pod -l app=httpbin -l version=v2 -o jsonpath={.items..status.podIP})
    4. $ kubectl exec -it $SLEEP_POD -c istio-proxy -- sudo tcpdump -A -s 0 host $V1_POD_IP or host $V2_POD_IP
    5. tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
    6. listening on eth0, link-type EN10MB (Ethernet), capture size 262144 bytes
    7. 05:47:50.159513 IP sleep-7b9f8bfcd-2djx5.38836 > 10-233-75-11.httpbin.default.svc.cluster.local.80: Flags [P.], seq 4039989036:4039989832, ack 3139734980, win 254, options [nop,nop,TS val 77427918 ecr 76730809], length 796: HTTP: GET /headers HTTP/1.1
    8. E..P2.X.X.X.
    9. .K.
    10. .K....P..W,.$.......+.....
    11. ..t.....GET /headers HTTP/1.1
    12. host: httpbin:8000
    13. user-agent: curl/7.35.0
    14. accept: */*
    15. x-forwarded-proto: http
    16. x-request-id: 571c0fd6-98d4-4c93-af79-6a2fe2945847
    17. x-envoy-decorator-operation: httpbin.default.svc.cluster.local:8000/*
    18. x-b3-traceid: 82f3e0a76dcebca2
    19. x-b3-spanid: 82f3e0a76dcebca2
    20. x-b3-sampled: 0
    21. x-istio-attributes: Cj8KGGRlc3RpbmF0aW9uLnNlcnZpY2UuaG9zdBIjEiFodHRwYmluLmRlZmF1bHQuc3ZjLmNsdXN0ZXIubG9jYWwKPQoXZGVzdGluYXRpb24uc2VydmljZS51aWQSIhIgaXN0aW86Ly9kZWZhdWx0L3NlcnZpY2VzL2h0dHBiaW4KKgodZGVzdGluYXRpb24uc2VydmljZS5uYW1lc3BhY2USCRIHZGVmYXVsdAolChhkZXN0aW5hdGlvbi5zZXJ2aWNlLm5hbWUSCRIHaHR0cGJpbgo6Cgpzb3VyY2UudWlkEiwSKmt1YmVybmV0ZXM6Ly9zbGVlcC03YjlmOGJmY2QtMmRqeDUuZGVmYXVsdAo6ChNkZXN0aW5hdGlvbi5zZXJ2aWNlEiMSIWh0dHBiaW4uZGVmYXVsdC5zdmMuY2x1c3Rlci5sb2NhbA==
    22. content-length: 0
    23. 05:47:50.159609 IP sleep-7b9f8bfcd-2djx5.49560 > 10-233-71-7.httpbin.default.svc.cluster.local.80: Flags [P.], seq 296287713:296288571, ack 4029574162, win 254, options [nop,nop,TS val 77427918 ecr 76732809], length 858: HTTP: GET /headers HTTP/1.1
    24. E.....X.X...
    25. .G....P......l......e.....
    26. ..t.....GET /headers HTTP/1.1
    27. host: httpbin-shadow:8000
    28. user-agent: curl/7.35.0
    29. accept: */*
    30. x-request-id: 571c0fd6-98d4-4c93-af79-6a2fe2945847
    31. x-envoy-decorator-operation: httpbin.default.svc.cluster.local:8000/*
    32. x-b3-traceid: 82f3e0a76dcebca2
    33. x-b3-spanid: 82f3e0a76dcebca2
    34. x-b3-sampled: 0
    35. x-istio-attributes: Cj8KGGRlc3RpbmF0aW9uLnNlcnZpY2UuaG9zdBIjEiFodHRwYmluLmRlZmF1bHQuc3ZjLmNsdXN0ZXIubG9jYWwKPQoXZGVzdGluYXRpb24uc2VydmljZS51aWQSIhIgaXN0aW86Ly9kZWZhdWx0L3NlcnZpY2VzL2h0dHBiaW4KKgodZGVzdGluYXRpb24uc2VydmljZS5uYW1lc3BhY2USCRIHZGVmYXVsdAolChhkZXN0aW5hdGlvbi5zZXJ2aWNlLm5hbWUSCRIHaHR0cGJpbgo6Cgpzb3VyY2UudWlkEiwSKmt1YmVybmV0ZXM6Ly9zbGVlcC03YjlmOGJmY2QtMmRqeDUuZGVmYXVsdAo6ChNkZXN0aW5hdGlvbi5zZXJ2aWNlEiMSIWh0dHBiaW4uZGVmYXVsdC5zdmMuY2x1c3Rlci5sb2NhbA==
    36. x-envoy-internal: true
    37. x-forwarded-for: 10.233.75.12
    38. content-length: 0
    39. 05:47:50.166734 IP 10-233-75-11.httpbin.default.svc.cluster.local.80 > sleep-7b9f8bfcd-2djx5.38836: Flags [P.], seq 1:472, ack 796, win 276, options [nop,nop,TS val 77427925 ecr 77427918], length 471: HTTP: HTTP/1.1 200 OK
    40. E....3X.?...
    41. .K.
    42. .K..P...$....ZH...........
    43. ..t...t.HTTP/1.1 200 OK
    44. server: envoy
    45. date: Fri, 15 Feb 2019 05:47:50 GMT
    46. content-type: application/json
    47. content-length: 241
    48. access-control-allow-origin: *
    49. access-control-allow-credentials: true
    50. x-envoy-upstream-service-time: 3
    51. {
    52. "headers": {
    53. "Accept": "*/*",
    54. "Content-Length": "0",
    55. "Host": "httpbin:8000",
    56. "User-Agent": "curl/7.35.0",
    57. "X-B3-Sampled": "0",
    58. "X-B3-Spanid": "82f3e0a76dcebca2",
    59. "X-B3-Traceid": "82f3e0a76dcebca2"
    60. }
    61. }
    62. 05:47:50.166789 IP sleep-7b9f8bfcd-2djx5.38836 > 10-233-75-11.httpbin.default.svc.cluster.local.80: Flags [.], ack 472, win 262, options [nop,nop,TS val 77427925 ecr 77427925], length 0
    63. E..42.X.X.\.
    64. .K.
    65. .K....P..ZH.$.............
    66. ..t...t.
    67. 05:47:50.167234 IP 10-233-71-7.httpbin.default.svc.cluster.local.80 > sleep-7b9f8bfcd-2djx5.49560: Flags [P.], seq 1:512, ack 858, win 280, options [nop,nop,TS val 77429926 ecr 77427918], length 511: HTTP: HTTP/1.1 200 OK
    68. E..3..X.>...
    69. .G.
    70. .K..P....l....;...........
    71. ..|...t.HTTP/1.1 200 OK
    72. server: envoy
    73. date: Fri, 15 Feb 2019 05:47:49 GMT
    74. content-type: application/json
    75. content-length: 281
    76. access-control-allow-origin: *
    77. access-control-allow-credentials: true
    78. x-envoy-upstream-service-time: 3
    79. {
    80. "headers": {
    81. "Accept": "*/*",
    82. "Content-Length": "0",
    83. "Host": "httpbin-shadow:8000",
    84. "User-Agent": "curl/7.35.0",
    85. "X-B3-Sampled": "0",
    86. "X-B3-Spanid": "82f3e0a76dcebca2",
    87. "X-B3-Traceid": "82f3e0a76dcebca2",
    88. "X-Envoy-Internal": "true"
    89. }
    90. }
    91. 05:47:50.167253 IP sleep-7b9f8bfcd-2djx5.49560 > 10-233-71-7.httpbin.default.svc.cluster.local.80: Flags [.], ack 512, win 262, options [nop,nop,TS val 77427926 ecr 77429926], length 0
    92. E..4..X.X...
    93. .K.
    94. .G....P...;..n............
    95. ..t...|.

    您可以看到流量​​的请求和响应内容。

    • 删除规则:
    1. $ kubectl delete virtualservice httpbin
    2. $ kubectl delete destinationrule httpbin
    • 关闭 httpbin 服务和客户端:
    1. $ kubectl delete deploy httpbin-v1 httpbin-v2 sleep

    Istio as a Proxy for External Services

    Configure Istio ingress gateway to act as a proxy for external services.

    Deploy environments that require isolation into separate meshes and enable inter-mesh communication by mesh federation.

    Secure Control of Egress Traffic in Istio, part 3

    Comparison of alternative solutions to control egress traffic including performance considerations.

    Use Istio Egress Traffic Control to prevent attacks involving egress traffic.

    Attacks involving egress traffic and requirements for egress traffic control.