服务可见性

istio 中每个服务默认可以访问网格中的任何服务,因此 istio 下发给每个 sidecar 的配置中会包含全量的服务数据,sidecar 可能会出现负载过高、内存过大的问题,影响业务容器的正常运行。istio 中设置服务可见性,主要有两种方式:

  • sidecar 资源:设置负载代理的出入口配置,此 crd 可以设置全局默认配置、命名空间默认配置、针对某个服务负载的特定配置。
  • exportTo 字段:包含两个部分,一是 istio 启动时的 meshConfig.defaultXxxExportTo 配置,二是 ServiceEntry、DestinationRule、VirtualService 三个 crd 中的 exportTo 字段和 annotations 中的“networking.istio.io/exportTo”标签。

sidecar 资源

命名空间中的 Sidecar 配置将应用于同一命名空间中的一个或多个工作负载实例,使用 workloadSelector 字段进行选择。

  • 没有设置 workloadSelector,应用于同一名称空间中的所有工作负载实例,只能有一个,推荐使用 default 来命名此 Sidecar。
  • 设置了 workloadSelector,应用于同一名称空间中具有选择器匹配的工作负载实例。
  • 默认情况下,MeshConfig 根命名空间中的 Sidecar 配置将应用于所有没有 Sidecar 配置的命名空间。这个全局默认 Sidecar 配置不应该有任何 workloadSelector。

全局默认配置

在根命名空间 istio-config 中的 Sidecar 配置,配置了所有命名空间下的负载只能访问当前命名空间和 istio-system 命名空间的所有服务。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
apiVersion: networking.istio.io/v1beta1
kind: Sidecar
metadata:
  name: default
  namespace: istio-config
spec:
  egress:
  - hosts:
    - "./*"
    - "istio-system/*"

命名空间下的默认配置

在 prod-us1 命名空间下的默认配置,重写了上面的全局默认配置,配置了只能访问 prod-us1、prod-apis、istio-system 空间下的所有服务。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
apiVersion: networking.istio.io/v1beta1
kind: Sidecar
metadata:
  name: default
  namespace: prod-us1
spec:
  egress:
  - hosts:
    - "prod-us1/*"
    - "prod-apis/*"
    - "istio-system/*"

某个服务的负载配置

下面针对 ratings 的所有负载,设置了入向和出向的访问配置:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
apiVersion: networking.istio.io/v1beta1
kind: Sidecar
metadata:
  name: ratings
  namespace: prod-us1
spec:
  workloadSelector:
    labels:
      app: ratings
  ingress:
  - port:
      number: 9080
      protocol: HTTP
      name: somename
    defaultEndpoint: unix:///var/run/someuds.sock
  egress:
  - port:
      number: 9080
      protocol: HTTP
      name: egresshttp
    hosts:
    - "prod-us1/*"
  - hosts:
    - "istio-system/*"

exportTo 字段

istio 启动配置中,下面三个参数控制了其 CRD 的可见性,值可以为“.”、“*”、“~”:

  • defaultServiceExportTo:控制所有 ServiceEntry 和 service 的可见性,默认所有命令空间可见。
  • defaultVirtualServiceExportTo: 控制所有 VirtualService 的可见性,默认所有命令空间可见。
  • defaultDestinationRuleExportTo:控制所有 DestinationRule 的可见性,默认所有命令空间可见。

至于 crd 中 exportTo 字段和 annotations 的 exportTo 标签,效果和上面差不多,值可以为“.”、“*”:

  • ServiceEntry:控制当前 ServiceEntry 的可见性,默认所有命令空间可见。
  • DestinationRule:控制当前 DestinationRule 的可见性,默认所有命令空间可见。
  • VirtualService:控制当前 VirtualService 的可见性,默认所有命令空间可见。
  • networking.istio.io/exportTo:控制当前 k8s service 的可见性,默认所有命令空间可见。

部署示例

