写点什么

跨集群流量调度实现 Kubernetes 集群金丝雀升级

作者:Flomesh
  • 2023-01-06
    北京
  • 本文字数:6025 字

    阅读完需:约 20 分钟

跨集群流量调度实现 Kubernetes 集群金丝雀升级

有了多集群服务和跨集群的流量调度之后,使用 Kubernetes 的方式会发生很大的变化。流量的管理不再限制单一集群内,而是横向跨越了多个集群。最重要的是这一切“静悄悄地”发生,对应用来说毫无感知。


就拿 Kubernetes 版本升级来说吧。记得曾经经历过集群的原地升级:团队的几个人经过多次、多个环境的演练,还要在凌晨的时候进行生产环境的升级。幸好最后是有惊无险,整个过程的体验就像是下图一样:为飞行中的飞机换引擎。



解决了跨集群的流量调度之后,一切就会变得简单:只需重新建个集群,慢慢将应用迁移到新的集群,让乘客来个“空中转机”。

方案

与之前所做的示例类似,整个方案的核心仍然是跨集群的服务调用:服务可以像使用本地 Service 一样使用多集群 Service。



升级过程中,创建新版本的 Kubernetes 集群,其他的中间件复用现有的,这样少了数据同步等问题。剩下的便是,调整 CD 的流程将服务“同时部署到新的集群”。然后再通过 全局流量策略 慢慢放少部分流量到新的集群中进行测试,边测试边调整流量。两个集群的服务也可保持一段时间观察稳定性,然后再慢慢减少原集群的实例直至所有实例下线。


接下来,我们使用进行下示例演示。

演示

环境准备

如上面的图展示的那样,我们先创建两个集群:control-plane1-231-25。 集群 1-23 就是我们现有的集群,1-25 就是最新版本的集群。


环境搭建

获取主机 IP 地址备用,192.168.1.110 是我本机的地址。


export HOST_IP=192.168.1.110
复制代码

搭建集群

使用 k3d 来创建这 3 个 集群。


API_PORT=6444 #6445 6446PORT=80 #81 82for CLUSTER_NAME in control-plane 1-23 1-25do  k3d cluster create ${CLUSTER_NAME} \    --image docker.io/rancher/k3s:v1.23.8-k3s2 \    --api-port "${HOST_IP}:${API_PORT}" \    --port "${API_PORT}:6443@server:0" \    --port "${PORT}:80@server:0" \    --servers-memory 4g \    --k3s-arg "--disable=traefik@server:0" \    --timeout 120s \    --wait    ((API_PORT=API_PORT+1))    ((PORT=PORT+1))done
复制代码

安装 FSM

在 3 个集群中安装 FSM。


helm repo updateexport FSM_NAMESPACE=flomeshexport FSM_VERSION=0.2.0-alpha.10for CLUSTER_NAME in control-plane 1-23 1-25do   kubectx k3d-${CLUSTER_NAME}  sleep 1  helm install --namespace ${FSM_NAMESPACE} --create-namespace --version=${FSM_VERSION} --set fsm.logLevel=5 fsm fsm/fsm  sleep 1  kubectl wait --for=condition=ready pod --all -n $FSM_NAMESPACE --timeout=120sdone
复制代码

加入集群组

将集群 1-231-25 纳入集群 control-plane 的管理。 不管是新集群还是旧集群,如果要进行跨集群的服务调用,都是要加入集群组的。


export HOST_IP=192.168.1.110kubectx k3d-control-planesleep 1PORT=81for CLUSTER_NAME in 1-23 1-25do  kubectl apply -f - <<EOFapiVersion: flomesh.io/v1alpha1kind: Clustermetadata:  name: ${CLUSTER_NAME}spec:  gatewayHost: ${HOST_IP}  gatewayPort: ${PORT}  kubeconfig: |+`k3d kubeconfig get ${CLUSTER_NAME} | sed 's|^|    |g' | sed "s|0.0.0.0|$HOST_IP|g"`EOF((PORT=PORT+1))done
复制代码

安装服务网格

