多集群联邦负载均衡实践

最近更新时间:2021-01-29 21:07:14

概述

Istio 实现联邦集群负载均衡

Istio 支持多集群部署,并能够实现跨集群服务发现和跨集群负载均衡。Istio 多集群的部署方案一般有两种,分别是多副本控制平面部署和共享控制平面部署。而共享控制平面部署又分为单一网络和多网络部署。在多集群联邦环境中集群常分布在不同可用区下,因此集群不会处于同一网络,则共享控制平面的多网络部署模式符合多集群联邦场景下的负载均衡场景。

Istio 多网络共享控制平面方案

多集群联邦负载均衡实践

使用共享控制平面方案部署,需要有一个主集群用来部署 control plane,其他远程集群要加入 istio 网格的集群则需要连接主集群的 control plane。在Istio 1.7的部署方案中,控制平面将会以istiod形态部署,远程集群中部署的istiod用于CA和集群中工作负载的webhook注入,服务发现则会导向主集群中的控制面实现。集群间通信通过gateway实现,不同集群的工作负载之间既不要求 VPN 连接也不要求直接网络访问。

应用及配置

前提条件

  • Kubernetes 集群版本要求1.16及以上。
  • 创建联邦集群要求至少两个 Kubernetes 集群,多个集群可以属于不同VPC。
  • 若Member/remote集群与Host集群不属于同一VPC,需为Member/remote集群开启Api Server公网访问。
  • Host集群worker节点预留资源≥4核8G。

以下以两个处于不同VPC环境的集群为例,基于集群联邦实现Istio多集群网格部署以及跨集群负载均衡。

准备工作

创建两个集群

登录容器服务控制台,分别在北京6区和上海2区创建两个Kubernetes 集群。

多集群联邦负载均衡实践

多集群联邦负载均衡实践

创建联邦集群

在容器控制台进入多集群-联邦管理,联邦地域为目标Host集群所在地域,点击新建集群联邦

多集群联邦负载均衡实践

选择地域与目标Host集群,点击创建,即开始联邦控制面的部署。

多集群联邦负载均衡实践

查看部署状态,当全部组件部署成功后,此时Host集群处于"未加入"状态,点击更多 > 加入联邦将集群加入到联邦集群中。

多集群联邦负载均衡实践

接着添加另一个Kubernetes 集群,点击新增Member集群

多集群联邦负载均衡实践

为了演示我们选择与Host集群不同的区域下的集群,这也是使用联邦集群实现跨地域容灾常用部署方式。

多集群联邦负载均衡实践

添加完成后,联邦集群页面中已经可以看到有两个Kubernetes集群。

多集群联邦负载均衡实践

查看联邦集群状态

进入联邦Host集群节点,获取当前联邦的集群信息。

$ kubectl get kubefedclusters -n kube-federation-system
NAME                                   AGE    READY
d161c69e-286b-4541-901f-b121c9517f4e   5m     True
fcb0f8d3-9907-4a4a-9653-4584b367ee29   12m    True

准备联邦证书及kubefedctl

在安装好联邦集群后,联邦证书会默认保存在Host集群kube-system namespaces下的kubefedconfig的configMap中。将集群证书保存到本地~/.kube/config中,便于kubectl及kubefedctl读取使用。

$ kubectl get cm -n kube-system kubefedconfig -o yaml

验证证书是否正确配置,并将当前context指向Host集群。

$ kubectl config get-contexts
CURRENT   NAME                                           CLUSTER                                        AUTHINFO                                     NAMESPACE
          context-d161c69e-286b-4541-901f-b121c9517f4e   cluster-d161c69e-286b-4541-901f-b121c9517f4e   admin-d161c69e-286b-4541-901f-b121c9517f4e   
          context-fcb0f8d3-9907-4a4a-9653-4584b367ee29   cluster-fcb0f8d3-9907-4a4a-9653-4584b367ee29   admin-fcb0f8d3-9907-4a4a-9653-4584b367ee29

$ kubectl config use-context context-fcb0f8d3-9907-4a4a-9653-4584b367ee29
Switched to context "context-fcb0f8d3-9907-4a4a-9653-4584b367ee29".

下载kubefedctl命令行工具。

