K8s烧起来了,很多概念和工具也烧起来,像service mesh,CNCF,FAAS,serverless等等,太火了,但是除了k8s,其他工具在企业中并没有完全铺开。
就拿serverless而言,概念是很具有前瞻性的,之前我们维护服务器,现在我们维护集群,将来我们应该只维护服务代码,任何IAAS和PAAS我们都不用care,乍一听,牛逼啊,那k8s这种PAAS级别的最终boss是不是也马上要被淘汰了,理论上来说是的,但实际情况呢。
serverless强调我们只维护function,这本身就和面向对象群体有冲突了,人家对你呵,就像对python连续蝉联最热门的编程语言一样的呵。及时你能说服所有人,在用起来的时候你还要考虑上下文复用,比如数据库连接和数据复用,要复用就意味着持久化,要么放在内存里,要么放在数据库里,那对于faas来说,只能放在内存里,那问题来了如果某个function分配的内存只有1G因为他平时调用量很少,如果某个瞬间有很多请求进来,那不就炸了。即使不炸,那这么多连接我都需要放在内存里,后面的客户端怎么连接呢,如果释放了,每次冷启动都需要好几秒,那用户体验是有多差可想而知。这是本身技术的短板,还有就是企业的需求,现在云服务这么便宜,我可以多起一些机器,架构矬一点,但是至少稳定我也不用考虑像faas的日志问题。企业需求导致了有些火过了,企业需求也导致了为啥k8s能烧的这么旺。有点扯远了,我们今天的主题是consul,那consul和k8s有啥关系呢,为啥k8s这么火,consul好像变成了计算时代的CPU如此重要呢,因为k8s是一个为亿万级别设计的平台,对于如此量级的调用,你无法再通过人为去管理那么多服务,所以大家开始探究如果让服务自动注册与发现,而consul就是这么设计。
其实在早k8s时代(docker swarm,Apache mesos, kubernetes三足鼎立的时候),consul就已经比较成熟了,他有kvstore,有dns服务,有服务注册发现。而且还可以是集群模式,甚至多数据中心模式,都是基于raft的一致性算法,raft如果不熟悉,etcd你肯定听过,etcd也是基于raft的一致性算法。那时候还流行两个consul的套件,一个是vault一个是consul-template。vault是一个加密插件,consul-template是基于模板动态生成文件的工具,其语法和现在的helm template很像,因为都是go template语法。consul 很早就尝试把变量维护到一个分布式存储里,如etcd。把服务地址用dns解析的形式,事实证明确实这才是大方向,consul之所有有今天的地位也不是靠运气的。
Consul给我们提供了KV, dns服务, 服务注册,服务发现。微服务时代,我们更需要consul的是服务的注册与发现,所以以下都是基于生产实践中对consul服务注册与发现的讨论。
要使用consul,必须要有一个服务端提供服务,然后客户端可以通过api cli等方式与服务端通信。
对于服务端有两种模式:单主,集群。
对于客户端使用也有两种通信方式:
同consul 服务端直接通信
通过client代理到服务端的通信
服务端和客户端使用方式的不同,一共有四种连接方式,单主直连模式,单主client模式,集群直连模式,集群client模式。而在实际使用中,我们都会有集群server以保证集群的HA。
consul服务端不管是何种方式都是一个二进制文件通过指定data目录和配置启动的,生产中一般都是以docker的方式启动,方便监控和维护状态。
对于单主模式下的直连和client模式:
这种服务端模式部署简单,一般只需要考虑数据持久化就可以,再加上一些ACL策略就能用了。但问题也很明显,单主没有HA,所以此种模式一般只考虑用kv功能,不使用服务注册发现功能。仅在应用程序启动的时候获取对应的kv就可以了,程序启动后不依赖consul server。仅使用kv模式的consul可以说是太屈才,不如直接使用Apollo,专业的配置管理工具来的实在。
集群直连模式:
这里只讨论同一dc的结构,顾名思义需要维护一个consul 集群,保证server的HA。consul的集群是一主多从,即一个leader,多个followers。官方建议同个dc中,集群数量控制在3或者5,太多会有开销问题。
该模式下集群中所有server都可以提供服务发现和注册的功能,向任意一个server注册,信息都会同步到集群中的各个节点上。乍一听,没啥问题,对于静态信息,KV,服务注册后的名字和endpoint关系都会同步。但是对于注册服务本身而言,我们是需要对其做健康检查来确保服务是活着的。在集群直连模式下,就会有一个严重的问题
当集群中某台server宕机了,通过该server注册的所有服务都会宕机。
这是为什么?主要和consul 集群的工作机制有关,我们对集群发起一次服务注册,如果客户端是通过endpoint或者反向代理到达consul server的话,那每一次注册必定会随机分配到后面某一台服务器上,收到请求的server会把服务注册到集群中,并且会带有一个自己server的地址,然后通过raft算法在集群内部同步。那为什么要在注册的时候带上自己地址呢,因为后续这个注册服务的server需要对服务进行健康检查,就像是师傅领了个徒弟,可以把徒弟介绍给其他师傅,但是对徒弟负责的只有自己。那么问题来了,当这个注册服务的server宕机后,没有人在对服务做健康检查,那么此时服务就变成unhealthy。就像师傅领了徒弟进来,师傅的师兄弟都知道有这个徒弟的存在,但如果师傅圆寂了,那此时徒弟就没人接管,徒弟很伤心以后没人带他了,整个状态就不健康了。
这是集群模式在客户端直连通信中面临的最大问题,或者说,这是一个伪集群模式。那么如果你目前正在使用的是集群模式,那有什么办法解决这伪HA问题呢。
答案就是集群client模式。
client工作方式:当有服务往client注册时,client会通知集群中的leader,然后信息同步到其他的follower。此时client接管服务的健康检查,即使集群中有server宕机(follower)也不会影响client和服务,因为静态信息在集群中每个节点上都有。
集群client模式在k8s场景中,还有两种变形。
client SideCar模式
client DaemonSet模式
client SideCar模式架构
SideCar模式中新增emptyDir卷,存放pod中consul的service和nodeid等信息,防止pod中Consul Client重启导致丢失service数据。
测试场景
停掉pod里的consul client
服务不可用可以被发现。consul client会立马重启,启动完成后,服务恢复,此过程一般在3s之内恢复。
停掉pod里的服务服务不可用可以被发现,在服务重启后,服务恢复(此服务仍为旧服务,nodeid,node都没有变化,因为信息持久化到emptyDir中),此过程依赖服务本身的启动时间。
停掉pod
3.1 如果停掉pod之前consul client处于failed状态 服务不可用可以被发现,pod重新启动完成,新服务注册到consul,旧服务不注销,此过程依赖服务本身的启动时间。
3.2 如果停掉pod之前consul client处于active状态服务不可用可以被发现,pod重新启动完成,新服务注册到consul,旧服务注销,此过程依赖服务本身的启动时间。停掉一个consul server follower
服务不受影响。停掉consul server leader服务不可用不可被发现,等重新选主完成后,服务恢复。
所有consul server 都停掉(模拟网络出现问题)服务不可用不可被发现,等server恢复后,服务恢复,此过程主要依赖网络恢复时间。
client DaemonSet模式架构
测试场景
停掉consul client
服务不可用不可被发现,从consul上直接注销,consul client会立马重启,启动完成后,服务出现并恢复,此过程一般在5s之内恢复。
停掉pod服务不可用可以被发现,pod重新启动完成,新服务注册到consul,旧服务未注销,此过程依赖服务本身的启动时间。
停掉一个consul server follower
服务不受影响。停掉consul server leader服务不可用不可被发现,等重新选主完成后,服务恢复。
所有consul server 都停掉(模拟网络出现问题)服务不可用不可被发现,等server恢复后,服务恢复,此过程主要依赖网络恢复时间。
几种状态说明
consul server中没有服务信息,即既不显示health也不显示unhealthy。
a. 在SideCar模式下,如果服务和consul client状态都是healthy的情况下,直接delete/update pod
b. 在DaemonSet模式下,consul client down。consul server中有服务信息,状态为unhealthy,且一直保留。
a. 在SideCar模式下,pod中consul client 或者 service down的前提下,直接delete/update pod。
b. 在DaemonSet模式下,delete/update pod。consul server中有服务信息为unhealthy,然后自动注销a. 在SideCar模式下,服务和consul client都healthy的模式下,更新pod。旧服务会出现短暂的unhealthy(也可能不出现),然后新pod启动,注册新服务,老服务注销。
两种变形的优劣
Sidecar模式最大程度上隔离consul对服务的影响,一个consul client只会影响一个服务的一个POD。
Sidecar模式会额外增大服务器开销。
DaemonSet模式折中了服务器开销和consul影响面。让一个node为一个单位,单个consul client影响单个node上的服务。
DaemonSet需要在服务启动脚本中注入代码,修改服务连接consul client的地址为当前node IP(这会影响所有服务的部署方式,有些用裸java跑的服务影响较大)。
DaemonSet需要持久化consul client的data目录,防止重启后会丢失相关service信息。
在实际生产中,一个consul sidecar容器100m cpu和128m 内存足以支撑,这里仅供参考,还是需要先做性能测试调整到一个合适的数值。
总结
场景 | SideCar | DaemonSet | ||||
---|---|---|---|---|---|---|
注册发现 | 被发现 | 恢复 | 注册发现 | 被发现 | 恢复 | |
consul client down | ✗ | ✔(不可用) | ✔ | ✗ | ✗ | ✔ |
pod service down | ✗ | ✔(不可用) | ✔ | ✗ | ✔(不可用) | ✔ |
pod down | ✗ | ✔(不可用) | ✔ | ✗ | ✔(不可用) | ✔ |
consul server follower down | ✔ | ✔ | N/A | ✔ | ✔ | N/A |
consul server leader down | ✗ | ✗ | ✔ | ✗ | ✗ | ✔ |
consul server cluster down | ✗ | ✗ | ✔ | ✗ | ✗ | ✔ |
本文提及内容如果有技术上的错误麻烦通知更正,同时欢迎各种技术探讨
如果喜欢小编写的文章,记得点点关注哦。
下一期预告:聊聊k8s证书更新问题