生产环境K8S的etcd备份脚本
脚本内容
#!/bin/bash# 日志处理函数
log() {local timestamp=$(date +%Y%m%d_%H%M%S)local message="$1"echo "[$timestamp] $message" | tee -a "$LOG_FILE"
}# 错误处理函数
error_exit() {log "错误:$1"exit 1
}# 检查是否以root用户运行
if [ "$(whoami)" != "root" ]; thenlog "错误:必须以root用户运行此脚本!" exit 1
fi# 定义日志文件路径
LOG_DIR="/var/log/etcd-backup"
LOG_FILE="${LOG_DIR}/messages"# 确保日志目录和文件存在
if [ ! -d "$LOG_DIR" ]; thenlog "目录 $LOG_DIR 不存在,正在创建..."mkdir -p "$LOG_DIR" || { log "无法创建日志目录 $LOG_DIR"; exit 1; }touch "$LOG_FILE" || { log "无法创建日志文件 $LOG_FILE"; exit 1; }
elif [ ! -f "$LOG_FILE" ]; thenlog "文件 $LOG_FILE 不存在,正在创建..."touch "$LOG_FILE" || { log "无法创建日志文件 $LOG_FILE"; exit 1; }
filog "开始执行etcd备份脚本"# 检查依赖命令是否存在
if ! command -v etcdctl &> /dev/null; thenerror_exit "etcdctl 命令未找到,请先安装etcdctl"
fi# 定义目标目录路径
BACKUP_DIR="/opt/etcd-backups"# 创建备份目录(如果不存在)
if [ ! -d "$BACKUP_DIR" ]; thenlog "目录 $BACKUP_DIR 不存在,正在创建..."mkdir -p "$BACKUP_DIR"if [ $? -eq 0 ]; thenlog "目录 $BACKUP_DIR 创建成功"elseerror_exit "目录 $BACKUP_DIR 创建失败"fi
elselog "目录 $BACKUP_DIR 已存在,无需创建"
fi# 配置etcd连接参数变量
ETCD_ENDPOINTS="https://127.0.0.1:2379"
ETCD_CACERT="/etc/kubernetes/pki/etcd/ca.crt"
ETCD_CERT="/etc/kubernetes/pki/apiserver-etcd-client.crt"
ETCD_KEY="/etc/kubernetes/pki/apiserver-etcd-client.key"# 检查etcd证书文件是否存在
if [ ! -f "$ETCD_CACERT" ]; thenerror_exit "CA证书文件 $ETCD_CACERT 不存在"
fiif [ ! -f "$ETCD_CERT" ]; thenerror_exit "客户端证书文件 $ETCD_CERT 不存在"
fiif [ ! -f "$ETCD_KEY" ]; thenerror_exit "客户端密钥文件 $ETCD_KEY 不存在"
fi# 检查etcd连接状态
log "检查etcd连接状态..."
etcdctl --endpoints="${ETCD_ENDPOINTS}" \--cacert="${ETCD_CACERT}" \--cert="${ETCD_CERT}" \--key="${ETCD_KEY}" \endpoint health > /dev/null 2>&1if [ $? -ne 0 ]; thenerror_exit "无法连接到etcd服务器,请检查配置"
fi# 备份文件路径变量
BACKUP_FILE="${BACKUP_DIR}/etcd-snapshot-$(date +%Y%m%d_%H%M%S).db"# 执行备份操作
log "开始执行etcd备份..."
etcdctl --endpoints="${ETCD_ENDPOINTS}" \--cacert="${ETCD_CACERT}" \--cert="${ETCD_CERT}" \--key="${ETCD_KEY}" \snapshot save "${BACKUP_FILE}"# 检查备份命令执行结果
if [ $? -ne 0 ]; thenerror_exit "etcd备份命令执行失败"
fi# 验证备份文件是否存在且不为空
if [ ! -f "$BACKUP_FILE" ]; thenerror_exit "备份文件 $BACKUP_FILE 未创建"
fiif [ $(stat -c%s "$BACKUP_FILE") -lt 1024 ]; thenerror_exit "备份文件 $BACKUP_FILE 过小,可能不完整"
fi# 验证备份文件有效性
log "验证备份文件有效性..."
# 执行快照状态检查并捕获输出
SNAPSHOT_STATUS=$(etcdctl snapshot status "${BACKUP_FILE}" 2>&1)
STATUS_EXIT_CODE=$?if [ $STATUS_EXIT_CODE -ne 0 ]; thenerror_exit "备份文件 $BACKUP_FILE 无效,错误信息: $SNAPSHOT_STATUS"
else# 提取关键信息用于显示,移除版本信息FILE_NAME=$(basename "$BACKUP_FILE") # 提取文件名# 尝试从snapshot status获取文件大小,如果失败则使用du命令DB_SIZE=$(echo "$SNAPSHOT_STATUS" | grep -oP 'DB Size:\s+\K.+' | awk '{print $1 " " $2}')if [ -z "$DB_SIZE" ]; then# 使用du命令获取文件大小,以人类可读格式显示DB_SIZE=$(du -h "$BACKUP_FILE" | awk '{print $1}')filog "备份文件验证成功!"# 按要求格式显示信息,每个属性单独一行echo "[$(date +%Y%m%d_%H%M%S)] 文件名: $FILE_NAME" | tee -a "$LOG_FILE"echo "[$(date +%Y%m%d_%H%M%S)] 文件大小: $DB_SIZE" | tee -a "$LOG_FILE"echo "[$(date +%Y%m%d_%H%M%S)] 文件的绝对路径: $BACKUP_FILE" | tee -a "$LOG_FILE"
fi# 移除了"备份成功,文件路径:${BACKUP_FILE}"这行日志输出# 清理30天前的备份文件
log "开始清理30天前的备份文件..."
find "$BACKUP_DIR" -maxdepth 1 -type f -name "etcd-snapshot-*.db" -mtime +30 -print0 | while IFS= read -r -d '' file; dorm -f "$file"log "已删除旧备份:$file"
done# 获取当前脚本的绝对路径
if command -v readlink &> /dev/null; thenSCRIPT_PATH=$(readlink -f "$0")
elif command -v realpath &> /dev/null; thenSCRIPT_PATH=$(realpath "$0")
elseSCRIPT_PATH=$(cd "$(dirname "$0")" && pwd)/$(basename "$0")
fi# 转义特殊字符,确保正则匹配正确
ESCAPED_SCRIPT_PATH=$(printf '%s\n' "$SCRIPT_PATH" | sed 's/[.[\*^$()+?{|]/\\&/g')# 配置计划任务(每天凌晨2点执行)
log "开始配置计划任务..."# 精确匹配完整的cron任务行(时间表达式+脚本路径)
CRON_TASK="0 2 * * * $SCRIPT_PATH"
ESCAPED_CRON_TASK="^0 2 \* \* \* $ESCAPED_SCRIPT_PATH$"# 检查计划任务是否已存在
if crontab -l 2>/dev/null | grep -qE "$ESCAPED_CRON_TASK"; thenlog "计划任务已存在,无需重复添加"
else# 添加新的计划任务(crontab -l 2>/dev/null; echo "$CRON_TASK") | crontab -# 检查计划任务是否添加成功if [ $? -eq 0 ]; thenlog "计划任务添加成功,每天凌晨2点自动执行备份"elseerror_exit "计划任务添加失败"fi
filog "所有操作完成"
执行条件
默认在etcd节点上执行,用root用户,etcd的证书名称为默认位置和路径,需要有对应版本的etcd命令。
不符合执行条件可以自行更改参数以适配环境。