背景

Etcd是用于共享配置和服务发现的分布式、满足一致性的KV系统,受到Zookeeper启发,该项目是由CoreOS公司发起。目前在各种云环境中应用广泛,几个比较流行的云项目都采用了Etcd,如CloudFoundry、Kubernetes、Docker。Etcd采用Raft协议进行主节点的选举,并把相应的成员信息存储在各成员的db中。更多关于Etcd的详细介绍,可以在网上或者官方看到,这里不进行相关描述。

由于Kubernetes采用Etcd作为后端数据存储,如果Etcd出现问题,会导致整个Kubernetes集群的数据不一致,严重的甚至会导致集群不可用,因此需要掌握Etcd集群的常见问题解决方式,以便在该组件出现故障的时候,可以及时修复,从而保证Kubernetes集群的正常可用,不影响用户的使用。

如果Kubernetes集群部署完成后,更新整个集群所有节点的IP地址,当前Kubernetes的控制节点和Etcd成员节点在同一主机上运行,这也意味着如果修改Kubernetes控制节点的IP地址,需要对Etcd集群进行操作,以便Etcd集群可以使用新的IP地址进行通信。因此本文档重点介绍Etcd集群成员变更的两种方式。

成员变更解决方式

Etcd集群本身满足(n/2)+1的成员容忍性。下面为集群大小对应的可容忍的异常成员数量。

集群大小 Majority 容忍数量
1 1 0
2 2 0
3 2 1
4 3 1
5 3 2
6 4 2
7 4 3
8 5 3
9 5 4

目前我们的Kubernetes集群采用大多数是3节点的Etcd集群,因此允许一个节点失联。

下面详细描述下如何进行集群成员IP变更,因为我们的Etcd集群运行在容器中,采用Static Pod的方式由Kubelet进行管理,所以如果Etcd集群部署形态并非容器的话,请根据实际情况进行相应调整,关于etcdctl的操作是一致的,没有区别。

我们仍然使用下面的三节点集群,所有节点IP地址如下:

成员名称 旧IP 新IP
master1 192.168.100.10 192.168.110.10
master2 192.168.100.11 192.168.110.11
master3 192.168.100.12 192.168.110.12

在保证集群可用的情况进行成员变更

为满足对集群中节点进行IP地址变更的需求,在进行成员变更的时候,需要保证原有集群仍然可以对外进行服务,因此需要依次更换集群中的成员信息。

查看集群成员信息并检测集群是否健康。我们先进行master3节点的IP地址变更操作,看下如何在保证集群可用的情况下进行成员的IP地址变更。

$ ETCDCTL_API=3 etcdctl --endpoints=https://etcd.cluster.local.lb:2379 --cacert=/etc/kubernetes/pki/etcd/ca.crt --cert=/etc/kubernetes/pki/etcd/healthcheck-client.crt --key=/etc/kubernetes/pki/etcd/healthcheck-client.key member list
e2ef974167ea1d35, started, master1, https://192.168.100.10:2380, https://192.168.100.10:2379
1f2e59c3615624b3, started, master2, https://192.168.100.11:2380, https://192.168.100.11:2379
2430ba10b69efc5a, started, master3, https://192.168.100.12:2380, https://192.168.100.12:2379
$ /usr/bin/etcdctl --cert-file /etc/kubernetes/pki/etcd/peer.crt --key-file /etc/kubernetes/pki/etcd/peer.key --ca-file /etc/kubernetes/pki/etcd/ca.crt --endpoints https://etcd.cluster.local.lb:2379 cluster-health
member e2ef974167ea1d35 is healthy: got healthy result from https://192.168.100.10:2379
member 1f2e59c3615624b3 is healthy: got healthy result from https://192.168.100.11:2379
member 2430ba10b69efc5a is healthy: got healthy result from https://192.168.100.12:2379

我们更改master3的IP地址192.168.100.12192.168.110.12。下面是针对Etcd集群的变更操作。

  1. 重启master3上etcd的服务,可以看到该etcd成员正常启动。

