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

2024-03-17 13:40

本文主要是介绍跨集群流量调度实现 Kubernetes 集群金丝雀升级,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

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

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

在这里插入图片描述

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

方案

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

在这里插入图片描述

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

接下来,我们使用进行下示例演示如何使用 Flomesh 服务网格 实现集群的平滑升级。

演示

环境准备

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

集群对外访问地址api-server 对外端口LB 对外端口描述
control-planeHOST_IP(192.168.1.110)6444N/A控制平面集群
1-23HOST_IP(192.168.1.110)644581应用集群
1-25HOST_IP(192.168.1.110)644682应用集群

工具准备

  • Docker
  • K3d
  • kubectl
  • jq

环境搭建

创建一个 bridge 类型的网络 multi-clusters,所有的容器都工作在这个网络中。

docker network create multi-clusters

获取主机 IP 地址,这个地址将作为集群的访问地址。

export HOST_IP=$(if [ "$(uname)" == "Darwin" ]; then ipconfig getifaddr en0; else ip -o route get to 8.8.8.8 | sed -n 's/.*src \([0-9.]\+\).*/\1/p'; fi)

搭建集群

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

API_PORT=6444 #6445 6446
PORT=80 #81 82
for CLUSTER_NAME in control-plane 1-23 1-25
doif [ "$CLUSTER_NAME" = "control-plane" ]; thenIMAGE_TAG="v1.25.15-k3s2"elif [ "$CLUSTER_NAME" = "1-23" ]; thenIMAGE_TAG="v1.23.8-k3s2"elif [ "$CLUSTER_NAME" = "1-25" ]; thenIMAGE_TAG="v1.25.15-k3s2"fik3d cluster create ${CLUSTER_NAME} \--image docker.io/rancher/k3s:${IMAGE_TAG} \--api-port "${HOST_IP}:${API_PORT}" \--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

保存集群的 kubeconfig

kubeconfig_cp=${KUBECONFIG_CP:-"/tmp/cp.kubeconfig"}
kubeconfig_123=${KUBECONFIG_C1:-"/tmp/123.kubeconfig"}
kubeconfig_125=${KUBECONFIG_C2:-"/tmp/125.kubeconfig"}k0="kubectl --kubeconfig ${kubeconfig_cp}"
k1="kubectl --kubeconfig ${kubeconfig_123}"
k2="kubectl --kubeconfig ${kubeconfig_125}"k3d kubeconfig get control-plane >"${kubeconfig_cp}"
k3d kubeconfig get 1-23 >"${kubeconfig_123}"
k3d kubeconfig get 1-25 >"${kubeconfig_125}"

安装 FSM

下载 FSM CLI

system=$(uname -s | tr '[:upper:]' '[:lower:]')
arch=$(uname -m | sed -E 's/x86_/amd/' | sed -E 's/aarch/arm/')
release=v1.1.4
curl -L https://github.com/flomesh-io/fsm/releases/download/$release/fsm-$release-$system-$arch.tar.gz | tar -vxzf -
./$system-$arch/fsm version
sudo cp ./$system-$arch/fsm /usr/local/bin/fsm

在 3 个集群中安装 FSM。

export FSM_NAMESPACE=fsm-system
export FSM_MESH_NAME=fsm
for CONFIG in kubeconfig_cp kubeconfig_123 kubeconfig_125
do DNS_SVC_IP="$(kubectl --kubeconfig ${!CONFIG} get svc -n kube-system -l k8s-app=kube-dns -o jsonpath='{.items[0].spec.clusterIP}')"KUBECONFIG=${!CONFIG} fsm install \--mesh-name "$FSM_MESH_NAME" \--fsm-namespace "$FSM_NAMESPACE" \--set=fsm.fsmIngress.enabled=true \--set=fsm.localDNSProxy.enable=true \--set=fsm.localDNSProxy.primaryUpstreamDNSServerIPAddr="${DNS_SVC_IP}" \--timeout=900ssleep 2kubectl --kubeconfig ${!CONFIG} wait --for=condition=ready pod --all -n $FSM_NAMESPACE --timeout=120s
done

加入集群组

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

