控制平面副本集

请参照本指南安装具有副本集控制平面实例的 Istio 多集群部署,并在每个群集中使用 gateway 来提供跨集群连接服务。

在此配置中,每个集群都使用它自己的 Istio 控制平面来完成安装,并管理自己的 endpoint, 而不是使用共享的 Istio 控制平面来管理网格。 出于以下目的,所有群集都在共同的管理控制下,执行策略与安全行为

通过共享服务副本及命名空间,并在所有群集中使用公共的根证书,可以在群集中实现一个 Istio 服务网格。 跨集群通信基于各个集群的 Istio gateway。

使用 Istio Gateway 跨越多个基于 Kubernetes 集群的 Istio 网格并最终到达远端 pod

使用 Istio Gateway 跨越多个基于 Kubernetes 集群的 Istio 网格并最终到达远端 pod

前提条件

  • 两个以上 Kubernetes 集群,且版本为:1.15, 1.16, 1.17, 1.18。

  • 有权限在 每个 Kubernetes 集群上,部署 Istio 控制平面

  • 每个集群 istio-ingressgateway 服务的 IP 地址,必须允许其它集群访问,最好使用 4 层负载均衡(NLB)。 有些云服务商不支持负载均衡或者需要特别注明才能使用。所以,请查阅您的云服务商的文档,为负载均衡类型的服务对象启用 NLB。 在不支持负载均衡的平台上部署时,可能需要修改健康检查,使得负载均衡对象可以注册为 ingress gateway。

  • 一个 根 CA。跨集群的服务通信必须使用双向 TLS 连接。 为了在集群之间使用双向 TLS 通信,每个集群的 Citadel 都将由共享的根 CA 生成中间 CA 凭据。 为方便演示,您在安装 Istio 时,可以使用 samples/certs 目录下的一个根 CA 证书样本。

在每个集群中部署 Istio 控制平面

  1. 从组织的根 CA 为每个集群的 Citadel 生成中间 CA 证书。 共享的根 CA 支持跨集群的双向 TLS 通信。

    为方便演示,后面两个集群的演示都将使用 Istio 样本目录下的证书。 在实际部署中,一般会使用一个公共根 CA 为每个集群签发不同的 CA 证书。

  2. 想要在 每个集群 上部署相同的 Istio 控制平面,请运行下面的命令:

    请确保当前用户拥有集群的管理员(cluster-admin)权限。 如果没有权限,请授权给它。例如,在 GKE 平台下,可以使用以下命令授权:

    1. $ kubectl create clusterrolebinding cluster-admin-binding --clusterrole=cluster-admin --user="$(gcloud config get-value core/account)"
    • 使用类似于下面的命令,为生成的 CA 证书创建 Kubernetes secret。了解详情,请参见 CA 证书

      示例目录中的根证书和中间证书已被广泛分发和知道。 千万不要 在生成环境中使用这些证书,这样集群就容易受到安全漏洞的威胁和破坏。

      ZipZipZipZip

      1. $ kubectl create namespace istio-system
      2. $ kubectl create secret generic cacerts -n istio-system \
      3. --from-file=@samples/certs/ca-cert.pem@ \
      4. --from-file=@samples/certs/ca-key.pem@ \
      5. --from-file=@samples/certs/root-cert.pem@ \
      6. --from-file=@samples/certs/cert-chain.pem@
    • 安装 Istio:

      1. $ istioctl manifest apply \
      2. -f install/kubernetes/operator/examples/multicluster/values-istio-multicluster-gateways.yaml
  1. 想了解更多细节和自定义选项,请参考[使用 Istioctl 安装]($f1a08e6169faaa80.md)。

配置 DNS

应用一般需要通过他们的 DNS 解析服务名然后访问返回的 IP,为远端集群中的服务提供 DNS 解析,将允许已有的应用不做修改就可以正常运行。 Istio 本身不会为两个服务之间的请求使用 DNS。集群本地的服务共用一个通用的 DNS 后缀(例如,svc.cluster.local)。Kubernetes DNS 为这些服务提供了 DNS 解析。

要为远端集群的服务提供类似的配置,远端集群内的服务需要以 <name>.<namespace>.global 的格式命名。 Istio 还附带了一个名为 CoreDNS 的服务,它可以为这些服务提供 DNS 解析。 想要使用 CoreDNS,Kubernetes DNS 的 .global 必须配置为 stub a domain

