如何使用 Istio 进行多集群部署管理:多控制平面

头图.png

做者 | 王夕宁  阿里云高级技术专家json

导读:本文摘自于阿里云高级技术专家王夕宁撰写的《Istio 服务网格技术解析与实战》一书,讲述了如何使用 Istio 进行多集群部署管理来阐述服务网格对多云环境、多集群即混合部署的支持能力。api

前文详情:服务器

在多控制平面拓扑的配置中,每一个 Kubernetes 集群都会安装相同的 Istio 控制平面,而且每一个控制平面只会管理本身集群内的服务端点。经过使用 Istio 网关、公共根证书颁发机构(CA)以及服务条目 ServiceEntry,能够将多个集群配置组成一个逻辑上的单一服务网格。这种方法没有特殊的网络要求,所以一般被认为是在 Kubernetes 集群之间没有通用网络链接时的一种最简单方法。网络

在这种拓扑配置下,Kubernetes 跨集群通讯须要服务之间的双向 TLS 链接,要在集群之间启用双向 TLS 通讯,每一个集群的 Citadel 将配置由共享的根 CA 生成的中间 CA 证书,如图所示。app

1.png
(多控制平面)负载均衡

部署控制平面

从共享的根 CA 为每一个集群的 Citadel 生成中间 CA 证书,共享的根 CA 启用跨不一样集群的双向 TLS 通讯。为了便于说明,咱们将 samples/certs 目录下 Istio 安装中提供的示例根 CA 证书用于两个集群。在实际部署中,你可能会为每一个集群使用不一样的 CA 证书,全部 CA 证书都由公共根 CA 签名。less

在每一个 Kubernetes 集群中实施如下步骤,以在全部集群中部署相同的 Istio 控制平面配置。curl

  1. 使用如下的命令为生成的 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
  1. 安装 Istio 的 CRD 并等待几秒钟,以便将它们提交给 Kubernetes API 服务器,以下所示:
for
i in install/kubernetes/helm/istio-init/files/crd*yaml; do kubectl apply -f $i;
done
  1. 部署 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 的命令执行一次便可。ide

设置 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 服务。

  1. 部署 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})
  1. 部署 httpbin 服务到第二个集群 cluster2 中,执行以下命令:
kubectl
create namespace app2
kubectl
label namespace app2 istio-injection=enabled
kubectl
apply -n app2 -f samples/httpbin/httpbin.yaml
  1. 获取集群 cluster2 的入口网关地址,以下所示:
export
CLUSTER2_GW_ADDR=$(kubectl get svc --selector=app=istio-ingressgateway \
  -n istio-system -o
jsonpath="{.items[0].status.loadBalancer.ingress[0].ip}")
  1. 为了让在集群 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

执行结果以下所示:

2.png

  1. 验证在集群 cluster1 中的 sleep 服务是否能够正常调用位于集群 cluster2 中的 httpbin 服务,在集群 cluster1 执行以下命令:
kubectl
exec $SLEEP_POD -n app1 -c sleep -- curl httpbin.app2.global:8000/headers

执行结果以下所示:

3.png

至此,集群 cluster1 与 cluster2 在多控制平面配置下完成了连通。

跨集群的版本路由

经过前面的文章,咱们已经了解了 Istio 的不少功能,例如基本版本的路由等,能够在单个 Kubernetes 集群上很容易地实现。而不少真实的业务场景中,基于微服务的应用程序并不是那么简单,而是须要在多个位置跨集群去分配和运行服务。那么问题就来了,是否 Istio 的这些功能一样能够很简单地运行在这些真实的复杂环境中呢?

下面咱们将会经过一个示例来了解 Istio 的流量管理功能如何在具备多个控制平面拓扑的多集群网格中正常运行。

  1. 首先,部署版本 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
  1. 部署版本 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
  1. 如前面章节中所述,多控制平面下,须要使用以 .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
  1. 在两个集群上建立目标规则。在集群 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
  1. 建立虚拟服务以路由流量。

应用下面的虚拟服务将会使得来自用户 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

执行屡次调用,能够从下面的执行结果中看出,上述流量路由的规则生效,这也说明了在多控制平面拓扑下,用于路由的规则定义与在本地集群的使用方式是同样的:

4.png

设置多集群网格的最简单方法是使用多控制平面拓扑,由于它没有特殊的网络要求。经过上述示例能够看出,在单个 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 等技术领域、聚焦云原生流行技术趋势、云原生大规模的落地实践,作最懂云原生开发者的公众号。”

相关文章
相关标签/搜索