写在最前

服务器只要来一次意外宕机etcd它大概率就会损坏启动不起来,没有备份的你只能提桶跑路了。在此情景我们必须要掌握备份和恢复etcd的技能。

1. 前置环境

为了模拟etcd损坏无法恢复的情况,我们需要准备一个纯净的 k8s 环境。

  1. 部署 kubesphere 单机 all-in-one 模式

2. 操作流程

2.1 快速备份

无论是集群还是单机etcd中的内容都是一致彼此同步的,通常会使用定时任务每隔xx小时xx分钟备份一次快照文件。

注意设置 etcd 证书路径,你的证书名称和我的可能会不一样,要检查配置好脚本。

#!/bin/bash

SERVICE_NAME="etcd"

# 检查服务是否在运行
if systemctl is-active --quiet $SERVICE_NAME; then
  echo "$SERVICE_NAME is running."
else
  echo "$SERVICE_NAME is not running."
  exit 1
fi

# 获取当前主机的 IP 地址
CURRENT_IP=$(hostname -I | cut -d' ' -f1)

# 设置备份存储路径
BACKUP_DIR="/root/etcd-backup"

# 设置 etcd 证书路径
CACERT="/etc/ssl/etcd/ssl/ca.pem"
CERT="/etc/ssl/etcd/ssl/admin-master1.pem"
KEY="/etc/ssl/etcd/ssl/admin-master1-key.pem"

# 检查备份目录是否存在,如果不存在则创建
if [ ! -d "$BACKUP_DIR" ]; then
  mkdir -p "$BACKUP_DIR"
fi

# 获取当前时间的年、月、日、小时和分钟
YEAR=$(date +"%Y")
MONTH=$(date +"%m")
DAY=$(date +"%d")
HOUR=$(date +"%H")
MINUTE=$(date +"%M")

# 构建格式化的时间戳
TIMESTAMP="${YEAR}年${MONTH}月${DAY}日${HOUR}时${MINUTE}分"

# 构建备份文件名
BACKUP_FILE="$BACKUP_DIR/etcd_backup_$TIMESTAMP.db"

# 执行 etcd 备份命令
ETCDCTL_API=3 /usr/local/bin/etcdctl --endpoints="https://$CURRENT_IP:2379" snapshot save "$BACKUP_FILE" \
--cacert="$CACERT" \
--cert="$CERT" \
--key="$KEY"

if [ $? -eq 0 ]; then
  echo "Backup successful: $BACKUP_FILE"
else
  echo "Backup failed"
fi

可以看到成功通过脚本备份出etcd的db快照文件了。

image-nbqa.png

2.2 etcd单机还原

我长按笔记本电脑开机按钮几秒强制关机模拟服务器断电宕机,可以看到启动完linux后etcd已经死透了,此操作非常危险如果你没有备份 etcd 那你的环境就死定了。

  1. 注意,还原脚本中我写的上来直接删除etcd数据目录,因为实在启动不起来而且我也事先做好备份了。

  2. 脚本中的 HOSTNAME 可以在 /etc/hosts 中看到,因为我是单节点k8s所以只有一个,你的环境要按实际内容调整。

#!/bin/bash

#删除之前的,也不备份了!!!
rm -rf /var/lib/etcd

# 获取当前主机的 IP 地址
CURRENT_IP=$(hostname -I | cut -d' ' -f1)
#HOSTNAME=$(hostname)
HOSTNAME="localhost.localdomain.cluster.local"

# 构建自动添加端口的 peer 地址
PEER_URL="https://${CURRENT_IP}:2380"

# 定义 etcd 集群的初始成员信息
INITIAL_CLUSTER="${HOSTNAME}=https://${CURRENT_IP}:2380"

# 定义 etcd 集群的初始成员令牌
INITIAL_CLUSTER_TOKEN="etcd-cluster"

# 设置恢复快照文件
BACKUP_FILE="/root/etcd-backup/etcd_backup_2024年09月19日15时05分.db"

