[关闭]
@levinzhang 2022-06-12T16:58:31.000000Z 字数 11606 阅读 537

Istio服务网格:深入学习网络流量和架构

摘要

本文首先介绍了Istio的基础知识,然后结合实际的样例阐释了Istio是如何将sidecar容器注入到Kubernetes集群中,并实现流量拦截的。


本文最初发表于Solo官方博客,经原作者Kasun Talwatta授权,由InfoQ中文站翻译分享。

Istio这样的服务网格项目会为我们的架构引入很多的特性和收益,包括更安全地管理集群中微服务之间的流量、服务发现、请求路由以及服务之前可靠的通信。

尽管Istio是平台中立的服务网格,但是它更受欢迎的使用场景是与Kubernetes协作。虽然它如此流行,但对于刚接触服务网格的人来说,理解Istio的网络和核心机制可能会很复杂和困难,例如:

在本系列的博客文章的第一篇中,我们将会分析Istio的架构和实现原理,从而解释这些机制是如何运行的,我们将会介绍Istio的网络基础知识、数据平面和控制平面、网络、以及使用Envoy代理的sidecar注入。借助一个演示环境,我们将会看到Istio如何注入init和sidecar容器,以及这些容器在pod模板中的配置。

Istio的网络基础

Istio概览已在官方文档中进行了详尽的介绍,但在继续后面的内容之前,我们着重再看一下它的几个核心组件。

Istio主要由两部分组成,分别是数据平面和控制平面。

样例环境中的Istio网络

在介绍下面的内容之前,我们创建一个本地的沙箱环境。这能确保我们会有一个部署在Kubernetes中的Istio服务网格以及运行在网格中的示例应用。

所需的工具包括:

部署Istio服务网格的步骤如下:

1.使用hyperkit驱动在本地创建一个1.22.2版本的Kubernetes集群,如果你使用非Mac OS X的机器的话,那么需要使用virtualbox来代替。

  1. minikube start --memory=4096 --cpus=2 --disk-size='20gb' --kubernetes-version=1.22.2 --driver=hyperkit -p istio-demo

2.集群启动之后,执行如下的命令来搭建Istio

  1. # Deploy Istio operator
  2. istioctl operator init
  3. # Inject operator configuration
  4. cat << EOF | kubectl apply -f -
  5. apiVersion: install.istio.io/v1alpha1
  6. kind: IstioOperator
  7. metadata:
  8. name: istio-control-plane
  9. namespace: istio-system
  10. spec:
  11. profile: minimal
  12. meshConfig:
  13. accessLogFile: /dev/stdout
  14. enableAutoMtls: true
  15. defaultConfig:
  16. proxyMetadata:
  17. # Enable basic DNS proxying
  18. ISTIO_META_DNS_CAPTURE: 'true'
  19. # Enable automatic address allocation
  20. ISTIO_META_DNS_AUTO_ALLOCATE: 'true'
  21. EOF

3.部署示例应用

  1. # Create apps namespace
  2. kubectl create ns apps
  3. # Label apps namespace for sidecar auto injection
  4. kubectl label ns apps istio-injection=enabled
  5. # Deploy a unprivileged sleep application
  6. cat << EOF | kubectl apply -n apps -f -
  7. apiVersion: v1
  8. kind: ServiceAccount
  9. metadata:
  10. name: sleep
  11. ---
  12. apiVersion: v1
  13. kind: Service
  14. metadata:
  15. name: sleep
  16. labels:
  17. app: sleep
  18. service: sleep
  19. spec:
  20. ports:
  21. - name: http
  22. port: 80
  23. selector:
  24. app: sleep
  25. ---
  26. apiVersion: apps/v1
  27. kind: Deployment
  28. metadata:
  29. name: sleep
  30. spec:
  31. replicas: 1
  32. selector:
  33. matchLabels:
  34. app: sleep
  35. template:
  36. metadata:
  37. labels:
  38. app: sleep
  39. spec:
  40. terminationGracePeriodSeconds: 0
  41. serviceAccountName: sleep
  42. containers:
  43. - name: sleep
  44. image: curlimages/curl
  45. command: ["/bin/sleep", "3650d"]
  46. imagePullPolicy: IfNotPresent
  47. volumeMounts:
  48. - name: secret-volume
  49. mountPath: /etc/sleep/tls
  50. volumes:
  51. - name: secret-volume
  52. secret:
  53. secretName: sleep-secret
  54. optional: true
  55. EOF

