默认情况下,istiod 会 watch 集群中所有的 namespace,生成对应的配置,实时的通过 xDS 协议,推送给所有实例的 sidecar 容器。

业务实例会被分发到大量不相关的配置,根本用不到,不仅增加 istiod 分发配置的时效性,也增加了 sidecar 的资源消耗。

背景知识

xDS 协议 是数据平面的 API,我们目前主要接触到的是 CDS、LDS、EDS、RDS。

  • CDS: Cluster,集群发现服务,envoy 中的 Cluster 其实就是服务的概念,Cluster 是一组 Endpoint 的集合,也包含了请求要以何种 lb 的方式路由到不同的 Endpoint。
  • LDS: Listener, 监听器发现服务,比较核心的概念,每一个 service 的一个端口,都会有与之对应的 listener,每一个 listener 又可以配置一些 Filter Chain。比如 HTTP 七层的插件配置也会放在这里。
  • EDS: Endpoint,集群成员发现服务,在 K8s 集群中通常对应 Endpoints 的概念。
  • RDS: Route,路由发现服务,提供给 HTTP Filter 使用,是一些路由规则和重试之类的配置。匹配的请求会被发送到指定的 Cluster。
  • ADS 聚合发现服务,主要是解决上述其他类型配置的顺序更新问题。

即使没有创建 istio 的 VirtualService 之类的资源,K8s 原生的 Service 也会被 istio 转换为内部的资源,这样的好处是即使对端服务没有接入 istio,只要客户端接入了,也可以拥有部分的能力。

减少配置的方式

目前 Istio 提供了多种方式来解决这个问题,但是并不完美,因为不能提前知道一个服务会访问其他哪些服务,所以仍然需要推送很多并不会被用到的配置。

DiscoverySelectors

在 MeshConfig 中配置,MeshConfig 通常配置在 istio-system namespace 下名为 istio 的 configmap 中。

可以通过 DiscoverySelectors 让 istiod 只处理满足匹配条件的指定 namespace 下的资源,包括 Services,Pods,Endpoints。

Istiod 不会去 watch 和处理不匹配的 namespace 的资源。和后面的几种方式相比较,这种方式不仅不会生成 xDS 配置推送给 sidecar,也不会去 watch 相关资源的变更。

例如,kube-system 下会创建一个 kubelet 的 headless Service,istio 会给 headless Service 的所有 IP Port 创建一个对应的 Listener,导致 LDS 会多下发很多无用的配置。我们想要让 istio 不去管理 kube-system 下的资源。

discoverySelectors:
  - matchExpressions:
    - key: istio-discovery
      operator: NotIn
      values:
        - disabled

之后再给 kube-system 加上 istio-discovery: disabled 的 annotation。

这样,istiod 就不会去 watch kube-system 下的资源,也不会将这部分配置推送给 sidecar。

Sidecar

Istio 提供了一个 Sidecar 的 CRD 可以用于限制指定 namespace 下可访问到的资源。

apiVersion: networking.istio.io/v1alpha3
kind: Sidecar
metadata:
  name: default
  namespace: istio-system
spec:
  egress:
  - hosts:
    - "./*"
    - "istio-system/*"

Sidecar 创建在 istio-system namespace 下,则表示是全局的默认配置,对所有 namespace 生效。如果在其他业务 namespace 下,则只在对应的 namespace 下生效。

. 表示当前 namespace,* 表示匹配所有。./*,表示匹配当前 namespace 下的所有服务。

istio-system/* 表示,可以访问 istio-system 下的所有服务。

通过合理的配置 Sidecar,可以在 namespace 级别,控制不下发一些无效的配置。前提是对端的资源要对自身 namespace 可见,如果对端已经通过 exportTo 限制了其他 ns 的访问,则即使在 Sidecar 中声明也没有用。

缺点是必须持续维护服务在 namespace 之间的访问规则,对运维来说有心智负担。

Istio Resource 的 ExportTo

Istio 的原生 Resource 一般都会有一个 exportTo 的字段,用于标识。

像 VirtualService 和 DestinationRule 都有对应的字段。

exportTo 的值是一个 string 数组,可以是 .* 以及指定的 namespace。

例如如果 exportTo = ["."],则只会将当前配置分发给同 namespace 下的其他 sidecar。

K8s Resource 的 ExportTo

由于 K8s 的 Resource 不受 istio 控制,没有提供 exportTo 的语义,所以需要通过添加 annotation 的方式来使用。

目前支持在 K8s Service 上通过加 networking.istio.io/exportTo annotation 来声明当前 Service 的暴露范围。

目前的值应该仅支持 .*~。默认是 *,通过配置成 . ,可以避免将该 Service 的配置分发到其他 namespace 下的 sidecar。如果配置成 ~,则会完全被 istio 忽略掉。