etcd是k8s的核心组件之一,主要用于存储k8s集群的所有数据,包括集群的配置信息、集群状态、集群资源等。etcd是一个分布式的、高可用的、一致性的、高性能的键值存储系统,是k8s集群的数据存储中心。

ETCD命令行工具

拷贝etcdctl命令行工具(附加启动etcd命令):

二进制方式

ETCD_VER=v3.4.27

# choose either URL
GOOGLE_URL=https://storage.googleapis.com/etcd
GITHUB_URL=https://github.com/etcd-io/etcd/releases/download
DOWNLOAD_URL=${GOOGLE_URL}

rm -f /tmp/etcd-${ETCD_VER}-linux-amd64.tar.gz
rm -rf /tmp/etcd-download-test && mkdir -p /tmp/etcd-download-test

curl -L ${DOWNLOAD_URL}/${ETCD_VER}/etcd-${ETCD_VER}-linux-amd64.tar.gz -o /tmp/etcd-${ETCD_VER}-linux-amd64.tar.gz
tar xzvf /tmp/etcd-${ETCD_VER}-linux-amd64.tar.gz -C /tmp/etcd-download-test --strip-components=1
rm -f /tmp/etcd-${ETCD_VER}-linux-amd64.tar.gz

/tmp/etcd-download-test/etcd --version
/tmp/etcd-download-test/etcdctl version

ln -s /tmp/etcd-download-test/etcdctl /usr/local/bin/

启动etcd命令(对之后的说明没有影响):

# start a local etcd server
/tmp/etcd-download-test/etcd

# write,read to etcd
/tmp/etcd-download-test/etcdctl --endpoints=localhost:2379 put foo bar
/tmp/etcd-download-test/etcdctl --endpoints=localhost:2379 get foo

docker 方式

docker pull gcr.io/etcd-development/etcd:v3.4.27
docker run --rm --name etcd -d gcr.io/etcd-development/etcd:v3.4.27 /usr/local/bin/etcd
docker cp etcd:/usr/local/bin/etcdctl /usr/local/bin/

启动etcd命令(对之后的说明没有影响):

rm -rf /tmp/etcd-data.tmp && mkdir -p /tmp/etcd-data.tmp && \
  docker rmi gcr.io/etcd-development/etcd:v3.4.27 || true && \
  docker run \
  -p 2379:2379 \
  -p 2380:2380 \
  --mount type=bind,source=/tmp/etcd-data.tmp,destination=/etcd-data \
  --name etcd-gcr-v3.4.27 \
  gcr.io/etcd-development/etcd:v3.4.27 \
  /usr/local/bin/etcd \
  --name s1 \
  --data-dir /etcd-data \
  --listen-client-urls http://0.0.0.0:2379 \
  --advertise-client-urls http://0.0.0.0:2379 \
  --listen-peer-urls http://0.0.0.0:2380 \
  --initial-advertise-peer-urls http://0.0.0.0:2380 \
  --initial-cluster s1=http://0.0.0.0:2380 \
  --initial-cluster-token tkn \
  --initial-cluster-state new \
  --log-level info \
  --logger zap \
  --log-outputs stderr

docker exec etcd-gcr-v3.4.27  /usr/local/bin/etcd --version
docker exec etcd-gcr-v3.4.27  /usr/local/bin/etcdctl version
docker exec etcd-gcr-v3.4.27  /usr/local/bin/etcdctl endpoint health
docker exec etcd-gcr-v3.4.27  /usr/local/bin/etcdctl put foo bar
docker exec etcd-gcr-v3.4.27  /usr/local/bin/etcdctl get foo

ETCD常用操作

查看etcd集群的成员节点

$ export 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 member list -w table

$ alias etcdctl='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'

$ etcdctl member list -w table

查看etcd集群节点状态

$ etcdctl endpoint status -w table

$ etcdctl endpoint health -w table

设置key值

$ etcdctl put test 1
$ etcdctl get test

查看所有key值

$ etcdctl get / --prefix --keys-only

查看具体的key对应的数据

$ etcdctl get /registry/pods/test/myblog-84985b5b66-jp9gg

list-watch

$ etcdctl watch /test/ --prefix

# 另一个终端输入,之前的终端会有输出
$ etcdctl put /test/key1 val1

快照与恢复(重要!)

添加定时任务做数据快照

$ etcdctl snapshot save `hostname`-etcd_`date +%Y%m%d%H%M`.db

恢复快照:

  1. 停止etcd和apiserver(防止新数据写入)
  2. 移走当前数据目录
$ mv /var/lib/etcd/ /tmp
  1. 恢复快照
$ etcdctl snapshot restore `hostname`-etcd_`date +%Y%m%d%H%M`.db --data-dir=/var/lib/etcd/
  1. 集群恢复
    https://github.com/etcd-io/etcd/blob/release-3.3/Documentation/op-guide/recovery.md

namespace删除问题

很多情况下,会出现namespace删除卡住的问题,此时可以通过操作etcd来删除数据:

# 查看namespace相关的数据
$ etcdctl get / --prefix --keys-only | grep namespace | grep delete-ns
/registry/namespaces/delete-ns

# 删除namespace相关的数据
$ etcdctl del /registry/namespaces/delete-ns

故障恢复

etcd集群故障恢复

