پاک کردن ایمیج های اضافی
برای پاک کردن ایمیجهای قابل حذف ابتدا دستور زیر را اجرا کنید:
cat > /opt/registry-retention/delete-registry-candidates.sh <<'EOF'
#!/usr/bin/env bash
set -euo pipefail
SCRIPT_DIR="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)"
CONFIG_FILE="${CONFIG_FILE:-${SCRIPT_DIR}/registry-retention.conf}"
if [[ ! -f "$CONFIG_FILE" ]]; then
echo "ERROR: Configuration file not found: $CONFIG_FILE" >&2
exit 1
fi
# shellcheck source=/dev/null
source "$CONFIG_FILE"
required_variables=(
REGISTRY_SCHEME
REGISTRY_ADDRESS
REPORT_FILE
)
for variable_name in "${required_variables[@]}"; do
if [[ -z "${!variable_name:-}" ]]; then
echo "ERROR: Required configuration is empty: ${variable_name}" >&2
exit 1
fi
done
for command_name in curl awk sort; do
if ! command -v "$command_name" >/dev/null 2>&1; then
echo "ERROR: Required command not found: ${command_name}" >&2
exit 1
fi
done
REGISTRY_URL="${REGISTRY_SCHEME}://${REGISTRY_ADDRESS}"
REPORT="$REPORT_FILE"
if [[ ! -f "$REPORT" ]]; then
echo "ERROR: Report file not found: $REPORT" >&2
echo "Run registry-retention-dry-run.sh first." >&2
exit 1
fi
expected_header=$'action\trepository\ttag\tpushed_epoch\tpushed_at\tdigest'
actual_header="$(head -n 1 "$REPORT")"
if [[ "$actual_header" != "$expected_header" ]]; then
echo "ERROR: Unexpected report format." >&2
echo "Expected:" >&2
printf '%s\n' "$expected_header" >&2
echo "Found:" >&2
printf '%s\n' "$actual_header" >&2
exit 1
fi
AUTH_ARGS=()
if [[ -n "${REGISTRY_USERNAME:-}" ]]; then
AUTH_ARGS=(
--user "${REGISTRY_USERNAME}:${REGISTRY_PASSWORD:-}"
)
fi
candidate_tag_count="$(
awk -F'\t' '
NR > 1 && $1 == "DELETE-CANDIDATE" {
count++
}
END {
print count + 0
}
' "$REPORT"
)"
candidate_digest_count="$(
awk -F'\t' '
NR > 1 &&
$1 == "DELETE-CANDIDATE" &&
$6 != "" &&
$6 != "unknown" {
print $2 "\t" $6
}
' "$REPORT" |
sort -u |
wc -l
)"
if [[ "$candidate_tag_count" -eq 0 ]]; then
echo "No DELETE-CANDIDATE rows were found."
echo "Nothing was deleted."
exit 0
fi
echo "Registry : ${REGISTRY_URL}"
echo "Report : ${REPORT}"
echo "Candidate tags : ${candidate_tag_count}"
echo "Unique delete digests: ${candidate_digest_count}"
echo
echo "The following images are candidates for deletion:"
echo
awk -F'\t' '
NR > 1 && $1 == "DELETE-CANDIDATE" {
printf " %-65s %s\n", $2 ":" $3, $6
}
' "$REPORT"
echo
if [[ "${CONFIRM:-}" != "YES" ]]; then
echo "Deletion is disabled."
echo
echo "Review the list above, then run:"
echo "CONFIRM=YES ./delete-registry-candidates.sh"
exit 1
fi
# Additional confirmation value to prevent accidental execution.
if [[ "${CONFIRM_REGISTRY:-}" != "$REGISTRY_ADDRESS" ]]; then
echo "ERROR: Registry confirmation does not match." >&2
echo
echo "Run with both confirmation variables:" >&2
echo "CONFIRM=YES CONFIRM_REGISTRY=${REGISTRY_ADDRESS} ./delete-registry-candidates.sh" >&2
exit 1
fi
declare -A processed=()
deleted_count=0
failed_count=0
duplicate_count=0
invalid_count=0
protected_count=0
while IFS=$'\t' read -r \
action \
repository \
tag \
pushed_epoch \
pushed_at \
digest
do
[[ "$action" != "DELETE-CANDIDATE" ]] && continue
if [[ -z "$repository" || -z "$tag" ]]; then
echo "SKIP invalid row: missing repository or tag"
invalid_count=$((invalid_count + 1))
continue
fi
if [[ -z "$digest" || "$digest" == "unknown" ]]; then
echo "SKIP missing digest: ${repository}:${tag}"
invalid_count=$((invalid_count + 1))
continue
fi
if [[ ! "$digest" =~ ^sha256:[0-9a-fA-F]{64}$ ]]; then
echo "SKIP invalid digest: ${repository}:${tag} ${digest}"
invalid_count=$((invalid_count + 1))
continue
fi
key="${repository}|${digest}"
if [[ -n "${processed[$key]:-}" ]]; then
echo "SKIP duplicate digest: ${repository}:${tag} ${digest}"
duplicate_count=$((duplicate_count + 1))
continue
fi
# Safety check:
# Never delete a digest if the report also marks the same
# repository+digest as KEEP-LATEST or KEEP-PREVIOUS.
if awk -F'\t' \
-v repository="$repository" \
-v digest="$digest" '
NR > 1 &&
$2 == repository &&
$6 == digest &&
($1 == "KEEP-LATEST" || $1 == "KEEP-PREVIOUS") {
found = 1
exit
}
END {
exit(found ? 0 : 1)
}
' "$REPORT"
then
echo "PROTECTED: ${repository}:${tag} ${digest}"
echo "Reason: the same digest is marked as KEEP in the report."
protected_count=$((protected_count + 1))
processed["$key"]=1
continue
fi
processed["$key"]=1
status="$(
curl \
"${AUTH_ARGS[@]}" \
--silent \
--show-error \
--output /dev/null \
--write-out '%{http_code}' \
--request DELETE \
"${REGISTRY_URL}/v2/${repository}/manifests/${digest}" ||
true
)"
case "$status" in
202)
echo "DELETED: ${repository}:${tag} ${digest}"
deleted_count=$((deleted_count + 1))
;;
404)
echo "NOT FOUND: ${repository}:${tag} ${digest}"
echo "The manifest may already have been deleted."
failed_count=$((failed_count + 1))
;;
405)
echo "FAILED: ${repository}:${tag} HTTP=405"
echo "Manifest deletion is not enabled in Registry configuration."
failed_count=$((failed_count + 1))
;;
*)
echo "FAILED: ${repository}:${tag} HTTP=${status:-curl-error} digest=${digest}"
failed_count=$((failed_count + 1))
;;
esac
done < <(tail -n +2 "$REPORT")
echo
echo "============================================================"
echo "Registry manifest deletion completed"
echo "============================================================"
echo "Deleted unique digests : ${deleted_count}"
echo "Failed deletions : ${failed_count}"
echo "Duplicate report rows : ${duplicate_count}"
echo "Invalid report rows : ${invalid_count}"
echo "Protected digests : ${protected_count}"
echo
echo "Manifest deletion does not immediately release disk space."
echo "Run Registry garbage collection after reviewing the result."
EOF
chmod +x /opt/registry-retention/delete-registry-candidates.sh
برای تست ابتدا به صورت امتحانی این دستور را اجرا میکنیم:
./delete-registry-candidates.sh
خروجی قابل انتظار:
[root@node1 registry-retention]# ./delete-registry-candidates.sh
Registry : http://10.10.10.99:5000
Report : /opt/registry-retention/registry-retention-report.tsv
Candidate tags : 1
Unique delete digests: 1
The following images are candidates for deletion:
data-foundation-panel-backend:null sha256:e5d8350338ebb63631b2c2c5bfa781b3b59981c73e03352a592f8f794b651e3b
Deletion is disabled.
Review the list above, then run:
CONFIRM=YES ./delete-registry-candidates.sh
سپس پس از اطمینان از خروجی دستور زیر را برای پاک کردن نهایی ایمیج ها وارد کنید:
CONFIRM=YES \
CONFIRM_REGISTRY=10.10.10.99:5000 \
./delete-registry-candidates.sh
این دستور فقط manifest هارا پاک میکند. برای پاک کردن blob های بدون manifest باید از garbage collector استفاده کنیم:
docker run --rm \
-v rke-registry:/var/lib/registry \
registry:latest \
garbage-collect --delete-untagged \
/etc/distribution/config.yml
توجه کنید که مورد اخر این دستور را باید با توجه به تنظیمات registry خود قرار دهید.