使用下面的默认配置安装 istio,设置了“ISTIO_META_DNS_CAPTURE”是因为需要测试 ServiceEntry:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
apiVersion: install.istio.io/v1alpha1
kind: IstioOperator
spec:
  hub: docker.io/istio
  meshConfig:
    accessLogFile: /dev/stdout
    defaultConfig:
      proxyMetadata:
        ISTIO_META_DNS_CAPTURE: "true"
  profile: demo
  tag: 1.16.1
  values:
    global:
      logging:
        level: default:debug
      configValidation: true
      istioNamespace: istio-system

在两个命名空间分布部署服务来进行测试,先在 foo 空间部署 helloworld 服务:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
apiVersion: v1
kind: Namespace
metadata:
  name: foo
  labels:
    istio-injection: enabled
---
apiVersion: v1
kind: Service
metadata:
  name: helloworld
  namespace: foo
  labels:
    app: helloworld
    service: helloworld
spec:
  ports:
  - port: 5000
    name: http
  selector:
    app: helloworld
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: helloworld-v1
  labels:
    app: helloworld
    version: v1
spec:
  replicas: 1
  selector:
    matchLabels:
      app: helloworld
      version: v1
  template:
    metadata:
      labels:
        app: helloworld
        version: v1
    spec:
      containers:
      - name: helloworld
        image: docker.io/istio/examples-helloworld-v1
        resources:
          requests:
            cpu: "100m"
        imagePullPolicy: IfNotPresent
        ports:
        - containerPort: 5000

再在 bar 空间部署 sleep 服务:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
apiVersion: v1
kind: Namespace
metadata:
  name: bar
  labels:
    istio-injection: enabled
---
apiVersion: v1
kind: Service
metadata:
  name: sleep
  namespace: bar
  labels:
    app: sleep
    service: sleep
spec:
  ports:
  - port: 80
    name: http
  selector:
    app: sleep
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: sleep
  namespace: bar
spec:
  replicas: 1
  selector:
    matchLabels:
      app: sleep
  template:
    metadata:
      labels:
        app: sleep
    spec:
      containers:
      - name: sleep
        image: curlimages/curl
        command: ["/bin/sleep", "infinity"]
        imagePullPolicy: IfNotPresent

场景一:不配置 sidecar 和 exportTo

部署 helloworld 和 sleep 服务后,查看 sleep 服务的 pod 的 sidecar 配置,可以看到除了当前命名空间 bar 的服务外,还包含了 istio-system、kube-system、foo 等其他命名空间的服务:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
root@dev:~# istioctl pc cluster sleep-5597f78777-5phkz.bar
SERVICE FQDN                                            PORT      SUBSET     DIRECTION     TYPE             DESTINATION RULE
helloworld.foo.svc.cluster.local                        5000      -          outbound      EDS
istio-egressgateway.istio-system.svc.cluster.local      80        -          outbound      EDS
kube-dns.kube-system.svc.cluster.local                  53        -          outbound      EDS
kubernetes.default.svc.cluster.local                    443       -          outbound      EDS
sleep.bar.svc.cluster.local                             80        -          outbound      EDS
...
root@dev:~# istioctl pc ep sleep-5597f78777-5phkz.bar
ENDPOINT                                                STATUS      OUTLIER CHECK     CLUSTER
10.42.0.3:53                                            HEALTHY     OK                outbound|53||kube-dns.kube-system.svc.cluster.local
10.42.0.41:8080                                         HEALTHY     OK                outbound|80||istio-ingressgateway.istio-system.svc.cluster.local
10.42.0.43:80                                           HEALTHY     OK                outbound|80||sleep.bar.svc.cluster.local
10.42.0.44:5000                                         HEALTHY     OK                outbound|5000||helloworld.foo.svc.cluster.local
192.168.60.113:6443                                     HEALTHY     OK                outbound|443||kubernetes.default.svc.cluster.local
...

场景二:配置 exportTo 字段,不配置 sidecar

在 istio 的启动配置中增加以下字段,并重新安装:

1
2
3
4
5
6
7
  meshConfig:
    defaultServiceExportTo:
      - "."
    defaultVirtualServiceExportTo:
      - "."
    defaultDestinationRuleExportTo:
      - "."

