zh: content/docs/concepts/multicluster-deployments/index.md (#3153)

* multicluster-deployments

* keep 'local' and 'remote' as EN
This commit is contained in:
Chris 2019-01-30 11:22:20 +08:00 committed by istio-bot
parent e958cf600f
commit 8421daee42
2 changed files with 470 additions and 0 deletions

View File

@ -0,0 +1,62 @@
---
title: 多集群部署
description: 描述如何配置服务网格以包含来自多个集群的服务。
weight: 60
---
Istio 是一个[服务网格](/zh/docs/concepts/what-is-istio/#什么是服务网格),其基本属性是监控和管理单个管理域下协作微服务网格的能力。服务网格本质上是将一组单独的微服务组合成单个可控的复合应用程序。
对于特定大小的应用,组成应用程序的所有的微服务都可以在单个编排平台上运行(例如 Kubernetes 集群)。然而,由于诸如规模、冗余等许多原因,大多数应用程序最终将需要分布式设计并使其中的一些服务能够运行在任何地方。
Istio 支持将一个应用程序的服务以多种拓扑分布,而不仅仅是分布在单一集群,例如:
* 集群内部的服务可以使用 [service entry](/zh/docs/concepts/traffic-management/#service-entry) 访问独立的外部服务,或访问其余松散耦合服务网格(或者说是*网格联邦*)公开的服务。
* 您可以[扩展服务网格](/zh/docs/setup/kubernetes/mesh-expansion/)以包含运行在虚拟机或裸金属主机上的服务。
* 您可以将多个集群中的服务组合成一个单一复合服务网格,也即*多集群网格*。
## 多集群服务网格
多集群服务网格是由在多个底层集群中运行的 service 组成的网格,但所有 service 都在单一管控机制下运行。在一个多集群网格中,集群 1 里 namespace `ns1` 下名为 `foo` 的 service 与集群 2 里 `ns1` 下的 `foo` 是同一个 service。与松散耦合的服务网格联邦不同在联邦中两个集群对相同 service 的定义可能不同,在集成集群时需要对其进行协调。
多集群网格的好处是所有 service 对客户端看起来都一样,不管工作负载实际上运行在哪里。无论是部署在单个还是多个网格中,它对应用程序都是透明的。要实现此行为,需要使用单个逻辑控制平面管理所有 service。但是单个逻辑控制平面不一定需要是单个物理 Istio 控制平面。存在两种可能的部署方法:
1. 多个同步的 Istio 控制平面,具有复制的 service 和路由配置。
1. 单个 Istio 控制平面,可以访问和配置网格中的所有 service。
即使在这两种拓扑中,也有多种配置多集群网格的方法。使用哪种方法,以及如何配置取决于应用程序的要求,
以及底层云平台的功能和限制。
### 多控制平面拓扑
在多控制平面配置中,每个集群具有相同的 Istio 控制平面安装方式,每个控制平面管理自己的 endpoint。
使用 Istio gateway、公共根证书颁发机构CA和 service entry您可以配置由参与集群组成的单个逻辑服务网格。这种方法没有特殊的网络要求因此通常被认为是在集群之间没有通用连接时最简单的方法。
{{< image width="80%" ratio="36.01%"
link="/docs/concepts/multicluster-deployments/multicluster-with-gateways.svg"
caption="Istio 网格跨越多个 Kubernetes 集群,使用多个 Istio 控制平面和 Gateway 到达远程 pod"
>}}
要在集群中实现单个 Istio 服务网格,您需要配置一个公共根 CA 并复制所有集群中共享的 service 和 namespace。跨集群通信发生在各个集群的 Istio 网关上。所有集群都共享策略实施和安全性的控制。
在这个配置中,每个集群中的工作负载都可以像平常一样使用 Kubernetes DNS 后缀们访问其他本地 service例如`foo.ns1.svc.cluster.local`。为了给远程集群中的 service 提供DNS解析Istio 包含了 一个 CoreDNS 服务器,此服务器被配置为可以处理 `<name>。<namespace> .global` 形式的 service 名称。例如,从任何集群到 `foo.ns1.global` 的调用将解析到任意集群 namespace `ns1` 中运行的 `foo` service。要进行这种多集群配置请访问我们提供的[带网关指令的多控制平面](/zh/docs/setup/kubernetes/multicluster-install/gateways/)页面。
### 单一控制平面拓扑
这种多集群配置使用运行在某个集群上的单个 Istio 控制平面。控制平面的 Pilot 管理本地和远程集群上的 service并为所有集群配置 Envoy sidecar。这种方法在所有参与集群都具有 VPN 连接的环境中效果最佳,从其他任何地方都可以通过相同的 IP 地址访问网格中的每个 pod。
{{< image width="80%" ratio="36.01%"
link="/docs/concepts/multicluster-deployments/multicluster-with-vpn.svg"
caption="Istio 网格跨越多个 Kubernetes 集群,通过 VPN 直接访问远程 pod"
>}}
在此配置中Istio 控制平面部署在其中一个集群上,而所有其他集群上运行一个更简单的远程 Istio 配置,以将它们连接到单个 Istio 控制平面,该平面将所有 Envoy 作为单个网格进行管理。各个集群上的 IP 地址不得重叠,且需注意远程集群上的 service 的 DNS 解析不是自动的。用户需要在每个参与集群上复制 service。您可以在我们提供的[使用 VPN 指令的单一控制平面](/zh/docs/setup/kubernetes/multicluster-install/vpn/)中找到设置这种多集群拓扑的详细步骤。
如果设置具有全局 pod-to-pod 连接的环境很困难或不可能,您仍然可以使用 Istio 网关并和启用 Istio Pilot 的位置感知服务路由功能(也即`水平分割 EDSEndpoint Discovery Service终端发现服务`)来配置单个控制平面拓扑。此方法仍需要从所有集群到 Kubernetes API server 的连接,例如在一个托管的 Kubernetes 平台上,其 API server 运行的网络可以被所有租户集群访问。如果无法做到这一点,那么多控制平面拓扑可能是更好的选择
{{< image width="80%" ratio="36.01%"
link="/docs/concepts/multicluster-deployments/multicluster-split-horizon-eds.svg"
caption="Istio 网格使用单个控制平面和 Gateway 跨越多个 Kubernetes 集群到达远程 pod"
>}}
在此配置中,从一个集群中的 sidecar 到同一集群中的 service 的请求仍然被转发到本地 service IP。如果目标工作负载在其他集群中运行远程集群网关 IP 会替代 service 用于连接。访问我们的[单一控制平面](/zh/docs/examples/multicluster/split-horizon-eds/)页面,并使用网关示例来试验此功能。

View File

@ -0,0 +1,408 @@
---
title: 集群感知的服务路由
description: 利用 Istio 的水平分割 EDS 来创建多集群网格。
weight: 85
keywords: [kubernetes,multicluster]
---
这个示例展示了如何使用[单一控制平面拓扑](/zh/docs/concepts/multicluster-deployments/#单一控制平面拓扑)配置一个多集群网格,并使用 Istio 的`水平分割 EDSEndpoints Discovery ServiceEndpoint 发现服务)`特性(在 Istio 1.1 中引入),通过 ingress gateway 将服务请求路由到 remote 集群。水平分割 EDS 使 Istio 可以基于请求来源的位置,将其路由到不同的 endpoint。
按照此示例中的说明,您将设置一个两集群网格,如下图所示:
{{< image width="80%" ratio="36.01%"
link="/docs/examples/multicluster/split-horizon-eds/diagram.svg"
caption="单个 Istio 控制平面配置水平分割 EDS跨越多个 Kubernetes 集群" >}}
`local` 集群将运行 Istio Pilot 和其它 Istio 控制平面组件,而 `remote` 集群仅运行 Istio Citadel、Sidecar Injector 和 Ingress gateway。不需要 VPN 连接,不同集群中的工作负载之间也无需直接网络访问。
## 开始之前
除了安装 Istio 的先决条件之外,此示例还需要以下条件:
* 两个 Kubernetes 集群(称之为 `local``remote`)。
> {{< warning_icon >}} 为了运行此配置,要求必须可以从 `local` 集群访问 `remote` 集群的 Kubernetes API server。
* `kubectl` 命令使用 `--context` 参数,同时访问 `local``remote` 集群。请使用下列命令列出您的 context
{{< text bash >}}
$ kubectl config get-contexts
CURRENT NAME CLUSTER AUTHINFO NAMESPACE
cluster1 cluster1 user@foo.com default
cluster2 cluster2 user@foo.com default
{{< /text >}}
* 使用配置的 context 名称导出以下环境变量:
{{< text bash >}}
$ export CTX_LOCAL=<KUBECONFIG_LOCAL_CONTEXT_NAME>
$ export CTX_REMOTE=<KUBECONFIG_REMOTE_CONTEXT_NAME>
{{< /text >}}
## 多集群设置示例
在此示例中,您将安装对控制平面和应用程序 pod 都启用了双向 TLS 的 Istio。为了共享根 CA您将使用同一个来自 Istio 示例目录的证书,在 `local``remote` 集群上创建一个相同的 `cacerts` secret。
下面的说明还设置了 `remote` 集群,包含一个无 selector 的 service 和具有 `local` Istio ingress gateway 地址的 `istio-pilot.istio-system` endpoint。这将用于通过 ingress gateway 安全地访问 `local` pilot而无需双向 TLS 终止。
### 配置 local 集群
1. 定义网格网络:
默认情况下 Istio 的 `global.meshNetworks` 值为空,但是您需要对其进行修改以为 `remote` 集群上的 endpoint 定义一个新的网络。修改 `install/kubernetes/helm/istio/values.yaml` 并添加一个 `network2` 定义:
{{< text yaml >}}
meshNetworks:
network2:
endpoints:
- fromRegistry: remote_kubecfg
gateways:
- address: 0.0.0.0
port: 443
{{< /text >}}
请注意gateway address 被设置为 `0.0.0.0`。这是一个临时占位符,稍后将被更新为 `remote` 集群 gateway 的公共 IP 地址,此 gateway 将在下一小节中部署。
1. 使用 Helm 创建 Istio `local` deployment YAML
{{< text bash >}}
$ helm template --namespace=istio-system \
--values install/kubernetes/helm/istio/values.yaml \
--set global.mtls.enabled=true \
--set global.enableTracing=false \
--set security.selfSigned=false \
--set mixer.telemetry.enabled=false \
--set mixer.policy.enabled=false \
--set global.useMCP=false \
--set global.controlPlaneSecurityEnabled=true \
--set gateways.istio-egressgateway.enabled=false \
--set global.meshExpansion.enabled=true \
install/kubernetes/helm/istio > istio-auth.yaml
{{< /text >}}
1. 部署 Istio 到 `local` 集群:
{{< text bash >}}
$ kubectl create --context=$CTX_LOCAL ns istio-system
$ kubectl create --context=$CTX_LOCAL 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
$ for i in install/kubernetes/helm/istio-init/files/crd*yaml; do kubectl apply --context=$CTX_LOCAL -f $i; done
$ kubectl create --context=$CTX_LOCAL -f istio-auth.yaml
{{< /text >}}
通过检查 `local` pod 的状态等待其被拉起:
{{< text bash >}}
$ kubectl get pods --context=$CTX_LOCAL -n istio-system
{{< /text >}}
### 设置 remote 集群
1. 导出 `local` gateway 地址:
{{< text bash >}}
$ export LOCAL_GW_ADDR=$(kubectl get --context=$CTX_LOCAL svc --selector=app=istio-ingressgateway \
-n istio-system -o jsonpath="{.items[0].status.loadBalancer.ingress[0].ip}")
{{< /text >}}
此命令将值设置为 gateway 的公共 IP但请注意您也可以将其设置为一个 DNS 名称(如果有)。
1. 使用 Helm 创建 Istio `remote` deployment YAML
{{< text bash >}}
$ helm template install/kubernetes/helm/istio-remote \
--name istio-remote \
--namespace=istio-system \
--set global.mtls.enabled=true \
--set global.enableTracing=false \
--set gateways.enabled=true \
--set gateways.istio-egressgateway.enabled=false \
--set gateways.istio-ingressgateway.enabled=true \
--set security.selfSigned=false \
--set global.controlPlaneSecurityEnabled=true \
--set global.createRemoteSvcEndpoints=true \
--set global.remotePilotCreateSvcEndpoint=true \
--set global.remotePilotAddress=${LOCAL_GW_ADDR} \
--set global.proxy.envoyStatsd.enabled=false \
--set global.disablePolicyChecks=true \
--set global.policyCheckFailOpen=true \
--set gateways.istio-ingressgateway.env.ISTIO_META_NETWORK="network2" \
--set global.network="network2" > istio-remote-auth.yaml
{{< /text >}}
1. 部署 Istio 到 `remote` 集群:
{{< text bash >}}
$ kubectl create --context=$CTX_REMOTE ns istio-system
$ kubectl create --context=$CTX_REMOTE 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
$ kubectl create --context=$CTX_REMOTE -f istio-remote-auth.yaml
{{< /text >}}
通过检查 `remote` pod 的状态等待其被拉起:
{{< text bash >}}
$ kubectl get pods --context=$CTX_REMOTE -n istio-system
{{< /text >}}
1. 更新网格网络配置中的 gateway 地址:
* 确定 `remote` 网关地址:
{{< text bash >}}
$ kubectl get --context=$CTX_REMOTE svc --selector=app=istio-ingressgateway -n istio-system -o jsonpath="{.items[0].status.loadBalancer.ingress[0].ip}"
169.61.102.93
{{< /text >}}
* 编辑 istio configmap
{{< text bash >}}
$ kubectl edit cm -n istio-system --context=$CTX_LOCAL istio
{{< /text >}}
* 将 `network2` 的 gateway address 从 `0.0.0.0` 修改为 `remote` gateway 地址,保存并退出。
一旦保存Pilot 将自动读取并更新网络配置。
1. 准备环境变量以构建 service account `istio-multi``remote_kubecfg` 文件:
{{< text bash >}}
$ CLUSTER_NAME=$(kubectl --context=$CTX_REMOTE config view --minify=true -o "jsonpath={.clusters[].name}")
$ SERVER=$(kubectl --context=$CTX_REMOTE config view --minify=true -o "jsonpath={.clusters[].cluster.server}")
$ SECRET_NAME=$(kubectl --context=$CTX_REMOTE get sa istio-multi -n istio-system -o jsonpath='{.secrets[].name}')
$ CA_DATA=$(kubectl get --context=$CTX_REMOTE secret ${SECRET_NAME} -n istio-system -o "jsonpath={.data['ca\.crt']}")
$ TOKEN=$(kubectl get --context=$CTX_REMOTE secret ${SECRET_NAME} -n istio-system -o "jsonpath={.data['token']}" | base64 --decode)
{{< /text >}}
> 许多系统上使用 `openssl enc -d -base64 -A` 替代 `base64 --decode`
1. 在工作目录创建 `remote_kubecfg` 文件:
{{< text bash >}}
$ cat <<EOF > remote_kubecfg
apiVersion: v1
clusters:
- cluster:
certificate-authority-data: ${CA_DATA}
server: ${SERVER}
name: ${CLUSTER_NAME}
contexts:
- context:
cluster: ${CLUSTER_NAME}
user: ${CLUSTER_NAME}
name: ${CLUSTER_NAME}
current-context: ${CLUSTER_NAME}
kind: Config
preferences: {}
users:
- name: ${CLUSTER_NAME}
user:
token: ${TOKEN}
EOF
{{< /text >}}
### 开始监听 remote 集群
执行下列命令,添加并标记 `remote` Kubernetes 的 secret。执行这些命令之后local Istio Pilot 将开始监听 `remote` 集群的 service 和 instance就像在 `local` 集群中一样。
{{< text bash >}}
$ kubectl create --context=$CTX_LOCAL secret generic iks --from-file remote_kubecfg -n istio-system
$ kubectl label --context=$CTX_LOCAL secret iks istio/multiCluster=true -n istio-system
{{< /text >}}
现在您已经设置了 `local``remote` 集群,可以开始部署示例 service。
## 示例 service
在这个实例中,您将了解到一个 service 的流量是如何被分发到 local endpoint 和 remote gateway。如上图所示您将为 `helloworld` service 部署两个实例,一个在 `local` 集群,另一个在 `remote` 集群。两个实例的区别在于其 `helloworld` 镜像的版本。
### 在 remote 集群部署 helloworld v2
1. 使用 sidecar 自动注入标签创建一个 `sample` namespace
{{< text bash >}}
$ kubectl create --context=$CTX_REMOTE ns sample
$ kubectl label --context=$CTX_REMOTE namespace sample istio-injection=enabled
{{< /text >}}
1. 使用以下内容创建 `helloworld-v2.yaml` 文件:
{{< text yaml >}}
apiVersion: v1
kind: Service
metadata:
name: helloworld
labels:
app: helloworld
spec:
ports:
- port: 5000
name: http
selector:
app: helloworld
---
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
name: helloworld-v2
spec:
replicas: 1
template:
metadata:
labels:
app: helloworld
version: v2
spec:
containers:
- name: helloworld
image: istio/examples-helloworld-v2
imagePullPolicy: IfNotPresent
ports:
- containerPort: 5000
{{< /text >}}
1. 部署此文件:
{{< text bash >}}
$ kubectl create --context=$CTX_REMOTE -f helloworld-v2.yaml -n sample
{{< /text >}}
### 在 local 集群部署 helloworld v1
1. 使用 sidecar 自动注入标签创建一个 `sample` namespace
{{< text bash >}}
$ kubectl create --context=$CTX_LOCAL ns sample
$ kubectl label --context=$CTX_LOCAL namespace sample istio-injection=enabled
{{< /text >}}
1. 使用以下内容创建 `helloworld-v1.yaml` 文件:
{{< text yaml >}}
apiVersion: v1
kind: Service
metadata:
name: helloworld
labels:
app: helloworld
spec:
ports:
- port: 5000
name: http
selector:
app: helloworld
---
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
name: helloworld-v1
spec:
replicas: 1
template:
metadata:
labels:
app: helloworld
version: v1
spec:
containers:
- name: helloworld
image: istio/examples-helloworld-v1
imagePullPolicy: IfNotPresent
ports:
- containerPort: 5000
{{< /text >}}
1. 使用下列内容创建 `helloworld-gateway.yaml` 文件:
{{< text yaml >}}
apiVersion: networking.istio.io/v1alpha3
kind: Gateway
metadata:
name: helloworld-gateway
namespace: sample
spec:
selector:
istio: ingressgateway
servers:
- port:
number: 443
name: tls
protocol: TLS
tls:
mode: AUTO_PASSTHROUGH
hosts:
- "*"
{{< /text >}}
虽然是本地部署,这个 Gateway 实例仍然会影响 `remote` 集群,方法是将其配置为允许相关 remote service基于 SNI通过但保持从源到目标 sidecar 的双向 TLS。
1. 部署此文件:
{{< text bash >}}
$ kubectl create --context=$CTX_LOCAL -f helloworld-v1.yaml -n sample
$ kubectl create --context=$CTX_LOCAL -f helloworld-gateway.yaml -n sample
{{< /text >}}
### 横向分割 EDS 实战
我们将从另一个集群中 `sleep` service 请求 `helloworld.sample` service。
1. 部署 `sleep` service
{{< text bash >}}
$ kubectl create --context=$CTX_LOCAL -f samples/sleep/sleep.yaml -n sample
{{< /text >}}
1. 多次请求 `helloworld.sample` service
{{< text bash >}}
$ kubectl exec --context=$CTX_LOCAL -it -n sample $(kubectl get pod --context=$CTX_LOCAL -n sample -l app=sleep -o jsonpath={.items[0].metadata.name}) -- curl helloworld.sample:5000/hello
{{< /text >}}
如果设置正确,到 `helloworld.sample` service 的流量将在 local 和 remote 实例之间进行分发,导致响应 body 中 `v1``v2` 都可能出现。
{{< text bash >}}
$ kubectl exec --context=$CTX_LOCAL -it -n sample $(kubectl get pod --context=$CTX_LOCAL -n sample -l app=sleep -o jsonpath={.items[0].metadata.name}) -- curl helloworld.sample:5000/hello
Defaulting container name to sleep.
Use 'kubectl describe pod/sleep-57f9d6fd6b-q4k4h -n sample' to see all of the containers in this pod.
Hello version: v2, instance: helloworld-v2-758dd55874-6x4t8
{{< /text >}}
{{< text bash >}}
$ kubectl exec --context=$CTX_LOCAL -it -n sample $(kubectl get pod --context=$CTX_LOCAL -n sample -l app=sleep -o jsonpath={.items[0].metadata.name}) -- curl helloworld.sample:5000/hello
Defaulting container name to sleep.
Use 'kubectl describe pod/sleep-57f9d6fd6b-q4k4h -n sample' to see all of the containers in this pod.
Hello version: v1, instance: helloworld-v1-86f77cd7bd-cpxhv
{{< /text >}}
您可以通过打印 sleep pod 的 `istio-proxy` 容器日志来验证访问的 endpoint 的 IP 地址。
{{< text bash >}}
$ kubectl logs --context=$CTX_LOCAL -n sample $(kubectl get pod --context=$CTX_LOCAL -n sample -l app=sleep -o jsonpath={.items[0].metadata.name}) istio-proxy
[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:443" outbound|5000||helloworld.sample.svc.cluster.local - 10.20.194.146:5000 10.10.0.89:59496 -
[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 -
{{< /text >}}
v2 被调用时将记录 remote gateway IP `192.23.120.32:443`v1 被调用时将记录 local 实例 IP `10.10.0.90:5000`
## 清理
执行下列命令清理 demo service __和__ Istio 组件。
清理 `remote` 集群:
{{< text bash >}}
$ kubectl delete --context=$CTX_REMOTE -f istio-remote-auth.yaml
$ kubectl delete --context=$CTX_REMOTE ns istio-system
$ kubectl delete --context=$CTX_REMOTE -f helloworld-v2.yaml -n sample
$ kubectl delete --context=$CTX_REMOTE ns sample
{{< /text >}}
清理 `local` 集群:
{{< text bash >}}
$ kubectl delete --context=$CTX_LOCAL -f istio-auth.yaml
$ kubectl delete --context=$CTX_LOCAL ns istio-system
$ helm delete --purge --kube-context=$CTX_LOCAL istio-init
$ kubectl delete --context=$CTX_LOCAL -f helloworld-v1.yaml -n sample
$ kubectl delete --context=$CTX_LOCAL -f samples/sleep/sleep.yaml -n sample
$ kubectl delete --context=$CTX_LOCAL ns sample
{{< /text >}}