在集群 control-plane 为两个集群创建 CR Cluster,同时提供入口的 IP 地址 gatewayHost 和端口 gatewayPort,以及服务网格所在的命名空间 fsmNamespace。最终要的还有集群的 kubeconfig,用于访问集群。

PORT=81
for CLUSTER_NAME in 1-23 1-25
dokubectl --kubeconfig ${kubeconfig_cp} apply -f - <<EOF
apiVersion: flomesh.io/v1alpha1
kind: Cluster
metadata:name: ${CLUSTER_NAME}
spec:gatewayHost: ${HOST_IP}gatewayPort: ${PORT}fsmNamespace: ${FSM_NAMESPACE}kubeconfig: |+
$(k3d kubeconfig get ${CLUSTER_NAME} | sed 's|^|    |g' | sed "s|0.0.0.0|$HOST_IP|g")
EOF((PORT = PORT + 1))
done

查看集群组的情况:

$k0 get cluster
NAME   REGION    ZONE      GROUP     GATEWAY HOST   GATEWAY PORT   MANAGED   MANAGED AGE   AGE
1-23   default   default   default   10.0.2.4       81             True      10s           11s
1-25   default   default   default   10.0.2.4       82             True      9s            11s

部署实例应用

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

NAMESPACE=httpbin
CLUSTER_NAME="1-23"
kubectl --kubeconfig ${kubeconfig_123} create namespace ${NAMESPACE}
KUBECONFIG=${kubeconfig_123} fsm namespace add ${NAMESPACE}
kubectl --kubeconfig ${kubeconfig_123} apply -n ${NAMESPACE} -f - <<EOF
apiVersion: apps/v1
kind: Deployment
metadata:name: httpbinlabels:app: pipy
spec:replicas: 1selector:matchLabels:app: pipytemplate:metadata:labels:app: pipyspec:containers:- name: pipyimage: flomesh/pipy:latestports:- containerPort: 8080command:- pipy- -e- |pipy().listen(8080).serveHTTP(new Message('Hi, I am from ${CLUSTER_NAME} and controlled by mesh!\n'))
---
apiVersion: v1
kind: Service
metadata:name: httpbin
spec:ports:- port: 8080targetPort: 8080protocol: TCPselector:app: pipy
EOFsleep 3
kubectl --kubeconfig ${kubeconfig_123} wait --for=condition=ready pod -n ${NAMESPACE} --all --timeout=60s

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

export NAMESPACE=curl
kubectl --kubeconfig ${kubeconfig_123} create namespace ${NAMESPACE}
KUBECONFIG=${kubeconfig_123} fsm namespace add ${NAMESPACE}
kubectl --kubeconfig ${kubeconfig_123} apply -n ${NAMESPACE} -f - <<EOF
apiVersion: v1
kind: ServiceAccount
metadata:name: curl
---
apiVersion: v1
kind: Service
metadata:name: curllabels:app: curlservice: curl
spec:ports:- name: httpport: 80selector:app: curl
---
apiVersion: apps/v1
kind: Deployment
metadata:name: curl
spec:replicas: 1selector:matchLabels:app: curltemplate:metadata:labels:app: curlspec:serviceAccountName: curlcontainers:- image: curlimages/curlimagePullPolicy: IfNotPresentname: curlcommand: ["sleep", "365d"]
EOFsleep 3
kubectl --kubeconfig ${kubeconfig_123} wait --for=condition=ready pod -n ${NAMESPACE} --all --timeout=60s

测试

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

curl_client="$(kubectl --kubeconfig ${kubeconfig_123} get pod -n curl -l app=curl -o jsonpath='{.items[0].metadata.name}')"kubectl --kubeconfig ${kubeconfig_123} 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=httpbin
kubectl --kubeconfig ${kubeconfig_123} apply -f - <<EOF
apiVersion: flomesh.io/v1alpha1
kind: ServiceExport
metadata:namespace: ${NAMESPACE_MESH}name: httpbin
spec:serviceAccountName: "*"rules:- portNumber: 8080path: "/httpbin"pathType: Prefix
EOF
sleep 1

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

在这里插入图片描述

升级迁移

新集群中部署应用

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