4.验证istio-init和istio-proxy容器已经就绪并处于运行状态

  1. kubectl get po -l app=sleep -n apps -o jsonpath='{range .items[*]}{range @.status.containerStatuses[*]}{.name},{"ready="}{.ready},{"started="}{.started}{"\n"}{end}{range @.status.initContainerStatuses[*]}{.name},{"ready="}{.ready},{"terminated="}{.state.terminated.reason}{end}' | sort

该命令将会输出:

  1. istio-init,ready=true,terminated=Completed
  2. istio-proxy,ready=true,started=true

Istio sidecar容器和Envoy代理

在Istio中,sidecar注入是关键功能之一,它简化了以pod模板的形式添加和运行额外容器的过程。在这个注入过程中,会提供两个额外的容器,分别是:

深入研究sidecar的清单

我们首先看一下在之前部署的应用pod中,这两个容器的YAML清单(manifest)。

kubectl get po -l app=sleep -n apps -o yaml

我们来看一下istio-init和istio-proxy容器的片段。

istio-init容器:

  1. initContainers:
  2. - name: istio-init
  3. image: docker.io/istio/proxyv2:1.11.4
  4. imagePullPolicy: IfNotPresent
  5. args:
  6. - istio-iptables
  7. - -p
  8. - "15001"
  9. - -z
  10. - "15006"
  11. - -u
  12. - "1337"
  13. - -m
  14. - REDIRECT
  15. - -i
  16. - '*'
  17. - -x
  18. - ""
  19. - -b
  20. - '*'
  21. - -d
  22. - 15090,15021,15020
  23. env:
  24. - name: ISTIO_META_DNS_AUTO_ALLOCATE
  25. value: "true"
  26. - name: ISTIO_META_DNS_CAPTURE
  27. value: "true"
  28. resources:
  29. limits:
  30. cpu: "2"
  31. memory: 1Gi
  32. requests:
  33. cpu: 100m
  34. memory: 128Mi
  35. securityContext:
  36. allowPrivilegeEscalation: false
  37. capabilities:
  38. add:
  39. - NET_ADMIN
  40. - NET_RAW
  41. drop:
  42. - ALL
  43. privileged: false
  44. readOnlyRootFilesystem: false
  45. runAsGroup: 0
  46. runAsNonRoot: false
  47. runAsUser: 0

istio-proxy容器:

  1. containers:
  2. - name: istio-proxy
  3. image: docker.io/istio/proxyv2:1.11.4
  4. imagePullPolicy: IfNotPresent
  5. args:
  6. - proxy
  7. - sidecar
  8. - --domain
  9. - $(POD_NAMESPACE).svc.cluster.local
  10. - --proxyLogLevel=warning
  11. - --proxyComponentLogLevel=misc:error
  12. - --log_output_level=default:info
  13. - --concurrency
  14. - "2"
  15. ports:
  16. - name: http-envoy-prom
  17. containerPort: 15090
  18. protocol: TCP
  19. readinessProbe:
  20. httpGet:
  21. path: /healthz/ready
  22. port: 15021
  23. scheme: HTTP
  24. failureThreshold: 30
  25. initialDelaySeconds: 1
  26. periodSeconds: 2
  27. successThreshold: 1
  28. timeoutSeconds: 3
  29. securityContext:
  30. allowPrivilegeEscalation: false
  31. capabilities:
  32. drop:
  33. - ALL
  34. privileged: false
  35. readOnlyRootFilesystem: true
  36. runAsGroup: 1337
  37. runAsNonRoot: true
  38. runAsUser: 1337
  39. env:
  40. - name: PROXY_CONFIG
  41. value: |
  42. {"proxyMetadata":{"ISTIO_META_DNS_AUTO_ALLOCATE":"true","ISTIO_META_DNS_CAPTURE":"true"}}
  43. - name: ISTIO_META_DNS_AUTO_ALLOCATE
  44. value: "true"
  45. - name: ISTIO_META_DNS_CAPTURE
  46. value: "true"
  47. ...

在这些片段中有一些有意思的事情:

  1. Istio Pilot agent runs in the sidecar or gateway container and bootstraps Envoy.
  2. Usage:
  3. pilot-agent [command]
  4. Available Commands:
  5. completion generate the autocompletion script for the specified shell
  6. help Help about any command
  7. istio-clean-iptables Clean up iptables rules for Istio Sidecar
  8. istio-iptables Set up iptables rules for Istio Sidecar
  9. proxy XDS proxy agent
  10. request Makes an HTTP request to the Envoy admin API
  11. version Prints out build version information
  12. wait Waits until the Envoy proxy is ready
  1. allowPrivilegeEscalation: false
  2. capabilities:
  3. add:
  4. - NET_ADMIN
  5. - NET_RAW
  6. drop:
  7. - ALL
  8. privileged: false
  9. readOnlyRootFilesystem: false
  10. runAsGroup: 0
  11. runAsNonRoot: false
  12. runAsUser: 0

