作者 | 王夕宁 阿里云高级技术专家
导读:本文摘自于阿里云高级技术专家王夕宁撰写的《Istio 服务网格技术解析与实战》一书,讲述了如何使用 Istio 进行多集群部署管理来阐述服务网格对多云环境、多集群即混合部署的支持能力。
在多控制平面拓扑的配置中,每个 Kubernetes 集群都会安装相同的 Istio 控制平面,并且每个控制平面只会管理自己集群内的服务端点。通过使用 Istio 网关、公共根证书颁发机构(CA)以及服务条目 ServiceEntry,可以将多个集群配置组成一个逻辑上的单一服务网格。这种方法没有特殊的网络要求,因此通常被认为是在 Kubernetes 集群之间没有通用网络连接时的一种最简单方法。
在这种拓扑配置下,Kubernetes 跨集群通信需要服务之间的双向 TLS 连接,要在集群之间启用双向 TLS 通信,每个集群的 Citadel 将配置由共享的根 CA 生成的中间 CA 证书,如图所示。
(多控制平面)
从共享的根 CA 为每个集群的 Citadel 生成中间 CA 证书,共享的根 CA 启用跨不同集群的双向 TLS 通信。为了便于说明,我们将 samples/certs 目录下 Istio 安装中提供的示例根 CA 证书用于两个集群。在实际部署中,你可能会为每个集群使用不同的 CA 证书,所有 CA 证书都由公共根 CA 签名。
在每个 Kubernetes 集群中实施以下步骤,以在所有集群中部署相同的 Istio 控制平面配置。
kubectl create namespace istio-system kubectl create 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 -f $i; done
helm template install/kubernetes/helm/istio --name istio --namespace istio-system \ -f install/kubernetes/helm/istio/values-istio-multicluster-gateways.yaml > ./istio.yaml kubectl apply -f ./istio.yaml
确保上述步骤在每个 Kubernetes 集群中都执行成功。当然,通过 helm 生成 istio.yaml 的命令执行一次即可。
为远程集群中的服务提供 DNS 解析,则现有应用程序不需要做修改就可以运行,因为应用程序通常期望通过其 DNS 名称来解析服务并访问所得到的 IP 地址。Istio 本身不使用 DNS 在服务之间路由请求,同一个 Kubernetes 集群下的服务会共享一个相同的 DNS 后缀(例如 svc.cluster.local)。Kubernetes DNS 为这些服务提供 DNS 解析能力。为了给远程集群中的服务提供相似的设置,将远程集群中的服务以 ..global 的格式命名。
Istio 安装包中附带了一个 CoreDNS 服务器,该服务器将为这些服务提供DNS解析能力。为了利用这个 DNS 解析能力,需要配置 Kubernetes 的 DNS 服务指向该 CoreDNS 服务。该 CoreDNS 服务将作为 .global DNS 域的 DNS 服务器。
对于使用 kube-dns 的集群,请创建以下配置项或更新现有的配置项:
kubectl apply -f - <<EOF apiVersion: v1 kind: ConfigMap metadata: name: kube-dns namespace: kube-system data: stubDomains: | {"global": ["$(kubectl get svc -n istio-system istiocoredns -o jsonpath={.spec.clusterIP})"]} EOF
对于使用 CoreDNS 的集群,请创建以下配置项或更新现有的配置项:
kubectl apply -f - <<EOF apiVersion: v1 kind: ConfigMap metadata: name: coredns namespace: kube-system data: Corefile: | .:53 { errors health kubernetes cluster.local in-addr.arpa ip6.arpa { pods insecure upstream fallthrough in-addr.arpa ip6.arpa } prometheus :9153 proxy . /etc/resolv.conf cache 30 reload loadbalance } global:53 { errors cache 30 proxy . $(kubectl get svc -n istio-system istiocoredns -o jsonpath={. spec.clusterIP}) } EOF
为了演示跨集群访问,在一个 Kubernetes 集群中部署 sleep 应用服务,在第二个集群中部署 httpbin 应用服务,然后验证 sleep 应用是否可以调用远程集群的 httpbin 服务。
kubectl create namespace app1 kubectl label namespace app1 istio-injection=enabled kubectl apply -n app1 -f samples/sleep/sleep.yaml export SLEEP_POD=$(kubectl get -n app1 pod -l app=sleep -o jsonpath={.items..metadata.name})
kubectl create namespace app2 kubectl label namespace app2 istio-injection=enabled kubectl apply -n app2 -f samples/httpbin/httpbin.yaml
export CLUSTER2_GW_ADDR=$(kubectl get svc --selector=app=istio-ingressgateway \ -n istio-system -o jsonpath="{.items[0].status.loadBalancer.ingress[0].ip}")
对于 *.global 域下服务的 DNS 解析,需要为这些服务分配一个 IP 地址,并且保证 .globalDNS 域中的每个服务在集群中必须具有唯一的 IP 地址。这些 IP 地址在 pod 之外是不可路由的。在这个例子中,我们将使用网段 127.255.0.0/16 来避免与其他的IP冲突。这些 IP 的应用流量将由 Sidecar 代理捕获并路由到适当的其他远程服务。
在集群 cluster1 中创建该 httpbin 服务对应的 ServiceEntry,执行如下命令:
kubectl apply -n app1 -f - <<EOF apiVersion: networking.istio.io/v1alpha3 kind: ServiceEntry metadata: name: httpbin-app2 spec: hosts: # must be of form name.namespace.global - httpbin.app2.global # Treat remote cluster services as part of the service mesh # as all clusters in the service mesh share the same root of trust. location: MESH_INTERNAL ports: - name: http1 number: 8000 protocol: http resolution: DNS addresses: # the IP address to which httpbin.bar.global will resolve to # must be unique for each remote service, within a given cluster. # This address need not be routable. Traffic for this IP will be captured # by the sidecar and routed appropriately. - 127.255.0.2 endpoints: # This is the routable address of the ingress gateway in cluster2 that # sits in front of sleep.bar service. Traffic from the sidecar will be # routed to this address. - address: ${CLUSTER2_GW_ADDR} ports: http1: 15443 # Do not change this port value EOF
上面的配置将会使集群 cluster1 中访问 httpbin.app2.global 的所有流量,包括访问它的任何端口的流量,都会被路由到启用了双向 TLS 连接的端点 :15443 上。
端口 15443 的网关是一个特殊的 SNI 感知的 Envoy 代理,它是在前面开始部分中作为多集群 Istio 安装步骤的一部分预先配置和安装的。进入端口 15443 的流量将在目标集群的适当内部服务的 pod 中进行负载均衡。
在集群 cluster1 下执行如下命令查看容器 istiocoredns,可以看到上述 ServiceEntry 的域名映射关系已经被加载:
export ISTIO_COREDNS=$(kubectl get -n istio-system po -l app=istiocoredns -o jsonpath={.items..metadata.name}) kubectl logs --tail 2 -n istio-system ${ISTIO_COREDNS} -c istio-coredns-plugin
执行结果如下所示:
kubectl exec $SLEEP_POD -n app1 -c sleep -- curl httpbin.app2.global:8000/headers
执行结果如下所示:
至此,集群 cluster1 与 cluster2 在多控制平面配置下完成了连通。
通过前面的文章,我们已经了解了 Istio 的很多功能,例如基本版本的路由等,可以在单个 Kubernetes 集群上很容易地实现。而很多真实的业务场景中,基于微服务的应用程序并非那么简单,而是需要在多个位置跨集群去分配和运行服务。那么问题就来了,是否 Istio 的这些功能同样可以很简单地运行在这些真实的复杂环境中呢?
下面我们将会通过一个示例来了解 Istio 的流量管理功能如何在具有多个控制平面拓扑的多集群网格中正常运行。
kubectl create namespace hello kubectl label namespace hello istio-injection=enabled kubectl apply -n hello -f samples/sleep/sleep.yaml kubectl apply -n hello -f samples/helloworld/service.yaml kubectl apply -n hello -f samples/helloworld/helloworld.yaml -l version=v1
kubectl create namespace hello kubectl label namespace hello istio-injection=enabled kubectl apply -n hello -f samples/helloworld/service.yaml kubectl apply -n hello -f samples/helloworld/helloworld.yaml -l version=v2 kubectl apply -n hello -f samples/helloworld/helloworld.yaml -l version=v3
在我们的例子中,它是 helloworld.hello.global,所以我们需要在集群 cluster1 中创建服务条目 ServiceEntry 和目标规则 DestinationRule。服务条目 ServiceEntry 将使用集群 cluster2 的入口网关作为端点地址来访问服务。
通过使用以下命令在集群 cluster1 中创建 helloworld 服务对应的服务条目 ServiceEntry 和目标规则DestinationRule:
kubectl apply -n hello -f - <<EOF apiVersion: networking.istio.io/v1alpha3 kind: ServiceEntry metadata: name: helloworld spec: hosts: - helloworld.hello.global location: MESH_INTERNAL ports: - name: http1 number: 5000 protocol: http resolution: DNS addresses: - 127.255.0.8 endpoints: - address: ${CLUSTER2_GW_ADDR} labels: cluster: cluster2 ports: http1: 15443 # Do not change this port value --- apiVersion: networking.istio.io/v1alpha3 kind: DestinationRule metadata: name: helloworld-global spec: host: helloworld.hello.global trafficPolicy: tls: mode: ISTIO_MUTUAL subsets: - name: v2 labels: cluster: cluster2 - name: v3 labels: cluster: cluster2 EOF
kubectl apply -n hello -f - <<EOF apiVersion: networking.istio.io/v1alpha3 kind: DestinationRule metadata: name: helloworld spec: host: helloworld.hello.svc.cluster.local trafficPolicy: tls: mode: ISTIO_MUTUAL subsets: - name: v1 labels: version: v1 EOF
而在集群 cluster2 中创建子集 v2 和 v3 对应的目标规则,执行如下命令:
kubectl apply -n hello -f - <<EOF apiVersion: networking.istio.io/v1alpha3 kind: DestinationRule metadata: name: helloworld spec: host: helloworld.hello.svc.cluster.local trafficPolicy: tls: mode: ISTIO_MUTUAL subsets: - name: v2 labels: version: v2 - name: v3 labels: version: v3 EOF
应用下面的虚拟服务将会使得来自用户 jason 对 helloworld 的流量请求指向位于集群 cluster2 中的版本 v2 和 v3,其中 v2 比例为 70%,v3 比例为 30%;来自任何其他用户对 helloworld 的流量请求都将转到位于集群 cluster1 中的版本 v1:
kubectl apply -n hello -f - <<EOF apiVersion: networking.istio.io/v1alpha3 kind: VirtualService metadata: name: helloworld spec: hosts: - helloworld.hello.svc.cluster.local - helloworld.hello.global http: - match: - headers: end-user: exact: jason route: - destination: host: helloworld.hello.global subset: v2 weight: 70 - destination: host: helloworld.hello.global subset: v3 weight: 30 - route: - destination: host: helloworld.hello.svc.cluster.local subset: v1 EOF
执行多次调用,可以从下面的执行结果中看出,上述流量路由的规则生效,这也说明了在多控制平面拓扑下,用于路由的规则定义与在本地集群的使用方式是一样的:
设置多集群网格的最简单方法是使用多控制平面拓扑,因为它没有特殊的网络要求。通过上述示例可以看出,在单个 Kubernetes 集群上运行的路由功能同样很容易地在多个集群中使用运行。
**《Istio服务网格技术解析与实战》读者可免费体验 ASM 产品进行学习!**点击了解阿里云服务网格产品 ASM:www.aliyun.com/product/servicemesh
王夕宁 阿里云高级技术专家,阿里云服务网格产品 ASM 及 Istio on Kubernetes 技术负责人,专注于 Kubernetes、云原生、服务网格等领域。曾在 IBM 中国开发中心工作,担任过专利技术评审委员会主席,拥有 40 多项相关领域的国际技术专利。《Istio 服务网格解析与实战》一书由其撰写,详细介绍了 Istio 的基本原理与开发实战,包含大量精选案例和参考代码可以下载,可快速入门 Istio 开发。Gartner 认为,2020 年服务网格将成为所有领先的容器管理系统的标配技术。本书适合所有对微服务和云原生感兴趣的读者,推荐大家对本书进行深入的阅读。