#!/usr/bin/env bash
set -Eeuo pipefail
IFS=$'\n\t'
# Production-safe kubelet cleanup utility.
# Default behavior is dry-run. Destructive execution requires:
# sudo ./cleanup-kubelet.sh --execute --confirm-host "$(hostname)"
readonly PROGRAM="${0##*/}"
readonly LOG_FILE="/var/log/cleanup-kubelet.log"
readonly -a TARGETS=(
"/export/kubelet"
"/var/lib/kubelet"
"/etc/systemd/system/kubelet.service"
"/etc/kubernetes"
"/etc/ssl/etcd/ssl"
)
MODE="dry-run"
CONFIRM_HOST=""
usage() {
cat <<EOF
Usage:
sudo $PROGRAM # preview only
sudo $PROGRAM --execute --confirm-host HOSTNAME
Options:
--execute Perform the cleanup.
--confirm-host HOSTNAME Must exactly match this machine's hostname.
-h, --help Show this help.
EOF
}
log() {
local message="$*"
printf '%s [%s] %s\n' "$(date '+%Y-%m-%dT%H:%M:%S%z')" "$MODE" "$message" | tee -a "$LOG_FILE"
}
die() {
log "ERROR: $*"
exit 1
}
while (($#)); do
case "$1" in
--execute)
MODE="execute"
shift
;;
--confirm-host)
(($# >= 2)) || { usage >&2; exit 2; }
CONFIRM_HOST="$2"
shift 2
;;
-h|--help)
usage
exit 0
;;
*)
printf 'Unknown argument: %s\n' "$1" >&2
usage >&2
exit 2
;;
esac
done
((EUID == 0)) || { printf 'Run as root (sudo).\n' >&2; exit 1; }
touch "$LOG_FILE"
chmod 0600 "$LOG_FILE"
current_host="$(hostname)"
if [[ "$MODE" == "execute" ]]; then
[[ -n "$CONFIRM_HOST" ]] || die "--confirm-host is required with --execute"
[[ "$CONFIRM_HOST" == "$current_host" ]] || die "hostname mismatch (expected: $current_host)"
fi
validate_target() {
local target="$1"
[[ "$target" == /* ]] || die "non-absolute target rejected: $target"
[[ "$target" != "/" ]] || die "root path rejected"
[[ "$target" != *'..'* ]] || die "parent traversal rejected: $target"
case "$target" in
/export/kubelet|/var/lib/kubelet|/etc/systemd/system/kubelet.service|/etc/kubernetes|/etc/ssl/etcd/ssl) ;;
*) die "target outside allowlist: $target" ;;
esac
}
for target in "${TARGETS[@]}"; do
validate_target "$target"
done
log "Host: $current_host"
log "Requested targets:"
for target in "${TARGETS[@]}"; do
if [[ -e "$target" || -L "$target" ]]; then
log " present: $target"
else
log " absent: $target"
fi
done
if [[ "$MODE" == "dry-run" ]]; then
log "Preview complete; nothing was changed."
log "To execute: sudo ./$PROGRAM --execute --confirm-host '$current_host'"
exit 0
fi
log "Stopping kubelet service"
if command -v systemctl >/dev/null 2>&1; then
systemctl stop kubelet.service || log "WARN: kubelet stop returned a non-zero status"
systemctl disable kubelet.service || log "WARN: kubelet disable returned a non-zero status"
fi
# Refuse to recursively delete through any active mount under a target.
if command -v findmnt >/dev/null 2>&1; then
for target in "${TARGETS[@]}"; do
while IFS= read -r mountpoint; do
[[ -z "$mountpoint" ]] && continue
die "active mount detected under $target: $mountpoint; unmount it explicitly first"
done < <(findmnt -rn -o TARGET -R "$target" 2>/dev/null || true)
done
fi
for target in "${TARGETS[@]}"; do
if [[ -e "$target" || -L "$target" ]]; then
log "Removing: $target"
rm -rf --one-file-system -- "$target"
else
log "Skipping absent target: $target"
fi
done
if command -v systemctl >/dev/null 2>&1; then
systemctl daemon-reload
fi
log "Cleanup completed successfully"
#!/usr/bin/env bash
set -Eeuo pipefail
IFS=$'\n\t'
readonly SCRIPT_NAME="${0##*/}"
readonly START_SECONDS="$SECONDS"
LOG_LEVEL="${LOG_LEVEL:-INFO}"
LOG_FILE="${LOG_FILE:-/var/log/${SCRIPT_NAME%.sh}.log}"
declare -Ar LEVEL_VALUE=(
[DEBUG]=10
[INFO]=20
[WARN]=30
[ERROR]=40
[FATAL]=50
)
init_log() {
local log_dir
log_dir="$(dirname -- "$LOG_FILE")"
mkdir -p -- "$log_dir"
if [[ -L "$LOG_FILE" ]]; then
printf 'Refusing symlink log file: %s\n' "$LOG_FILE" >&2
exit 1
fi
touch -- "$LOG_FILE"
chmod 0640 "$LOG_FILE"
exec 200>>"$LOG_FILE"
}
should_log() {
local level="$1"
[[ -n "${LEVEL_VALUE[$level]+x}" ]] || return 1
[[ -n "${LEVEL_VALUE[$LOG_LEVEL]+x}" ]] || return 1
((LEVEL_VALUE[$level] >= LEVEL_VALUE[$LOG_LEVEL]))
}
sanitize_message() {
local message="${1-}"
message="${message//$'\r'/\\r}"
message="${message//$'\n'/\\n}"
message="${message//$'\t'/\\t}"
printf '%s' "$message"
}
_log() {
local level="$1"
local file="$2"
local line_number="$3"
local function_name="$4"
shift 4
should_log "$level" || return 0
local message
local record
message="$(sanitize_message "$*")"
printf -v record \
'%s [%s] [%s] [pid=%d] [%s:%s:%s] %s' \
"$(date -u '+%Y-%m-%dT%H:%M:%SZ')" \
"$level" \
"$SCRIPT_NAME" \
"$$" \
"$file" \
"$line_number" \
"$function_name" \
"$message"
if command -v flock >/dev/null 2>&1; then
flock -x 200
printf '%s\n' "$record" >&200
flock -u 200
else
printf '%s\n' "$record" >&200
fi
printf '%s\n' "$record" >&2
}
log_debug() {
_log DEBUG "${BASH_SOURCE[1]##*/}" "${BASH_LINENO[0]}" "${FUNCNAME[1]:-main}" "$@"
}
log_info() {
_log INFO "${BASH_SOURCE[1]##*/}" "${BASH_LINENO[0]}" "${FUNCNAME[1]:-main}" "$@"
}
log_warn() {
_log WARN "${BASH_SOURCE[1]##*/}" "${BASH_LINENO[0]}" "${FUNCNAME[1]:-main}" "$@"
}
log_error() {
_log ERROR "${BASH_SOURCE[1]##*/}" "${BASH_LINENO[0]}" "${FUNCNAME[1]:-main}" "$@"
}
log_fatal() {
local rc="${1:?exit code is required}"
shift
_log FATAL "${BASH_SOURCE[1]##*/}" "${BASH_LINENO[0]}" "${FUNCNAME[1]:-main}" "$@"
exit "$rc"
}
on_error() {
local rc=$?
local line="${1:-unknown}"
log_error "unexpected failure: rc=$rc line=$line"
return "$rc"
}
on_exit() {
local rc=$?
local elapsed=$((SECONDS - START_SECONDS))
trap - ERR EXIT
if ((rc == 0)); then
log_info "script completed: rc=0 elapsed=${elapsed}s"
else
log_error "script terminated: rc=$rc elapsed=${elapsed}s"
fi
exit "$rc"
}
main() {
init_log
trap 'on_error "$LINENO"' ERR
trap on_exit EXIT
log_info "script started: host=$(hostname)"
log_debug "effective_user=$(id -un) effective_uid=$EUID"
# 在这里编写业务逻辑。
if [[ ! -r /etc/hosts ]]; then
log_fatal 1 "required file is not readable: /etc/hosts"
fi
log_info "business operation completed"
}
main "$@"
拉取镜像
#!/usr/bin/env bash
set -u
IMAGE_FILE="${1:-images.txt}"
if command -v docker >/dev/null 2>&1; then
CONTAINER_CLI="docker"
elif command -v nerdctl >/dev/null 2>&1; then
CONTAINER_CLI="nerdctl"
else
echo "错误:系统中未找到 docker 或 nerdctl" >&2
exit 1
fi
if [[ ! -f "$IMAGE_FILE" ]]; then
echo "错误:镜像列表文件不存在:$IMAGE_FILE" >&2
exit 1
fi
echo "使用命令:$CONTAINER_CLI"
echo "读取文件:$IMAGE_FILE"
success=0
failed=0
while IFS= read -r image || [[ -n "$image" ]]; do
# 去除 Windows 文本的回车符和首尾空白
image="${image//$'\r'/}"
image="${image#"${image%%[![:space:]]*}"}"
image="${image%"${image##*[![:space:]]}"}"
# 跳过空行和注释行
[[ -z "$image" || "$image" == \#* ]] && continue
echo "正在拉取:$image"
if "$CONTAINER_CLI" pull "$image"; then
((success += 1))
else
echo "拉取失败:$image" >&2
((failed += 1))
fi
done < "$IMAGE_FILE"
echo "完成:成功 $success 个,失败 $failed 个"
[[ "$failed" -eq 0 ]]
最后编辑于 :
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。