另一方面,istio-proxy容器以1337用户在限制权限下运行。因为这是一个保留用户,所以应用工作负载的UID(User ID)必须要与之不同,不能与1337冲突。1337 UID是由Istio团队任意选择的,以便于将流量重定向到istio-proxy容器。我们可以看到在初始化iptables的时候,1337也作为了istio-iptables的参数。由于这个容器会与应用工作负载一起运行,Istio还确保它对根文件系统只有读的权限。

  1. allowPrivilegeEscalation: false
  2. capabilities:
  3. drop:
  4. - ALL
  5. privileged: false
  6. readOnlyRootFilesystem: true
  7. runAsGroup: 1337
  8. runAsNonRoot: true
  9. runAsUser: 1337
  1. readinessProbe:
  2. httpGet:
  3. path: /healthz/ready
  4. port: 15021
  5. scheme: HTTP
  6. initialDelaySeconds: 1
  7. failureThreshold: 30
  8. periodSeconds: 2
  9. successThreshold: 1
  10. timeoutSeconds: 3

sidecar注入分析

Istio采用了两种不同的方式将sidecar代理注入应用的工作负载中,分别是手动和自动方式。这两种方法都遵循相同的注入原则,那就是指定的“某些”应用工作负载(这能够以更高级的Kubernetes资源的形式来进行定义,如Deployment、Statefulset、DaemonSet,甚至可以作为Pod)允许Kubernetes使用sidecar注入模板和配置参数(istio-sidecar-injector configmap)注入sidecar容器。

Istio中的手动sidecar注入

在这两种方法中,手动方式更易于理解。手动注入是通过istioctl命令并借助kube-inject参数完成的。你可以使用下面的任何一种格式来注入:

  1. istioctl kube-inject -f application.yaml | kubectl apply -f -

或者

  1. kubectl apply -f <(istioctl kube-inject -f application.yaml)

当使用istioctl kube-inject来注入sidecar的时候,默认它会使用集群中名为istio-sidecar-injector Kubernetes configmap。它是以一组标记的形式提供的,我们可以声明它们以自定义这种行为:

  1. --injectConfigFile string Injection configuration filename. Cannot be used with --injectConfigMapName
  2. --meshConfigFile string Mesh configuration filename. Takes precedence over --meshConfigMapName if set
  3. --meshConfigMapName string ConfigMap name for Istio mesh configuration, key should be "mesh" (default "istio")
  4. --injectConfigMapNam string ConfigMap name for Istio sidecar injection, key should be "config" (default "istio-sidecar-injector")

注意,在istioctl kube-inject中,--injectConfigMapNam是一个隐藏标记,它允许我们重写集群中sidecar的注入配置。

另外,注入也可以通过配置的本地副本和上述标记来实现:

  1. kubectl -n istio-system get configmap istio-sidecar-injector -o=jsonpath='{.data.config}' > inject-config.yaml
  2. kubectl -n istio-system get configmap istio-sidecar-injector -o=jsonpath='{.data.values}' > inject-values.yaml
  3. kubectl -n istio-system get configmap istio -o=jsonpath='{.data.mesh}' > mesh-config.yaml
  4. istioctl kube-inject \
  5. --injectConfigFile inject-config.yaml \
  6. --meshConfigFile mesh-config.yaml \
  7. --valuesFile inject-values.yaml \
  8. --filename application.yaml \
  9. | kubectl apply -f -

必须要注意的是,在手动注入的时候,不要破坏sidecar,尤其是使用自定义配置的时候。

Istio中的自动sidecar注入

这种方式被认为是Istio中注入sidecar的标准方法。与手动方式相比,它涉及的配置步骤更少,但是它取决于底层的Kubernetes分发版本是否启用了对admission控制器的支持。Istio使用了一个mutating webhook admission控制器来实现这一点。

如下是Kubernetes mutating admission在sidecar注入时的处理过程:

