• 集群感知的服务路由
    • 开始之前
    • 多集群设置示例
      • 配置 cluster1(主) 集群
      • 配置 cluster2
      • 开始监听 cluster2 集群
    • 示例 service
      • 在 cluster2 集群部署 helloworld v2
      • 在 cluster1 中部署 helloworld v1
      • 横向分割 EDS 实战
    • 清理
    • 相关内容

    集群感知的服务路由

    这个示例展示了如何使用单一控制平面拓扑配置一个多集群网格,并使用 Istio 的水平分割 EDS(Endpoints Discovery Service,端点发现服务)特性(在 Istio 1.1 中引入),通过 ingress gateway 将服务请求路由到其他集群。水平分割 EDS 使 Istio 可以基于请求来源的位置,将其路由到不同的 endpoint。

    按照此示例中的说明,您将设置一个两集群网格,如下图所示:

    单个 Istio 控制平面配置水平分割 EDS,跨越多个 Kubernetes 集群

    单个 Istio 控制平面配置水平分割 EDS,跨越多个 Kubernetes 集群

    原始集群 cluster1 将运行完整的 Istio 控制平面组件,而 cluster2 集群仅运行 Istio Citadel、Sidecar Injector 和 Ingress gateway。不需要 VPN 连接,不同集群中的工作负载之间也无需直接网络访问。

    开始之前

    除了安装 Istio 的先决条件之外,此示例还需要以下条件:

    • 两个 Kubernetes 集群(称之为 cluster1cluster2)。

    为了运行此配置,要求必须可以从 cluster1 集群访问 cluster2 集群的 Kubernetes API server。

    • kubectl 能通过 —context 参数切换上下文,以支持对不同集群 cluster1cluster2 的访问。使用如下命令列出现存的上下文:
    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=<KUBECONFIG_CONTEXT_NAME_FOR_CLUSTER_1>
    2. $ export CTX_CLUSTER2=<KUBECONFIG_CONTEXT_NAME_FOR_CLUSTER_2>

    多集群设置示例

    在此示例中,您将安装对控制平面和应用程序 pod 都启用了双向 TLS 的 Istio。为了共享根 CA,您将使用同一个来自 Istio 示例目录的证书,在 cluster1cluster2 集群上创建一个相同的 cacerts secret。

    下面的说明还设置了 cluster2 集群,包含一个无 selector 的 service 和具有 cluster1 Istio 入口网关地址的 istio-pilot.istio-system 端点。这将用于通过入口网关安全地访问 cluster1 pilot,而无需双向 TLS 终止。

    配置 cluster1(主) 集群

    • 使用 Helm 创建 Istio cluster1 的部署 YAML:

    如果不确定 helm 的依赖项是否为最新版本, 在运行下列命令前,请先根据 Helm 安装步骤 更新依赖项.

    1. $ helm template --name=istio --namespace=istio-system \
    2.   --set global.mtls.enabled=true \
    3.   --set security.selfSigned=false \
    4.   --set global.controlPlaneSecurityEnabled=true \
    5.   --set global.proxy.accessLogFile="/dev/stdout" \
    6.   --set global.meshExpansion.enabled=true \
    7.   --set 'global.meshNetworks.network2.endpoints[0].fromRegistry'=n2-k8s-config \
    8.   --set 'global.meshNetworks.network2.gateways[0].address'=0.0.0.0 \
    9.   --set 'global.meshNetworks.network2.gateways[0].port'=443 \
    10.   install/kubernetes/helm/istio > istio-auth.yaml
    11. 注意,网关地址设置为了 0.0.0.0.这个值将在下面章节中使用 cluster2 部署后的网关的真实 IP 值替换
    • 部署 Istio 到 cluster1 集群:
    1. $ kubectl create --context=$CTX_CLUSTER1 ns istio-system
    2. $ kubectl create --context=$CTX_CLUSTER1 secret generic cacerts -n istio-system --from-file=samples/certs/ca-cert.pem --from-file=samples/certs/ca-key.pem --from-file=samples/certs/root-cert.pem --from-file=samples/certs/cert-chain.pem
    3. $ for i in install/kubernetes/helm/istio-init/files/crd*yaml; do kubectl apply --context=$CTX_CLUSTER1 -f $i; done
    4. $ kubectl create --context=$CTX_CLUSTER1 -f istio-auth.yaml
    1. 等待 `cluster1` Istio pods 准备完毕:
    2. $ kubectl get pods --context=$CTX_CLUSTER1 -n istio-system
    3. NAME                                      READY   STATUS      RESTARTS   AGE
    4. istio-citadel-9bbf9b4c8-nnmbt             1/1     Running     0          2m8s
    5. istio-cleanup-secrets-1.1.0-x9crw         0/1     Completed   0          2m12s
    6. istio-galley-868c5fff5d-9ph6l             1/1     Running     0          2m9s
    7. istio-ingressgateway-6c756547b-dwc78      1/1     Running     0          2m8s
    8. istio-pilot-54fcf8db8-sn9cn               2/2     Running     0          2m8s
    9. istio-policy-5fcbd55d8b-xhbpz             2/2     Running     2          2m8s
    10. istio-security-post-install-1.1.0-ww5zz   0/1     Completed   0          2m12s
    11. istio-sidecar-injector-6dcc9d5c64-7hnnl   1/1     Running     0          2m8s
    12. istio-telemetry-57875ffb6d-n2vmf          2/2     Running     3          2m8s
    13. prometheus-66c9f5694-8pccr                1/1     Running     0          2m8s
    • cluster2 中创建访问服务的入口网关:
    1. $ kubectl create --context=$CTX_CLUSTER1 -f - <<EOF
    2. apiVersion: networking.istio.io/v1alpha3
    3. kind: Gateway
    4. metadata:
    5. name: cluster-aware-gateway
    6. namespace: istio-system
    7. spec:
    8. selector:
    9. istio: ingressgateway
    10. servers:
    11. - port:
    12. number: 443
    13. name: tls
    14. protocol: TLS
    15. tls:
    16. mode: AUTO_PASSTHROUGH
    17. hosts:
    18. - "*.local"
    19. EOF

    Gateway 配置 443 端口,以便将传入的流量传递到目标服务并指定 SNI 请求头,以用于本地顶级域名的 SNI 值 (i.e., the Kubernetes DNS domain).双向 TLS 链接将会从源端一直到目标端的 sidercar。

    由于两个集群使用都是相同都 Pilot,虽然是应用在 cluster1上,但是网关示例也会影响 cluster2

    配置 cluster2

    • 导出 cluster1 的网管地址:
    1. $ export LOCAL_GW_ADDR=$(kubectl get --context=$CTX_CLUSTER1 svc --selector=app=istio-ingressgateway \
    2. -n istio-system -o jsonpath='{.items[0].status.loadBalancer.ingress[0].ip}') && echo ${LOCAL_GW_ADDR}

    上述命令设置网关公共的 IP 地址并输出地址。

    如果负载均衡的配置不包含 IP 地址,上述命令则会失败。DNS 名称支持的服务状态为 pending。

    • 使用 Helm 创建 Istio cluster2 deployment YAML:

    Zip

    1. $ helm template --name istio-remote --namespace=istio-system \
    2. --values @install/kubernetes/helm/istio/values-istio-remote.yaml@ \
    3. --set global.mtls.enabled=true \
    4. --set gateways.enabled=true \
    5. --set security.selfSigned=false \
    6. --set global.controlPlaneSecurityEnabled=true \
    7. --set global.createRemoteSvcEndpoints=true \
    8. --set global.remotePilotCreateSvcEndpoint=true \
    9. --set global.remotePilotAddress=${LOCAL_GW_ADDR} \
    10. --set global.remotePolicyAddress=${LOCAL_GW_ADDR} \
    11. --set global.remoteTelemetryAddress=${LOCAL_GW_ADDR} \
    12. --set gateways.istio-ingressgateway.env.ISTIO_META_NETWORK="network2" \
    13. --set global.network="network2" \
    14. install/kubernetes/helm/istio > istio-remote-auth.yaml
    • 部署 Istio 到 cluster2
    1. $ kubectl create --context=$CTX_CLUSTER2 ns istio-system
    2. $ kubectl create --context=$CTX_CLUSTER2 secret generic cacerts -n istio-system --from-file=samples/certs/ca-cert.pem --from-file=samples/certs/ca-key.pem --from-file=samples/certs/root-cert.pem --from-file=samples/certs/cert-chain.pem
    3. $ kubectl create --context=$CTX_CLUSTER2 -f istio-remote-auth.yaml

    等待 cluster2 pod 的状态,特别是 istio-ingressgateway 的状态为已就绪:

    1. $ kubectl get pods --context=$CTX_CLUSTER2 -n istio-system -l istio!=ingressgateway
    2. NAME READY STATUS RESTARTS AGE
    3. istio-citadel-75c8fcbfcf-9njn6 1/1 Running 0 12s
    4. istio-cleanup-secrets-1.1.0-vtp62 0/1 Completed 0 14s
    5. istio-sidecar-injector-cdb5d4dd5-rhks9 1/1 Running 0 12s
    1. 需要在 cluster1 的控制平面中监听 cluster2 之后,istio-ingressgateway 的状态才会变成已就绪。你可以在下一章节中尝试配置。
    • 确定 cluster2 的入口 IP 和端口号

      • 设置 kubectl 当前的上下文为 CTX_CLUSTER2
    1. $ export ORIGINAL_CONTEXT=$(kubectl config current-context)
    2. $ kubectl config use-context $CTX_CLUSTER2
    • 根据确定入口 IP 和端口的命令,设置 INGRESS_HOSTSECURE_INGRESS_PORT 环境变量。

    • 恢复 kubectl 之前的上下文:

    1. $ kubectl config use-context $ORIGINAL_CONTEXT
    2. $ unset ORIGINAL_CONTEXT
    • 输出 INGRESS_HOSTSECURE_INGRESS_PORT 的值:
    1. $ echo The ingress gateway of cluster2: address=$INGRESS_HOST, port=$SECURE_INGRESS_PORT
    • 更新网格网络配置中的 gateway 地址。编辑 istio ConfigMap
    1. $ kubectl edit cm -n istio-system --context=$CTX_CLUSTER1 istio

    更新 network2 的网关地址和端口,并映射到 cluster2 入口地址和端口,分别保存并退出。

    一旦保存,Pilot 将会自动读取更新后的网络配置。

    • 确定 remote 网关地址:
    1. $ kubectl get --context=$CTX_REMOTE svc --selector=app=istio-ingressgateway -n istio-system -o jsonpath="{.items[0].status.loadBalancer.ingress[0].ip}"
    2. 169.61.102.93
    • 编辑 istio configmap:
    1. $ kubectl edit cm -n istio-system --context=$CTX_LOCAL istio
    • network2 的 gateway address 从 0.0.0.0 修改为 remote gateway 地址,保存并退出。

    一旦保存,Pilot 将自动读取并更新网络配置。

    • 准备环境变量以构建 service account istio-multin2-k8s-config 文件:
    1. $ CLUSTER_NAME=$(kubectl --context=$CTX_CLUSTER2 config view --minify=true -o jsonpath='{.clusters[].name}')
    2. $ SERVER=$(kubectl --context=$CTX_CLUSTER2 config view --minify=true -o jsonpath='{.clusters[].cluster.server}')
    3. $ SECRET_NAME=$(kubectl --context=$CTX_CLUSTER2 get sa istio-multi -n istio-system -o jsonpath='{.secrets[].name}')
    4. $ CA_DATA=$(kubectl get --context=$CTX_CLUSTER2 secret ${SECRET_NAME} -n istio-system -o jsonpath="{.data['ca\.crt']}")
    5. $ TOKEN=$(kubectl get --context=$CTX_CLUSTER2 secret ${SECRET_NAME} -n istio-system -o jsonpath="{.data['token']}" | base64 --decode)

    许多系统上使用 openssl enc -d -base64 -A 替代 base64 —decode

    • 在工作目录创建 n2-k8s-config 文件:
    1. $ cat <<EOF > n2-k8s-config
    2. apiVersion: v1
    3. kind: Config
    4. clusters:
    5. - cluster:
    6. certificate-authority-data: ${CA_DATA}
    7. server: ${SERVER}
    8. name: ${CLUSTER_NAME}
    9. contexts:
    10. - context:
    11. cluster: ${CLUSTER_NAME}
    12. user: ${CLUSTER_NAME}
    13. name: ${CLUSTER_NAME}
    14. current-context: ${CLUSTER_NAME}
    15. users:
    16. - name: ${CLUSTER_NAME}
    17. user:
    18. token: ${TOKEN}
    19. EOF

    开始监听 cluster2 集群

    • 执行下列命令,添加并标记 cluster2 Kubernetes 的 secret。执行这些命令之后,cluster1 的 Istio Pilot 将开始监听 cluster2 集群的服务和实例,就像在 cluster1 集群中一样。
    1. $ kubectl create --context=$CTX_CLUSTER1 secret generic n2-k8s-secret --from-file n2-k8s-config -n istio-system
    2. $ kubectl label --context=$CTX_CLUSTER1 secret n2-k8s-secret istio/multiCluster=true -n istio-system
    • 等待 istio-ingressgateway准备完成:
    1. $ kubectl get pods --context=$CTX_CLUSTER2 -n istio-system -l istio=ingressgateway
    2. NAME READY STATUS RESTARTS AGE
    3. istio-ingressgateway-5c667f4f84-bscff 1/1 Running 0 16m

    现在您已经设置了 cluster1cluster2 集群,可以开始部署示例 service。

    示例 service

    在这个实例中,您将了解到一个 service 的流量是如何在两个集群间分发的。如上图所示,您将为 helloworld service 部署两个实例,一个在 cluster1 集群,另一个在 cluster2 集群。两个实例的区别在于其 helloworld 镜像的版本。

    在 cluster2 集群部署 helloworld v2

    • 使用 sidecar 自动注入标签创建一个 sample namespace:
    1. $ kubectl create --context=$CTX_CLUSTER2 ns sample
    2. $ kubectl label --context=$CTX_CLUSTER2 namespace sample istio-injection=enabled
    • 部署 helloworld v2

    ZipZip

    1. $ kubectl create --context=$CTX_CLUSTER2 -f @samples/helloworld/helloworld.yaml@ -l app=helloworld -n sample
    2. $ kubectl create --context=$CTX_CLUSTER2 -f @samples/helloworld/helloworld.yaml@ -l version=v2 -n sample
    • 确定 helloworld v2 在运行中:
    1. $ kubectl get po --context=$CTX_CLUSTER2 -n sample
    2. NAME READY STATUS RESTARTS AGE
    3. helloworld-v2-7dd57c44c4-f56gq 2/2 Running 0 35s

    在 cluster1 中部署 helloworld v1

    • 使用 sidecar 自动注入标签创建一个 sample namespace:
    1. $ kubectl create --context=$CTX_CLUSTER1 ns sample
    2. $ kubectl label --context=$CTX_CLUSTER1 namespace sample istio-injection=enabled
    • 部署 helloworld v1:

    ZipZip

    1. $ kubectl create --context=$CTX_CLUSTER1 -f @samples/helloworld/helloworld.yaml@ -l app=helloworld -n sample
    2. $ kubectl create --context=$CTX_CLUSTER1 -f @samples/helloworld/helloworld.yaml@ -l version=v1 -n sample
    • 确定 helloworld v1 运行中:
    1. $ kubectl get po --context=$CTX_CLUSTER1 -n sample
    2. NAME READY STATUS RESTARTS AGE
    3. helloworld-v1-d4557d97b-pv2hr 2/2 Running 0 40s

    横向分割 EDS 实战

    我们将从另一个集群中 sleep 服务请求 helloworld.sample 服务。

    • 部署 sleep 服务:

    Zip

    1. $ kubectl create --context=$CTX_CLUSTER1 -f @samples/sleep/sleep.yaml@ -n sample
    • 等到 sleep 服务启动:
    1. $ kubectl get po --context=$CTX_CLUSTER1 -n sample -l app=sleep
    2. sleep-754684654f-n6bzf 2/2 Running 0 5s
    • 多次请求 helloworld.sample 服务:
    1. $ kubectl exec --context=$CTX_CLUSTER1 -it -n sample -c sleep $(kubectl get pod --context=$CTX_CLUSTER1 -n sample -l app=sleep -o jsonpath='{.items[0].metadata.name}') -- curl helloworld.sample:5000/hello

    如果设置正确,到 helloworld.sample 服务的流量将在 cluster1cluster2 实例之间进行分发,导致响应 body 中 v1v2 都可能出现。

    1. Hello version: v2, instance: helloworld-v2-758dd55874-6x4t8
    1. Hello version: v1, instance: helloworld-v1-86f77cd7bd-cpxhv

    您可以通过打印 sleep pod 的 istio-proxy 容器日志来验证访问的 endpoint 的 IP 地址。

    1. $ kubectl logs --context=$CTX_CLUSTER1 -n sample $(kubectl get pod --context=$CTX_CLUSTER1 -n sample -l app=sleep -o jsonpath='{.items[0].metadata.name}') istio-proxy
    2. [2018-11-25T12:37:52.077Z] "GET /hello HTTP/1.1" 200 - 0 60 190 189 "-" "curl/7.60.0" "6e096efe-f550-4dfa-8c8c-ba164baf4679" "helloworld.sample:5000" "192.23.120.32:15443" outbound|5000||helloworld.sample.svc.cluster.local - 10.20.194.146:5000 10.10.0.89:59496 -
    3. [2018-11-25T12:38:06.745Z] "GET /hello HTTP/1.1" 200 - 0 60 171 170 "-" "curl/7.60.0" "6f93c9cc-d32a-4878-b56a-086a740045d2" "helloworld.sample:5000" "10.10.0.90:5000" outbound|5000||helloworld.sample.svc.cluster.local - 10.20.194.146:5000 10.10.0.89:59646 -

    v2 被调用时将记录 cluster2 网关 IP 192.23.120.32:15443,v1 被调用时将记录 cluster1 实例 IP 10.10.0.90:5000

    清理

    执行下列命令清理 demo 服务 and Istio 组件。

    清理 cluster2 集群:

    1. $ kubectl delete --context=$CTX_CLUSTER2 -f istio-remote-auth.yaml
    2. $ kubectl delete --context=$CTX_CLUSTER2 ns istio-system
    3. $ kubectl delete --context=$CTX_CLUSTER2 ns sample
    4. $ unset CTX_CLUSTER2 CLUSTER_NAME SERVER SECRET_NAME CA_DATA TOKEN INGRESS_HOST SECURE_INGRESS_PORT INGRESS_PORT
    5. $ rm istio-remote-auth.yaml

    清理 cluster1 集群:

    1. $ kubectl delete --context=$CTX_CLUSTER1 -f istio-auth.yaml
    2. $ kubectl delete --context=$CTX_CLUSTER1 ns istio-system
    3. $ for i in install/kubernetes/helm/istio-init/files/crd*yaml; do kubectl delete --context=$CTX_CLUSTER1 -f $i; done
    4. $ kubectl delete --context=$CTX_CLUSTER1 ns sample
    5. $ unset CTX_CLUSTER1
    6. $ rm istio-auth.yaml n2-k8s-config

    相关内容

    Gateway 连接

    使用 Istio Gateway 跨越多个 Kubernetes 集群安装 Istio 网格以访问远程 pod。

    Google Kubernetes Engine

    基于 GKE 的 Istio 多集群安装。

    IBM Cloud Private

    多 IBM Cloud Private 集群安装 Istio 示例。

    VPN 连接

    通过直连远程 pods 实现多 Kubernetes 集群安装 Istio 网格。

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

    在一个使用网关进行连接的多集群网格中配置远程服务。

    多集群服务网格中的分版本路由

    在多集群服务网格环境中配置 Istio 的路由规则。