پاک کردن ایمیج های اضافی

برای پاک کردن ایمیج‌های قابل حذف ابتدا دستور زیر را اجرا کنید:

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 خود قرار دهید.