跟上面一样,部署 helloworld 和 sleep 服务后,查看 sleep 服务的 pod 的 sidecar 配置,可以看到只有当前命名空间下的服务了。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
root@dev:~# istioctl pc cluster sleep-5597f78777-ktwkm.bar
SERVICE FQDN                      PORT     SUBSET     DIRECTION     TYPE             DESTINATION RULE
                                  80       -          inbound       ORIGINAL_DST
BlackHoleCluster                  -        -          -             STATIC
InboundPassthroughClusterIpv4     -        -          -             ORIGINAL_DST
PassthroughCluster                -        -          -             ORIGINAL_DST
agent                             -        -          -             STATIC
prometheus_stats                  -        -          -             STATIC
sds-grpc                          -        -          -             STATIC
sleep.bar.svc.cluster.local       80       -          outbound      EDS
xds-grpc                          -        -          -             STATIC
zipkin                            -        -          -             STRICT_DNS
root@dev:~# istioctl pc ep sleep-5597f78777-ktwkm.bar
ENDPOINT                                                STATUS      OUTLIER CHECK     CLUSTER
10.42.0.39:80                                           HEALTHY     OK                outbound|80||sleep.bar.svc.cluster.local
127.0.0.1:15000                                         HEALTHY     OK                prometheus_stats
127.0.0.1:15020                                         HEALTHY     OK                agent
unix://./etc/istio/proxy/XDS                            HEALTHY     OK                xds-grpc
unix://./var/run/secrets/workload-spiffe-uds/socket     HEALTHY     OK                sds-grpc

需要注意的是:sidecar 配置不存在其他命名空间服务,不代表业务程序不能访问,比如在 sleep pods 中通过“curl “http://helloworld.foo:5000/hello"”,仍然能获取到结果,只是这个访问不是通过 sidecar 到 endpoints 访问的,而是通过 istio-proxy 的 iptables 规则到 endpoint 访问的,比如下面的测试:

1
2
3
4
// 使用 k8s 原生方式访问的,没有通过 sidecar
root@dev:~# kubectl exec -it -nbar sleep-5597f78777-ktwkm -- sh
/ $ curl "http://helloworld.foo:5000/hello"
Hello version: v1, instance: helloworld-v1-77489ccb5f-rgdl

如果要跨命名空间访问,则可以通过在具体的 crd 里面设置 exportTo 字段,比如设置如下的 ServiceEntry,来让所有命名空间可访问:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
apiVersion: networking.istio.io/v1beta1
kind: ServiceEntry
metadata:
  name: demo-helloworld
  namespace: foo
spec:
  addresses:
  - 240.240.0.20
  exportTo:
  - '*'
  hosts:
  - demo.helloworld
  location: MESH_INTERNAL
  ports:
  - name: http
    number: 5000
    protocol: HTTP
  resolution: STATIC
  workloadSelector:
    labels:
      app: helloworld

而对应 k8s 的原生服务,可以通过设置 annotation 来让所有命名空间可见:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
apiVersion: v1
kind: Service
metadata:
  name: helloworld
  namespace: foo
  annotations:
    networking.istio.io/exportTo: "*"
  labels:
    app: helloworld
    service: helloworld
spec:
  ports:
  - port: 5000
    name: http
  selector:
    app: helloworld

此时查看 sleep pod 的 cluster 配置,可以看到两种方式的服务都存在了:

1
2
3
4
root@dev:~# istioctl pc cluster sleep-5597f78777-ktwkm.bar --port 5000
SERVICE FQDN                         PORT     SUBSET     DIRECTION     TYPE     DESTINATION RULE
demo.helloworld                      5000     -          outbound      EDS
helloworld.foo.svc.cluster.local     5000     -          outbound      EDS

场景三:配置 sidecar,不配置 exportTo

先在 istio-system 空间下设置全局默认的 sidecar 配置:

1
2
3
4
5
6
7
8
9
apiVersion: networking.istio.io/v1beta1
kind: Sidecar
metadata:
  name: default
  namespace: istio-system
