如何使用 Istio 进行多集群部署管理:多控制平面
作者 | 王夕宁? 阿里云高级技术专家
导读:本文摘自于阿里云高级技术专家王夕宁撰写的《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?控制平面配置。
- 使用以下的命令为生成的?CA?证书创建?Kubernetes?密钥,如下所示:
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
- 安装?Istio?的?CRD?并等待几秒钟,以便将它们提交给?Kubernetes API?服务器,如下所示:
for i in install/kubernetes/helm/istio-init/files/crd*yaml; do kubectl apply -f $i; done
- 部署?Istio?控制平面:如果?helm?依赖项缺失或者不是最新的,可以通过?helm dep update?来更新这些依赖项。注意因为没有使用?istio-cni,可以暂时将其从依赖项?requirements.yaml?中去掉再执行更新操作。具体执行命令如下:
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?解析,则现有应用程序不需要做修改就可以运行,因为应用程序通常期望通过其?DNS?名称来解析服务并访问所得到的?IP?地址。Istio?本身不使用?DNS?在服务之间路由请求,同一个?Kubernetes?集群下的服务会共享一个相同的?DNS?后缀(例如?svc.cluster.local)。Kubernetes DNS?为这些服务提供?DNS?解析能力。为了给远程集群中的服务提供相似的设置,将远程集群中的服务以?<name>.<namespace>.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?服务。
- 部署?sleep?服务到第一个集群?cluster1?中,执行如下命令:
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})
- 部署?httpbin?服务到第二个集群?cluster2?中,执行如下命令:
kubectl create namespace app2 kubectl label namespace app2 istio-injection=enabled kubectl apply -n app2 -f samples/httpbin/httpbin.yaml
- 获取集群?cluster2?的入口网关地址,如下所示:
export CLUSTER2_GW_ADDR=$(kubectl get svc --selector=app=istio-ingressgateway -n istio-system -o jsonpath="{.items[0].status.loadBalancer.ingress[0].ip}")
- 为了让在集群?cluster1?中的服务?sleep?能够访问集群?cluster2?中的服务?httpbin,我们需要在集群?cluster1?中为服务?httpbin?创建一个服务条目?ServiceEntry?资源。服务条目?ServiceEntry?的主机名应该是<name>.<namespace>.globalname,其中?name?和?namespace?分别对应于集群?cluster2?中的远程服务的名称和命名空间。
对于?*.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?连接的端点?<IPofCluster2IngressGateway>: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
执行结果如下所示:
- 验证在集群?cluster1?中的?sleep?服务是否可以正常调用位于集群?cluster2?中的?httpbin?服务,在集群?cluster1?执行如下命令:
kubectl exec $SLEEP_POD -n app1 -c sleep -- curl httpbin.app2.global:8000/headers
执行结果如下所示:
至此,集群?cluster1?与?cluster2?在多控制平面配置下完成了连通。
跨集群的版本路由
通过前面的文章,我们已经了解了?Istio?的很多功能,例如基本版本的路由等,可以在单个?Kubernetes?集群上很容易地实现。而很多真实的业务场景中,基于微服务的应用程序并非那么简单,而是需要在多个位置跨集群去分配和运行服务。那么问题就来了,是否?Istio?的这些功能同样可以很简单地运行在这些真实的复杂环境中呢?
下面我们将会通过一个示例来了解?Istio?的流量管理功能如何在具有多个控制平面拓扑的多集群网格中正常运行。
- 首先,部署版本?v1?的?helloworld?服务到第一个集群?cluster1?中,执行如下命令:
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
- 部署版本?v2?与?v3?的?helloworld?服务到第二个集群?cluster2?中,执行如下命令:
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
- 如前面章节中所述,多控制平面下,需要使用以?.global?为后缀的?DNS?名称访问远程服务。
在我们的例子中,它是?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
- 在两个集群上创建目标规则。在集群?cluster1?中创建子集?v1?对应的目标规则,执行如下命令:
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 年服务网格将成为所有领先的容器管理系统的标配技术。本书适合所有对微服务和云原生感兴趣的读者,推荐大家对本书进行深入的阅读。
课程推荐
为了更多开发者能够享受到 Serverless 带来的红利,这一次,我们集结了 10+ 位阿里巴巴 Serverless 领域技术专家,打造出最适合开发者入门的 Serverless 公开课,让你即学即用,轻松拥抱云计算的新范式——Serverless。
点击即可免费观看课程:https://developer.aliyun.com/learning/roadmap/serverless
“阿里巴巴云原生关注微服务、Serverless、容器、Service Mesh 等技术领域、聚焦云原生流行技术趋势、云原生大规模的落地实践,做最懂云原生开发者的公众号。”