一些云提供商的 Kubernetes 服务可能有不同的、特殊的 DNS domain stub 程序和功能。 请参考云提供商的文档,以确定如何为不同环境的 stub DNS domains。 这个 bash 的目的是为 53 端口上的 .global 存根域引用或代理 Istio 的 service namespace 中的 istiocoredns 服务。

在每个要调用远端集群中服务的集群中(通常是所有集群), 选择并创建下面这些 ConfigMaps 中的一个,或直接使用现有的做修改。

  1. $ kubectl apply -f - <<EOF
  2. apiVersion: v1
  3. kind: ConfigMap
  4. metadata:
  5. name: kube-dns
  6. namespace: kube-system
  7. data:
  8. stubDomains: |
  9. {"global": ["$(kubectl get svc -n istio-system istiocoredns -o jsonpath={.spec.clusterIP})"]}
  10. EOF
  1. $ kubectl apply -f - <<EOF
  2. apiVersion: v1
  3. kind: ConfigMap
  4. metadata:
  5. name: coredns
  6. namespace: kube-system
  7. data:
  8. Corefile: |
  9. .:53 {
  10. errors
  11. health
  12. kubernetes cluster.local in-addr.arpa ip6.arpa {
  13. pods insecure
  14. upstream
  15. fallthrough in-addr.arpa ip6.arpa
  16. }
  17. prometheus :9153
  18. proxy . /etc/resolv.conf
  19. cache 30
  20. loop
  21. reload
  22. loadbalance
  23. }
  24. global:53 {
  25. errors
  26. cache 30
  27. forward . $(kubectl get svc -n istio-system istiocoredns -o jsonpath={.spec.clusterIP}):53
  28. }
  29. EOF
  1. $ kubectl apply -f - <<EOF
  2. apiVersion: v1
  3. kind: ConfigMap
  4. metadata:
  5. name: coredns
  6. namespace: kube-system
  7. data:
  8. Corefile: |
  9. .:53 {
  10. errors
  11. health
  12. ready
  13. kubernetes cluster.local in-addr.arpa ip6.arpa {
  14. pods insecure
  15. upstream
  16. fallthrough in-addr.arpa ip6.arpa
  17. }
  18. prometheus :9153
  19. forward . /etc/resolv.conf
  20. cache 30
  21. loop
  22. reload
  23. loadbalance
  24. }
  25. global:53 {
  26. errors
  27. cache 30
  28. forward . $(kubectl get svc -n istio-system istiocoredns -o jsonpath={.spec.clusterIP}):53
  29. }
  30. EOF
  1. $ kubectl apply -f - <<EOF
  2. apiVersion: v1
  3. kind: ConfigMap
  4. metadata:
  5. name: coredns
  6. namespace: kube-system
  7. data:
  8. Corefile: |
  9. .:53 {
  10. errors
  11. health
  12. kubernetes cluster.local in-addr.arpa ip6.arpa {
  13. pods insecure
  14. upstream
  15. fallthrough in-addr.arpa ip6.arpa
  16. }
  17. prometheus :9153
  18. forward . /etc/resolv.conf
  19. cache 30
  20. loop
  21. reload
  22. loadbalance
  23. }
  24. global:53 {
  25. errors
  26. cache 30
  27. forward . $(kubectl get svc -n istio-system istiocoredns -o jsonpath={.spec.clusterIP})
  28. }
  29. EOF

应用服务的配置

一个集群中所有需要被其它远端集群访问的服务,都需要在远端集群中配置 ServiceEntry。 service entry 使用的 host 应该采用如下格式:<name>.<namespace>.global。 其中 name 和 namespace 分别对应服务名和命名空间。

为了演示跨集群访问,需要配置: 在第一个集群中运行的 sleep service 并调用 在第二个集群中运行的 httpbin service。 开始之前:

  • 选择两个 Istio 集群,分别称之为 cluster1cluster2

  • 你可以使用 kubectl 命令带上 --context 参数去访问集群 cluster1cluster2, 例如 kubectl get pods --context cluster1。 使用如下命令列出你的上下文:

    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=$(kubectl config view -o jsonpath='{.contexts[0].name}')
    2. $ export CTX_CLUSTER2=$(kubectl config view -o jsonpath='{.contexts[1].name}')
    3. $ echo CTX_CLUSTER1 = ${CTX_CLUSTER1}, CTX_CLUSTER2 = ${CTX_CLUSTER2}
    4. CTX_CLUSTER1 = cluster1, CTX_CLUSTER2 = cluster2

    如果你有超过两个集群的上下文并且你想要使用前两个以外的集群配置你的网格,你需要手动将环境变量设置为你需要的上下文名称。