NAMESPACE=httpbin
CLUSTER_NAME="1-25"kubectl --kubeconfig ${kubeconfig_125} create namespace ${NAMESPACE}
KUBECONFIG=${kubeconfig_125} fsm namespace add ${NAMESPACE}
kubectl --kubeconfig ${kubeconfig_125} apply -n ${NAMESPACE} -f - <<EOF
apiVersion: apps/v1
kind: Deployment
metadata:name: httpbinlabels:app: pipy
spec:replicas: 1selector:matchLabels:app: pipytemplate:metadata:labels:app: pipyspec:containers:- name: pipyimage: flomesh/pipy:latestports:- containerPort: 8080command:- pipy- -e- |pipy().listen(8080).serveHTTP(new Message('Hi, I am from ${CLUSTER_NAME} and controlled by mesh!\n'))
---
apiVersion: v1
kind: Service
metadata:name: httpbin
spec:ports:- port: 8080targetPort: 8080protocol: TCPselector:app: pipy
---
apiVersion: v1
kind: Service
metadata:name: httpbin-${CLUSTER_NAME}
spec:ports:- port: 8080targetPort: 8080protocol: TCPselector:app: pipy
EOFsleep 3
kubectl --kubeconfig ${kubeconfig_125} wait --for=condition=ready pod -n ${NAMESPACE} --all --timeout=60s

导出服务

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

export NAMESPACE_MESH=httpbin
kubectl --kubeconfig ${kubeconfig_125} apply -f - <<EOF
apiVersion: flomesh.io/v1alpha1
kind: ServiceExport
metadata:namespace: ${NAMESPACE_MESH}name: httpbin
spec:serviceAccountName: "*"rules:- portNumber: 8080path: "/httpbin"pathType: Prefix
EOF
sleep 1

发现多集群服务

回到集群 1-23,查看 ServiceImports httpbin,可以看到已经发现了集群 1-25 注册的服务。其中 82 就是新集群访问入口的端口。

