Luga Lee
作者Luga Lee·2023-03-22 09:59
系统架构师·None

Istio 网络:深入了解流量和架构

字数 16630阅读 749评论 0赞 1

像 Istio 这样的服务网格项目为我们的架构引入了许多功能和优势,包括更安全地管理集群微服务之间的流量、服务发现、请求路由以及服务之间的可靠通信。

尽管 Istio 是中立 平台 ,但其已成为与 Kubernetes 一起使用的更流行的服务网格之一。尽管如此受欢迎,但对于服务网格新手来说,理解 Istio 的网络和核心机制可能很复杂,例如:

1、Envoy Sidecar 代理注入

2、Sidecar 如何拦截和路由流量

3、下发流量管理配置

4、流量规则如何在数据平面上生效

在通过分析 Istio 的架构和实现机制来解释这些机制的系列博客的第一篇文章中,我们将介绍 Istio 的网络基础知识、数据平面和控制平面、网络以及使用 Envoy 代理的 Sidecar 注入。使用演示环境,我们将能够看到 Istio 如何注入 Init 和 Sidecar 容器以及这些容器在 Pod 模板中的配置。

Istio 网络基础

Istio 的概述已在官方文档中进行了广泛的介绍,但在进一步进行之前,我们将先重点介绍关键组件。

Istio 由两个主要部分组成:数据平面和控制平面。

数据平面:数据平面或数据层由一组代理服务组成,这些代理服务表示为每 个 Kubernetes Pod 中的边车容器,使用扩展的 Envoy 代理服务器。这些边车调解和控制微服务之间的所有网络通信,同时还收集和报告有用的遥测数据。

控制平面:控制平面或控制层由一个名为 istiod 的二进制文件组成,负责将高级路由规则和流量控制行为转换为 Envoy 特定的配置,然后在运行时将它们传播到 Sidecar。此外,控制平面提供安全措施,通过内置身份和凭证管理实现强大的服务到服务和最终用户身份验证,同时根据服务身份实施安全策略。

Istio 环境演示

在继续之前,让我们创建一个本地沙箱环境。 这将确保我们在 Kubernetes 中部署了 Istio 服务网格,并在网格中运行了一个示例应用程序。