spec:
  egress:
  - hosts:
    - "./*"

跟上面一样,部署 helloworld 和 sleep 服务后,查看 sleep 服务的 pod 的 sidecar 配置,可以看到只有当前命名空间服务了。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
root@dev:~# istioctl pc cluster sleep-5597f78777-7vtj8.bar
SERVICE FQDN                                            PORT      SUBSET     DIRECTION     TYPE             DESTINATION RULE
                                                        80        -          inbound       ORIGINAL_DST
BlackHoleCluster                                        -         -          -             STATIC
InboundPassthroughClusterIpv4                           -         -          -             ORIGINAL_DST
PassthroughCluster                                      -         -          -             ORIGINAL_DST
agent                                                   -         -          -             STATIC
prometheus_stats                                        -         -          -             STATIC
sds-grpc                                                -         -          -             STATIC
sleep.bar.svc.cluster.local                             80        -          outbound      EDS
xds-grpc                                                -         -          -             STATIC
zipkin                                                  -         -          -             STRICT_DNS
root@dev:~# istioctl pc ep sleep-5597f78777-7vtj8.bar
ENDPOINT                                                STATUS      OUTLIER CHECK     CLUSTER
10.42.0.49:80                                           HEALTHY     OK                outbound|80||sleep.bar.svc.cluster.local
127.0.0.1:15000                                         HEALTHY     OK                prometheus_stats
127.0.0.1:15020                                         HEALTHY     OK                agent
unix://./etc/istio/proxy/XDS                            HEALTHY     OK                xds-grpc
unix://./var/run/secrets/workload-spiffe-uds/socket     HEALTHY     OK                sds-grpc

继续测试 VirtualService 效果是否一样,部署如下配置使请求延迟 2s 返回:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
# kubectl apply -f helloworld_delay.yaml
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
  name: helloworld
  namespace: foo
spec:
  hosts:
  - helloworld
  http:
  - fault:
      delay:
        percent: 100
        fixedDelay: 2s
    route:
    - destination:
        host: helloworld

查看当前空间的 helloworld 代理的 route 配置,设置了 fixedDelay 等于 2s,并且在 pods 中测试也是延迟 2s 返回的,而 bar 命名空间的 sleep 并没有看到对应的 route 配置。

1
2
3
4
root@dev:~# kubectl exec -it -nfoo helloworld-v1-7dddc45478-lpmjq -- bash
root@helloworld-v1-7dddc45478-lpmjq:/opt/microservices# curl -w "time:%{time_total}" "http://helloworld:5000/hello"
Hello version: v1, instance: helloworld-v1-7dddc45478-lpmjq
time:2.120

继续测试 DestinationRule 效果是否一样,部署如下配置设置 subset,查看当前空间的 helloworld 代理的 cluster 配置,有设置了 v1 subset,而 bar 命名空间的 sleep 并没有看到对应的 cluster 配置。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
apiVersion: networking.istio.io/v1alpha3
kind: DestinationRule
metadata:
  name: helloworld
  namespace: foo
spec:
  host: helloworld
  subsets:
  - name: v1
    labels:
      version: v1

继续测试 ServiceEntry 效果是否一样,部署如下配置,在当前空间 pods 测试访问此 host,可以成功返回,并且在 pod 的 envoy 中也能看到对应的 cluster 和 endpoints 配置。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
apiVersion: networking.istio.io/v1alpha3
kind: ServiceEntry
metadata:
  name: demo-helloworld
  namespace: foo
spec:
  addresses:
  - 240.240.0.20
  hosts:
  - demo.helloworld
  ports:
  - number: 5000
    name: http
    protocol: HTTP
  resolution: STATIC
  location: MESH_INTERNAL
  workloadSelector:
    labels:
      app: helloworld

在 bar 命名空间的 sleep pods 的代理配置中,没有看到“demo.helloworld”配置,因此只设置 ServiceEntry 无法实现跨命名空间访问。因为在全局的 sidecar crd 中,只设置了当前命名空间和 istio-system 空间,那考虑在 bar 命名空间新增一个 sidecar crd 配置,增加 ServiceEntry 的 host 配置,来覆盖全局配置:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
apiVersion: networking.istio.io/v1beta1
kind: Sidecar
metadata:
  name: default
  namespace: bar