kubectl --kubeconfig ${kubeconfig_123} get serviceimports httpbin -n httpbin -o jsonpath='{.spec}' | jq
{"ports": [{"endpoints": [{"clusterKey": "default/default/default/1-25","target": {"host": "10.0.2.4","ip": "10.0.2.4","path": "/httpbin","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 将其设置为 50,表示将 50/150 的流量导入集群 1-25 中,记住本集群的权重总是 100

kubectl --kubeconfig ${kubeconfig_123} apply -n httpbin -f  - <<EOF
apiVersion: flomesh.io/v1alpha1
kind: GlobalTrafficPolicy
metadata:name: httpbin
spec:lbType: ActiveActivetargets:- clusterKey: default/default/default/1-25weight: 50
EOF

这次我们请求 20 次。

curl_client="$(kubectl  --kubeconfig ${kubeconfig_123} get pod -n curl -l app=curl -o jsonpath='{.items[0].metadata.name}')"
for i in {1..20}; do kubectl --kubeconfig ${kubeconfig_123} exec "${curl_client}" -n curl -c curl -- curl -s http://httpbin.httpbin:8080/ ; done

在结果中可以看到有 1/3 的流量进入到了新集群。

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-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-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-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-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-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-25 and controlled by mesh!

接下来,下线旧集群中的服务实例。

kubectl --kubeconfig ${kubeconfig_123} delete deploy,svc httpbin -n httpbin

再次请求,所有的流量都会进入新的集群。

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

在这里插入图片描述

总结

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

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

这篇关于跨集群流量调度实现 Kubernetes 集群金丝雀升级的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/819130

相关文章

服务器集群同步时间手记

1.时间服务器配置(必须root用户) (1)检查ntp是否安装 [root@node1 桌面]# rpm -qa|grep ntpntp-4.2.6p5-10.el6.centos.x86_64fontpackages-filesystem-1.41-1.1.el6.noarchntpdate-4.2.6p5-10.el6.centos.x86_64 (2)修改ntp配置文件 [r

HDFS—集群扩容及缩容

白名单:表示在白名单的主机IP地址可以,用来存储数据。 配置白名单步骤如下: 1)在NameNode节点的/opt/module/hadoop-3.1.4/etc/hadoop目录下分别创建whitelist 和blacklist文件 (1)创建白名单 [lytfly@hadoop102 hadoop]$ vim whitelist 在whitelist中添加如下主机名称,假如集群正常工作的节

Hadoop集群数据均衡之磁盘间数据均衡

生产环境,由于硬盘空间不足,往往需要增加一块硬盘。刚加载的硬盘没有数据时,可以执行磁盘数据均衡命令。(Hadoop3.x新特性) plan后面带的节点的名字必须是已经存在的,并且是需要均衡的节点。 如果节点不存在,会报如下错误: 如果节点只有一个硬盘的话,不会创建均衡计划: (1)生成均衡计划 hdfs diskbalancer -plan hadoop102 (2)执行均衡计划 hd

hdu1043(八数码问题,广搜 + hash(实现状态压缩) )

利用康拓展开将一个排列映射成一个自然数,然后就变成了普通的广搜题。 #include<iostream>#include<algorithm>#include<string>#include<stack>#include<queue>#include<map>#include<stdio.h>#include<stdlib.h>#include<ctype.h>#inclu

【C++】_list常用方法解析及模拟实现

相信自己的力量,只要对自己始终保持信心,尽自己最大努力去完成任何事,就算事情最终结果是失败了,努力了也不留遗憾。💓💓💓 目录   ✨说在前面 🍋知识点一:什么是list? •🌰1.list的定义 •🌰2.list的基本特性 •🌰3.常用接口介绍 🍋知识点二:list常用接口 •🌰1.默认成员函数 🔥构造函数(⭐) 🔥析构函数 •🌰2.list对象

【Prometheus】PromQL向量匹配实现不同标签的向量数据进行运算

✨✨ 欢迎大家来到景天科技苑✨✨ 🎈🎈 养成好习惯,先赞后看哦~🎈🎈 🏆 作者简介:景天科技苑 🏆《头衔》:大厂架构师,华为云开发者社区专家博主,阿里云开发者社区专家博主,CSDN全栈领域优质创作者,掘金优秀博主,51CTO博客专家等。 🏆《博客》:Python全栈,前后端开发,小程序开发,人工智能,js逆向,App逆向,网络系统安全,数据分析,Django,fastapi

让树莓派智能语音助手实现定时提醒功能

最初的时候是想直接在rasa 的chatbot上实现,因为rasa本身是带有remindschedule模块的。不过经过一番折腾后,忽然发现,chatbot上实现的定时,语音助手不一定会有响应。因为,我目前语音助手的代码设置了长时间无应答会结束对话,这样一来,chatbot定时提醒的触发就不会被语音助手获悉。那怎么让语音助手也具有定时提醒功能呢? 我最后选择的方法是用threading.Time

Android实现任意版本设置默认的锁屏壁纸和桌面壁纸(两张壁纸可不一致)

客户有些需求需要设置默认壁纸和锁屏壁纸  在默认情况下 这两个壁纸是相同的  如果需要默认的锁屏壁纸和桌面壁纸不一样 需要额外修改 Android13实现 替换默认桌面壁纸: 将图片文件替换frameworks/base/core/res/res/drawable-nodpi/default_wallpaper.*  (注意不能是bmp格式) 替换默认锁屏壁纸: 将图片资源放入vendo

C#实战|大乐透选号器[6]:实现实时显示已选择的红蓝球数量

哈喽,你好啊,我是雷工。 关于大乐透选号器在前面已经记录了5篇笔记,这是第6篇; 接下来实现实时显示当前选中红球数量,蓝球数量; 以下为练习笔记。 01 效果演示 当选择和取消选择红球或蓝球时,在对应的位置显示实时已选择的红球、蓝球的数量; 02 标签名称 分别设置Label标签名称为:lblRedCount、lblBlueCount

搭建Kafka+zookeeper集群调度

前言 硬件环境 172.18.0.5        kafkazk1        Kafka+zookeeper                Kafka Broker集群 172.18.0.6        kafkazk2        Kafka+zookeeper                Kafka Broker集群 172.18.0.7        kafkazk3