初衷
为什么要在kubernetes上搭建codis系统,一个原因是很多服务已经运行在kubernetes上,将codis迁移到kubernetes上,可以更方便的使用;另一个重要原因是,结合kubernetes的特性,搭建一套基本不需要人为运维参与的codis系统(如果熟悉codis的话,会知道codis需要人的参与有些重,异常情况基本都需要人为参与处理),后面会详细说明如果做到基本不需要人为参与,原理、实现及一些折中选择。
目标
在kubernetes环境,一键部署一套codis系统,该codis系统能够稳定运行,并在一些意外情况,甚至极端下(如物理机当机等),能够自己进行修复,并恢复正常运行状态(部署完成后,基本不再需要人为参与)。一键对codis进行扩容(包括proxy和后端server)。经过大量测试,并在生产环境运行验证,当前这些目标基本满足。
github
https://github.com/left2right/codis (基于codis 3.2)
依赖
kubernetes环境
在kubernetes上部署一套codis系统,需要基本的kubernetes环境和docker环境,具体部署方法见相应文档。
如果想快速部署一套自己测试或者研究的单机kubernets环境,可以按照下面两个步骤来(mac):
- 部署docker
https://docs.docker.com/toolbox/toolbox_install_mac/ - 部署minikube
https://github.com/kubernetes/minikube (brew cask install minikube)kubernetes启动及验证(minikube)
###启动 $ minikube start ###验证 $ minikube status minikubeVM: Running localkube: Running
image依赖
- zookeeper image:
gcr.io/google_samples/k8szk:v1 - golang image:
golang:1.7.5
这两个image均需要翻墙才能获取,可以找个国内的代理获取。image 获取成功检查
### 检查zookeeper image $ docker images |grep k8szk gcr.io/google_samples/k8szk v1 ... ... ### 检查 golang image $ docker images |grep golang golang 1.7.5 ... ...
构建步骤
在kubernetes平台安装启动成功,而且拉取到上面依赖的两个image后,就可以构建codis系统了,具体如下:
###获取codis源码
$ git clone https://github.com/left2right/codis -b release3.2-k8s
### 构建codis docker镜像
$ cd codis
$ docker build -f Dockerfile -t codis-image .
### 构建kubernetes环境的codis集群
$ cd kubernetes
$ sh start.sh buildup
... ... plz wait
PONG
#当你看到这个PONG的时候说明这套codis集群已经构建成功了,
#如果你没有等到PONG,那就需要检查下具体的报错内容了,祝顺利~
构成组件
文件说明
在codis/kubernetes/目录里面有以下这些文件:
- README.md 使用说明,包括创建codis集群,销毁集群,扩容等,具体如下:
### Build one codis cluster (codis master server has one slave) $ sh start.sh buildup ### Clean up the codis cluster $ sh start.sh cleanup ### Scale codis cluster proxy $ sh start.sh scale-proxy $(number) ### Scale codis cluster server $ sh start.sh scale-server $(number)
- start.sh 真个codis集群操作脚本,具体功能如上面README.md说明,挺简单的一个脚本,如果你想做些什么改动或者测试,可以在这个基础上做些尝试~
- codis-service.yaml codis在kubernetes环境中所有service的yaml文件,里面包括codis-dashboard, codis-proxy, codis-server, codis-fe, codis-ha
- codis-dashboard.yaml codis dashboard在kuberenetes环境的yaml文件
- codis-proxy.yaml codis proxy在kuberenetes环境的yaml文件
- codis-server.yaml codis server 在kuberenetes环境的yaml文件
- codis-ha.yaml codis-ha 在kuberenetes环境的yaml文件
- codis-fe.yaml codis-fe 在kuberenetes环境的yaml文件
- zookeeper/zookeeper-service.yaml zookeeper service 在kubernetes环境yaml文件
- zookeeper/zookeeper-service.yaml zookeeper 在kubernetes环境yaml文件
组件介绍
通过上面文件介绍,可以看到主要有zookeeper(可以用etcd等替代),codis-dashboard, codis-proxy, codis-server, codis-ha, codis-fe这几个组件组成,每个组件有一个相应的service,及一个具体的pod组织实现。
-
zookeeper
zookeeper是参考kubernetes官网https://kubernetes.io/docs/tutorials/stateful-application/zookeeper/ ,只是为了操作方便将相应的yaml文件的volume mounts注释掉了(生产环境使用中应该将zookeeper的持久化数据通过volume mounts 挂载出来),具体使用及相关说明见官网介绍 -
codis-dashboard
codis-dashboard即codis dashboard,其在codis中起的作用见GitHub介绍,dashboard使用StatefulSet来组织pod,并且replicas设置为1,整个集群只允许有一个。如果codis-dashboard异常,由kubernetes将其关闭,并重新启动起来,并加入到集群中。
dashboard启动成功后,会通过kubernetes的postStart hook,发送一条命令检查zookeeper是否连接正常,以方便快速定位问题原因。在dashboard关闭时通过preStop hook,来确保dashboard正常关闭(将zookeeper上的锁删掉)。
当一些极端情况(如物理机当机)导致dashboard非正常关闭,zookeeper上的锁没有删除掉(这会导致dashboard下次启动不起来),为了能让集群快速恢复正常状态,我们为codis-dashboard启动时添加了一个清除锁的开关(--remove-lock),如果打开开关,每次注册dashboard锁前,会先做一次清除锁的操作。 -
codis-proxy
codis-proxy即codis proxy,由于proxy是无状态的,使用ReplicationController来组织pod,这样可以快速的扩容缩容。通过replicas设置数量,默认是2个。
codis proxy关闭前会发送一条命令将自己从集群中摘除出去。如果是一些异常情况,我们下面会介绍,由codis-ha将proxy从集群中摘除出去。然后由kubernetes重启一个proxy加入集群。 -
codis-server
codis-server即codis后端redis server,由于redis server要分属不同的group,为方便管理以及方便在没有人参与情况下恢复正常,使用StatefulSet来组织pod,replicas的数量即是整个集群中所有codis-server的数量,codis-server.yaml中有个配置的环境变量SERVER_REPLICA,这个设置的是每个group中codis-server的数量,默认是2个(一主一从)。
为了使codis的server,尤其是一个group的server不部署在一个物理节点上,我们使用了kubernetes pod的反亲和性(podAntiAffinity),为了在物理节点有限时能够顺利部署codis集群,我们使用preferredDuringSchedulingIgnoredDuringExecution。如果想让所有codis server绝对不在一个node上,可以设置为requiredDuringSchedulingIgnoredDuringExecution,这样会提高系统的高可用性,但如果node节点不足会导致部署codis集群失败或者扩容codis server失败。在codis-server启动时,通过postStart hook,尝试为该server创建group,并将该server加入到group中,并尝试和group的master建立主从关系。在codis server关闭时尝试将该server从group中删除出去。该codis-server从属于哪个group,是由StatefulSet为该server分配的id,以及配置的环境变量SERVER_REPLICA,计算获得
gid=$(expr $sid / ${SERVER_REPLICA} + 1)
。这个过程中一些操作会失败,如创建已经存在的group等。一些操作失败并没有什么影响,直接跳过,一些失败会导致系统状态不正常,由后面的codis-ha将状态恢复为正常的状态,具体见后面说明。为提高系统的高可用性,可以将save的rdb文件挂载到外面,在启动时加载,挂载的目录是/codis,具体挂载方式和zookeeper类似,但考虑到性能,默认没有将rdb文件挂载出来,如果为了更高的可用性,可以参考zookeeper.yaml文件挂载出来。
-
codis-ha
当前codis 官方维护server ha使用的工具是redis-sentinel,并将之前的codis-ha去掉,这样选择的原因是codis-ha是一个单点,如果它状态不正常了,那集群的高可用就难以保证了,尤其是如果codis-ha需要人为参与的情况下,恢复正常着实会花费不少时间,redis-sentinel是集群的模式,不会因为某个节点的不正常,导致不能正常工作。但我们最终选择了codis-ha(在原有codis-ha的基础上进行修改),是因为sentinel能实现的功能太有限,只是在master状态异常时,重新选举一个master,但如果slave状态异常,以及重新建立主从关系,将group内各个server状态恢复正常,以及将proxy状态恢复正常,sentinel显然还无法做到。我们知道在kubernetes环境如果一个pod状态不正常了,会快速的关闭并重新启动起来,在这种场景下,codis-ha前面对比sentinel的不足,一定程度上缩减了不少,实际使用中效果确实可以。
codis-ha的策略就是,通过dashboard,检查proxy和server的状态,如果proxy状态异常就关闭proxy,由kubernetes重启proxy,并加入集群;如果server状态异常,分几种情况:1. 是master,且有slave,则选择一个合适的slave(根据和master断开连接时间),提升为master;2.是master,没有slave,则直接关闭重启;3.是slave,则关闭,然后重启,加入集群。
由于codis-ha严重依赖codis-dashboard,我们使用pod亲和性,将codis-ha和codis-dashboard部署在同一个节点。
-
codis-fe
codis-fe即codis-fe,codis的网页图形操作界面。PS在kubernetes环境主要用来观察下集群状态,qps等。
源码改动
kubernetes环境下的codis对源码做了如下修改:
- 增加kubernetes目录,里面存放相应的kubernets yaml文件及脚本
- 修改codis-ha,维护集群(server和proxy)状态,使codis proxy和server快速恢复为正常状态。
- 修改codis-dashboard,增加命令行传递product_name, product_auth,以满足创建不同product的codis,增加remove-lock开关,以使异常情况下,快速恢复dashboard
- 修改codis-proxy,增加命令行传递product_name, product_auth,以满足创建不同product的codis
注意点
注意事项
- 尽量确保codis-server的replicas能够整除SERVER_REPLICA,通过yaml文件可以看到SERVER_REPLICA为1,和其他在加入group时是对错误处理是不一样的,思考下你就明白,这是为了避免一个死锁。无论SERVER_REPLICA设置为任何正整数均可以,只是请确保集群中的group不会出现有的group有slave,有的只有master,整除即可~
- 留意你的数据量和机器实际可用内存~
- 建议先将该codis作为缓存使用一段时间,直到你熟悉了相关内容,及对其稳定性(也许和你的实际环境有很大关系)有了更深入的了解,再考虑将其作为一些数据的DB
问题定位
-
codis-ha的log会实时的反应整个集群的状态,包括proxy,server,以及ha和dashboard,因为如果ha有问题log就看不了了,dashboard有问题,ha也就挂掉了。。。查看方法
$ kubectl exec -it codis-ha-0 bash $ tail -f log/codis-ha-0.....
-
通过dashboard log更进一步定位问题的原因,方法类似
$ kubectl exec -it codis-dashboard-0 bash $ tail -f log/codis-dashboard-0.....
-
如果你按照上面操作,尤其是你成功运行codis后,测试发现的任何问题,欢迎和我交流(yqzhang@easemob.com)~
其他
还有些事情及思考没有写完,改天再补充~
有疑问加站长微信联系(非本文作者)