通过网关进行连接的多集群

这个示例展示了如何在多控制平面拓扑的多集群网格中配置和调用远程服务。为了演示跨集群访问,会在一个集群中使用 Sleep 服务调用另一个集群中的 httpbin 服务

开始之前

  • 根据使用网关连接多控制平面的介绍,建立两个 Istio 网格组成的集群环境。

  • kubectl—context 参数来访问两个不同的集群。用下面的命令列出配置文件中的 context(上下文):

  1. $ kubectl config get-contexts
  2. CURRENT NAME CLUSTER AUTHINFO NAMESPACE
  3. * cluster1 cluster1 user@foo.com default
  4. cluster2 cluster2 user@foo.com default
  • 将配置文件中的上下文名称导出为环境变量:
  1. $ export CTX_CLUSTER1=<cluster1 context name>
  2. $ export CTX_CLUSTER2=<cluster2 context name>

配置示例服务

  • cluster1 中部署 sleep 服务:

Zip

  1. $ kubectl create --context=$CTX_CLUSTER1 namespace foo
  2. $ kubectl label --context=$CTX_CLUSTER1 namespace foo istio-injection=enabled
  3. $ kubectl apply --context=$CTX_CLUSTER1 -n foo -f @samples/sleep/sleep.yaml@
  • cluster2 中部署 httpbin 服务:

Zip

  1. $ kubectl create --context=$CTX_CLUSTER2 namespace bar
  2. $ kubectl label --context=$CTX_CLUSTER2 namespace bar istio-injection=enabled
  3. $ kubectl apply --context=$CTX_CLUSTER2 -n bar -f @samples/httpbin/httpbin.yaml@
  • 导出 cluster2 的网关地址:
  1. $ export CLUSTER2_GW_ADDR=$(kubectl get --context=$CTX_CLUSTER2 svc --selector=app=istio-ingressgateway \
  2. -n istio-system -o jsonpath="{.items[0].status.loadBalancer.ingress[0].ip}")

这个命令使用了网关的公网 IP,如果条件允许,这里使用 DNS 名称也是可以的

如果 cluster2 正在一个不支持外部负载均衡的环境下运行,需要使用 nodePort 来完成对网关的访问。可以在控制 Ingress 流量一文中,找到获取网关地址和端口的说明。还需要把服务的入口端点用下面的步骤从 15443 更换为对应的 nodePort(也就是 kubectl —context=$CTX_CLUSTER2 get svc -n istio-system istio-ingressgateway -o=jsonpath='{.spec.ports[?(@.port==15443)].nodePort}')。

  • cluster1 中为 httpbin 服务创建 ServiceEntry

为了让 cluster1 中的 sleep 能够访问到 cluster2 中的 httpbin,需要创建一个 ServiceEntryServiceEntry 的主机名称应该是 <name>.<namespace>.global 的格式,其中的 namenamespace 代表的是远端服务的名称和命名空间。

为了让 DNS 为 *.global 域的服务进行解析。必须给这些服务提供 IP 地址。

.global DNS 域)中的每个服务必须在集群内具有唯一的 IP 地址。

如果这些全局服务具有真实的 VIP,可以直接使用;否则我们推荐使用 loopback 范围内的 127.0.0.0/8。这些 IP 在 Pod 之外是不可路由的。在这个例子中我们会使用 127.255.0.0/16,这样就不会和一些知名地址例如 127.0.0.1 重叠了。对这些 IP 的访问会被 Sidecar 截获,并路由到对应的远程服务之中。

  1. $ kubectl apply --context=$CTX_CLUSTER1 -n foo -f - <<EOF
  2. apiVersion: networking.istio.io/v1alpha3
  3. kind: ServiceEntry
  4. metadata:
  5. name: httpbin-bar
  6. spec:
  7. hosts:
  8. # 必须是 name.namespace.global 的形式
  9. - httpbin.bar.global
  10. # 把远程集群服务作为网格的一部分。网格中的所有集群都有同样的信任关系。
  11. location: MESH_INTERNAL
  12. ports:
  13. - name: http1
  14. number: 8000
  15. protocol: http
  16. resolution: DNS
  17. addresses:
  18. # 在一个集群内,httpbin.bar.global 应该对应唯一的远程服务 IP 地址。
  19. # 这个地址不需要是可路由的。到这个 IP 的流量会被 Sidecar 截获和路由。
  20. - 127.255.0.2
  21. endpoints:
  22. # 对于 sleep.bar 服务来说,这是一个 Cluster2 中的 Ingress gateway 的可路由地址。
  23. # 从这个 Sidecar 中发出的流量会被路由到这个地址。
  24. - address: ${CLUSTER2_GW_ADDR}
  25. ports:
  26. http1: 15443 # 不要修改端口值
  27. EOF

