大版本升级,从ES 2.1到ES5.5,两年的数据,每天15GB,5个节点,前后历时一个月左右。
限制条件:
- 升级过程有新的数据不断进来,不能停止整个集群,否则会丢失数据。
- 没有额外的机器搭建一个全新的ES5集群,只有一个机器供缓冲使用。
- 一个临时的ES5节点cpu 8核,内存32GB,10T 磁盘,升级完成后需要释放掉这台机器。
- 很多线上的服务依赖于目前的ES2提供服务,线上服务不能中断。
记录一下整个流程。
首先是做决定到底是采用全集群方式升级还是平滑方式升级。
升级方案选择
全集群升级
1.关闭shard分配,防止关闭一个节点后ES集群误认为node故障,在剩余节点上执行 shard 恢复,如果数据过多,可能会由于产生大量IO造成ES集群挂起。
PUT _cluster/settings
{
"persistent": {
"cluster.routing.allocation.enable": "none"
}
}
2.执行同步刷新, 这样集群重新启动后shard恢复更快。
POST _flush/synced
3.关闭集群中所有的ES进程。
4.安装ES5,并且修改配置文件,配置好data路径。不要直接指向2.x的路径,否则一旦升级失败,老数据无法恢复。
5.配置好路径后将2.x的data目录copy到新的路径下。
6.启动ES5 集群,等待集群状态变为green即可
优点
简单,速度快,一步到位
缺点
- 由于ES2.x到ES5.x内部结构变化较大,升级过程不可控,中间可能会有大坑或者升级失败
- 目前ES存储的数据结构有需要改进的部分(比如某些字段类型,analyzed等),这样升级后还是原来的结构,后续可能还需要对数据进行reindex。
Reindex 平滑升级
这种升级方式其实就是对所有的数据做一次重新处理然后自己通过http接口重新写入到新的 ES5缓冲节点,然后再将原来的ES2的节点逐台加入的新的集群,之前的数据和配置都清空。
程序大致流程
ES2 -> scanner -> redis -> reindex -> ES5
引入Redis作为中间缓冲的考虑:
- 数据量巨大,索引过程很难保证一次成功,基于scroll方式取数据无法保证顺序,所以一旦中途失败无法判断offset,为了不丢不重复,只能从头再来
- 需要对每一个doc进行特有字段的部分处理, Redis中缓存的是处理后的doc
- ES的查询速度和索引速度不一致,从ES2读取数据经过处理后写入ES5整个流水线耗时太长,容易网络超时失败
升级方案踩坑记
经过内部讨论决定采用平滑升级的方式。
使用python将每天的数据从ES2->ES5,每天大概2000万条数据,使用官方的elasticsearch python库每次到几百万数据的时候就会出现连接超时,由于没有offset机制,所以只能删除从头再来。后来基于requests库自己封装scroll API来进行reindex,有时发现1000多万的时候scroll API就返回没有谁了,后来经过调试发现不能只是简单的根据hits的条数是否为0来判断数据是否全都读取完毕。这里官方文档中并没有提及,最终添加了好几个判断条件和错误处理,终于可以完整的读完所有数据,一条不差。
中间引入了redis作为缓存,防止reindex程序中间挂了,从头再来。es2->redis->es5,由于读的速度远远大于写的速度,而且只有一台es5节点,所以需要开多个进程往es5中写。突然有一天发现es5挂掉了,查找原因,发现redis的内存已经占满了所有机器的内存,操作系统自动杀掉了es5节点。于是加入流控机制,一旦redis中的数据超过限制,那么读取程序需要挂起。
数据全都写入到ES5的临时节点后,开始一台一台的升级ES2。之前在ES2的升级过程中通过yum安装ES时发现ES已经从5.5.1升级到了5.5.2,ES对于版本的控制非常严格,虽然可以同时正常的查询工作,但是之前的数据都是5.5.1版本,无法在5.5.2版本的node上写入数据,没办法,只能先升级原先的es5tmp节点到5.5.2版本,由于是小版本升级,直接yum update然后重新启动节点即可。
数据的存储之前并没有采用LVM管理,所以添加一块磁盘,需要修改ES的配置文件,然后重新启动机器,这次升级顺便将所有的ES的数据盘配置为LVM方式。由于之前已经有一部分数据写入到了新加入的非LVM节点上,所以需要将其中的shards再写回到es5tmp临时节点,查看文档,发现使用cluster.routing.allocation.exclude._ip设置可以达到要求,设置完成后分片会自动写回到临时节点上。LVM磁盘配置好后,重新加入集群,使用cluster.routing.allocation.include._ip设置后发现并没有作用,经过尝试发现,必须cluster.routing.allocation.exclude._ip设置为空字符串,同时cluster.routing.allocation.include._ip也必须设置为空。等待所有的ES2的节点都加入集群后,将临时节点的ip设置到exclude中,这样所有的shards和副本都会自动转移到新的节点上。
由于内存和资源限制,整个升级过程是每次打开一个月的index,所有的主分片都从临时节点转移后,再设置replica个数为1,等所有副本都完成后,close这个月的所有index,然后开始下一个月。这样整个过程只保留了当前最新的一个月的数据(供在线服务使用)和正在进行中的那个月的数据,整个集群压力较小。
升级完成后安装Kibana,发现ES已经升级到5.6了,不想折腾ES的各个节点升级到5.6,所以通过yum安装5.5.2的Kibana。
升级完成后http query的接口已经完全变了,所有之前依赖es2的查询接口都需要重写。
所以的kibana和ES节点都监听内网地址,不安装x-pack,通过部署一个nginx的代理和简单的用户名密码认证来做简单的安全验证。使用cerebro(之前的kopf)来对集群的状态进行简单的监控和更新配置。
中间还包括了logstash配置更新,kibana配置更新,ES->Hadoop的代码更新不再一一细说。
知识点总结
整个过程涉及到的知识点如下:
- python requests库使用。
- es scroll API使用。
- yum 安装特定版本软件包以及配置清华源。
- 阿里云系统盘镜像创建以及替换部署。
- lvm管理多个磁盘。
- systemctl 使用以及service配置文件。
- es index template 配置。
- nginx basic auth反向代理配置。
- es hadoop map reduce API使用。
- python redis 使用。
- Logstash,ES,Kibana 安装部署以及配置。