关于完整的配置,请使用如下的命令kubectl get mutatingwebhookconfiguration istio-sidecar-injector -o yaml进行查阅。为了简洁起见,下面的片段中仅包含了四个webhook配置中的两个:

  1. apiVersion: admissionregistration.k8s.io/v1
  2. kind: MutatingWebhookConfiguration
  3. metadata:
  4. name: istio-sidecar-injector
  5. webhooks:
  6. - admissionReviewVersions:
  7. - v1beta1
  8. - v1
  9. clientConfig:
  10. caBundle: cert
  11. service:
  12. name: istiod
  13. namespace: istio-system
  14. path: /inject
  15. port: 443
  16. failurePolicy: Fail
  17. matchPolicy: Equivalent
  18. name: namespace.sidecar-injector.istio.io
  19. namespaceSelector:
  20. matchExpressions:
  21. - key: istio-injection
  22. operator: In
  23. values:
  24. - enabled
  25. objectSelector:
  26. matchExpressions:
  27. - key: sidecar.istio.io/inject
  28. operator: NotIn
  29. values:
  30. - "false"
  31. reinvocationPolicy: Never
  32. rules:
  33. - apiGroups:
  34. - ""
  35. apiVersions:
  36. - v1
  37. operations:
  38. - CREATE
  39. resources:
  40. - pods
  41. scope: '*'
  42. sideEffects: None
  43. timeoutSeconds: 10
  44. - admissionReviewVersions:
  45. - v1beta1
  46. - v1
  47. clientConfig:
  48. caBundle: cert
  49. service:
  50. name: istiod
  51. namespace: istio-system
  52. path: /inject
  53. port: 443
  54. failurePolicy: Fail
  55. matchPolicy: Equivalent
  56. name: namespace.sidecar-injector.istio.io
  57. namespaceSelector:
  58. matchExpressions:
  59. - key: istio-injection
  60. operator: In
  61. values:
  62. - enabled
  63. objectSelector:
  64. matchExpressions:
  65. - key: sidecar.istio.io/inject
  66. operator: NotIn
  67. values:
  68. - "false"
  69. reinvocationPolicy: Never
  70. rules:
  71. - apiGroups:
  72. - ""
  73. apiVersions:
  74. - v1
  75. operations:
  76. - CREATE
  77. resources:
  78. - pods
  79. scope: '*'
  80. sideEffects: None
  81. timeoutSeconds: 10
  82. - admissionReviewVersions:
  83. - v1beta1
  84. - v1
  85. clientConfig:
  86. caBundle: cert
  87. service:
  88. name: istiod
  89. namespace: istio-system
  90. path: /inject
  91. port: 443
  92. failurePolicy: Fail
  93. matchPolicy: Equivalent
  94. name: object.sidecar-injector.istio.io
  95. namespaceSelector:
  96. matchExpressions:
  97. - key: istio-injection
  98. operator: DoesNotExist
  99. - key: istio.io/rev
  100. operator: DoesNotExist
  101. objectSelector:
  102. matchExpressions:
  103. - key: sidecar.istio.io/inject
  104. operator: In
  105. values:
  106. - "true"
  107. - key: istio.io/rev
  108. operator: DoesNotExist
  109. reinvocationPolicy: Never
  110. rules:
  111. - apiGroups:
  112. - ""
  113. apiVersions:
  114. - v1
  115. operations:
  116. - CREATE
  117. resources:
  118. - pods
  119. scope: '*'
  120. sideEffects: None
  121. timeoutSeconds: 10

这个配置会告诉Kubernetes mutating控制器在HTTPS端口上安全地将请求发送到istiod服务的“/inject”端点。在调用mutating webhook之前,Kubernetes会检查发送请求的用户是否允许发起该请求。在Istio中,webhook是作为istiod二进制文件的一部分实现的

注入可以使用命名空间级别的标签(istio-injection=enabled),也可以使用对象级别的注解(sidecar.istio.io/inject="true")来触发。每个webhook配置在namespaceSelector和objectSelector中为这些触发器定义了匹配规则。当注入基于命名空间级别定义的标签触发时,在命名空间中创建的任何部署对象(Deployment、StatefulSet、DaemonSet)都将注入sidecar代理的变更。下面是对匹配规则的小结。

命名空间标签 对象注解 Sidecar是否注入
istio-injection=enabled
sidecar.istio.io/inject="true"
istio-injection=enabled sidecar.istio.io/inject="true"
istio-injection=enabled sidecar.istio.io/inject="false"
istio-injection=disabled sidecar.istio.io/inject="true"
istio-injection=disabled sidecar.istio.io/inject="false"

在注入Pod清单时,也可以直接变更pod对象(如果命名空间还没有标签的话)。Pod清单必须要有一个sidecar.istio.io/inject="true"标签。举例来说:

  1. apiVersion: v1
  2. kind: Pod
  3. metadata:
  4. name: sleep
  5. namespace: apps
  6. labels:
  7. app: sleep
  8. sidecar.istio.io/inject: "true"
  9. ...

到目前为止,我们已经了解了Istio的基础知识、数据平面和控制平面、网络,以及Envoy代理的sidecar注入,并且展示了Istio如何使用演示环境在pod模板中注入init和sidecar容器以及这些容器的配置。在后续的博客文章中,我们将分析如何配置和管理iptables。

添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注