上面的配置会把 cluster1 中所有到 httpbin.bar.global所有端口的流量使用 mTLS 连接路由到 <IPofCluster2IngressGateway>:15443

端口 15443 的 Gateway 是一个定义了 SNI 感知的 Envoy,是开始之前一节中的多集群部署步骤中设置的。进入 15443 端口的流量在对应内部服务的 Pod 中做负载均衡(本例中就是 cluster 中的 httpbin.bar)。

不要为 15443 端口创建 Gateway 配置。

  • sleep 服务中检查对 httpbin 的访问:
  1. $ kubectl exec --context=$CTX_CLUSTER1 $(kubectl get --context=$CTX_CLUSTER1 -n foo pod -l app=sleep -o jsonpath={.items..metadata.name}) \
  2. -n foo -c sleep -- curl httpbin.bar.global:8000/ip

使用 Egress Gateway 向远程集群发送流量

如果要从 cluster1 中使用单独的 Egress Gateway 进行流量路由,而非直接通过 Sidecar 完成。可以用下面的 ServiceEntry 定义来替代前一节中的定义:

  1. $ kubectl apply --context=$CTX_CLUSTER1 -n foo -f - <<EOF
  2. apiVersion: networking.istio.io/v1alpha3
  3. kind: ServiceEntry
  4. metadata:
  5. name: httpbin-bar
  6. spec:
  7. hosts:
  8. # 必须是 name.namespace.global 的形式
  9. - httpbin.bar.global
  10. location: MESH_INTERNAL
  11. ports:
  12. - name: http1
  13. number: 8000
  14. protocol: http
  15. resolution: DNS
  16. addresses:
  17. - 127.255.0.2
  18. endpoints:
  19. - address: ${CLUSTER2_GW_ADDR}
  20. network: external
  21. ports:
  22. http1: 15443 # 不要修改端口值
  23. - address: istio-egressgateway.istio-system.svc.cluster.local
  24. ports:
  25. http1: 15443
  26. EOF

为远程服务提供分版本路由

如果远程服务具有多个版本,可以为 ServiceEntry 加入一个或多个标签,例如:

  1. $ kubectl apply --context=$CTX_CLUSTER1 -n foo -f - <<EOF
  2. apiVersion: networking.istio.io/v1alpha3
  3. kind: ServiceEntry
  4. metadata:
  5. name: httpbin-bar
  6. spec:
  7. hosts:
  8. # 必须是 name.namespace.global 这样的形式
  9. - httpbin.bar.global
  10. location: MESH_INTERNAL
  11. ports:
  12. - name: http1
  13. number: 8000
  14. protocol: http
  15. resolution: DNS
  16. addresses:
  17. # 每个服务对 httpbin.bar.global 的解析地址必须是唯一的。
  18. - 127.255.0.2
  19. endpoints:
  20. - address: ${CLUSTER2_GW_ADDR}
  21. labels:
  22. version: beta
  23. some: thing
  24. foo: bar
  25. ports:
  26. http1: 15443 # 不要修改这个端口号
  27. EOF

接下来可以根据配置请求路由任务中的说明来创建对应的 VirtualServiceDestinationRuleDestinationRule 使用标签选择器来定义 httpbin.bar.global 服务的子集。具体步骤和本地服务是一致的。

清理

执行下列命令,清理示例服务。

  • 清理 cluster1:

Zip

  1. $ kubectl delete --context=$CTX_CLUSTER1 -n foo -f @samples/httpbin/httpbin.yaml@
  2. $ kubectl delete --context=$CTX_CLUSTER1 -n foo serviceentry httpbin-bar
  3. $ kubectl delete --context=$CTX_CLUSTER1 ns foo
  • 清理 cluster2:

Zip

  1. $ kubectl delete --context=$CTX_CLUSTER2 -n bar -f @samples/httpbin/httpbin.yaml@
  2. $ kubectl delete --context=$CTX_CLUSTER2 ns bar