$ wget https://github.com/kubernetes-sigs/kubefed/releases/download/v0.4.1/kubefedctl-0.4.1-linux-amd64.tgz
$ tar zxvf kubefedctl-0.4.1-linux-amd64.tgz
$ rm kubefedctl /usr/local/bin/

Istio多集群网格部署

部署示例用到了istioctl命令和istio提供的配置文件,下载并安装。

$ curl -L https://istio.io/downloadIstio | sh -
$ cd istio-1.7.2 && export PATH=$PWD/bin:$PATH

在本配置中,安装 Istio 时同时开启控制平面和应用pods的双向TLS。对于共享的根CA使用Istio示例目录下相同的Istio证书,在host和member集群中都创建相同的namespace和secret保存根证书。

# 创建 Namespace/istio-system 和 FederatedNamespace/istio-system
$ kubectl create namespace istio-system
$ kubefedctl federate ns istio-system

# 创建secret 和 FederatedSecret
$ 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

$ kubefedctl federate secret cacerts -n istio-system
安装主集群

在联邦Host集群部署istio控制面板服务,在部署istio控制面服务时会使用以下环境变量用于替换其中配置模板中定义的变量并生成配置文件。

# 设置环境变量
$ export MAIN_CLUSTER_CTX=context-fcb0f8d3-9907-4a4a-9653-4584b367ee29
$ export REMOTE_CLUSTER_CTX=context-d161c69e-286b-4541-901f-b121c9517f4e

$ export MAIN_CLUSTER_NAME=cluster-fcb0f8d3-9907-4a4a-9653-4584b367ee29
$ export REMOTE_CLUSTER_NAME=cluster-d161c69e-286b-4541-901f-b121c9517f4e

$ export MAIN_CLUSTER_NETWORK=network1
$ export REMOTE_CLUSTER_NETWORK=network2

# 创建主集群配置文件
cat <<EOF> istio-main-cluster.yaml
apiVersion: install.istio.io/v1alpha1
kind: IstioOperator
spec:
  values:
    global:
      multiCluster:
        clusterName: ${MAIN_CLUSTER_NAME}
      network: ${MAIN_CLUSTER_NETWORK}

      # Mesh network configuration. This is optional and may be omitted if
      # all clusters are on the same network.
      meshNetworks:
        ${MAIN_CLUSTER_NETWORK}:
          endpoints:
          - fromRegistry:  ${MAIN_CLUSTER_NAME}
          gateways:
          - registry_service_name: istio-ingressgateway.istio-system.svc.cluster.local
            port: 443

        ${REMOTE_CLUSTER_NETWORK}:
          endpoints:
          - fromRegistry: ${REMOTE_CLUSTER_NAME}
          gateways:
          - registry_service_name: istio-ingressgateway.istio-system.svc.cluster.local
            port: 443

      # Use the existing istio-ingressgateway.
      meshExpansion:
        enabled: true
EOF

# 部署控制面板
$ istioctl install -f istio-main-cluster.yaml --context=${MAIN_CLUSTER_CTX}
Detected that your cluster does not support third party JWT authentication. Falling back to less secure first party JWT. See https://istio.io/docs/ops/best-practices/security/#configure-third-party-service-account-tokens for details.
✔ Istio core installed                                                                                                                                       
✔ Istiod installed                                                                                                                                           
✔ Ingress gateways installed                                                                                                                                 
✔ Installation complete    

$ kubectl get pod -n istio-system --context=${MAIN_CLUSTER_CTX}
NAME                                    READY   STATUS    RESTARTS   AGE
istio-ingressgateway-6bdbbc5566-c9kxk   1/1     Running   0          26s
istiod-689b5cbd7d-2dsml                 1/1     Running   0          37s