示例服务的配置

  1. 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@
    4. $ export SLEEP_POD=$(kubectl get --context=$CTX_CLUSTER1 -n foo pod -l app=sleep -o jsonpath={.items..metadata.name})
  2. 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@
  3. 暴露 cluster2 的 gateway 地址:

    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}')

    该命令使用了 gateway 的公网 IP,如果您有域名的话,您也可以直接使用域名。

    如果 cluster2 运行在一个不支持对外负载均衡的环境下,您需要使用 nodePort 访问 gateway。 有关获取使用 IP 的说明,请参见教程:控制 Ingress 流量。 在后面的步骤中,您还需要将 service entry 的 endpoint 的端口从 15443 修改为其对应的 nodePort (例如,kubectl --context=$CTX_CLUSTER2 get svc -n istio-system istio-ingressgateway -o=jsonpath='{.spec.ports [?(@.port==15443)].nodePort}')。

  4. cluster1 中为 httpbin 服务创建一个 service entry。

    为了让 cluster1 中的 sleep 访问 cluster2 中的 httpbin,我们需要在 cluster1 中为 httpbin 服务创建一个 service entry。 service entry 的 host 命名应采用 <name>.<namespace>.global 的格式。 其中 name 和 namespace 分别与远端服务的 name 和 namespace 对应。

    为了让 DNS 解析 *.global 域下的服务,您需要给这些服务分配一个 IP 地址。

    每个(.global 域下的)服务都必须有一个在其所属集群内唯一的 IP 地址。

    如果 global service 需要使用虚拟 IP,您可以使用,但除此之外,我们建议使用范围在 240.0.0.0/4 的 E 类 IP 地址。 使用这类 IP 地址的应用的流量将被 sidecar 捕获,并路由至适当的远程服务。

    组播地址 (224.0.0.0 ~ 239.255.255.255) 不应该被使用,因为这些地址默认不会被路由。 环路地址 (127.0.0.0/8) 不应该被使用,因为 sidecar 可能会将其重定向至 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. # must be of form name.namespace.global
    9. - httpbin.bar.global
    10. # Treat remote cluster services as part of the service mesh
    11. # as all clusters in the service mesh share the same root of trust.
    12. location: MESH_INTERNAL
    13. ports:
    14. - name: http1
    15. number: 8000
    16. protocol: http
    17. resolution: DNS
    18. addresses:
    19. # the IP address to which httpbin.bar.global will resolve to
    20. # must be unique for each remote service, within a given cluster.
    21. # This address need not be routable. Traffic for this IP will be captured
    22. # by the sidecar and routed appropriately.
    23. - 240.0.0.2
    24. endpoints:
    25. # This is the routable address of the ingress gateway in cluster2 that
    26. # sits in front of sleep.foo service. Traffic from the sidecar will be
    27. # routed to this address.
    28. - address: ${CLUSTER2_GW_ADDR}
    29. ports:
    30. http1: 15443 # Do not change this port value
    31. EOF

    上面的配置会基于双向 TLS 连接,将 cluster1 中对 httpbin.bar.global任意端口 的访问,路由至 <IPofCluster2IngressGateway>:15443 endpoint。

    gateway 的 15443 端口是一个特殊的 SNI-aware Envoy,当您在集群中部署 Istio 控制平面时,它会自动安装。 进入 15443 端口的流量会为目标集群内适当的服务的 pods 提供负载均衡(在这个例子中是,cluster2 集群中的 httpbin.bar 服务)。

    不要手动创建一个使用 15443 端口的 Gateway

  5. 验证 sleep 是否可以访问 httpbin

    1. $ kubectl exec --context=$CTX_CLUSTER1 $SLEEP_POD -n foo -c sleep -- curl -I httpbin.bar.global:8000/headers

通过 egress gateway 发送远程流量

