Linux 之父出席、干货分享、圆桌讨论,精彩尽在 OpenCloudOS 社区开放日,报名戳 了解详情
写点什么

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

  • 2022 年 6 月 13 日
  • 本文字数:7997 字

    阅读完需:约 26 分钟

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

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


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


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


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

  • Envoy sidecar 代理的注入

  • sidecar 是如何拦截和路由流量的

  • 流量管理配置的发布

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



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

Istio 的网络基础

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



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

  • 数据平面:数据平面或者数据层是由代理服务的集合所组成的,它们会使用扩展的Envoy代理服务器,表现形式是每个 Kubernetes pod 中的 sidecar 容器。这些 sidecar 会协调和控制所有微服务之间的网络通信,同时还会收集和报告有用的遥测数据。

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

样例环境中的 Istio 网络

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


所需的工具包括:

  • minikube

  • istioctl(可以通过curl -L https://istio.io/downloadIstio | ISTIO_VERSION=1.11.4 sh安装)

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

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

minikube start --memory=4096 --cpus=2 --disk-size='20gb' --kubernetes-version=1.22.2 --driver=hyperkit -p istio-demo
复制代码

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

# Deploy Istio operatoristioctl operator init# Inject operator configurationcat << EOF | kubectl apply -f -apiVersion: install.istio.io/v1alpha1kind: IstioOperatormetadata:  name: istio-control-plane  namespace: istio-systemspec:  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 namespacekubectl create ns apps# Label apps namespace for sidecar auto injectionkubectl label ns apps istio-injection=enabled# Deploy a unprivileged sleep applicationcat << EOF | kubectl apply -n apps -f -apiVersion: v1kind: ServiceAccountmetadata:  name: sleep---apiVersion: v1kind: Servicemetadata:  name: sleep  labels:    app: sleep    service: sleepspec:  ports:  - name: http    port: 80  selector:    app: sleep---apiVersion: apps/v1kind: Deploymentmetadata:  name: sleepspec:  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: trueEOF
复制代码

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

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=Completedistio-proxy,ready=true,started=true
复制代码

Istio sidecar 容器和 Envoy 代理

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

  • istio-init:这个容器会配置应用 pod 中的 iptables,这样 Envoy 代理(以另外一个容器的形式运行)就能拦截入站和出站流量。在所有其他的容器启动之前,Kubernetes 会将其以Init容器的形式运行,以初始化 pod 中的网络。请注意,允许 istio-init 在内核空间操纵 iptables,需要升级 Kubernetes 的权限。这个容器成功完成任务之后就会自动终止。在此之前,pod 将不会进入就绪状态。请注意,为了消除部署该容器时产生安全问题和运维方面的麻烦,Istio引入了CNI插件,因此它会直接与底层的 Kubernetes CNI 集成,而不需要操作 iptables。

  • istio-proxy:它会打包成上游 Envoy 代理的扩展版本。请参阅官方文档以了解所支持的扩展列表。


深入研究 sidecar 的清单

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

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

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

istio-init 容器:

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 容器:

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 能力之外,其他的 Linux 能力都被禁用了。这些能力为 istio-init init 容器提供了运行时的权限,以重写应用 pod 的 iptables。更多细节,请参阅Istio的文档

allowPrivilegeEscalation: falsecapabilities:  add:  - NET_ADMIN  - NET_RAW  drop:  - ALLprivileged: falsereadOnlyRootFilesystem: falserunAsGroup: 0runAsNonRoot: falserunAsUser: 0
复制代码

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

allowPrivilegeEscalation: falsecapabilities:  drop:  - ALLprivileged: falsereadOnlyRootFilesystem: truerunAsGroup: 1337runAsNonRoot: truerunAsUser: 1337
复制代码


  • istio-proxy 容器运行时会使用如下所示的就绪性(readiness)探针。在 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 资源的形式来进行定义,如 Deployment、Statefulset、DaemonSet,甚至可以作为 Pod)允许 Kubernetes 使用 sidecar 注入模板和配置参数(istio-sidecar-injector configmap)注入 sidecar 容器。

Istio 中的手动 sidecar 注入

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

istioctl kube-inject -f application.yaml | kubectl apply -f -
复制代码

或者

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")
复制代码

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


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

kubectl -n istio-system get configmap istio-sidecar-injector -o=jsonpath='{.data.config}' > inject-config.yamlkubectl -n istio-system get configmap istio-sidecar-injector -o=jsonpath='{.data.values}' > inject-values.yamlkubectl -n istio-system get configmap istio -o=jsonpath='{.data.mesh}' > mesh-config.yamlistioctl kube-inject \    --injectConfigFile inject-config.yaml \    --meshConfigFile mesh-config.yaml \    --valuesFile inject-values.yaml \    --filename application.yaml \    | kubectl apply -f -
复制代码

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