docker ps -a -q --filter name=k8s_etcd | xargs -r docker rm --force --volumes,可以看到etcd的容器被移除,并在1分钟之内会由Kubelet启动一个新的etcd服务。

可以通过docker ps -a -q --filter name=k8s_etcd | xargs docker logs 查看etcd的启动日志,启动会可以通过netstat -anp|grep 2379 | grep LISTEN查看到etcd服务已经启动完成,并监听在2379端口。

  1. 登录到master1上,查看当前集群成员信息
$ ETCDCTL_API=3 etcdctl --endpoints=https://etcd.cluster.local.lb:2379 --cacert=/etc/kubernetes/pki/etcd/ca.crt --cert=/etc/kubernetes/pki/etcd/healthcheck-client.crt --key=/etc/kubernetes/pki/etcd/healthcheck-client.key member list
e2ef974167ea1d35, started, master1, https://192.168.100.10:2380, https://192.168.100.10:2379
1f2e59c3615624b3, started, master2, https://192.168.100.11:2380, https://192.168.100.11:2379
2430ba10b69efc5a, started, master3, https://192.168.100.12:2380, https://192.168.110.12:2379

我们可以看到集群中master3成员的client地址变为了新IP,而peer的地址仍然是旧IP,这是因为peer的信息存储在成员的db中,因此我们需要更改当前集群中master3的peer地址为新IP。

  1. 使用如下命令更改peer地址

ETCDCTL_API=3 etcdctl --endpoints=https://etcd.cluster.local.lb:2379 --cacert=/etc/kubernetes/pki/etcd/ca.crt --cert=/etc/kubernetes/pki/etcd/healthcheck-client.crt --key=/etc/kubernetes/pki/etcd/healthcheck-client.key member update 2430ba10b69efc5a --peer-urls=https://192.168.110.12:2380

  1. 查看当前集群成员信息是否正确
$ ETCDCTL_API=3 etcdctl --endpoints=https://etcd.cluster.local.lb:2379 --cacert=/etc/kubernetes/pki/etcd/ca.crt --cert=/etc/kubernetes/pki/etcd/healthcheck-client.crt --key=/etc/kubernetes/pki/etcd/healthcheck-client.key member list
e2ef974167ea1d35, started, master1, https://192.168.100.10:2380, https://192.168.100.10:2379
1f2e59c3615624b3, started, master2, https://192.168.100.11:2380, https://192.168.100.11:2379
2430ba10b69efc5a, started, master3, https://192.168.110.12:2380, https://192.168.110.12:2379

可以看到master3的成员信息更新为新的IP。

要替换整个集群的成员信息,我们需要按上述步骤依次变更Etcd集群成员信息,该方式可以保证Etcd集群在成员变更的情况下,仍然可以对外提供服务。并且该方式对于整个集群风险偏低。

在不保证集群可用的情况下进行成员变更

在对集群中节点进行IP地址变更的过程中,还存在一种情况,那就是集群中所有节点事先修改了IP,在这种情况下,我们不能通过上面的依次变更成员的方式进行IP地址的变更,因为在这个时候,整个Etcd集群是不可用的,无法进行成员信息的在线变更,因此我们提供另一种变更方式来支持这种情况。

该方式主要是基于原有的Etcd集群数据,恢复出来一个新的Etcd集群,保证数据不丢失,因为在这个时候Etcd集群已经不可用了,并且所有成员信息全部进行了变更,相当于使用原有数据新建集群。

$ ETCDCTL_API=3 etcdctl --endpoints=https://etcd.cluster.local.lb:2379 --cacert=/etc/kubernetes/pki/etcd/ca.crt --cert=/etc/kubernetes/pki/etcd/healthcheck-client.crt --key=/etc/kubernetes/pki/etcd/healthcheck-client.key member list
e2ef974167ea1d35, started, master1, https://192.168.100.10:2380, https://192.168.100.10:2379
1f2e59c3615624b3, started, master2, https://192.168.100.11:2380, https://192.168.100.11:2379
2430ba10b69efc5a, started, master3, https://192.168.100.12:2380, https://192.168.100.12:2379
  1. 在任意一台成员节点生成现有Etcd集群的数据快照,并拷贝快照到其他成员节点/tmp目录下

