前言
etcd 是一个典型的健值库(Key Vaule),用于共享配置和服务发现的分布式、一致性的数据库系统。etcd 本身是 CoreOS 公司发起的一个开源项目,和大部分开源软件一样。etcd 存储了 Kubernetes 集群的大部分数据信息,是极为重要的服务之一。如果发生灾难或者 etcd 的数据丢失,将对集群数据的恢复产生非常严重的影响,本文重点讲如何备份和恢复数据。
1. etcd 与 Kubernetes
etcd 是一个典型的健值库(Key Vaule),用于共享配置和服务发现的分布式、一致性的数据库系统。etcd 本身是 CoreOS 公司发起的一个开源项目,和大部分开源软件一样。etcd 存储了 Kubernetes 集群的大部分数据信息,是极为重要的服务之一。如果发生灾难或者 etcd 的数据丢失,将对集群数据的恢复产生非常严重的影响,本文重点讲如何备份和恢复数据。
etcd 开源软件协议
https://github.com/etcd-io/etcd/blob/main/LICENSE
2. Kubernetes 集群的日常运维工作 etcd 的备份
2.1 准备工作 etcdctl 的下载与安装
etcdctl 是我们日常要备份、恢复 etcd 所使用的工具,工欲善其事,必先利其器。我们首先要进行 etcdctl 下载与安装。etcdctl 与 etcd 的软件包在一起,我们可以通过以下链接找到。
https://github.com/etcd-io/etcd/releases
$ wget https://github.com/etcd-io/etcd/releases/download/v3.5.0/etcd-v3.5.0-linux-amd64.tar.gz
$ tar zxvf etcd-v3.5.0-linux-amd64.tar.gz
$ sudo cp etcd-v3.5.0-linux-amd64/etcdctl /usr/bin/
2.2 验证 etcdctl 可用
etcdctl --help
2.3 手动备份与恢复 etcd
$ kubectl describe node |grep ip
flannel.alpha.coreos.com/public-ip: 192.168.182.180
2.4 etcd 备份所需要的参数
$ kubectl describe po -n etcd-ubuntu -n kube-system > etcd.txt
$ cat etcd.txt |grep 2379
Annotations: kubeadm.kubernetes.io/etcd.advertise-client-urls: https://192.168.182.180:2379
--advertise-client-urls=https://192.168.182.180:2379
--listen-client-urls=https://127.0.0.1:2379,https://192.168.182.180:2379
--etcd-servers=https://127.0.0.1:2379
$ cat etcd.txt |grep ca.crt
--peer-trusted-ca-file=/etc/kubernetes/pki/etcd/ca.crt
--trusted-ca-file=/etc/kubernetes/pki/etcd/ca.crt
--client-ca-file=/etc/kubernetes/pki/ca.crt
--etcd-cafile=/etc/kubernetes/pki/etcd/ca.crt
--requestheader-client-ca-file=/etc/kubernetes/pki/front-proxy-ca.crt
--client-ca-file=/etc/kubernetes/pki/ca.crt
--cluster-signing-cert-file=/etc/kubernetes/pki/ca.crt
--requestheader-client-ca-file=/etc/kubernetes/pki/front-proxy-ca.crt
--root-ca-file=/etc/kubernetes/pki/ca.crt
$ cat etcd.txt |grep server.crt
--cert-file=/etc/kubernetes/pki/etcd/server.crt
--tls-cert-file=/etc/kubernetes/pki/apiserver.crt
$ cat etcd.txt |grep server.key
--key-file=/etc/kubernetes/pki/etcd/server.key
--tls-private-key-file=/etc/kubernetes/pki/apiserver.key
2.5 etcd Backup
# 为 etcd 备份文件建立备份目录
sudo mkdir -p $HOME/etcd-backup-file
# 执行备份操作
$ sudo ECTD_API=3 etcdctl --endpoints=https://192.168.182.180:2379 \
--cacert=/etc/kubernetes/pki/etcd/ca.crt \
--cert=/etc/kubernetes/pki/etcd/server.crt \
--key=/etc/kubernetes/pki/etcd/server.key \
snapshot save $HOME/etcd-backup-file/etcd-snapshot-`date +%Y%m%d`.db
{"level":"info","ts":1632917737.5671601,"caller":"snapshot/v3_snapshot.go:68","msg":"created temporary db file","path":"/home/mars/etcd-backup-file/etcd-snapshot-20210929.db.part"}
{"level":"info","ts":1632917737.5744143,"logger":"client","caller":"v3/maintenance.go:211","msg":"opened snapshot stream; downloading"}
{"level":"info","ts":1632917737.5744812,"caller":"snapshot/v3_snapshot.go:76","msg":"fetching snapshot","endpoint":"https://192.168.182.180:2379"}
{"level":"info","ts":1632917737.7435777,"logger":"client","caller":"v3/maintenance.go:219","msg":"completed snapshot read; closing"}
{"level":"info","ts":1632917737.7507617,"caller":"snapshot/v3_snapshot.go:91","msg":"fetched snapshot","endpoint":"https://192.168.182.180:2379","size":"4.6 MB","took":"now"}
{"level":"info","ts":1632917737.751167,"caller":"snapshot/v3_snapshot.go:100","msg":"saved","path":"/home/mars/etcd-backup-file/etcd-snapshot-20210929.db"}
Snapshot saved at /home/mars/etcd-backup-file/etcd-snapshot-20210929.db
# 查看备份文件情况
$ ls /home/mars/etcd-backup-file/
etcd-snapshot-20210929.db
3. 用 Kasten 进行 etcd 的备份
如上述文档,将 etcd 的备份进行了描述,但我们不难发现一个问题,如果备份集就是在本地保留,那么如果本地的数据中心出现灾难,则 K8S 及其内的应用都无法恢复。针对这种情况,我们可以使用 Kasten 进行 etcd 的备份。前提是 Kubernetes 集群是通过 Kubeadm 设置的,etcd pod 在 kube-system 命名空间中运行。托管的 Kubernetes 的备份与万利详见 Mars 的 Kasten 实战系列文章。
3.1 获取 etcd 信息
在备份 etcd 集群之前,需要在临时的新名称空间或现有名称空间中创建一个 Secret,其中包含有关 etcd 使用的身份验证机制的详细信息 。在的情况下 kubeadm,很可能已经使用基于 TLS 的身份验证部署了 etcd。可以通过运行以下命令来创建临时命名空间和访问 etcd 的 Secret:
$ kubectl get po -n kube-system
如果服务器密钥和证书的路径提供的不正确,备份将失败。这些路径可以通过在 在之前文档所描述的信息中获取
$ kubectl get po -n kube-system
NAME READY STATUS RESTARTS AGE
coredns-74ff55c5b-mgxnw 1/1 Running 0 14m
coredns-74ff55c5b-plq7k 1/1 Running 0 14m
etcd-ubuntu 1/1 Running 0 14m # <--这就是我们需要备份的 etcd 数据库
kube-apiserver-ubuntu 1/1 Running 0 14m
kube-controller-manager-ubuntu 1/1 Running 0 14m
kube-flannel-ds-h6gs5 1/1 Running 0 12m
kube-proxy-2vl6g 1/1 Running 0 14m
kube-scheduler-ubuntu 1/1 Running 0 14m
3.2 安装 Kasten K10
关于 Kasten 的安装在这里只是给出一个示例,详细的文档,请参考 Mars 的 Kasten 实战系列文章
# 建立命名空间
$ kubectl create ns kasten-io
# helm repo add kasten https://charts.kasten.io/
"kasten" has been added to your repositories
$ helm fetch kasten/k10 --version=4.0.12
# 安装 Kasten K10
$ helm install k10 k10-4.0.12.tgz --namespace kasten-io --set global.airgapped.repository=ccr.ccs.tencentyun.com/kasten \
--set auth.basicAuth.enabled=true \
--set injectKanisterSidecar.enabled=true \
--set auth.basicAuth.htpasswd='admin:$apr1$Cgu1sGVZ$w/8aLHZkVT73OqYZ06C0v.' \
--set metering.mode=airgap \
--set global.persistence.storageClass=ceph-csi-rbd
# 查看安装情况
$ kubectl get po -n kasten-io
NAME READY STATUS RESTARTS AGE
aggregatedapis-svc-7b6788d55-cfwp5 1/1 Running 0 5m16s
auth-svc-5f79658bd-xjnt5 1/1 Running 0 5m16s
catalog-svc-6db6f6cc69-ghknx 2/2 Running 0 5m16s
config-svc-5cb9f6696f-r9w2g 1/1 Running 0 5m16s
crypto-svc-6c77f4f498-xdq7g 2/2 Running 0 5m17s
dashboardbff-svc-76d56754cd-f7k67 1/1 Running 0 5m16s
executor-svc-78679647fd-6wq8d 2/2 Running 0 5m16s
executor-svc-78679647fd-8g5f6 2/2 Running 0 5m16s
executor-svc-78679647fd-pqc8s 2/2 Running 0 5m16s
frontend-svc-69bbc56d6b-4h24r 1/1 Running 0 5m15s
gateway-b8bb9bb44-hczdx 1/1 Running 0 5m17s
jobs-svc-85545bc5df-p5b8v 1/1 Running 0 5m16s
k10-grafana-589cddff75-6mxzb 1/1 Running 0 5m17s
kanister-svc-68cbcd5d55-mzlxt 1/1 Running 0 5m17s
logging-svc-5cc8675cc8-nzng8 1/1 Running 0 5m16s
metering-svc-56b885f455-59rt5 1/1 Running 0 5m17s
prometheus-server-fc8fb9bb7-hgvtm 2/2 Running 0 5m17s
state-svc-77bb58d6c8-859vf 1/1 Running 0 5m15s
# 暴露 Web UI 服务
$ kubectl expose service gateway -n kasten-io --type=NodePort --name=gateway-nodeport
service/gateway-nodeport exposed
$ kubectl get svc -n kasten-io gateway-nodeport
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
gateway-nodeport NodePort 10.108.54.108 <none> 8000:32057/TCP 11s
$ kubectl describe node |grep ip
flannel.alpha.coreos.com/public-ip: 192.168.182.180
访问如下地址 http://192.168.182.180:32057/k10/#/dashboard
3.3 etcd-backup 准备工作
# 建立 etcd-backup 命名空间
$ kubectl create ns etcd-backup
namespace/etcd-backup created
# 建立关键信息链接
$ kubectl create secret generic etcd-details \
--from-literal=cacert=/etc/kubernetes/pki/etcd/ca.crt \
--from-literal=cert=/etc/kubernetes/pki/etcd/server.crt \
--from-literal=endpoints=https://192.168.182.180:2379 \
--from-literal=key=/etc/kubernetes/pki/etcd/server.key \
--from-literal=etcdns=kube-system \
--from-literal=labels=component=etcd,tier=control-plane \
--namespace etcd-backup
为避免备份来自命名空间的任何其他工作负载,可以标记 Secret 以确保仅此 Secret 包含在备份中。可以执行以下命令来标记 Secret:etcd-backupetcd-details
$ kubectl get secret -n etcd-backup
NAME TYPE DATA AGE
default-token-bg42p kubernetes.io/service-account-token 3 92s
etcd-details Opaque 6 47s
$ kubectl label secret -n etcd-backup etcd-details include=true
secret/etcd-details labeled
$ kubectl get secrets --show-labels -n etcd-backup
NAME TYPE DATA AGE LABELS
default-token-bg42p kubernetes.io/service-account-token 3 113m <none>
etcd-details Opaque 6 113m include=true
3.4 用 Kasten 备份 etcd
要创建 K10 将用于备份 etcd 的蓝图资源 ,请运行以下命令:
kubectl --namespace kasten-io apply -f \
https://raw.githubusercontent.com/kanisterio/kanister/0.65.0/examples/etcd/etcd-in-cluster/k8s/etcd-incluster-blueprint.yaml
blueprint.cr.kanister.io/etcd-blueprint created
$ kubectl get blueprint -n kasten-io
NAME AGE
etcd-blueprint 30s
k10-namespace-generic-volume-2.0.7 88s
创建蓝图后,需要对上面创建的 Secret 进行注释,以指示 K10 使用蓝图在 etcd pod 上执行备份。以下命令演示了如何使用之前创建的蓝图的名称来注释 Secret。
$ kubectl annotate secret -n etcd-backup etcd-details kanister.kasten.io/blueprint='etcd-blueprint'
secret/etcd-details annotated
$ kubectl describe secret etcd-details -n etcd-backup
Name: etcd-details
Namespace: etcd-backup
Labels: include=true
Annotations: kanister.kasten.io/blueprint: etcd-blueprint
Type: Opaque
Data
====
endpoints: 26 bytes
etcdns: 11 bytes
key: 35 bytes
labels: 33 bytes
cacert: 31 bytes
cert: 35 bytes
创建 S3 桶
注释 Secret 后,使用 K10 使用新命名空间备份 etcd。如果如前面的步骤之一所述,对 Secret 进行了标记,则在创建策略时,可以通过添加如下所示的资源过滤器将该 Secret 包含在备份中:
可以通过查看创建的还原点的 Kanister 工件找到etcd的备份位置。
4. 恢复 etcd
要恢复 etcd 备份,请登录到运行 etcd pod 的主机(很可能是 Kubernetes control plane)。通过在 K10 仪表板上查看备份操作的详细信息来获取恢复路径,并将快照下载到 etcd pod 主机上的特定位置(例如,/tmp/etcd-snapshot.db)。例如,如果使用 AWS S3 作为对象存储,则需要 AWS CLI 来获取备份。
从备份目标下载快照后,使用 CLI 工具将该快照恢复到特定位置,例如在 K8S Master Node 主机上。以下命令可用于恢复etcd备份:
etcdctl /var/lib/etcd-from-backup
etcdCTL_API=3 etcdctl --endpoints=https://127.0.0.1:2379 \
--cacert=/etc/kubernetes/pki/etcd/ca.crt \
--cert=/etc/kubernetes/pki/etcd/server.crt \
--key=/etc/kubernetes/pki/etcd/server.key \
--data-dir="/var/lib/etcd-from-backup" \
--initial-cluster="master=https://127.0.0.1:2380" \
--name="master" \
--initial-advertise-peer-urls="https://127.0.0.1:2380" \
--initial-cluster-token="etcd-cluster-1" \
snapshot restore /tmp/snapshot-pre-boot.db
可以从 etcd pod 清单(静态 pod)中发现为上述标志提供的所有值 。两个重要的标志是 --data-dir和--initial-cluster-token。--data-dir是etcd将其数据存储到的目录,并且--initial-cluster-token 是定义新成员加入该 etcd 集群的令牌的标志。注意:一旦备份恢复到新目录,则需要更新 etcd 清单(静态 pod)以将其数据目录指向这个新目录,并且需要在 etcd 命令参数中指定。除此之外, 和字段也应该更改为指向 我们将备份恢复到的新字段。
5. 结论
在本篇文章中我们通过说明了如何进行 etcd 的备份, 这对于日常运维 K8S 集群的人员来说是非常有效的,欢迎与我们讨论您在日常运维过程中的经验,谢谢!
参考文献
https://github.com/etcd-io/etcd/tree/main/etcdctl
etcdctl download
https://github.com/etcd-io/etcd/releases
How etcd works with and without Kubernetes
https://learnk8s.io/etcd-kubernetes