因为etcd中存储着最重要的数据,所以etcd集群的故障恢复是最重要的。如果节点故障,无法正常启动则需要添加新节点进行恢复。

  1. 将etcd备份数据拷贝到新节点
$ scp /backup/etcd-202109031100.db root@new-etcd:/tmp/
  1. 将数据恢复到新节点
$ etcdctl snapshot restore /tmp/etcd-202109031100.db --data-dir=/var/lib/etcd/
  1. 新节点启动etcd
$ name="etcd-new"
$ host="etcd-new-ip"
$ cluster="etcd1=http://etcd-new-ip:2380"
 
$ docker run -d --privileged=true \
-p 2379:2379 \
-p 2380:2380 \
-v /var/lib/etcd:/data/etcd \
--restart=always \
--name ${name} \
--net=host \
-v /etc/kubernetes/pki/etcd:/etc/kubernetes/pki/etcd \
gcr.io/coreos/etcd:v3.5.0 \
/usr/local/bin/etcd \
--name ${name} \
--data-dir /data/etcd \
--listen-client-urls http://${host}:2379 \
--advertise-client-urls http://${host}:2379 \
--listen-peer-urls http://${host}:2380 \
--initial-advertise-peer-urls http://${host}:2380 \
--initial-cluster ${cluster} \
--initial-cluster-token tkn \
--initial-cluster-state new \
--force-new-cluster \
--log-level info \
--logger zap \
--log-outputs stderr

虽然已经导入了数据,但是还是需要重新初始化集群,重新建立连接,所以需要加上--force-new-cluster参数。
4. 验证集群状态

$ export ETCDCTL_API=3
$ export ETCD_ENDPOINTS="http://etcd-new-ip:2379"
$ etcdctl --endpoints=${ETCD_ENDPOINTS} -w table member list
$ etcdctl --endpoints=${ETCD_ENDPOINTS} -w table endpoint status
$ etcdctl --endpoints=${ETCD_ENDPOINTS} get / --prefix --keys-only

etcd集群增量恢复

如果etcd集群中的数据丢失了一部分,可以通过增量恢复的方式来恢复数据。比如有备份数据etcd-backup1.db和备份数据etc-backup2.db,两次备份数据相差1天,我们丢失了数据,可以只恢复这1天的数据。

  1. etcdhelper源码下载
    etcdhelper不提供二进制文件,需要自己编译,所以需要下载源码:
$ git clone https://github.com/openshift/origin.git
  1. 编译etcdhelper
$ docker run -d --name go-builder golang:1.17 sleep 3600
$ docker cp origin-master go-builder:/go
$ docker exec -ti go-builder bash

# cd /go/orign-master
# go env -w GOPROXY=https://goproxy.cn,direct
# CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build   tools/etcdhelper/etcdhelper.go
# exit

$ docker cp go-builder:/go/origin-master/etcdhelper .
$ cp etcdhelper /usr/local/bin/
  1. etcdhelper使用
    etcdhelper使用方式和etcdctl类似,只是etcdhelper是对etcdctl的封装,可以批量操作,比如批量删除等。获取到的数据是json格式,可以直接查看。
$ export ETCDCTL_API=3
$ export ETCD_ENDPOINTS="http://etcd-new-ip:2379"
$ etcdhelper -endpoints=${ETCD_ENDPOINTS} ls
$ etcdhelper -endpoints=${ETCD_ENDPOINTS} get   /registry/pods/test/myblog-84985b5b66-jp9gg
  1. 增量恢复
    比如我们丢失了etcd-backup1.dbetcd-backup2.db之间的数据,我们可以通过etcdhelper获取到这段时间的数据,然后再导入到etcd集群中。这里我们举例误删除了namespace为test下的所有资源情况。
    首先我们找一台新机器,恢复etcd-backup1.db的数据,因为etcd-backup1.db是完整的数据,包含被删除的数据。恢复方法参考上面的故障恢复。
  2. 批量操作获取yaml文件
    kubectl没有办法直接使用json格式的文件,因此需要将json格式的文件转换为yaml格式的文件,然后再使用kubectl apply命令。
$ export ETCDCTL_API=3
$ export ETCD_ENDPOINTS="http://etcd-new-ip:2379"
$ etcdctl --endpoints=${ETCD_ENDPOINTS} get / --prefix --keys-only | grep test > keys.txt
 
# 使用脚本利用etcdhelper将key转换成为json文件
cat key_to_json.sh
 
#!/bin/bash

i=0
export ETCDCTL_API=3
export ETCD_ENDPOINTS=etcd-new-ip:2379
for line in `cat keys.txt`
do
  etcdhelper -endpoint $ETCD_ENDPOINTS get $line > $i.json
  # 第一行是资源类型和版本,不是json格式,需要删除
  sed -i '1d' $i.json
  let 'i+=1'
done

运行脚本,将key转换成为json文件:

$ ./key_to_json.sh

将json文件转换成为yaml文件:

# json to yaml,具体方法就不写了,内容还比较简单
$ python3 json_to_yaml.py
  1. 批量导入操作
$ cat apply.sh
#!/bin/bash
 
i=0
for line in `cat keys.txt`
do
    kubectl apply -f $i.yaml
    let 'i+=1'
done
 
$ ./apply.sh