spec:
  egress:
  - hosts:
    - "./*"
    - "foo/demo.helloworld"

此时查看代理的配置,有“demo.helloworld”的 cluster 和 endpoints 配置,并且在 pods 中访问也是正常的。ServiceEntry 有一项配置“exportTo”可以指明导出到哪些命名空间,默认是所有命名空间,因此在 bar 空间设置的 Sidecar 配置能够生效。如果某个 ServiceEntry 只想在当前命名空间生效,可以增加“exportTo:['.']”配置,这样在 sleep 的代理配置中就无法看到此 cluster 了。

命名空间隔离

在 kubernetes 集群规模较大的情况下,或者存在多租户的情况下,使用 istio 网格可能会出现一些问题,比如:

  • 性能问题:sidecar 会收到全量服务的数据,导致 cpu 过高和内存过大
  • 隔离问题:多个租户的服务需要隔离,不能互相访问,不能互相影响

参考这篇文章 多租户场景下 Istio 部署方案探索,讨论了多租户场景下的一些解决方案,总结在此:

  • 每一个用户提供一套 istio 服务网格:istio 官方网站的文章 Istio 的软性多租户支持 给出了方案,为每个产品提供一套 istio 网格,每套 istio 的控制面可以安装到指定的 namespace 中,数据面可以设置为产品部署的 namespace。但存在资源消耗和跨命名空间访问复杂的问题,每套 istio 都是需要消耗一定的资源,namespace 之间互相访问,需要部署 Ingress Gateway 和 Egress Gateway 控制进入和流出命名空间的流量,这样增加了部署复杂度。
  • 单控制面多 Gateway 方案:部署一套 istio 控制面,管理多个产品数据面,每个产品以及 Istio 控制面的 namespace 部署一套 Ingress Gateway,相当于产品共用 Istio 控制面,但不共用 Ingress Gateway,一定程度上减少产品的耦合。
  • 腾讯云方案:参考文章 腾讯云中间件团队在 Service Mesh 中的实践与探索,腾讯云实现了单控制面多集群网格的多租户方案。在这种场景下,每个租户一个网格,集群管理员控制和监控整个 Istio 控制面以及所有网格,租户管理员只能控制特定的网格,这种场景与云环境下的多租户概念比较稳合。

在官方文档 Istio 部署模型 中,也提到了多租户,给出了几种部署方案,总结如下:

  • 命名空间租户:同一个集群中使用不同的命名空间隔离用户,默认情况下,多个命名空间的服务可以相互通信,但可以通过选择将哪些服务暴露给其他命名空间来提高隔离度。
  • 集群租户:支持使用集群作为租户单位,给每个租户一个专门的集群或一组集群来部署其工作负载。
  • 网格租户:在具有网格联邦的多网格部署中,每个网格可以作为隔离的单位。

从以上内容总结来说,istio 的命名空间隔离或者多租户,常见的形式有:

  • 单集群单控制面多命名空间:通过 sidecar 或者 exportTo 字段隔离服务可见性。
  • 单集群多控制面多命名空间:istio 跟命名空间为一对一或者一对多关系。
  • 多集群多控制面:istio 跟集群为一对一或者一对多关系。

单集群单控制面多命名空间

这篇文章 多租户场景下 Istio 部署方案探索 中提到的单控制面多 Gateway 方案,经过实际测试,只能做到集群入口的隔离,不能做到命名空间服务的隔离,因为这种方案本质上也是单集群单控制面多命名空间,必须通过 sidecar 或者 exportTo 字段才能实现。

如何实现单集群单控制面多命名空间,可参考前面的服务可见性介绍,里面的场景二和场景三都是可行的方案。

单集群多控制面多命名空间

