背景
2022年,37手游完成了业务双云架构的部署
2023年,通过故障注入,确认任一单云故障下,都可以逃生到另外一朵云
此刻,虽然业务具备了双云双活能力,但也发现了一些边缘组件不具备,其中cicd依赖nacos就是其中之一
应用场景?
那么我们在nacos的使用场景是什么呢?其实主要用在CICD编译这个环节
面临的问题
一旦腾讯云整体故障,那么我们的cicd平台就无法使用,即使业务逃生到另外一朵云,无论是应用发布、重启,都将受到影响,如下构图,cicd单云
于是,我们做了cicd双云v1版本
前期没有关注度到nacos的强依赖,在实际使用上,任然是单点(nacos单云)
cicd v2版本
一开始,将nacos当做无状态服务,直接双云部署,然后烟囱式访问
cicd v2版本存在的问题:修改配置后,配置数据不一致
在实际的故障注入演练中,我们发现,在一朵云的nacos修改了配置后,虽然它们都是用的相同的数据库,无法同步到另外一朵云,如下架构
nacos双云不能同步的原因
Nacos应用会将数据库的配置数据缓存在本地内存,然后每6小时去全量dump更新一次,所以比如我们从腾讯云nacos集群入口修改配置,修改了nacos数据库,但是阿里云nacos容器集群因为不知道数据库配置已被修改,因此就导致,腾讯云是新修改的配置,阿里云还是旧的缓存数据,需要6小时后才会同步更新
那么腾讯云nacos容器集群内的所有Pod为什么是都是新的呢
nacos集群内,是有一个横向通知机制的,要搞明白这个问题,要先搞清楚,nacos集群运作的以下几个原理
nacos数据一致性原理:raft算法
1.raft算法选举Leader过程
1.1 如一个nacos集群有3个节点,节点一开始启动时都是Follow跟随者状态,会进入一个选举超时时间,等待Leader或者Candidate发送心跳包
1.2 如果等待超时,如第一个节点等待超时就会变为Candidate候选者,向其他节点发起投票选举自己为Leader
1.3 其他Follow节点收到Candidate发起的投票同意后,第一个节点节点成为Leader
2.raft算法的选举超时机制
在raft算法中,选举超时时间(Election Timeout)是用来定义Follower在自动转化为Candidate前的等待时间
默认的选举超时时间是在固定值(1000ms)的基础上,增加150ms到300ms随机值的值。这个时间设置是为了防止集群中的所有节点同时发起投票,导致选举无法进行
例如,如果集群由3个节点组成,每个节点的选举超时时间分别为168ms、210ms和200ms。在这个过程中,最先到达选举超时时间的节点会最先成为Candidate,并向其他两个节点发起投票请求。如果其他节点收到投票请求并且返回了投票,那么最先成为Candidate的节点会因为获得了过半数节点的投票而从Candidate状态转变为Leader状态
每个节点的选举超时时间是随机分配的,这个随机时间范围是150ms到300ms,可以有效地防止所有节点同时发起投票。如果一个节点在选举超时时间内没有收到来自Leader的心跳消息,它会从Follower状态自动转化为Candidate状态,并发起选举
nacos数据一致性原理:读写请求
nacos集群为了保证数据一致性,无论读写请求都会经过Leader节点,架构如下
如上图,第一步:客户端请求nacos的入口LB,随机请求到Follower2,第二步:Follower2会将读写请求都转给Leader节点,第三步:如果是一个读请求,那么Leader节点会将读请求处理后再返回给Follower2,第四步:再由Follower2节点返回给客户端数据
通过上面4个步骤,可以保证所有的写请求都由Leader节点处理,从而保证数据的一致性
nacos数据一致性原理:Leader节点故障期间
如果Leader节点挂掉了,那么集群会进行新的选举产生新的Leader,之前挂掉的Leader重启后作为Follower加入集群,并同步新选举的Leader上的数据。这样可以保证即使在Leader节点故障的情况下,也能保证数据的可靠性和一致性
那么Leader故障或者失联期间,Follower能否独自处理呢?答案是肯定的,见架构图
如上图,如果Leader故障,且又没有新的的Leader产生时,Follower接到请求后,还是按照原路径请求Leader,但此时是不通的,所以这个地方Follower就有个超时机制,毫秒级,如果拿不到Leader的返回,就将缓存的数据返回给客户端
nacos数据一致性原理:节点数据同步
Nacos通过发送心跳的方式来保持Leader和Follower之间的数据同步,Leader节点会定期向每个Follower节点发送心跳,这个心跳的默认时间是30秒,也就是30秒同步一次,并带上所有的key和key对应的时间戳。Follower节点收到心跳后,会检查其中的key是否存在或者时间戳是否较小,如果不满足条件,则会向Leader发送请求拿取最新的数据。这样可以保证各个节点数据的实时性和一致性
基于Nacos的原理,37手游的nacos双云双活设计
已知,Nacos可以在集群节点内,Follower节点都会从Leader节点同步数据,但问题是不同集群间,Nacos是无法做到同步的,也就是说,Nacos是有一定状态的,并非完全无状态,看看新旧架构对比
老的架构做法
从上图可以看到,老的架构由于是2个集群,虽然他们都读取了同一个数据库,但当任一一朵云的Nacos有写操作,都不会及时同步到另外一朵云,此时两朵云读取的配置信息是有差异的,原因是因为nacos有一层缓存存在,这个缓存时间默认6小时,每隔6小时,Leader会全量dump数据库的信息到本地缓存
新的架构做法
如上图,将2集群合并成一个集群,双云之间通过专线打通网络,访问和读取还是烟囱式,只是数据库还是单边,这样做的好处是,在任一一朵云修改数据,都会将腾讯云和阿里云所有节点都同步到,因为他们是同一个集群,使用原生的nacos集群同步方式
总结
在nacos集群的使用上,我们的业务场景主要还是cicd发布时需要读取配置,是一个读多写少的场景,基本上可以认为,只要读成功,我们的cicd就能完整运行,因此在设计上,我们优先考虑读场景的可用性,无论哪一朵云故障,我们的cicd系统都能正常运行,因为在一般故障情况下,我们也很少会写nacos
因此从架构上看,在腾讯云故障下,写是无法在阿里云进行的,因为阿里云只有2个节点,无法投票成为Leader,也就无法完成写入,因此正常情况下,是读写nacos双云双活,极端情况下,阿里云故障,腾讯云读写nacos无异常,腾讯云故障,阿里云可以读,但无法写