下载 osm CLI


system=$(uname -s | tr [:upper:] [:lower:])arch=$(dpkg --print-architecture)release=v1.3.0-beta.1curl -L https://github.com/flomesh-io/osm-edge/releases/download/$release/osm-edge-$release-$system-$arch.tar.gz | tar -vxzf -./${system}-${arch}/osm versioncp ./${system}-${arch}/osm /usr/local/bin/
复制代码


将服务网格 osm-edge 安装到集群 1-231-25。控制平面不处理应用流量,无需安装。


export OSM_NAMESPACE=osm-systemexport OSM_MESH_NAME=osmfor CLUSTER_NAME in 1-23 1-25do  kubectx k3d-${CLUSTER_NAME}  DNS_SVC_IP="$(kubectl get svc -n kube-system -l k8s-app=kube-dns -o jsonpath='{.items[0].spec.clusterIP}')"osm install \    --mesh-name "$OSM_MESH_NAME" \    --osm-namespace "$OSM_NAMESPACE" \    --set=osm.certificateProvider.kind=tresor \    --set=osm.image.pullPolicy=Always \    --set=osm.sidecarLogLevel=error \    --set=osm.controllerLogLevel=warn \    --timeout=900s \    --set=osm.localDNSProxy.enable=true \    --set=osm.localDNSProxy.primaryUpstreamDNSServerIPAddr="${DNS_SVC_IP}"done
复制代码

部署实例应用

在集群 1-23httpbin 命名空间(由网格管理,会注入 sidecar)下,部署 httpbin 应用。这里的 httpbin 应用由 Pipy 实现,会返回当前的集群名,并提示被网格管理。


NAMESPACE=httpbinCLUSTER_NAME="1-23"
kubectx k3d-${CLUSTER_NAME}kubectl create namespace ${NAMESPACE}osm namespace add ${NAMESPACE}kubectl apply -n ${NAMESPACE} -f - <<EOFapiVersion: apps/v1kind: Deploymentmetadata: name: httpbin labels: app: pipyspec: replicas: 1 selector: matchLabels: app: pipy template: metadata: labels: app: pipy spec: containers: - name: pipy image: flomesh/pipy:latest ports: - containerPort: 8080 command: - pipy - -e - | pipy() .listen(8080) .serveHTTP(new Message('Hi, I am from ${CLUSTER_NAME} and controlled by mesh!\n'))---apiVersion: v1kind: Servicemetadata: name: httpbinspec: ports: - port: 8080 targetPort: 8080 protocol: TCP selector: app: pipyEOF
sleep 3kubectl wait --for=condition=ready pod -n ${NAMESPACE} --all --timeout=60s
复制代码


在命名空间 curl 下部署 curl 应用,这个命名空间也是被网格管理的,注入的 sidecar 会完全流量的跨集群调度。


export NAMESPACE=curlkubectx k3d-1-23kubectl create namespace ${NAMESPACE}osm namespace add ${NAMESPACE}kubectl apply -n ${NAMESPACE} -f - <<EOFapiVersion: v1kind: ServiceAccountmetadata:  name: curl---apiVersion: v1kind: Servicemetadata:  name: curl  labels:    app: curl    service: curlspec:  ports:    - name: http      port: 80  selector:    app: curl---apiVersion: apps/v1kind: Deploymentmetadata:  name: curlspec:  replicas: 1  selector:    matchLabels:      app: curl  template:    metadata:      labels:        app: curl    spec:      serviceAccountName: curl      containers:      - image: curlimages/curl        imagePullPolicy: IfNotPresent        name: curl        command: ["sleep", "365d"]EOF
sleep 3kubectl wait --for=condition=ready pod -n ${NAMESPACE} --all --timeout=60s
复制代码

测试

在集群 1-23 中的 curlhttpbin 发送请求。


kubectx k3d-1-23curl_client="$(kubectl get pod -n curl -l app=curl -o jsonpath='{.items[0].metadata.name}')"
kubectl exec "${curl_client}" -n curl -c curl -- curl -s http://httpbin.httpbin:8080/
复制代码