Istio 中的自动 sidecar 注入

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


点击查看大图


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


  • 首先,istio-sidecar-injector mutating 配置会在 Istio 安装过程中注入进来,并且会发送一个包含了所有 pod 信息的 webhook 请求到 istiod 控制器。

  • 接下来,控制器会在运行时修改 pod 规范,将一个 init 和 sidecar 容器代理引入到实际的 pod 规范中。

  • 然后,控制器将修改后的对象返回给 admission webhook 进行对象校验。

  • 在检验完成后,修改后包含所有 sidecar 容器的 pod 规范会进行部署。


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

apiVersion: admissionregistration.k8s.io/v1kind: MutatingWebhookConfigurationmetadata:  name: istio-sidecar-injectorwebhooks:- 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 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 代理的变更。下面是对匹配规则的小结。


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

apiVersion: v1kind: Podmetadata:  name: sleep  namespace: apps  labels:    app: sleep    sidecar.istio.io/inject: "true"...
复制代码


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

2022 年 6 月 13 日 17:212816

评论

发布
暂无评论
发现更多内容

【线程】,Java自学宝典pdf

Java 程序员 后端

一口气面试6家大厂,已拿5家offer,大厂没有你想象中的难

Java 程序员 后端

一文看透Java高并发:Synchronized锁的性质、原理及其缺陷

Java 程序员 后端

【数据结构与算法 10】算法的时间复杂度和空间复杂度

Java 程序员 后端

【源码分析设计模式 7】Integer中的享元模式

Java 程序员 后端

【Spring Boot 8】Okhttp实现GitHub第三方登录

Java 程序员 后端

一年Java开发经验,阿里巴巴五面(已offer,java原理视频

Java 程序员 后端

【备战秋招冲击大厂】Java面试题系列(1),springboot入门程序

Java 程序员 后端

【关于封装的那些事】 缺失封装,2021年腾讯Java高级面试题及答案

Java 程序员 后端

【初学入门Demo注解版】SpringBoot ,java面试大全下载

Java 程序员 后端

【数据库实验】,java语言零基础自学

Java 程序员 后端

一份秀出新天际的SpringCloudAlibaba笔记,把微服务玩的出神入化

Java 程序员 后端

一场哔哩哔哩Java开发面试之旅,分享面试经历及复习资料

Java 程序员 后端

一文带你了解Java并发中的锁优化,让你的代码运行效率翻倍

Java 程序员 后端

一文彻底弄懂如何选择抽象类还是接口,linux基础入门知识

Java 程序员 后端

【大厂面经】我通过了某独角兽公司的魔鬼五面

Java 程序员 后端

【被面试官吊打】从系统角度考虑性能优化,kafkajvm调优

Java 程序员 后端

一招搞定 Spring Boot 可视化监控!,java进阶教程云盘

Java 程序员 后端

一文带你深扒ClassLoader内核,揭开它的神秘面纱

Java 程序员 后端

【备战秋招冲击大厂】Java面试题系列,你还没弄明白存储键值对

Java 程序员 后端

【计算机网络 1】计算机网络概述,Java高级工程师进阶学习—Java热修复原理

Java 程序员 后端

【Spring框架03】DI依赖注入,spring菜鸟教程pdf

Java 程序员 后端

【嵌入式实验】,面试官必问的技术问题之一

Java 程序员 后端

【并发编程】深入了解volatile,linux高级编程pdf

Java 程序员 后端

一夜之间火爆GitHub的好文!!阿里资深架构师整理分享

Java 程序员 后端

【Spring Boot 19】Spring Boot整合阿里云OSS实现云存储

Java 程序员 后端

【SpringMVC笔记】Ajax 入门,springboot源码解读与原理分析

Java 程序员 后端

【网络信息安全】身份认证,hadoop环境搭建教程

Java 程序员 后端

一文掌握大数据架构师需要具备的能力和格局,别再说你不会JVM性能监控和调优了

Java 程序员 后端

【白话设计模式】去哪儿网一面,java面试题刷题软件

Java 程序员 后端

【线程】(1),java高级特性编程及实战pdf百度云

Java 程序员 后端

GPU容器虚拟化:用户态和内核态的技术和实践详解

GPU容器虚拟化:用户态和内核态的技术和实践详解

Istio服务网格:深入学习网络流量和架构_架构_Kasun Talwatta_InfoQ精选文章