ETCDCTL_API=3 etcdctl --endpoints=https://127.0.0.1:2379 --cacert=/etc/kubernetes/pki/etcd/ca.crt --cert=/etc/kubernetes/pki/etcd/healthcheck-client.crt --key=/etc/kubernetes/pki/etcd/healthcheck-client.key snapshot save /tmp/snapshot.db

  1. 分别在所有成员节点主机上删除整个Etcd集群成员etcd数据目录

rm -rf <etcd数据目录>

  1. 分别在所有成员节点主机上执行快照恢复的命令

ETCDCTL_API=3 etcdctl --cacert=/etc/kubernetes/pki/etcd/ca.crt --cert=/etc/kubernetes/pki/etcd/healthcheck-client.crt --key=/etc/kubernetes/pki/etcd/healthcheck-client.key snapshot restore snapshot.db --name <当前主机成员节点名称> --initial-cluster master1=https://<master1新IP>:2380,master2=https://<master2新IP>:2380,master3=https://<master3新IP>:2380 --initial-advertise-peer-urls=https://<当前主机成员节点IP>:2380 --data-dir=<etcd数据目录>

  1. 分别在所有成员节点修改Etcd服务的启动项中--initial-cluster--initial-advertise-peer-urls--advertise-client-urls的配置,该配置属于Etcd基本操作,不在此进行描述。

  2. 分别在所有成员节点重启etcd服务,等待etcd成员正常启动。

docker ps -a -q --filter name=k8s_etcd | xargs -r docker rm --force --volumes,Kubelet会启动一个新的etcd服务。

可以通过docker ps -a -q --filter name=k8s_etcd | xargs docker logs 查看etcd的启动日志,启动会可以通过netstat -anp|grep 2379 | grep LISTEN查看到etcd服务已经启动完成,并监听在2379端口。

  1. 登录到任意一台成员主机上,查看当前集群成员信息
$ ETCDCTL_API=3 etcdctl --endpoints=https://etcd.cluster.local.lb:2379 --cacert=/etc/kubernetes/pki/etcd/ca.crt --cert=/etc/kubernetes/pki/etcd/healthcheck-client.crt --key=/etc/kubernetes/pki/etcd/healthcheck-client.key member list
e2ef974167ea1d35, started, master1, https://192.168.110.10:2380, https://192.168.110.10:2379
1f2e59c3615624b3, started, master2, https://192.168.110.11:2380, https://192.168.110.11:2379
2430ba10b69efc5a, started, master3, https://192.168.110.12:2380, https://192.168.110.12:2379

我们可以看到集群的client和peer地址变为了新的IP,使用etcdctl检测集群是否健康。

$ /usr/bin/etcdctl --cert-file /etc/kubernetes/pki/etcd/peer.crt --key-file /etc/kubernetes/pki/etcd/peer.key --ca-file /etc/kubernetes/pki/etcd/ca.crt --endpoints https://etcd.cluster.local.lb:2379 cluster-health
member e2ef974167ea1d35 is healthy: got healthy result from https://192.168.110.10:2379
member 1f2e59c3615624b3 is healthy: got healthy result from https://192.168.110.11:2379
member 2430ba10b69efc5a is healthy: got healthy result from https://192.168.110.12:2379

至此整个集群成员IP变更就完成了,并且集群可以正常对外提供服务。

总结

上面介绍了两种不同的方式来进行Etcd集群成员IP变更,在Kubernetes集群中,Etcd是被严重依赖的,因此对于Etcd集群的任何操作,我们都需要小心谨慎,否则可能会导致整个集群的崩溃,从而使得影响面无限扩大,因此建议如非必要,切勿进行上述的第二种方式进行集群成员的变更,因为快照数据一旦损坏,整个集群需要重建,建议采用第一种方式,该方式可以保证在修改的成员出现问题的时候,集群仍然是可用的,从而如果在变更的成员出现问题无法恢复的情况下,集群仍然可以进行节点添加、删除的操作,来保证集群的高可用。