如果您想在 cluster1 中通过一个专用的 egress gateway 路由流量,而不是从 sidecars 直连。 使用下面的 service entry 替换前面一节对 httpbin.bar 使用的配置。

该配置中使用的 egress gateway 依然不能处理其它的、非 inter-cluster 的 egress 流量。

如果 $CLUSTER2_GW_ADDR 是 IP 地址,请使用 $CLUSTER2_GW_ADDR - IP address 选项。如果 $CLUSTER2_GW_ADDR 是域名,请使用 $CLUSTER2_GW_ADDR - hostname 选项。

  • 暴露 cluster1 egress gateway 地址:
  1. $ export CLUSTER1_EGW_ADDR=$(kubectl get --context=$CTX_CLUSTER1 svc --selector=app=istio-egressgateway \
  2. -n istio-system -o yaml -o jsonpath='{.items[0].spec.clusterIP}')
  • 使 httpbin-bar 服务的 entry 生效:
  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. # must be of form name.namespace.global
  9. - httpbin.bar.global
  10. location: MESH_INTERNAL
  11. ports:
  12. - name: http1
  13. number: 8000
  14. protocol: http
  15. resolution: STATIC
  16. addresses:
  17. - 240.0.0.2
  18. endpoints:
  19. - address: ${CLUSTER2_GW_ADDR}
  20. network: external
  21. ports:
  22. http1: 15443 # Do not change this port value
  23. - address: ${CLUSTER1_EGW_ADDR}
  24. ports:
  25. http1: 15443
  26. EOF

如果 ${CLUSTER2_GW_ADDR} 是域名,您也可以使用 resolution: DNS 实现 endpoint 解析。

  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. # must be of form 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. - 240.0.0.2
  18. endpoints:
  19. - address: ${CLUSTER2_GW_ADDR}
  20. network: external
  21. ports:
  22. http1: 15443 # Do not change this port value
  23. - address: istio-egressgateway.istio-system.svc.cluster.local
  24. ports:
  25. http1: 15443
  26. EOF

示例的清理

运行下面的命令清理示例中的服务。

  • 清理 cluster1

    Zip

    1. $ kubectl delete --context=$CTX_CLUSTER1 -n foo -f @samples/sleep/sleep.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

Version-aware 路由到远端服务

如果远端服务有多个版本,您可以为 service entry endpoint 添加标签。比如:

  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. # must be of form 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. # the IP address to which httpbin.bar.global will resolve to
  18. # must be unique for each service.
  19. - 240.0.0.2
  20. endpoints:
  21. - address: ${CLUSTER2_GW_ADDR}
  22. labels:
  23. cluster: cluster2
  24. ports:
  25. http1: 15443 # Do not change this port value
  26. EOF

然后您就可以使用适当的 gateway 标签选择器,创建虚拟服务和目标规则去定义 httpbin.bar.global 的子集。 这些指令与路由到本地服务使用的指令相同。 完整的例子,请参考 multicluster version routing

卸载

若要卸载 Istio,请在 每个集群 上执行下面的命令:

  1. $ kubectl delete -f $HOME/istio.yaml
  2. $ kubectl delete ns istio-system

总结

使用 Istio gateway、公共的根 CA 和 service entry,您可以配置一个跨多个 Kubernetes 集群的单 Istio 服务网格。 经过这种方式配置后,应用无需任何修改,即可将流量路由到远端的集群内。 尽管此方法需要手动配置一些访问远端服务的选项,但 service entry 的创建过程可以自动化。

相关内容

使用 Admiral 管理 Istio 多集群的配置和服务发现

为 Istio deployment(cluster)提供自动化 Istio 配置,并让其像单个网格一样工作。

用于隔离和边界保护的多网格部署

将需要隔离的环境部署到单独的网格中,并通过网格联邦启用网格间通信。

共享控制平面(单一网络)

安装一个跨多个 Kubernetes 集群的 Istio 网格,多集群共享控制平面,并且集群间通过 VPN 互连。

共享的控制平面(多网络)

跨多个 Kubernetes 集群安装一个 Istio 网格,使互不联通的集群网络共享同一个控制平面。

简化地多集群安装[实验性]

配置一个跨多个 Kubernetes 集群的 Istio 网格。

DNS 证书管理

在 Istio 中配置和管理 DNS 证书。