# 设置环境变量`ISTIOD_REMOTE_EP`
$ export ISTIOD_REMOTE_EP=$(kubectl get svc -n istio-system --context=${MAIN_CLUSTER_CTX} istio-ingressgateway -o jsonpath='{.status.loadBalancer.ingress[0].ip}')
$ echo "ISTIOD_REMOTE_EP is ${ISTIOD_REMOTE_EP}"
远程集群安装
# 创建远程集群配置文件
cat <<EOF> istio-remote0-cluster.yaml
apiVersion: install.istio.io/v1alpha1
kind: IstioOperator
spec:
  values:
    global:
      # The remote cluster's name and network name must match the values specified in the
      # mesh network configuration of the primary cluster.
      multiCluster:
        clusterName: ${REMOTE_CLUSTER_NAME}
      network: ${REMOTE_CLUSTER_NETWORK}

      # Replace ISTIOD_REMOTE_EP with the the value of ISTIOD_REMOTE_EP set earlier.
      remotePilotAddress: ${ISTIOD_REMOTE_EP}

  ## The istio-ingressgateway is not required in the remote cluster if both clusters are on
  ## the same network. To disable the istio-ingressgateway component, uncomment the lines below.

  components:
   ingressGateways:
   - name: istio-ingressgateway
     enabled: true
EOF

$ istioctl install -f istio-remote0-cluster.yaml --context=${REMOTE_CLUSTER_CTX}
$ kubectl get pod -n istio-system

跨集群负载均衡设置

配置 ingress 网关

在联邦集群多网络共享控制平面场景下,使用Istio ingress gateway作为流量入口,实现跨网络间通信。为了提高网络通信安全性,需要配置ingress gateway使用443端口和SNI header。

cat <<EOF> cluster-aware-gateway.yaml
apiVersion: networking.istio.io/v1alpha3
kind: Gateway
metadata:
  name: cluster-aware-gateway
  namespace: istio-system
spec:
  selector:
    istio: ingressgateway
  servers:
  - port:
      number: 443
      name: tls
      protocol: TLS
    tls:
      mode: AUTO_PASSTHROUGH
    hosts:
    - "*.local"
EOF

在Host和Member集群中创建gateway,部署gateway用于实现跨集群服务路由。

$ kubectl apply -f cluster-aware-gateway.yaml --context=${MAIN_CLUSTER_CTX}
$ kubectl apply -f cluster-aware-gateway.yaml --context=${REMOTE_CLUSTER_CTX}

配置跨集群服务注册

为了实现联邦集群跨集群负载均衡,部署联邦的Host集群上的控制面必须要能够访问到全部集群的kube-apiserver服务,以实现服务发现,获取endpoints和pod属性等。 要访问member集群需要配置member集群的kube-apiserver公网访问证书。获取金山云kubernetes集群公网,容器服务控制台中选择需要获取证书的集群,进入集群基本信息页面,点击获取集群config,选择公网访问config

多集群联邦负载均衡实践

将获取到的remote集群的公网访问证书添加到.kube/config文件中,并执行以下操作。Istio将使用该公网证书在remote集群创建serviceaccount io-reader-service-account 和相关role、rolebinding, 并在host集群中创建secret保存io-reader-service-account的证书信息。

$ istioctl x create-remote-secret --name ${REMOTE_CLUSTER_NAME} --context=${REMOTE_CLUSTER_CTX} | \
    kubectl apply -f - --context=${MAIN_CLUSTER_CTX}

跨集群负载均衡功能测试

在Host集群和Member集群都部署一个 helloworld 服务,在主集群部署 v1 版本服务,在从集群部署 v2版本服务。部署完成后,在任何一个集群访问该服务,流量会在多集群服务间路由。

部署测试服务

创建 sample namespace 并设置 istio-injection=enabled 标签。

$ kubectl create namespace sample --context=${MAIN_CLUSTER_CTX}
$ kubectl label namespace sample istio-injection=enabled --context=${MAIN_CLUSTER_CTX}

创建 federatenamespace。

$ kubefedctl federate ns sample

创建 federatedeployment 部署在两个集群中的helloworld服务并使用不同的镜像版本,分别为v1和v2。helloworld-deploy.yaml:

apiVersion: types.kubefed.io/v1beta1
kind: FederatedDeployment
metadata:
  name: helloworld
  namespace: sample
spec:
  template:
    metadata:
      labels:
        app: helloworld
        version: v1
    spec:
      replicas: 1
      selector:
        matchLabels:
          app: helloworld
          version: v1
      template:
        metadata:
          labels:
            app: helloworld
            version: v1
        spec:
          containers:
            - image: docker.io/istio/examples-helloworld-v1
              name: helloworld
  placement:
    clusters:
      - name: fcb0f8d3-9907-4a4a-9653-4584b367ee29
      - name: d161c69e-286b-4541-901f-b121c9517f4e
  overrides:
    - clusterName: d161c69e-286b-4541-901f-b121c9517f4e
      clusterOverrides:
        - path: "/spec/template/spec/containers/0/image"
          value: "docker.io/istio/examples-helloworld-v2"
        - path: "/spec/template/metadata/labels/version"
          value: "v2"
        - path: "/spec/selector/matchLabels/version"
          value: "v2"
        - path: "/metadata/labels/version"
          value: "v2"