看到下面的输出,说明服务正常。


Hi, I am from 1-23 and controlled by mesh!
复制代码

导出服务

服务的导出就是向集群组注册服务,执行下面的命令将集群 1-23 中的服务 httpbin 注册到集群组。注意,该命令是在集群 1-23 中执行的。


export NAMESPACE_MESH=httpbinCLUSTER_NAME="1-23"kubectx k3d-${CLUSTER_NAME}kubectl apply -f - <<EOFapiVersion: flomesh.io/v1alpha1kind: ServiceExportmetadata:  namespace: ${NAMESPACE_MESH}  name: httpbinspec:  serviceAccountName: "*"  rules:    - portNumber: 8080      path: "/${CLUSTER_NAME}/httpbin-mesh"      pathType: PrefixEOFsleep 1
复制代码


此时,我们的系统如下图所示。



升级迁移

新集群中部署应用

有了新版本的集群之后,我们慢慢向新集群迁移服务。在集群 1-25 中部署 httpbin 服务。


NAMESPACE=httpbinCLUSTER_NAME="1-25"
kubectx k3d-${CLUSTER_NAME}kubectl create namespace ${NAMESPACE}osm namespace add ${NAMESPACE}kubectl apply -n ${NAMESPACE} -f - <<EOFapiVersion: apps/v1kind: Deploymentmetadata: name: httpbin labels: app: pipyspec: replicas: 1 selector: matchLabels: app: pipy template: metadata: labels: app: pipy spec: containers: - name: pipy image: flomesh/pipy:latest ports: - containerPort: 8080 command: - pipy - -e - | pipy() .listen(8080) .serveHTTP(new Message('Hi, I am from ${CLUSTER_NAME} and controlled by mesh!\n'))---apiVersion: v1kind: Servicemetadata: name: httpbinspec: ports: - port: 8080 targetPort: 8080 protocol: TCP selector: app: pipy---apiVersion: v1kind: Servicemetadata: name: httpbin-${CLUSTER_NAME}spec: ports: - port: 8080 targetPort: 8080 protocol: TCP selector: app: pipyEOF
sleep 3kubectl wait --for=condition=ready pod -n ${NAMESPACE} --all --timeout=60s
复制代码

导出服务

向集群组注册集群 1-25 的服务 httpbin


export NAMESPACE_MESH=httpbinCLUSTER_NAME="1-25"kubectx k3d-${CLUSTER_NAME}kubectl apply -f - <<EOFapiVersion: flomesh.io/v1alpha1kind: ServiceExportmetadata:  namespace: ${NAMESPACE_MESH}  name: httpbinspec:  serviceAccountName: "*"  rules:    - portNumber: 8080      path: "/${CLUSTER_NAME}/httpbin-mesh"      pathType: PrefixEOFsleep 1
复制代码


回到集群 1-23,查看 ServiceImports httpbin,可以看到已经发现了集群 1-25 注册的服务。


kubectl get serviceimports httpbin -n httpbin -o jsonpath='{.spec}' | jq{  "ports": [    {      "endpoints": [        {          "clusterKey": "default/default/default/1-25",          "target": {            "host": "192.168.1.110",            "ip": "192.168.1.110",            "path": "/1-25/httpbin-mesh",            "port": 82          }        }      ],      "port": 8080,      "protocol": "TCP"    }  ],  "serviceAccountName": "*",  "type": "ClusterSetIP"}
复制代码


但此时在 curl 发送请求,并不会收到集群 1-25 中的响应。还记得 上篇 中提到过,默认的全局流量策略是 Locality,因此集群外的节点并不会参与请求的处理。


Hi, I am from 1-23 and controlled by mesh!Hi, I am from 1-23 and controlled by mesh!Hi, I am from 1-23 and controlled by mesh!
复制代码


我们创建一个 ActiveActive 的全局流量策略。注意,这里设置 targets 的时候我们加上了代表权重的字段 weight 将其设置为 10,表示将 1/11 的流量导入集群 1-25 中,记住本集群的权重总是 100