参考官方文档 单个集群中安装多个控制面,是通过“meshConfig.discoverySelectors”和“revision”来实现的,“discoverySelectors”用于 istio 在计算 sidecar 配置时需要考虑的命名空间集合,“istio.io/rev”用于创建命名空间时指定使用的 istio 版本。首先在 usergroup-1 命名空间安装 istio,这里需要验证跨集群,与示例不同的是使用的 demo 配置:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
kubectl create ns usergroup-1
kubectl label ns usergroup-1 usergroup=usergroup-1
istioctl install -y -f - <<EOF
apiVersion: install.istio.io/v1alpha1
kind: IstioOperator
metadata:
  namespace: usergroup-1
spec:
  profile: demo
  revision: usergroup-1
  meshConfig:
    discoverySelectors:
      - matchLabels:
          usergroup: usergroup-1
  values:
    global:
      istioNamespace: usergroup-1
    pilot:
      env:
        ENABLE_ENHANCED_RESOURCE_SCOPING: true
EOF

接着在 usergroup-2 命名空间安装 istio:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
kubectl create ns usergroup-2
kubectl label ns usergroup-2 usergroup=usergroup-2
istioctl install -y -f - <<EOF
apiVersion: install.istio.io/v1alpha1
kind: IstioOperator
metadata:
  namespace: usergroup-2
spec:
  profile: demo
  revision: usergroup-2
  meshConfig:
    discoverySelectors:
      - matchLabels:
          usergroup: usergroup-2
  values:
    global:
      istioNamespace: usergroup-2
    pilot:
      env:
        ENABLE_ENHANCED_RESOURCE_SCOPING: true
EOF

查看安装结果,两个命名空间的 istiod、istio-ingressgateway、istio-egressgateway 都安装成功了:

1
2
3
4
5
6
7
8
root@dev:~/tenancy/test# kubectl get pods -A
NAMESPACE     NAME                                        READY   STATUS      RESTARTS   AGE
usergroup-1   istiod-usergroup-1-65f5c94ff-spvqv          1/1     Running     0          7m27s
usergroup-1   istio-egressgateway-5744b7bfc4-58pth        1/1     Running     0          7m23s
usergroup-1   istio-ingressgateway-85cc6bd7cb-lxjwn       1/1     Running     0          7m23s
usergroup-2   istiod-usergroup-2-557b7dffff-w65gz         1/1     Running     0          74s
usergroup-2   istio-ingressgateway-79b4445f54-rlskr       1/1     Running     0          70s
usergroup-2   istio-egressgateway-8584d76b68-dkw8x        1/1     Running     0          70s

创建两个应用进行测试:

1
2
3
4
5
6
7
8
kubectl create ns app-ns-1
kubectl create ns app-ns-2
kubectl label ns app-ns-1 usergroup=usergroup-1 istio.io/rev=usergroup-1
kubectl label ns app-ns-2 usergroup=usergroup-2 istio.io/rev=usergroup-2
kubectl -n app-ns-1 apply -f samples/sleep/sleep.yaml
kubectl -n app-ns-1 apply -f samples/httpbin/httpbin.yaml
kubectl -n app-ns-2 apply -f samples/sleep/sleep.yaml
kubectl -n app-ns-2 apply -f samples/httpbin/httpbin.yaml

安装完成查看 pods 的 envoy cluster 配置,只能看到本命名空间和对应的 istio 命名空间的服务:

1
2
3
4
5
6
7
8
9
root@dev:~# istioctl pc cluster sleep-bc9998558-twkpb.app-ns-1
SERVICE FQDN                                           PORT      SUBSET     DIRECTION     TYPE             DESTINATION RULE
httpbin.app-ns-1.svc.cluster.local                     8000      -          outbound      EDS
sleep.app-ns-1.svc.cluster.local                       80        -          outbound      EDS
istiod-usergroup-1.usergroup-1.svc.cluster.local       443       -          outbound      EDS
...

// 跨命名空间访问可以成功,没有经过代理,使用原生 k8s 访问的
kubectl -n app-ns-1 exec "$(kubectl -n app-ns-1 get pod -l app=sleep -o jsonpath={.items..metadata.name})" -c sleep -- curl -sIL http://httpbin.app-ns-2.svc.cluster.local:8000