helloworld-svc.yaml:

apiVersion: v1
kind: Service
metadata:
  name: helloworld
  labels:
    app: helloworld
spec:
  ports:
  - port: 5000
    name: http
  selector:
    app: helloworld

在Host和Member中部署helloworld服务:

kubectl apply -f helloworld-svc.yaml -n sample --context=${MAIN_CLUSTER_CTX}
kubectl apply -f helloworld-svc.yaml -n sample --context=${REMOTE_CLUSTER_CTX}

验证

为了演示访问 helloworld 服务的流量是分布到多集群的,需要部署一个示例提供的 sleep 服务来调用 helloworld 服务。

# 在Host和member集群分别部署sleep服务
$ kubectl apply -f samples/sleep/sleep.yaml -n sample --context=${MAIN_CLUSTER_CTX}
$ kubectl apply -f samples/sleep/sleep.yaml -n sample --context=${REMOTE_CLUSTER_CTX}

# 在Host集群中多次调用helloworld.sample服务
$ kubectl exec -it -n sample -c sleep --context=${MAIN_CLUSTER_CTX} $(kubectl get pod -n sample -l app=sleep --context=${MAIN_CLUSTER_CTX} -o jsonpath='{.items[0].metadata.name}') -- curl helloworld.sample:5000/hello

# 在Member集群中多次调用helloworld.sample服务
$ kubectl exec -it -n sample -c sleep --context=${REMOTE_CLUSTER_CTX} $(kubectl get pod -n sample -l app=sleep --context=${REMOTE_CLUSTER_CTX} -o jsonpath='{.items[0].metadata.name}') -- curl helloworld.sample:5000/hello

NOTE: 如果部署正确,服务helloworld.sample的流量会分发到cluster-d161c69e-286b-4541-901f-b121c9517f4e和cluster-fcb0f8d3-9907-4a4a-9653-4584b367ee29集群,得到的响应会是v1和v2两种。

Hello version: v2, instance: helloworld-v2-758dd55874-cxjnw
Hello version: v1, instance: helloworld-v1-7c5df4c84d-vzjtj

在Host集群中通过如下命令查看istio为helloworld 服务注册的路由信息,如下,对于 helloworld 服务,istio 发现并注册了两个路由,一个指向本地的 helloworld 服务,另一个通过成员集群的 ingress gateway 的EIP来路由到从集群部署的helloworld 服务。

$ kubectl get pod -n sample -l app=sleep --context=${MAIN_CLUSTER_CTX} -o name | cut -f2 -d'/' |     xargs -I{} istioctl -n sample --context=${MAIN_CLUSTER_CTX} proxy-config endpoints {} --cluster "outbound|5000||helloworld.sample.svc.cluster.local"
ENDPOINT              STATUS      OUTLIER CHECK     CLUSTER
10.2.2.14:5000        HEALTHY     OK                outbound|5000||helloworld.sample.svc.cluster.local
120.92.145.63:443     HEALTHY     OK                outbound|5000||helloworld.sample.svc.cluster.local

# 本地Host集群helloworld endpoint 信息
$ kubectl get ep helloworld -n sample --context=${MAIN_CLUSTER_CTX}
NAME         ENDPOINTS        AGE
helloworld   10.2.2.14:5000   20m

# 远程Member集群helloworld endpoint 信息
$ kubectl get svc -n istio-system istio-ingressgateway --context=${REMOTE_CLUSTER_CTX}
NAME                   TYPE           CLUSTER-IP    EXTERNAL-IP     PORT(S)                                                      AGE
istio-ingressgateway   LoadBalancer   10.254.60.1   120.92.145.63   15021:31509/TCP,80:32473/TCP,443:30816/TCP,15443:31135/TCP   20m

金山云,开启您的云计算之旅

免费注册