注:在使用脚本前要安装etcdctl工具
shell
#!/bin/bash
set -eo pipefail # 脚本出错时立即退出,增强容错性
#######################################
# 配置参数(集中管理,便于修改)
#######################################
BACKUP_DIR="/etcd-backup" # 备份目录
LOG_DIR="/var/log/backup" # 日志目录
LOG_FILE="${LOG_DIR}/messages" # 日志文件
RETENTION_DAYS=30 # 备份保留天数
ETCDCTL_TIMEOUT=300 # etcdctl超时时间(秒)
# 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"
# 显式定义PATH(解决crontab环境变量问题)
export PATH="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
#######################################
# 开始:脚本使用前准备
#######################################
echo -e "\033[31m注意事项:因为脚本会写到计划任务中,如果脚本路径移动请在移动后重新执行添加新任务\033[0m"
echo "脚本需要在安装etcdctl工具后才可执行"
# 定义函数:检查 etcdctl 是否存在
check_etcdctl() {
# 使用 command -v 检查命令是否存在,避免别名/函数干扰
# >/dev/null 2>&1 :屏蔽命令的标准输出和错误输出(仅保留退出码判断)
if ! command -v etcdctl >/dev/null 2>&1; then
# 输出错误信息到标准错误流(>&2),避免与正常输出混淆
echo "错误:未检测到 etcdctl 工具,请先安装或配置环境变量。" >&2
# 退出脚本,退出码 1 表示执行失败(非 0 均为失败)
exit 1
fi
}
# 执行检查(脚本入口:先检查工具,再执行后续逻辑)
check_etcdctl
# ---------------------- 以下是脚本后续业务逻辑(示例)----------------------
echo "成功:etcdctl 工具已存在,开始执行后续操作..."
#######################################
# 第一步:优先创建日志目录和文件
#######################################
if [ ! -d "$LOG_DIR" ]; then
echo "[$(date +%Y%m%d_%H%M%S)] 日志目录 $LOG_DIR 不存在,正在创建..."
mkdir -p "$LOG_DIR" || {
echo "[$(date +%Y%m%d_%H%M%S)] 错误:无法创建日志目录 $LOG_DIR,脚本退出" >&2
exit 1
}
echo "[$(date +%Y%m%d_%H%M%S)] 日志目录 $LOG_DIR 创建成功"
fi
if [ ! -f "$LOG_FILE" ]; then
echo "[$(date +%Y%m%d_%H%M%S)] 日志文件 $LOG_FILE 不存在,正在创建..."
touch "$LOG_FILE" || {
echo "[$(date +%Y%m%d_%H%M%S)] 错误:无法创建日志文件 $LOG_FILE,脚本退出" >&2
exit 1
}
echo "[$(date +%Y%m%d_%H%M%S)] 日志文件 $LOG_FILE 创建成功"
fi
#######################################
# 第二步:定义日志函数
#######################################
log() {
local timestamp=$(date +%Y%m%d_%H%M%S)
echo "[$timestamp] $1" | tee -a "$LOG_FILE"
}
#######################################
# 第三步:检查root权限
#######################################
if [ "$(whoami)" != "root" ]; then
log "错误:必须以root用户运行此脚本!"
exit 1
fi
#######################################
# 第四步:确保备份目录存在
#######################################
if [ ! -d "$BACKUP_DIR" ]; then
log "备份目录 $BACKUP_DIR 不存在,正在创建..."
mkdir -p "$BACKUP_DIR" || {
log "错误:目录 $BACKUP_DIR 创建失败"
exit 1
}
else
log "备份目录 $BACKUP_DIR 已存在"
fi
#######################################
# 第五步:执行备份
#######################################
BACKUP_FILE="${BACKUP_DIR}/etcd-snapshot-$(date +%Y%m%d_%H%M%S).db"
log "开始执行etcd备份(超时时间:${ETCDCTL_TIMEOUT}秒)..."
if timeout "${ETCDCTL_TIMEOUT}" etcdctl \
--endpoints="${ETCD_ENDPOINTS}" \
--cacert="${ETCD_CACERT}" \
--cert="${ETCD_CERT}" \
--key="${ETCD_KEY}" \
snapshot save "${BACKUP_FILE}"; then
if etcdctl snapshot status "${BACKUP_FILE}" >/dev/null 2>&1; then
log "备份成功,文件路径:${BACKUP_FILE}(大小:$(du -h "${BACKUP_FILE}" | awk '{print $1}'))"
else
log "错误:备份文件 ${BACKUP_FILE} 无效,已删除"
rm -f "${BACKUP_FILE}"
exit 1
fi
else
log "错误:etcd备份超时或失败(超时时间${ETCDCTL_TIMEOUT}秒)"
rm -f "${BACKUP_FILE}"
exit 1
fi
#######################################
# 第六步:清理过期备份 + 检测所有非规范文件(核心修改)
#######################################
log "开始清理${RETENTION_DAYS}天前的备份文件..."
ago=$(date -d "-${RETENTION_DAYS} day" +%Y%m%d)
# 匹配备份目录下所有文件(仅文件,不包括子目录),然后逐个检查
find "$BACKUP_DIR" -maxdepth 1 -type f | while read -r file; do
# 提取文件名(不含路径)
filename=$(basename "$file")
# 严格匹配规范格式:etcd-snapshot-YYYYMMDD_HHMMSS.db
if [[ "$filename" =~ ^etcd-snapshot-[0-9]{8}_[0-9]{6}\.db$ ]]; then
# 符合规范的文件:提取日期并判断是否过期
datestamp=$(echo "$filename" | sed -n 's/etcd-snapshot-\([0-9]\{8\}\)_[0-9]\{6\}\.db/\1/p')
if [ "$datestamp" -lt "$ago" ]; then
if [ -f "$file" ]; then
rm -f "$file" && log "已删除过期备份:$file"
fi
fi
else
# 不符合规范的文件:统一输出警告(包括22222、etcd-snapshot-233.db等)
log "警告:文件 $file 不符合备份命名规范(需为 etcd-snapshot-YYYYMMDD_HHMMSS.db 格式),未清理"
fi
done
#######################################
# 第七步:添加计划任务
#######################################
SCRIPT_ABSOLUTE_PATH=$(cd "$(dirname "$0")" && pwd)/$(basename "$0")
CRON_TASK="0 1 * * * export PATH=\"${PATH}\"; ${SCRIPT_ABSOLUTE_PATH}"
if ! crontab -l 2>/dev/null | grep -qxF "$CRON_TASK"; then
(crontab -l 2>/dev/null; echo "$CRON_TASK") | crontab - && \
log "计划任务已添加:每天凌晨1点自动执行备份,计划任务内容:$CRON_TASK"
else
log "计划任务已存在,无需重复添加"
fi
log "备份及清理操作完成"
exit 0
没有工具的情况下执行提示
有工具的情况下执行
查看执行结果