需要的工具如下:

  • minikube
  • istioctl (Installed with curl -L https://istio.io/downloadIstio | ISTIO_VERSION=1.11.4 sh)

    部署 Istio 服务网格步骤:

    1、使用 hyperkit 驱动程序在本地创建 1.22.2 版本的 Kubernetes 集群。如果使用的是非 Mac OS X 机器,则需要安装 virtualbox。

[administrator@JavaLangOutOfMemory ~ ] % minikube start --memory=4096 --cpus=2 --disk-size='20gb' --kubernetes-version=1.22.2 --driver=hyperkit -p istio-demo

2、集群完全启动后,执行以下命令设置 Istio。

# Deploy Istio operator
[administrator@JavaLangOutOfMemory ~ ] % istioctl operator init  

# Inject operator configuration
[administrator@JavaLangOutOfMemory ~ ] % cat << EOF | kubectl apply -f -
apiVersion: install.istio.io/v1alpha1
kind: IstioOperator
metadata: 
     name: istio-control-plane  
     namespace: istio-system
spec:  
    profile: minimal  
    meshConfig:    
         accessLogFile: /dev/stdout    
         enableAutoMtls: true    
         defaultConfig:      
             proxyMetadata:       
                 # Enable basic DNS proxying       
                 ISTIO_META_DNS_CAPTURE: 'true'       
                 # Enable automatic address allocation        
                 ISTIO_META_DNS_AUTO_ALLOCATE: 'true'EOF

3、部署示例应用程序。

# Create apps namespace
[administrator@JavaLangOutOfMemory ~ ] % kubectl create ns apps
# Label apps namespace for sidecar auto injection
[administrator@JavaLangOutOfMemory ~ ] % kubectl label ns apps istio-injection=enabled  
# Deploy a unprivileged sleep application
[administrator@JavaLangOutOfMemory ~ ] % cat << EOF | kubectl apply -n apps -f -
apiVersion: v1
kind: ServiceAccount
metadata:  
     name: sleep
---
apiVersion: v1
kind: Service
metadata: 
     name: sleep 
     labels:    
        app: sleep    
        service: sleep
spec:  
    ports: 
    - name: http   
       port: 80  
    selector:   
        app: sleep
---
piVersion: apps/v1
kind: Deployment
metadata:  
     name: sleep
spec:  
     replicas: 1 
     selector:    
        matchLabels:      
              app: sleep  
    template:   
       metadata:     
            labels:        
               app: sleep    
        spec:     
            terminationGracePeriodSeconds: 0      
            serviceAccountName: sleep     
            containers:      
            - name: sleep        
              image: curlimages/curl        
              command: ["/bin/sleep", "3650d"]      
              imagePullPolicy: IfNotPresent        
              volumeMounts:        
              - name: secret-volume         
                mountPath: /etc/sleep/tls      
          volumes:     
          - name: secret-volume        
            secret:         
                secretName: sleep-secret         
                optional: true
EOF

4、验证 istio-init 和 istio-proxy 容器都准备就绪并运行。

[administrator@JavaLangOutOfMemory ~ ] % 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

其结果显示如下:

istio-init,ready=true,terminated=Completed
istio-proxy,ready=true,started=true
Istio Sidecar 容器和 Envoy 代理

Sidecar 注入是 Istio 中的关键功能之一,它简化了添加和运行附加容器作为 Pod 模板的一部分的过程。 作为此注入过程的一部分,还提供了两个额外的容器:

1、istio-init – 此容器在应用程序 Pod 中配置 iptables,以便 Envoy 代理(作为单独的容器运行)可以拦截入站和出站流量。在任何其他容器启动之前,Kubernetes 会将其作为 Init 容器运行以初始化 Pod 中的网络。请注意,允许 istio-init 操作内核空间中的 iptables 确实需要升级 Kubernetes 权限。一旦成功完成任务,此容器将自动终止。在此之前,Pod 不会准备就绪。请注意,为了在部署此容器时消除任何安全问题和操作挑战,Istio 引入了 CNI 插件,因此它可以直接与底层 Kubernetes CNI 集成,而无需操作 iptables。

2、istio-proxy – 打包为上游 Envoy 代理的扩展版本。有关支持的扩展列表,请参阅官方文档。

对 Sidecar 清单的深入检查

让我们看一下我们之前部署的应用程序 Pod 中这两个容器的 YAML 清单。

[administrator@JavaLangOutOfMemory ~ ] % kubectl get po -l app=sleep -n apps -o yaml

我们将查看 istio-init 和 istio-proxy 容器的摘录。

istio-init Container:
initContainers
:- name: istio-init 
    image: docker.io/istio/proxyv2:1.11.4 
    imagePullPolicy: IfNotPresent  
    args:
    - istio-iptables 
    - -p  
    - "15001"  
    - -z 
    - "15006" 
    - -u  
    - "1337"  
    - -m  
    - REDIRECT 
    - -i  
    - '*'  
    - -x 
    - ""  
    - -b 
    - '*'  
    - -d  
    - 15090,15021,15020 
    env:  
    - name: ISTIO_META_DNS_AUTO_ALLOCATE    
      value: "true"  
    - name: ISTIO_META_DNS_CAPTURE    
      value: "true" 
    resources:    
        limits:    
          cpu: "2"     
          memory: 1Gi    
        requests:      
          cpu: 100m      
          memory: 128Mi  
    securityContext:    
        allowPrivilegeEscalation: false    
        capabilities:      
            add:    
            - NET_ADMIN    
            - NET_RAW      
            drop:      
            - ALL    
         privileged: false   
         readOnlyRootFilesystem: false    
         runAsGroup: 0    
         runAsNonRoot: false    
         runAsUser: 0
istio-proxy Container:

 containers
 :- name: istio-proxy  
     image: docker.io/istio/proxyv2:1.11.4  
     imagePullPolicy: IfNotPresent  
     args:  
     - proxy  
     - sidecar
     - --domain  
     - $(POD_NAMESPACE).svc.cluster.local 
     - --proxyLogLevel=warning 
     - --proxyComponentLogLevel=misc:error 
     - --log_output_level=default:info  
     - --concurrency 
     - "2"  
     ports: 
     - name: http-envoy-prom   
       containerPort: 15090   
       protocol: TCP  
    readinessProbe:   
        httpGet:    
           path: /healthz/ready      
           port: 15021      
           scheme: HTTP    
        failureThreshold: 30  
        initialDelaySeconds: 1   
        periodSeconds: 2    
        successThreshold: 1   
        timeoutSeconds: 3  
    securityContext:   
        allowPrivilegeEscalation: false    
        capabilities:      
            drop:     
            - ALL    
        privileged: false    
        readOnlyRootFilesystem: true   
        runAsGroup: 1337    
        runAsNonRoot: true   
        runAsUser: 1337  
   env: 
   - name: PROXY_CONFIG   
     value: |    
         {"proxyMetadata":{"ISTIO_META_DNS_AUTO_ALLOCATE":"true","ISTIO_META_DNS_CAPTURE":"true"}} 
    - name: ISTIO_META_DNS_AUTO_ALLOCATE   
      value: "true"  
    - name: ISTIO_META_DNS_CAPTURE    
      value: "true" 
    ...

在这些附加参数中有一些有趣的关键点需要注意:

两个容器都由同一个镜像提供服务:docker.io/istio/proxyv2:1.11。这是什么意思,它是如何工作的?istio-iptables 和 proxy(在 args 下)命令被装载到镜像中的 Pilot-agent 二进制文件中。因此,如果我们在 istio-proxy 容器中运行 Pilot-agent 二进制文件,将看到以下内容:

 kubectl exec $(kubectl get po -l app=sleep -n apps -o jsonpath="{.items[0]. metadata.name}") -n apps -c istio-proxy --pilot-agent

 Istio Pilot agent runs in the sidecar or gateway container and bootstraps Envoy.

 Usage: 
      pilot-agent [command]  

 Available 
     Commands:  completion           generate the autocompletion script for the specified shell 
     help                 Help about any command  
     istio-clean-iptables Clean up iptables rules for 
     Istio Sidecar  istio-iptables       Set up iptables rules for Istio Sidecar  proxy                XDS proxy agent  
     request              Makes an HTTP request to the Envoy admin API 
     version              Prints out build version information  
     wait                 Waits until the Envoy proxy is ready

为了最小化攻击面,istio-init 容器中的 securityContext 节(它是 PodSecurityContext 对象的一部分)表示容器以 root 权限(runAsUser: 0)运行,但是除了 NET_ADMIN 和 NET_RAW 功能。这些功能为 istio-init Init 容器提供了运行时权限以重写应用程序 Pod 的 iptables。这在 Istio 文档中有更详细的说明。

 allowPrivilegeEscalation: false    
       capabilities:      
           add:     
           - NET_ADMIN      
           - NET_RAW      
           drop:     
           - ALL   
        privileged: false    
        readOnlyRootFilesystem: false   
        runAsGroup: 0   
        runAsNonRoot: false   
        runAsUser: 0

另一方面, istio-proxy 容器以 1337 用户的受限权限运行。由于这是保留的,因此应用程序工作负载的 UID(用户 ID)必须不同且不得与 1337 冲突。1337 UID 已被任意选择 Istio 团队绕过流量重定向到 istio-proxy 容器。我们还可以看到 1337 在初始化 iptables 时用作 istio-iptables 的参数。由于此容器与应用程序工作负载一起积极运行,因此 Istio 还确保如果它受到威胁,它只能对根文件系统进行只读访问。

 allowPrivilegeEscalation: false   
       capabilities:     
           drop:      
           - ALL    
        privileged: false    
        readOnlyRootFilesystem: true   
        runAsGroup: 1337    
        runAsNonRoot: true    
        runAsUser: 1337

istio-proxy 容器与如下所示的就绪探针一起运行。Kubernetes 中的 Kubelet 使用此就绪探针来确定 istio-proxy 是否已准备好接受流量。只有当 istio-proxy 容器和所有相应的应用程序容器都处于运行状态并且健康探测已成功执行时,Kubelet 才会识别出 Pod 处于就绪状态。如果服务器路径的 /healthz/ready 处理程序(在pilot-agent 源代码中定义)返回成功的返回码,Kubelet 将假定容器是活动的且健康的。failureThreshold 配置指定了在容器被标记为未就绪之前此就绪探测可以失败的连续次数。

 readinessProbe:    
        httpGet:      
           path: /healthz/ready      
           port: 15021      
           scheme: HTTP   
        initialDelaySeconds: 1   
        failureThreshold: 30    
        periodSeconds: 2    
        successThreshold: 1   
        timeoutSeconds: 3
Sidecar 注入分析

Istio 采用了两种不同的方式将 Sidecar 代理注入应用程序工作负载:手动和自动。这两种方法都遵循相同的注入原则,给定“一些”应用程序工作负载(这可以定义为更高级别的 Kubernetes 资源,如部署、Statefulset、DaemonSet 甚至 Pod),允许 Kubernetes 使用 Sidecar 注入 Sidecar 容器 模板和配置参数(istio-sidecar-injector configmap)。

Istio 中的手动边车注入

在这两种方法中,这是最容易理解的。手动注入是通过 istioctl 命令使用 kube-inject 参数完成的。我们可以使用以下任一格式进行注入:

[administrator@JavaLangOutOfMemory ~ ] % istioctl kube-inject -f application.yaml | kubectl apply -f -

[administrator@JavaLangOutOfMemory ~ ] % kubectl apply -f <(istioctl kube-inject -f application.yaml)

当 istioctl kube-inject 用于注入 Sidecar 时,默认情况下它将使用编写为 istio-sidecar-injector Kubernetes configmap 的集群内配置。提供了许多我们可以指定以自定义此行为的标志:

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

请注意,--injectConfigMapNam 是 istioctl kube-inject 中的一个隐藏标志,它允许我们覆盖集群内 Sidecar 注入配置。 或者,可以使用配置的本地副本和上面的标志来完成注入:

[administrator@JavaLangOutOfMemory ~ ] % kubectl -n istio-system get configmap istio-sidecar-injector -o=jsonpath='{.data.config}' > inject-config.yaml
[administrator@JavaLangOutOfMemory ~ ] % kubectl -n istio-system get configmap istio-sidecar-injector -o=jsonpath='{.data.values}' > inject-values.yaml
[administrator@JavaLangOutOfMemory ~ ] % kubectl -n istio-system get configmap istio -o=jsonpath='{.data.mesh}' > mesh-config.yaml
[administrator@JavaLangOutOfMemory ~ ] % istioctl kube-inject \\   
        -injectConfigFile inject-config.yaml \\    
        --meshConfigFile mesh-config.yaml \\    
        --valuesFile inject-values.yaml \\   
        --filename application.yaml \\    
        | kubectl apply -f -

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

Istio 中的自动边车注入

这被认为是在 Istio 中注入边车的事实上的方法。与手动方法相比,这涉及更少的配置步骤;但是,这取决于底层 Kubernetes 发行版是否启用了对准入控制器的支持。为此,Istio 利用了一个变异的 webhook 准入控制器。

以下是 Kubernetes 变异准入控制器在 Sidecar 注入中处理的过程:

1、首先,在 Istio 安装过程中注入的 istio-sidecar-injector mutating 配置(如下所示)向 istiod 控制器发送带有所有 Pod 信息的 webhook 请求。

2、接下来,控制器在运行时修改 Pod 规范,将 Init 和 Sidecar 容器代理引入实际的 Pod 规范。

3、然后,控制器将修改后的对象返回给准入 Webhook 进行对象验证。

4、最后经过验证,修改后的 Pod 规范与所有 Sidecar 容器一起部署。

完整配置请看 kubectl get mutatingwebhookconfiguration istio-sidecar-injector -o yaml。为简洁起见,以下摘录中仅给出了四种 webhook 配置中的两种:

apiVersion: admissionregistration.k8s.io/v1
kind: MutatingWebhookConfiguration
metadata: 
     name: istio-sidecar-injector
webhooks:
- admissionReviewVersions:  
   - v1beta1 
   - v1  
   clientConfig:    
      caBundle: cert   
      service:      
          name: istiod     
          namespace: istio-system      
          path: /inject      
          port: 443  
    failurePolicy: Fail 
    matchPolicy: Equivalent  
    name: namespace.sidecar-injector.istio.io 
    namespaceSelector:    
        matchExpressions:   
        - key: istio-injection      
          operator: In      
          values:     
          - enabled  
    objectSelector:   
       matchExpressions:   
       - key: sidecar.istio.io/inject      
         operator: NotIn      
         values:      
         - "false"  
    reinvocationPolicy: Never  
    rules: 
    - apiGroups:   
       - ""    
       apiVersions:   
       - v1    
       operations:    
       - CREATE   
       resources:   
       - pods    
       scope: '*'  
    sideEffects: None  
    timeoutSeconds: 10
  - admissionReviewVersions: 
     - v1beta1 
     - v1 
     clientConfig:    
        caBundle: cert   
        service:      
            name: istiod     
            namespace: istio-system      
            path: /inject      
            port: 443 
       failurePolicy: Fail  
       matchPolicy: Equivalent 
       name: namespace.sidecar-injector.istio.io  
       namespaceSelector:    
           matchExpressions:   
           - key: istio-injection      
             operator: In     
             values:     
             - enabled  
        objectSelector:    
            matchExpressions:   
            - key: sidecar.istio.io/inject      
              operator: NotIn      
              values:      
              - "false" 
        reinvocationPolicy: Never 
        rules: 
        - apiGroups:  
           - ""    
           apiVersions:    
           - v1    
           operations:   
           - CREATE  
           resources:    
           - pods    
           scope: '*' 
        sideEffects: None 
        timeoutSeconds: 10
      - admissionReviewVersions: 
        - v1beta1 
        - v1  
        clientConfig:    
          caBundle: cert    
          service:     
              name: istiod     
              namespace: istio-system      
              path: /inject     
              port: 443  
        failurePolicy: Fail  
        matchPolicy: Equivalent  
        name: object.sidecar-injector.istio.io 
        namespaceSelector:    
            matchExpressions:   
            - key: istio-injection      
              operator: DoesNotExist    
            - key: istio.io/rev     
              operator: DoesNotExist  
        objectSelector:   
           matchExpressions:    
           - key: sidecar.istio.io/inject     
             operator: In      
             values:     
           - "true"    
         - key: istio.io/rev     
           operator: DoesNotExist 
     reinvocationPolicy: Never  
     rules:  
     - apiGroups:   
        - ""    apiVersions:   
        - v1    
        operations:    
        - CREATE   
        resources:    
        - pods   
        scope: '*' 
     sideEffects: None  
     timeoutSeconds: 10

此配置告诉 Kubernetes 变异控制器在 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注入(Y or N)
istio-injection=enabled
sidecar.istio.io/inject="true"
istio-injection=enabledsidecar.istio.io/inject="true"
istio-injection=enabledsidecar.istio.io/inject="false"
istio-injection=disabledsidecar.istio.io/inject="true"
istio-injection=disabledsidecar.istio.io/inject="false"

当 Pod 清单被注入时,也可以直接改变 Pod 对象(如果命名空间还没有标签)。Pod 清单必须有一个标签 sidecar.istio.io/inject="true"。例如:

apiVersion: v1
kind: Pod
metadata:  
     name: sleep  
     namespace: apps 
     labels:   
        app: sleep    
        sidecar.istio.io/inject: "true"
...

到目前为止,我们已经了解了 Istio 的网络基础知识、数据平面和控制平面、网络和使用 Envoy 代理的 Sidecar 注入,以及 Istio 如何使用演示注入 Init 和 Sidecar 容器以及这些容器在 Pod 模板中的配置 环境。在下一篇博客中,我们将分析 iptables 是如何配置和管理的。

在 Slack 上的 Solo.io 上与我们联系,以了解有关 Istio 和我们产品的更多信息。我们还提供了许多关于 Istio 的网络研讨会和研讨会,因此请随时注册以了解更多信息。

如果觉得我的文章对您有用,请点赞。您的支持将鼓励我继续创作!

1

添加新评论0 条评论

Ctrl+Enter 发表

作者其他文章

相关文章

相关问题

相关资料

X社区推广