kubectx k3d-1-23kubectl apply -n httpbin -f  - <<EOFapiVersion: flomesh.io/v1alpha1kind: GlobalTrafficPolicymetadata:  name: httpbinspec:  lbType: ActiveActive  targets:    - clusterKey: default/default/default/1-25      weight: 10EOF
复制代码


这次我们请求 20 次,结果也正如我们所期望的,只有 1-2 次请求进入集群 1-25


for i in {1..20}; do kubectl exec "${curl_client}" -n curl -c curl -- curl -s http://httpbin.httpbin:8080/ ; doneHi, I am from 1-23 and controlled by mesh!Hi, I am from 1-23 and controlled by mesh!Hi, I am from 1-25 and controlled by mesh!Hi, I am from 1-23 and controlled by mesh!Hi, I am from 1-23 and controlled by mesh!Hi, I am from 1-23 and controlled by mesh!Hi, I am from 1-23 and controlled by mesh!Hi, I am from 1-23 and controlled by mesh!Hi, I am from 1-23 and controlled by mesh!Hi, I am from 1-23 and controlled by mesh!Hi, I am from 1-23 and controlled by mesh!Hi, I am from 1-23 and controlled by mesh!Hi, I am from 1-23 and controlled by mesh!Hi, I am from 1-25 and controlled by mesh!Hi, I am from 1-23 and controlled by mesh!Hi, I am from 1-23 and controlled by mesh!Hi, I am from 1-23 and controlled by mesh!Hi, I am from 1-23 and controlled by mesh!Hi, I am from 1-23 and controlled by mesh!Hi, I am from 1-23 and controlled by mesh!
复制代码


我们将集群 1-25 的权重设置为 900,因此会有 90% 的流量进入集群 1-25


kubectx k3d-1-23kubectl apply -n httpbin -f  - <<EOFapiVersion: flomesh.io/v1alpha1kind: GlobalTrafficPolicymetadata:  name: httpbinspec:  lbType: ActiveActive  targets:    - clusterKey: default/default/default/1-25      weight: 900EOF
复制代码


然后请求 10 次,会看到 9 次的请求都进入到集群 1-25,也就是 90% 的流量分流到了其他集群。


for i in {1..10}; do kubectl exec "${curl_client}" -n curl -c curl -- curl -s http://httpbin.httpbin:8080/ ; doneHi, I am from 1-25 and controlled by mesh!Hi, I am from 1-23 and controlled by mesh!Hi, I am from 1-25 and controlled by mesh!Hi, I am from 1-25 and controlled by mesh!Hi, I am from 1-25 and controlled by mesh!Hi, I am from 1-25 and controlled by mesh!Hi, I am from 1-25 and controlled by mesh!Hi, I am from 1-25 and controlled by mesh!Hi, I am from 1-25 and controlled by mesh!Hi, I am from 1-25 and controlled by mesh!
复制代码


接着就是停止集群 1-23 中的实例,使所有流量都分流到外集群。


kubectl scale deploy httpbin -n httpbin --replicas 0
复制代码


最后的工作就是慢慢得将 curl 服务也迁移到新的集群中,进而是所有的服务都迁移完成之后,下线旧的集群。

总结

自维护的 Kubernetes 集群升级不是一件容易的事情,原地升级风险高,尤其是升级控制面。不管是蓝绿还是金丝雀升级,都面临着流量跨集群的问题:流量除了从入口进入还会有其他的途径,比如消息系统,定时任务等等。


解决了流量的跨集群调度问题后,这些问题都迎刃而解。剩下的问题就是如何让迁移做到自动化、可控的迁移了。


引用链接

[1] Pipy: https://flomesh.io/pipy

发布于: 刚刚阅读数: 5
用户头像

Flomesh

关注

微信订阅号:flomesh 2022-04-07 加入

一站式云原生应用流量管理供应商 官网:https://flomesh.io

评论

发布
暂无评论
跨集群流量调度实现 Kubernetes 集群金丝雀升级_K8s 多集群管理_Flomesh_InfoQ写作社区