加上负载策略,可以禁止跨命名空间访问,比如下面的配置执行后,跨命名空间访问将返回 503:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
kubectl apply -f - <<EOF
apiVersion: security.istio.io/v1beta1
kind: PeerAuthentication
metadata:
  name: "usergroup-1-peerauth"
  namespace: "usergroup-1"
spec:
  mtls:
    mode: STRICT
EOF

kubectl apply -f - <<EOF
apiVersion: security.istio.io/v1beta1
kind: PeerAuthentication
metadata:
  name: "usergroup-2-peerauth"
  namespace: "usergroup-2"
spec:
  mtls:
    mode: STRICT
EOF

可以在两个命名空间分别部署 gateway,隔离两个命名空间的对外入口,比如在 app-ns-1 空间部署 Gateway 和 VirtualService:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
kubectl apply -f - <<EOF
apiVersion: networking.istio.io/v1alpha3
kind: Gateway
metadata:
  name: httpbin-gateway
  namespace: app-ns-1
spec:
  selector:
    istio: ingressgateway # use Istio default gateway implementation
  servers:
  - port:
      number: 80
      name: http
      protocol: HTTP
    hosts:
    - "httpbin.example.com"
EOF
kubectl apply -f - <<EOF
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
  name: httpbin
  namespace: app-ns-1
spec:
  hosts:
  - "httpbin.example.com"
  gateways:
  - httpbin-gateway
  http:
  - match:
    - uri:
        prefix: /status
    - uri:
        prefix: /delay
    route:
    - destination:
        port:
          number: 8000
        host: httpbin
EOF

在集群外部通过 nodeport 访问,可以正常返回:

1
2
3
4
export INGRESS_HOST=$(kubectl get po -l istio=ingressgateway -n usergroup-1 -o jsonpath='{.items[0].status.hostIP}')
export INGRESS_PORT=$(kubectl -n usergroup-1 get service "${INGRESS_NAME}" -o jsonpath='{.spec.ports[?(@.name=="http2")].nodePort}')
// 示例:curl -s -I -HHost:httpbin.example.com "http://172.19.52.61:32115/status/200"
curl -s -I -HHost:httpbin.example.com "http://$INGRESS_HOST:$INGRESS_PORT/status/200"

如果需要在 app-ns-2 空间进行访问 app-ns-1 空间的服务,也可通过访问 ingress gateway 的 service 实现:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
root@dev:~/tenancy/test# kubectl exec -it -napp-ns-2 sleep-bc9998558-9x7fn -- sh
/ $ curl -s -I -HHost:httpbin.example.com "http://istio-ingressgateway.usergroup-1:80/status/200"
HTTP/1.1 200 OK
server: envoy
date: Fri, 26 May 2023 07:34:29 GMT
content-type: text/html; charset=utf-8
access-control-allow-origin: *
access-control-allow-credentials: true
content-length: 0
x-envoy-upstream-service-time: 19

多集群多控制面

这种方式比较简单,因为集群本身有隔离性,在每个集群独立部署 istio 即可,集群之间通过 ingress gateway 进行访问,这里不再举例说明。

总结

对三种方式命名空间隔离或者多租户方式,可以总结如下:

方案 说明 实现方式 跨命名空间/跨集群 优缺点
单集群单控制面多命名空间 istio 跟命名空间为一对多关系 sidecar 或者 exportTo 字段 可基于 sidecar 或者 exportTo,也可用 k8s service 方式 实现了代理配置的空间隔离,跨命名空间的调用隔离较弱,需配合其他策略,适合私有云场景
单集群多控制面多命名空间 istio 跟命名空间为一对一或一对多关系 meshConfig.discoverySelectors 和 revision 可基于 ingress gateway,也可用 k8s service 方式 实现了代理配置的空间隔离,跨命名空间的调用隔离较弱,需配合其他策略,适合私有云场景
多集群多控制面 istio 跟集群为一对一或者一对多关系 k8s 集群隔离性 ingress gateway 隔离性强,适合公有云场景

参考