# 设置 etcd 证书路径
CACERT="/etc/ssl/etcd/ssl/ca.pem"
CERT="/etc/ssl/etcd/ssl/admin-node1.pem"
KEY="/etc/ssl/etcd/ssl/admin-node1-key.pem"

ETCDCTL_API=3 etcdctl snapshot restore $BACKUP_FILE \
  --name $HOSTNAME \
  --cacert "$CACERT" \
  --cert "$CERT" \
  --key "$KEY" \
  --initial-cluster "$INITIAL_CLUSTER" \
  --initial-cluster-token etcd-cluster \
  --initial-advertise-peer-urls "$PEER_URL" \
  --data-dir=/var/lib/etcd

if [ $? -eq 0 ]; then
  echo "reset successful"
else
  echo "reset failed"
fi

可以看到成功通过脚本还原并顺利启动etcd了。

2.3 etcd集群还原

在恢复 etcd 集群时直接写死固定节点的 IP 地址和名称是一种快速且直接的方法,尤其是当你对集群的配置非常熟悉,我嘛就直接这样写了。

#!/bin/bash

#删除之前的,也不备份了!!!
rm -rf /var/lib/etcd

# 获取当前主机的 IP 地址
CURRENT_IP="172.31.0.31"
HOSTNAME="node1"

# 构建自动添加端口的 peer 地址
PEER_URL="https://172.31.0.31:2380"

# 定义 etcd 集群的初始成员信息
INITIAL_CLUSTER="node1=https://172.31.0.31:2380,node2=https://172.31.0.32:2380,node3=https://172.31.0.33:2380"

# 定义 etcd 集群的初始成员令牌
INITIAL_CLUSTER_TOKEN="etcd-cluster"

# 设置备份存储路径
BACKUP_FILE="/root/etcd-backup/etcd_backup_2024年09月19日15时05分.db"

# 设置 etcd 证书路径
CACERT="/etc/ssl/etcd/ssl/ca.pem"
CERT="/etc/ssl/etcd/ssl/admin-node1.pem"
KEY="/etc/ssl/etcd/ssl/admin-node1-key.pem"

ETCDCTL_API=3 etcdctl snapshot restore $BACKUP_FILE \
  --name $HOSTNAME \
  --cacert "$CACERT" \
  --cert "$CERT" \
  --key "$KEY" \
  --initial-cluster "$INITIAL_CLUSTER" \
  --initial-cluster-token etcd-cluster \
  --initial-advertise-peer-urls "$PEER_URL" \
  --data-dir=/var/lib/etcd

if [ $? -eq 0 ]; then
  echo "reset successful"
else
  echo "reset failed"
fi

3. 异常解决

3.1 connect: no route to host

[root@localhost shell]# kubectl logs -n kubesphere-system ks-controller-manager-5fbf7f9b9f-8vht4
W0919 16:30:58.374922       1 client_config.go:615] Neither --kubeconfig nor --master was specified.  Using the inClusterConfig.  This might not work.
I0919 16:30:58.376103       1 server.go:202] setting up manager
E0919 16:30:59.378441       1 deleg.go:144]  "msg"="Failed to get API Group-Resources" "error"="Get \"https://10.233.0.1:443/api?timeout=32s\": dial tcp 10.233.0.1:443: connect: no route to host"  
F0919 16:30:59.378480       1 server.go:207] unable to set up overall controller manager: Get "https://10.233.0.1:443/api?timeout=32s": dial tcp 10.233.0.1:443: connect: no route to host

image-odyr.png

出现这个奇怪的ip,我们可以通过ip a命令查找网卡,可以看到这个虚拟ip网段是k8s生成的。

image-tkxd.png

此情况我们可以通过清空防火墙来让kubelet重新添加刷新,执行完稍等一会就能恢复正常了。

iptables -P INPUT ACCEPT
iptables -P FORWARD ACCEPT
iptables -P OUTPUT ACCEPT
#超谨慎使用
iptables -F

4. 操作总结

脚本的值还是有点抽象的,具体轮到你如果操作失败可以留言我帮你看看。