etcd篇——运维指南

1. etcd集群

1.1 etcd集群简介

etcd为了提高可用性提供了集群模式,集群模式下会选举leader,并通过leader和raft机制保证集群模式下的一致性问题,同一时刻有且仅有一个leader。关于集群模式,需要关注以下:

  1. API操作etcd时,不需要关注节点是否是leader,对于需要保证一致性的请求,follower节点会自动转发给leader处理。
  2. 集群成员的数目一般是奇数,如果有3个成员,表明集群最大允许挂掉1个节点,如果有5个成员,则最大允许挂掉2个节点。当然节点数目不是越大越好,因为一致性机制下节点数目会影响写性能。

1.2 etcd集群搭建

参考手册:https://blog.lecury.cn/2016/06/27/Etcd集群搭建过程/

etcd架构图

2. etcd运维

集群模式大大提高了etcd的可用性,但是仍存在异常和挂掉的可能性。为了进一步提高稳定性和可用性,可以从以下方面着手。

2.1 etcd operator

etcd operator概念最早出现在k8s的生态中,目的是借助k8s平台自动创建、管理etcd集群,不仅提供部署能力,还提供故障迁移的能力。

方案的理念是相似的,是将etcd集群交给一个全局控制器来管理,这个控制器可以运行在k8s平台,也可以独立运行,以提高etcd集群的稳定性和可用性。

2.2 容灾和恢复

目前搜集到的方案大致是:

  1. 定期dump数据,推送到分布式存储中。
  2. 恢复时,将数据拷到每个节点上然后分别执行restore。

restore的方式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
$ ETCDCTL_API=3 etcdctl snapshot restore snapshot.db \
--name m1 \
--initial-cluster m1=http://host1:2380,m2=http://host2:2380,m3=http://host3:2380 \
--initial-cluster-token etcd-cluster-1 \
--initial-advertise-peer-urls http://host1:2380
$ ETCDCTL_API=3 etcdctl snapshot restore snapshot.db \
--name m2 \
--initial-cluster m1=http://host1:2380,m2=http://host2:2380,m3=http://host3:2380 \
--initial-cluster-token etcd-cluster-1 \
--initial-advertise-peer-urls http://host2:2380
$ ETCDCTL_API=3 etcdctl snapshot restore snapshot.db \
--name m3 \
--initial-cluster m1=http://host1:2380,m2=http://host2:2380,m3=http://host3:2380 \
--initial-cluster-token etcd-cluster-1 \
--initial-advertise-peer-urls http://host3:2380

然后再启动

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
$ etcd \
--name m1 \
--listen-client-urls http://host1:2379 \
--advertise-client-urls http://host1:2379 \
--listen-peer-urls http://host1:2380 &
$ etcd \
--name m2 \
--listen-client-urls http://host2:2379 \
--advertise-client-urls http://host2:2379 \
--listen-peer-urls http://host2:2380 &
$ etcd \
--name m3 \
--listen-client-urls http://host3:2379 \
--advertise-client-urls http://host3:2379 \
--listen-peer-urls http://host3:2380 &

整体操作还是挺麻烦的,完善的容灾机制需要快、准,最好提供平台化的支持,最终目标是实现一键回滚到某个时间点。

参考:https://etcd.io/docs/v3.3.12/op-guide/recovery/

2.3 etcd监控和报警

对etcd集群来说,监控和报警是必不可少的,没有机制能够保证100%的可用,我们需要监控和报警及时的发现问题。

etcd内置了很多prometheus metrics,可以通过prometheus+Grafana方便的监控etcd集群。主要监控点包括:

  • 成员状态
  • 集群吞吐 kps
  • snapshot时间
  • 错误情况
  • 内存相关
  • 等等

这里有个etcd监控模板:https://grafana.com/grafana/dashboards/3070

2.4 etcd集群新增节点

如果该节点曾属于集群成员,那么删除etcd data目录,重启即可。可以通过以下方式查看是否是集群成员

1
ETCDCTL_API=2 etcdctl member list

如果该节点是一个新的节点,那么启动etcd时,

  1. 先在集群节点上执行 etcdctl member add new_node --peer-urls=xxx
  2. 设置环境变量ETCD_INITIAL_CLUSTERETCD_INITIAL_CLUSTER_STATE:分别指明了etcd的集群地址和集群状态。
  3. 启动这个new node

3. 常见问题

3.1 如何选择etcd集群的节点数目?

首先要理解为什么需要增加节点数目?原因大致为:

  1. 提高etcd集群的可用性:3个节点能最大容忍一个节点不可用,5个节点则可以最大容忍2个节点不可用,节点数增加从概率角度上确实能增大集群的可用性。
  2. 提高读的吞吐:集群任何一个节点都能提供读的服务,增加节点类似于横向扩展了。

注意节点数并不是越多越好,因为etcd的一致性机制,每次写操作都需要同步到每个节点,节点数越多会降低写操作的吞吐。

在读写均存在的场景下,建议etcd集群有3个节点,如果读远大于写的场景下,可以考虑增加到5个节点。

3.2 etcd内存、snapshot大小异常

大致原因有以下几点:

  1. etcd的key/value具有版本功能,频繁更新key/value会使得版本增加过快。
  2. raft log的太多了
  3. golang gc问题

解决办法有:

  1. 开启自动compact:启动参数增加--auto-compaction-retention=168
  2. 设置存储quota上限: 启动参数增加--quota-backend-bytes=8589934592
  3. 限制raft log:调小--snapshot-count=10000

3.3 etcd节点挂掉时间太长,重启失败咋办?

集群没挂掉的情况下,直接删除data目录,重新加入集群处理。

3.4 mvcc: database space exceeded 报错如何处理?

etcd会保存key的历史版本,如果没有定期compact的话,就会耗尽etcd的存储空间。如果etcd的剩余空间很小的时候,就会报警:mvcc: database space exceeded. 解决办法:

  1. compact etcd历史
  2. 清理每个etcd endpoint
  3. 消除报警
1
2
3
4
5
recv=`ETCDCTL_API=3 ./etcdctl --endpoints=http://$addr endpoint status --write-out="json" | egrep -o '"revision":[0-9]*' | egrep -o '[0-9].*'`

ETCDCTL_API=3 ./etcdctl --endpoints=http://$addr compact $recv

ETCDCTL_API=3 ./etcdctl --endpoints=http://$addr defrag