Docker Registry clean up
- اضافه کردن delete به تنظیمات registry
- اجرای اسکریپت چک کردن ایمیج های قابل حذف
- پاک کردن ایمیج های اضافی
اضافه کردن delete به تنظیمات registry
در ابتدا تنظیمات اولیه registry را مشاهده کنیم تا از بودن مورد delete در آن مطمئن شویم.
docker exec registry cat /etc/docker/registry/config.yml
storage:
delete:
enabled: true
در صورتی که این مورد وحود نداشت باید در قسمت تنظیمات کانتینر رجیستری قرار بگیرد. برای این کار باید موارد زیر را انجام دهیم.
docker exec -it registry sh
vi /etc/docker/registry/config.yml
در تنظیمات قسمت زیر را اضافه کنید:
storage:
delete:
enabled: true
# EXAMPLE
version: 0.1
storage:
filesystem:
rootdirectory: /var/lib/registry
delete:
enabled: true
http:
addr: :5000
docker restart registry
اجرای اسکریپت چک کردن ایمیج های قابل حذف
ابتدا فایل های مورد نیاز را ایجاد میکنیم:
mkdir -p /opt/registry-retention
cat > /opt/registry-retention/registry-retention.conf <<'EOF'
# Registry API protocol
REGISTRY_SCHEME="http"
# Registry address
REGISTRY_ADDRESS="10.10.10.99:5000"
# Local Docker Registry container name
REGISTRY_CONTAINER="registry"
# Repository storage path INSIDE the Registry container
REGISTRY_REPOSITORIES_PATH="/var/lib/registry/docker/registry/v2/repositories"
# Keep the latest N unique image digests per repository
KEEP_LAST=2
# Detailed dry-run report
REPORT_FILE="/opt/registry-retention/registry-retention-report.tsv"
# Optional Registry Basic Authentication
# Leave empty if authentication is not enabled
REGISTRY_USERNAME=""
REGISTRY_PASSWORD=""
# Used by Registry garbage collection
REGISTRY_IMAGE="registry:latest"
# Registry storage source mounted at /var/lib/registry
# For a named Docker volume:
REGISTRY_STORAGE_SOURCE="rke-registry"
# Registry config file on the HOST
REGISTRY_CONFIG_HOST="/etc/docker/registry/config.yml"
EOF
chmod 600 /opt/registry-retention/registry-retention.conf
این موارد را بر اساس اطلاعات رجیستری خود تغییر میدهیم.
برای گرفتن گزارش کامل ایمیج های قابل حذف در رجیستری ابتدا اسکریپت زیر را اجرا میکنیم:
cat > /opt/registry-retention/registry-retention-dry-run.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}"
# -------------------------------------------------------------------
# Load and validate configuration
# -------------------------------------------------------------------
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_commands=(
curl
jq
docker
sort
awk
mktemp
head
tail
wc
)
for command_name in "${required_commands[@]}"; do
if ! command -v "$command_name" >/dev/null 2>&1; then
echo "ERROR: Required command not found: $command_name" >&2
exit 1
fi
done
required_variables=(
REGISTRY_SCHEME
REGISTRY_ADDRESS
REGISTRY_CONTAINER
REGISTRY_REPOSITORIES_PATH
KEEP_LAST
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
if ! [[ "$KEEP_LAST" =~ ^[1-9][0-9]*$ ]]; then
echo "ERROR: KEEP_LAST must be a positive integer." >&2
exit 1
fi
if ! docker inspect "$REGISTRY_CONTAINER" >/dev/null 2>&1; then
echo "ERROR: Registry container not found: $REGISTRY_CONTAINER" >&2
exit 1
fi
REGISTRY_URL="${REGISTRY_SCHEME}://${REGISTRY_ADDRESS}"
DETAIL_REPORT="$REPORT_FILE"
SUMMARY_REPORT="${SCRIPT_DIR}/registry-retention-summary.tsv"
AUTH_ARGS=()
if [[ -n "${REGISTRY_USERNAME:-}" ]]; then
AUTH_ARGS=(
--user "${REGISTRY_USERNAME}:${REGISTRY_PASSWORD:-}"
)
fi
# -------------------------------------------------------------------
# Registry API functions
# -------------------------------------------------------------------
api_get() {
local url="$1"
local response_file
local http_status
response_file="$(mktemp)"
http_status="$(
curl \
"${AUTH_ARGS[@]}" \
--silent \
--show-error \
--output "$response_file" \
--write-out '%{http_code}' \
"$url" ||
true
)"
if [[ "$http_status" != "200" ]]; then
echo "ERROR: Registry API request failed." >&2
echo "URL: $url" >&2
echo "HTTP status: ${http_status:-curl-error}" >&2
echo "Response:" >&2
cat "$response_file" >&2
echo >&2
rm -f "$response_file"
return 1
fi
cat "$response_file"
rm -f "$response_file"
}
get_digest() {
local repository="$1"
local tag="$2"
local response_headers
local digest
response_headers="$(mktemp)"
if ! curl \
"${AUTH_ARGS[@]}" \
--fail \
--silent \
--show-error \
--head \
-H 'Accept: application/vnd.oci.image.index.v1+json' \
-H 'Accept: application/vnd.oci.image.manifest.v1+json' \
-H 'Accept: application/vnd.docker.distribution.manifest.list.v2+json' \
-H 'Accept: application/vnd.docker.distribution.manifest.v2+json' \
--output "$response_headers" \
"${REGISTRY_URL}/v2/${repository}/manifests/${tag}"
then
rm -f "$response_headers"
return 1
fi
digest="$(
awk -F': ' '
BEGIN {
IGNORECASE = 1
}
tolower($1) == "docker-content-digest" {
gsub("\r", "", $2)
print $2
}
' "$response_headers" |
tail -n 1
)"
rm -f "$response_headers"
printf '%s\n' "$digest"
}
get_tag_time() {
local repository="$1"
local tag="$2"
local link_file
link_file="${REGISTRY_REPOSITORIES_PATH}/${repository}/_manifests/tags/${tag}/current/link"
docker exec "$REGISTRY_CONTAINER" \
stat -c $'%Y\t%y' "$link_file" 2>/dev/null ||
true
}
# -------------------------------------------------------------------
# Initial information
# -------------------------------------------------------------------
echo "Registry: ${REGISTRY_URL}"
echo "Container: ${REGISTRY_CONTAINER}"
echo "Keeping latest ${KEEP_LAST} unique digest(s) per repository"
echo
echo "Reading Registry catalog..." >&2
# Do not use ?n=10000 because some Registry versions reject it.
catalog_json="$(
api_get "${REGISTRY_URL}/v2/_catalog"
)"
mapfile -t repositories < <(
jq -r '
.repositories[]?
| select(type == "string")
| select(length > 0)
' <<< "$catalog_json" |
sort -u
)
repository_count="${#repositories[@]}"
if [[ "$repository_count" -eq 0 ]]; then
echo "No repositories found."
exit 0
fi
mkdir -p "$(dirname "$DETAIL_REPORT")"
mkdir -p "$(dirname "$SUMMARY_REPORT")"
printf "action\trepository\ttag\tpushed_epoch\tpushed_at\tdigest\n" \
> "$DETAIL_REPORT"
printf "repository\ttotal_tags\tunique_digests\tkeep_tags\tdelete_tags\treview_tags\n" \
> "$SUMMARY_REPORT"
# -------------------------------------------------------------------
# Global counters
# -------------------------------------------------------------------
total_tags=0
total_unique_digests=0
total_keep_latest=0
total_keep_previous=0
total_delete_candidates=0
total_review=0
# -------------------------------------------------------------------
# Scan repositories
# -------------------------------------------------------------------
for repository in "${repositories[@]}"; do
echo "Scanning: ${repository}" >&2
tags_json="$(
api_get "${REGISTRY_URL}/v2/${repository}/tags/list" ||
true
)"
if [[ -z "$tags_json" ]]; then
echo "WARNING: Unable to retrieve tags for ${repository}" >&2
printf "%s\t0\t0\t0\t0\t0\n" \
"$repository" >> "$SUMMARY_REPORT"
continue
fi
mapfile -t tags < <(
jq -r '
.tags[]?
| select(type == "string")
| select(length > 0)
' <<< "$tags_json" |
sort -u
)
repository_tag_count="${#tags[@]}"
if [[ "$repository_tag_count" -eq 0 ]]; then
printf "%s\t0\t0\t0\t0\t0\n" \
"$repository" >> "$SUMMARY_REPORT"
continue
fi
temp_file="$(mktemp)"
sorted_file="${temp_file}.sorted"
for tag in "${tags[@]}"; do
total_tags=$((total_tags + 1))
stat_result="$(get_tag_time "$repository" "$tag")"
pushed_epoch=""
pushed_at=""
if [[ -n "$stat_result" ]]; then
IFS=$'\t' read -r pushed_epoch pushed_at <<< "$stat_result"
fi
digest="$(get_digest "$repository" "$tag" || true)"
if [[ -z "$pushed_epoch" ]]; then
pushed_epoch="0"
fi
if [[ -z "$pushed_at" ]]; then
pushed_at="unknown"
fi
if [[ -z "$digest" ]]; then
digest="unknown"
fi
printf "%s\t%s\t%s\t%s\n" \
"$pushed_epoch" \
"$tag" \
"$pushed_at" \
"$digest" \
>> "$temp_file"
done
# Newest tag reference first.
sort \
-t $'\t' \
-k1,1nr \
-k2,2 \
"$temp_file" \
> "$sorted_file"
declare -A digest_rank=()
repository_unique_digests=0
repository_keep_tags=0
repository_delete_tags=0
repository_review_tags=0
while IFS=$'\t' read -r pushed_epoch tag pushed_at digest; do
action=""
if [[ "$digest" == "unknown" || "$pushed_epoch" == "0" ]]; then
action="REVIEW"
repository_review_tags=$((repository_review_tags + 1))
total_review=$((total_review + 1))
else
if [[ -z "${digest_rank[$digest]:-}" ]]; then
repository_unique_digests=$((repository_unique_digests + 1))
digest_rank["$digest"]="$repository_unique_digests"
fi
rank="${digest_rank[$digest]}"
if [[ "$rank" -eq 1 ]]; then
action="KEEP-LATEST"
repository_keep_tags=$((repository_keep_tags + 1))
total_keep_latest=$((total_keep_latest + 1))
elif [[ "$rank" -le "$KEEP_LAST" ]]; then
action="KEEP-PREVIOUS"
repository_keep_tags=$((repository_keep_tags + 1))
total_keep_previous=$((total_keep_previous + 1))
else
action="DELETE-CANDIDATE"
repository_delete_tags=$((repository_delete_tags + 1))
total_delete_candidates=$((total_delete_candidates + 1))
fi
fi
printf "%s\t%s\t%s\t%s\t%s\t%s\n" \
"$action" \
"$repository" \
"$tag" \
"$pushed_epoch" \
"$pushed_at" \
"$digest" \
>> "$DETAIL_REPORT"
done < "$sorted_file"
# Correct Bash arithmetic syntax.
total_unique_digests=$((total_unique_digests + repository_unique_digests))
printf "%s\t%s\t%s\t%s\t%s\t%s\n" \
"$repository" \
"$repository_tag_count" \
"$repository_unique_digests" \
"$repository_keep_tags" \
"$repository_delete_tags" \
"$repository_review_tags" \
>> "$SUMMARY_REPORT"
unset digest_rank
rm -f "$temp_file" "$sorted_file"
done
# -------------------------------------------------------------------
# Calculate final statistics
# -------------------------------------------------------------------
unique_delete_digests="$(
awk -F'\t' '
NR > 1 &&
$1 == "DELETE-CANDIDATE" &&
$6 != "" &&
$6 != "unknown" {
print $2 "\t" $6
}
' "$DETAIL_REPORT" |
sort -u |
wc -l
)"
repositories_with_delete_candidates="$(
awk -F'\t' '
NR > 1 && ($5 + 0) > 0 {
print $1
}
' "$SUMMARY_REPORT" |
wc -l
)"
total_kept_tags=$((total_keep_latest + total_keep_previous))
# -------------------------------------------------------------------
# Print overall summary
# -------------------------------------------------------------------
echo
echo "============================================================"
echo "Registry retention dry-run completed"
echo "============================================================"
printf "%-35s %s\n" "Registry repositories:" "$repository_count"
printf "%-35s %s\n" "Total tags scanned:" "$total_tags"
printf "%-35s %s\n" "Total unique digests:" "$total_unique_digests"
printf "%-35s %s\n" "KEEP-LATEST tags:" "$total_keep_latest"
printf "%-35s %s\n" "KEEP-PREVIOUS tags:" "$total_keep_previous"
printf "%-35s %s\n" "Total kept tags:" "$total_kept_tags"
printf "%-35s %s\n" "DELETE candidate tags:" "$total_delete_candidates"
printf "%-35s %s\n" "Unique delete digests:" "$unique_delete_digests"
printf "%-35s %s\n" \
"Repositories with deletions:" \
"$repositories_with_delete_candidates"
printf "%-35s %s\n" "REVIEW required:" "$total_review"
# -------------------------------------------------------------------
# Per-repository summary
# -------------------------------------------------------------------
echo
echo "============================================================"
echo "Repository tag summary"
echo "============================================================"
printf "%-62s %8s %9s %8s %8s %8s\n" \
"REPOSITORY" \
"TAGS" \
"DIGESTS" \
"KEEP" \
"DELETE" \
"REVIEW"
printf "%-62s %8s %9s %8s %8s %8s\n" \
"--------------------------------------------------------------" \
"--------" \
"---------" \
"--------" \
"--------" \
"--------"
tail -n +2 "$SUMMARY_REPORT" |
sort \
-t $'\t' \
-k2,2nr \
-k1,1 |
awk -F'\t' '
{
printf "%-62s %8s %9s %8s %8s %8s\n",
$1, $2, $3, $4, $5, $6
}
'
# -------------------------------------------------------------------
# Repositories with deletion candidates
# -------------------------------------------------------------------
echo
echo "============================================================"
echo "Repositories with more than ${KEEP_LAST} unique digests"
echo "============================================================"
found_large_repository=0
while IFS=$'\t' read -r \
repository \
tag_count \
digest_count \
keep_count \
delete_count \
review_count
do
if (( digest_count > KEEP_LAST )); then
found_large_repository=1
printf "%-62s tags=%-5s digests=%-5s delete=%s\n" \
"$repository" \
"$tag_count" \
"$digest_count" \
"$delete_count"
fi
done < <(
tail -n +2 "$SUMMARY_REPORT" |
sort \
-t $'\t' \
-k3,3nr \
-k1,1
)
if [[ "$found_large_repository" -eq 0 ]]; then
echo "No repository has more than ${KEEP_LAST} unique digests."
fi
# -------------------------------------------------------------------
# Exact delete candidates
# -------------------------------------------------------------------
echo
echo "============================================================"
echo "Delete candidates"
echo "============================================================"
if [[ "$total_delete_candidates" -eq 0 ]]; then
echo "No delete candidates found."
else
awk -F'\t' '
NR > 1 && $1 == "DELETE-CANDIDATE" {
printf "Repository : %s\n", $2
printf "Image : %s:%s\n", $2, $3
printf "Tag : %s\n", $3
printf "Pushed at : %s\n", $5
printf "Digest : %s\n", $6
printf "%s\n",
"------------------------------------------------------------"
}
' "$DETAIL_REPORT"
fi
# -------------------------------------------------------------------
# Exact kept images
# -------------------------------------------------------------------
echo
echo "============================================================"
echo "Images that will be kept"
echo "============================================================"
awk -F'\t' '
NR > 1 &&
($1 == "KEEP-LATEST" || $1 == "KEEP-PREVIOUS") {
printf "%-14s %-75s %s\n",
$1,
$2 ":" $3,
$6
}
' "$DETAIL_REPORT"
# -------------------------------------------------------------------
# Review items
# -------------------------------------------------------------------
if [[ "$total_review" -gt 0 ]]; then
echo
echo "============================================================"
echo "Items requiring manual review"
echo "============================================================"
awk -F'\t' '
NR > 1 && $1 == "REVIEW" {
printf "Repository : %s\n", $2
printf "Image : %s:%s\n", $2, $3
printf "Pushed at : %s\n", $5
printf "Digest : %s\n", $6
printf "%s\n",
"------------------------------------------------------------"
}
' "$DETAIL_REPORT"
fi
# -------------------------------------------------------------------
# Report paths
# -------------------------------------------------------------------
echo
echo "============================================================"
echo "Report files"
echo "============================================================"
echo "Detailed report : ${DETAIL_REPORT}"
echo "Summary report : ${SUMMARY_REPORT}"
echo
echo "No manifest, tag, digest, blob, image, or repository was deleted."
EOF
chmod +x /opt/registry-retention/registry-retention-dry-run.sh
برای اجرا دستور زیر را وارد میکنیم:
./registry-retention-dry-run.sh
مثال خروجی:
[root@node1 registry-retention]# ./registry-retention-dry-run.sh
Registry: http://10.10.10.99:5000
Container: registry
Keeping latest 2 unique digest(s) per repository
Reading Registry catalog...
Scanning: appsan-back-crm
Scanning: appsan-back-panel
Scanning: appsan-panel-ui
Scanning: auth-server
Scanning: bitnami/os-shell
Scanning: bitnami/postgresql
Scanning: bitnami/rabbitmq-cluster-operator
Scanning: bitnami/redis
Scanning: bitnami/redis-sentinel
Scanning: busybox
Scanning: customer-service-counter-miniapp
Scanning: data-foundation-panel-backend
Scanning: data-foundation-panel-backend-with-path
Scanning: data-foundation-panel-ui
Scanning: data-quality-control
Scanning: devops-fileserver
Scanning: energy-man
Scanning: engine
Scanning: esb-sum-tester
Scanning: fileserver
Scanning: flyway
Scanning: flyway/flyway
Scanning: form-man-miniapp
Scanning: fx-obligation-manager-diba-miniapp
Scanning: haj-multi-messanger
Scanning: haj-pilgrim-search
Scanning: keycloak
Scanning: log-correlator
Scanning: log-management
Scanning: mavaracrm-engine
Scanning: ms-common
Scanning: ms-datatools
Scanning: ms-system
Scanning: newfxs
Scanning: newfxsamd
Scanning: noiro/aci-containers-controller
Scanning: noiro/aci-containers-host
Scanning: noiro/cnideploy
Scanning: noiro/openvswitch
Scanning: noiro/opflex
Scanning: openapi-parser
Scanning: panel-ui
Scanning: pilgrim-search-miniapp
Scanning: pilgrim_search-miniapp
Scanning: pricing-panel-ui
Scanning: pricing-with-feign
Scanning: pricing-with-feign-dev
Scanning: quay.io/frrouting/frr
Scanning: quay.io/jetstack/cert-manager-cainjector
Scanning: quay.io/jetstack/cert-manager-controller
Scanning: quay.io/jetstack/cert-manager-startupapicheck
Scanning: quay.io/jetstack/cert-manager-webhook
Scanning: quay.io/metallb/controller
Scanning: quay.io/metallb/speaker
Scanning: rabbitmq
Scanning: rancher/calico-cni
Scanning: rancher/flannel-cni
Scanning: rancher/hyperkube
Scanning: rancher/local-path-provisioner
Scanning: rancher/mirrored-calico-ctl
Scanning: rancher/mirrored-calico-kube-controllers
Scanning: rancher/mirrored-calico-node
Scanning: rancher/mirrored-calico-pod2daemon-flexvol
Scanning: rancher/mirrored-cluster-proportional-autoscaler
Scanning: rancher/mirrored-coredns-coredns
Scanning: rancher/mirrored-coreos-etcd
Scanning: rancher/mirrored-flannel-flannel
Scanning: rancher/mirrored-ingress-nginx-kube-webhook-certgen
Scanning: rancher/mirrored-k8s-dns-dnsmasq-nanny
Scanning: rancher/mirrored-k8s-dns-kube-dns
Scanning: rancher/mirrored-k8s-dns-node-cache
Scanning: rancher/mirrored-k8s-dns-sidecar
Scanning: rancher/mirrored-metrics-server
Scanning: rancher/mirrored-nginx-ingress-controller-defaultbackend
Scanning: rancher/mirrored-pause
Scanning: rancher/nginx-ingress-controller
Scanning: rancher/rke-tools
Scanning: redis
Scanning: registry.k8s.io/sig-storage/nfs-subdir-external-provisioner
Scanning: rest-interface
Scanning: rest-invoker
Scanning: service-desk
Scanning: soap-invoker
Scanning: soap-parser
Scanning: soft-asset-man-miniapp
Scanning: software-identity-miniapp
Scanning: sso_keycloak
Scanning: timberio/vector
============================================================
Registry retention dry-run completed
============================================================
Registry repositories: 88
Total tags scanned: 112
Total unique digests: 110
KEEP-LATEST tags: 88
KEEP-PREVIOUS tags: 23
DELETE candidate tags: 1
Unique delete digests: 1
Repositories with deletions: 1
REVIEW required: 0
============================================================
Repository tag summary
============================================================
REPOSITORY TAGS DIGESTS KEEP DELETE REVIEW
------------------------------------------------------- -------- -------- -------- -------- --------
data-foundation-panel-backend 3 3 2 1 0
bitnami/os-shell 2 2 2 0 0
bitnami/redis 2 2 2 0 0
data-foundation-panel-backend-with-path 2 2 2 0 0
data-foundation-panel-ui 2 2 2 0 0
data-quality-control 2 2 2 0 0
devops-fileserver 2 2 2 0 0
haj-multi-messanger 2 2 2 0 0
haj-pilgrim-search 2 2 2 0 0
keycloak 2 2 2 0 0
ms-system 2 2 2 0 0
openapi-parser 2 2 2 0 0
panel-ui 2 2 2 0 0
pricing-panel-ui 2 2 2 0 0
pricing-with-feign-dev 2 2 2 0 0
rest-interface 2 2 2 0 0
rest-invoker 2 2 2 0 0
service-desk 2 2 2 0 0
software-identity-miniapp 2 2 2 0 0
timberio/vector 2 2 2 0 0
appsan-back-crm 1 1 1 0 0
appsan-back-panel 1 1 1 0 0
appsan-panel-ui 1 1 1 0 0
auth-server 1 1 1 0 0
bitnami/postgresql 1 1 1 0 0
bitnami/rabbitmq-cluster-operator 1 1 1 0 0
bitnami/redis-sentinel 1 1 1 0 0
busybox 1 1 1 0 0
customer-service-counter-miniapp 1 1 1 0 0
energy-man 1 1 1 0 0
engine 1 1 1 0 0
esb-sum-tester 1 1 1 0 0
fileserver 1 1 1 0 0
flyway 1 1 1 0 0
flyway/flyway 1 1 1 0 0
form-man-miniapp 1 1 1 0 0
fx-obligation-manager-diba-miniapp 1 1 1 0 0
log-correlator 1 1 1 0 0
log-management 1 1 1 0 0
mavaracrm-engine 1 1 1 0 0
ms-common 1 1 1 0 0
ms-datatools 1 1 1 0 0
newfxs 1 1 1 0 0
newfxsamd 1 1 1 0 0
noiro/aci-containers-controller 1 1 1 0 0
noiro/aci-containers-host 1 1 1 0 0
noiro/cnideploy 1 1 1 0 0
noiro/openvswitch 1 1 1 0 0
noiro/opflex 1 1 1 0 0
pilgrim-search-miniapp 1 1 1 0 0
pilgrim_search-miniapp 1 1 1 0 0
quay.io/frrouting/frr 1 1 1 0 0
quay.io/jetstack/cert-manager-cainjector 1 1 1 0 0
quay.io/jetstack/cert-manager-controller 1 1 1 0 0
quay.io/jetstack/cert-manager-startupapicheck 1 1 1 0 0
quay.io/jetstack/cert-manager-webhook 1 1 1 0 0
quay.io/metallb/controller 1 1 1 0 0
quay.io/metallb/speaker 1 1 1 0 0
rabbitmq 1 1 1 0 0
rancher/calico-cni 1 1 1 0 0
rancher/flannel-cni 1 1 1 0 0
rancher/hyperkube 1 1 1 0 0
rancher/local-path-provisioner 1 1 1 0 0
rancher/mirrored-calico-ctl 1 1 1 0 0
rancher/mirrored-calico-kube-controllers 1 1 1 0 0
rancher/mirrored-calico-node 1 1 1 0 0
rancher/mirrored-calico-pod2daemon-flexvol 1 1 1 0 0
rancher/mirrored-cluster-proportional-autoscaler 1 1 1 0 0
rancher/mirrored-coredns-coredns 1 1 1 0 0
rancher/mirrored-coreos-etcd 1 1 1 0 0
rancher/mirrored-flannel-flannel 1 1 1 0 0
rancher/mirrored-ingress-nginx-kube-webhook-certgen 1 1 1 0 0
rancher/mirrored-k8s-dns-dnsmasq-nanny 1 1 1 0 0
rancher/mirrored-k8s-dns-kube-dns 1 1 1 0 0
rancher/mirrored-k8s-dns-node-cache 1 1 1 0 0
rancher/mirrored-k8s-dns-sidecar 1 1 1 0 0
rancher/mirrored-metrics-server 1 1 1 0 0
rancher/mirrored-nginx-ingress-controller-defaultbackend 1 1 1 0 0
rancher/mirrored-pause 1 1 1 0 0
rancher/nginx-ingress-controller 1 1 1 0 0
rancher/rke-tools 1 1 1 0 0
redis 1 1 1 0 0
registry.k8s.io/sig-storage/nfs-subdir-external-provisioner 1 1 1 0 0
soap-invoker 1 1 1 0 0
soap-parser 1 1 1 0 0
soft-asset-man-miniapp 1 1 1 0 0
sso_keycloak 1 1 1 0 0
repository total_tags unique_digests keep_tags delete_tags review_tags
============================================================
Repositories with more than 2 unique digests
============================================================
data-foundation-panel-backend tags=3 digests=3 delete=1
============================================================
Delete candidates
============================================================
Repository : data-foundation-panel-backend
Image : data-foundation-panel-backend:null
Tag : null
Pushed at : 2026-03-28 21:28:29.616459450 +0000
Digest : sha256:e5d8350338ebb63631b2c2c5bfa781b3b59981c73e03352a592f8f794b651e3b
------------------------------------------------------------
============================================================
Images that will be kept
============================================================
KEEP-LATEST appsan-back-crm:5f9c84e sha256:1070a842b50327dec4bc247bd4b4ddecd7e616d3e32e82a5028534d550ddf61d
KEEP-LATEST appsan-back-panel:5f9c84e sha256:cae08d283dfd2eebb9613ec0cf9440935f2b9917b7eba3ed013c1433322a4a01
KEEP-LATEST appsan-panel-ui:4eb4a27 sha256:5d57ac29ccb0ecdf00eb71911afea357ef0ff8a8f5afaac7a7b8fbe5c1fed801
KEEP-LATEST auth-server:c5d2277 sha256:3f16471e046bd284329f3b0b713054c8c45d4838c562054a4d1ce83e95f71190
KEEP-LATEST bitnami/os-shell:12-debian-12-r40 sha256:86282b491360476e192fbd68a25b1f92ca9282e637f432599057d0a311340e56
KEEP-PREVIOUS bitnami/os-shell:12-debian-12-r24 sha256:399baf4d3dc5d2967f7ed95b07a6e48e8faf856c9a8ccf83601ccc4f5221d7e6
KEEP-LATEST bitnami/postgresql:16.3.0-debian-12-r19 sha256:33d97c701ceaf71641532e1fce98dcf3b12f7aacbba68aa4da5dc103edd4ec33
KEEP-LATEST bitnami/rabbitmq-cluster-operator:latest sha256:8d9f0ce53c7518327e870a9d6ca2aa3f9faa56c7c9692533a733a753a04868d6
KEEP-LATEST bitnami/redis:7.4.2-debian-12-r6 sha256:233e80ba5fc68b959f34ba5c91ef47caf5a678bf6d0ff19f416539f0122ec304
KEEP-PREVIOUS bitnami/redis:7.4.1-debian-12-r2 sha256:9ea3d45afc7a1bf95192a9591f2debe2ab5e37712a0e456d859e8f450a9cb7ee
KEEP-LATEST bitnami/redis-sentinel:7.4.2-debian-12-r6 sha256:8379ae0ba492ebe8d119adf085d5adad29ad7942687d97e581a8c8e36d6d1724
KEEP-LATEST busybox:latest sha256:d319b0e3e1745e504544e931cde012fc5470eba649acc8a7b3607402942e5db7
KEEP-LATEST customer-service-counter-miniapp:ebc7f15 sha256:fb25defb7f3649eae6bdca6f295b4823eba41dc8c4918be35a8a803e9c2b4799
KEEP-LATEST data-foundation-panel-backend:d0f5602-fakher sha256:bb4481c0c5440b1ddf84f77117c0abfeaa905580abb30aae9de4773ff63fdb08
KEEP-PREVIOUS data-foundation-panel-backend:61952da-fakher sha256:cc2ffe7233376b5619ba7752bdf61afd9c79ba449b112342f33a8218ceae7159
KEEP-LATEST data-foundation-panel-backend-with-path:9b4bda5-fakher sha256:86586eafad027c848f7c0091a0db1dee2baba5b9d0addd137c9d356c1fcc51e8
KEEP-PREVIOUS data-foundation-panel-backend-with-path:b6eb421-fakher sha256:af2459a1a3c9e81ce4d7eafaa6768988b654ff79d4663998afd6d1080ecd9594
KEEP-LATEST data-foundation-panel-ui:dfp-ec38808 sha256:9193cef90b772104f6980c5c62436df4c7967693dd80c2060745840b3df0486d
KEEP-PREVIOUS data-foundation-panel-ui:dfp-fdc20dd sha256:6baddf1a722912681ae77dacbac8e9ce81814b30b4f20191f9dbb768511fed40
KEEP-LATEST data-quality-control:0826d3b-fakher sha256:5b6e5be9a7e761043390f39e2c6d4a12077d5e75a997e77c9fd102a33580ac1a
KEEP-PREVIOUS data-quality-control:3e3dd03-fakher sha256:7869e7beea0428f93735f3fd037a26d73517c0a716d24e3868fd46cf0dc216b9
KEEP-LATEST devops-fileserver:V12.2 sha256:794a758b44bcd7eb4ab1b59e3791232f37b759d2201f1eef67eacdb92bceedd5
KEEP-PREVIOUS devops-fileserver:V12.1 sha256:4bbf0fc9a84d4a3f82004812a9aa8741469a5e98f2c783844548007038a080c9
KEEP-LATEST energy-man:e89a0b0 sha256:e78567dbdbbe25e22ee347ce94b9936ffb16fdeb63b47e632b2b8e5c06516e1e
KEEP-LATEST engine:5bb50e9 sha256:7970b1b72c7723843f391bb84d716857ce6a783d7647c2b73c67c4b3399a421f
KEEP-LATEST esb-sum-tester:v1.0 sha256:e080d70e1d65225266c0ced477392c682c2e9366118f5a178c61444b2d9303b1
KEEP-LATEST fileserver:v12.3 sha256:cf9f363e807f110b347d567613e687235bbb3f6d60e40947a93460ea4def8100
KEEP-LATEST flyway:latest sha256:fb8e1d3f838527e4b0e9d11228797ddcec4955c4d9b9540ceb521e4e2122dfea
KEEP-LATEST flyway/flyway:latest sha256:fb8e1d3f838527e4b0e9d11228797ddcec4955c4d9b9540ceb521e4e2122dfea
KEEP-LATEST form-man-miniapp:65c9e4f sha256:03c86dd369397172405895c617aca2a6630fafcdef8edbecbb97b603f026b7a5
KEEP-LATEST fx-obligation-manager-diba-miniapp:509c7eb sha256:bd6b3f1b6d71930eac9a771f4a5e52b0b636c0efee092413df1b9de4066cf1e9
KEEP-LATEST haj-multi-messanger:3688422 sha256:2f373cbe74dffb363a97f6d185f6938193fb1ebe554d0ee3c46be9ae7f081e93
KEEP-PREVIOUS haj-multi-messanger:78c0297 sha256:43e47661e2a0e4722194779bc228b1948ab184e38d437160169179c69fe26a29
KEEP-LATEST haj-pilgrim-search:integropia-42 sha256:5b0139ca99514d4f68c5babbcd2b346b2c4fe82cecba297981ddb45e0c16f34e
KEEP-PREVIOUS haj-pilgrim-search:integropia-41 sha256:5831ad7934ffe4e630504991d52332fd462d1b0a03b1480d4aec2961fd4f2c1e
KEEP-LATEST keycloak:itg-26 sha256:e0c89dfbd34ad42e5724e2b7a2e8e6dce008f2cd5438db55601901f187bbc56f
KEEP-PREVIOUS keycloak:itg-23 sha256:775bb2314f81a0586580a4e85934bd59c0442aa171c2441345f5870a042d1461
KEEP-LATEST log-correlator:latest sha256:4f0a59dd39b8b56dcbea138eed048c1c281f27b042b315bf8910436d8aef3ce6
KEEP-LATEST log-management:442ee29 sha256:d1540fd13c41316271882e68cd7ba1d8b3997a53af33a7bb799ee4f23cf44732
KEEP-LATEST mavaracrm-engine:363389e sha256:bdcbafad6b3f431cd0ef61244668dde77f835bc497402549064673d307a64a83
KEEP-LATEST ms-common:e1a4646 sha256:6c84b8603830f9c02de2ca6e458f970d27b7a7fb844a8aebe25c1bf531aee4f1
KEEP-LATEST ms-datatools:96b6805 sha256:9565b9c4307ce6751962683f5a28f85abf042ffa2e193afce704cb095e50feeb
KEEP-LATEST ms-system:itg-4 sha256:912e07235b6f7d6c088d20bd727f2a85c58dc641c4ae31bed4398c0542ea277a
KEEP-PREVIOUS ms-system:itg-3 sha256:a6a29af68391ba67c99741c12674db88e062d2b6092e2610c1c26623d6208780
KEEP-LATEST newfxs:latest sha256:93e548ab1a984799db74acbf0b0486019d0281867263e29b59bd5ff48fec01f8
KEEP-LATEST newfxsamd:latest sha256:66a03d56c6c90ce836ed82365dce82c27c873e4f4636af0ad9b6bf88b9fb29d0
KEEP-LATEST noiro/aci-containers-controller:6.1.1.1.81c2369 sha256:b7f1ebf49d1c681b8001213f86b39649cd5dc118981b3f6b83771af9ad10d472
KEEP-LATEST noiro/aci-containers-host:6.1.1.1.81c2369 sha256:eadeb41433bea5c9461d1a3f70c90be31f7b25517e26fe74b43f95bd68695f40
KEEP-LATEST noiro/cnideploy:6.1.1.1.81c2369 sha256:de30bae131703ad03b9cbbe325543bcced695a5cdb0e0700196046c766ba7823
KEEP-LATEST noiro/openvswitch:6.1.1.1.81c2369 sha256:c07a4073eb3de76beb6271d6408b5d0ce6e59ed85be3b2c36e2bb1aa88f720d4
KEEP-LATEST noiro/opflex:6.1.1.1.81c2369 sha256:eb8f7fb50773dfc09f7a7f55fad80683a7e4f96dec0bc10d8802bf575ebd7527
KEEP-LATEST openapi-parser:itg-5 sha256:621a14ba28154487369d0c36a4c7c66f51a593125831ebb56c5fb9afd2895314
KEEP-PREVIOUS openapi-parser:integropia-4 sha256:44a793f146341d67df35c1974a25447346454659c5388514f8b44bff32838600
KEEP-LATEST panel-ui:itg-16 sha256:c2750dc4130d830a728855c6e3806b165f82aa577a410cefdf3248f097ea8823
KEEP-PREVIOUS panel-ui:itg-15 sha256:1aa1b29a1bee82e06cacb282eac26bb5b310bdba063f8fedfcc6084fa78edce9
KEEP-LATEST pilgrim-search-miniapp:eb71481 sha256:fcdf6a33799a925adb6645a2ff3ff468d105a72cf6b56a3c92e86037afd711b1
KEEP-LATEST pilgrim_search-miniapp:eb71481 sha256:fcdf6a33799a925adb6645a2ff3ff468d105a72cf6b56a3c92e86037afd711b1
KEEP-LATEST pricing-panel-ui:dfp-72-internal sha256:d38bbf554176dfcd210addbfd6e559610b8dcab23875d42677342411cf0a8511
KEEP-PREVIOUS pricing-panel-ui:dfp-71-internal sha256:86b4d37d87b265a89c871c64b53dd4ede90411623d1b5366bd46e606df9c7c64
KEEP-LATEST pricing-with-feign:296d81f-fakher sha256:1f9fffd92889bede2ff9d6b1c25542db2d439cdc878b829407ea6516f156a3d2
KEEP-PREVIOUS pricing-with-feign:b068181-fakher sha256:4b4dc316210fa15e49b07ef88912071b45c11ac11265e849cccc8dc9d2bcec85
KEEP-PREVIOUS pricing-with-feign:4641768-fakher sha256:4b4dc316210fa15e49b07ef88912071b45c11ac11265e849cccc8dc9d2bcec85
KEEP-PREVIOUS pricing-with-feign:0c5882d-fakher sha256:4b4dc316210fa15e49b07ef88912071b45c11ac11265e849cccc8dc9d2bcec85
KEEP-LATEST pricing-with-feign-dev:ac6d1ed-fakher sha256:020f8cb6170891c85299031a2a58c1679cedc8c1f2000031e49c30797f8266ab
KEEP-PREVIOUS pricing-with-feign-dev:fa1a824-fakher sha256:06fb7831f3172f96265088a83e74783fcff4f6ac0f065dc1e70aa0b864206e2f
KEEP-LATEST quay.io/frrouting/frr:9.1.0 sha256:e9eef6f1be059274fc1280f54eea41ebf1db36c9e1a22d454c72ee08583c1f07
KEEP-LATEST quay.io/jetstack/cert-manager-cainjector:v1.16.1 sha256:171f9a9a7affd5905e509a52f13a7cae75b2303510e0455fda30a49a4b8847c0
KEEP-LATEST quay.io/jetstack/cert-manager-controller:v1.16.1 sha256:8f82029c681ec7812c3dcae27136412d67cb8334e78c3f61a93b301c14802fa8
KEEP-LATEST quay.io/jetstack/cert-manager-startupapicheck:v1.16.1 sha256:c00bc206b040b4563b72d73a022607e79f73df26de94749e2ec1725d8d9d9b15
KEEP-LATEST quay.io/jetstack/cert-manager-webhook:v1.16.1 sha256:71606ab773978c8f0172759bf06b17d268f67cb4fea5303e39d1d29033cc4bb0
KEEP-LATEST quay.io/metallb/controller:v0.14.8 sha256:ecf533ca0b175a88f91a767f675c1cc1e5058e675d678d4c2ac9dff45d355c9c
KEEP-LATEST quay.io/metallb/speaker:v0.14.8 sha256:87f1be308b8d42b227a989cf6c13b5d11d4e0611cee06d4f335ca1ff3ab8638b
KEEP-LATEST rabbitmq:4.0.3-management-alpine sha256:63c260b34b6c657c1273b98a8a5adcf57fde85534fda0fa6698d6865f15847aa
KEEP-LATEST rancher/calico-cni:v3.28.1-rancher1 sha256:6270ed548cf1402e1e337f7c5a57a4ee7131386c31e37909cb83aa8e663c8dcd
KEEP-LATEST rancher/flannel-cni:v1.4.1-rancher1 sha256:6525affdfa48affc550487d9c7c1f02beed36427854a6424191a0a1bdd7627d2
KEEP-LATEST rancher/hyperkube:v1.30.5-rancher1 sha256:a6bdbda952a78af0978ee0b5b6f228dbcba34025a0d0f1581152cc67d8617d88
KEEP-LATEST rancher/local-path-provisioner:v0.0.26 sha256:9325057706239e408ed417b19356cd892ee67b046ee08ff11798777d67288bd5
KEEP-LATEST rancher/mirrored-calico-ctl:v3.28.1 sha256:deea3c3b5931520f6b77654626053bd265504645b4fa33348c0a2b08ca918dd0
KEEP-LATEST rancher/mirrored-calico-kube-controllers:v3.28.1 sha256:8579fad4baca75ce79644db84d6a1e776a3c3f5674521163e960ccebd7206669
KEEP-LATEST rancher/mirrored-calico-node:v3.28.1 sha256:f72bd42a299e280eed13231cc499b2d9d228ca2f51f6fd599d2f4176049d7880
KEEP-LATEST rancher/mirrored-calico-pod2daemon-flexvol:v3.28.1 sha256:72be5fbe1cef7a60245bdc0c9f37f9f92800d115c63760955382d6597ea58e23
KEEP-LATEST rancher/mirrored-cluster-proportional-autoscaler:v1.8.9 sha256:f7ed92f1e5c511a4ffcf266851db8ddedb45d87d861ced63f3bcfe62b1731051
KEEP-LATEST rancher/mirrored-coredns-coredns:1.11.1 sha256:2169b3b96af988cf69d7dd69efbcc59433eb027320eb185c6110e0850b997870
KEEP-LATEST rancher/mirrored-coreos-etcd:v3.5.12 sha256:162fe639721588329bfea28d8b5e1286423cebd2b48c789ce7facfe7e0db0e1d
KEEP-LATEST rancher/mirrored-flannel-flannel:v0.25.1 sha256:707bb127c71edb8c619a0485d98796fc849eff1618a1214f335df88ed0b5615d
KEEP-LATEST rancher/mirrored-ingress-nginx-kube-webhook-certgen:v1.4.1 sha256:887b7f4495677473f1bef5bfb48200a1070e526183b515682a7e78e43c7d7da4
KEEP-LATEST rancher/mirrored-k8s-dns-dnsmasq-nanny:1.23.0 sha256:5f420006a6da5263570e2c8c35ad133c1f29ea562092af24f0b655be6202d2bb
KEEP-LATEST rancher/mirrored-k8s-dns-kube-dns:1.23.0 sha256:32b101995e0742f18af3528b1da23be0e5bb9353f8ed9667ca74cd85d3cc968f
KEEP-LATEST rancher/mirrored-k8s-dns-node-cache:1.23.0 sha256:6177e8cdbafa04b5a33df4f3b3bb1d826584e34cfe1dfdb0b773eb8f566b8047
KEEP-LATEST rancher/mirrored-k8s-dns-sidecar:1.23.0 sha256:33a513ad5eba3bed82e073c14c778b4951fab04a45d44be00bda1c7354f0a38f
KEEP-LATEST rancher/mirrored-metrics-server:v0.7.1 sha256:799bbdcdd394890e7e4564ff692d3e7506e34cbe2117f7e8db867257f6bc6e08
KEEP-LATEST rancher/mirrored-nginx-ingress-controller-defaultbackend:1.5-rancher1 sha256:4dc5e07c8ca4e23bddb3153737d7b8c556e5fb2f29c4558b7cd6e6df99c512c7
KEEP-LATEST rancher/mirrored-pause:3.7 sha256:445a99db22e9add9bfb15ddb1980861a329e5dff5c88d7eec9cbf08b6b2f4eb1
KEEP-LATEST rancher/nginx-ingress-controller:nginx-1.11.2-rancher1 sha256:f0402daaa8551afd3342d7cdbe2096e94988bfe4b6519367f676592e475ac9d6
KEEP-LATEST rancher/rke-tools:v0.1.103 sha256:d4d2f8a11901ba950fc61b8e2d0a849b1797796f495a683cd84ca8b521dcb33b
KEEP-LATEST redis:8.0-M03-alpine3.21 sha256:64a6d11ec570f253bf47392d70820cef20ff859fd1f7b150232561d97ddf912b
KEEP-LATEST registry.k8s.io/sig-storage/nfs-subdir-external-provisioner:v4.0.2 sha256:f741e403b3ca161e784163de3ebde9190905fdbf7dfaa463620ab8f16c0f6423
KEEP-LATEST rest-interface:be5cb08 sha256:d285b34b0fbba68955fe379ed7ff578374bee3b9252bed65838ac48609e47919
KEEP-PREVIOUS rest-interface:d2ade9b sha256:209cb8b8ddd69dcc92129fb4fdd5784fd75f572e18a8a785135b47f45d5c0c75
KEEP-LATEST rest-invoker:92290fb sha256:0b112c4e309ccd29b9d45baa3c89a9aaa57d5f5955045ea04231518554a328f2
KEEP-PREVIOUS rest-invoker:1d409fe sha256:0fc206944ed9d8da616b66ed1f33cd8eca6b8f209baab722166af7dd0ac32c3a
KEEP-LATEST service-desk:itg-37 sha256:b393a8a7246bd7bbabbe2dc3390cf690163d408ee243c048b6a816bd576c858c
KEEP-PREVIOUS service-desk:itg-36 sha256:dd3daa69c29e91bdf78bbbf6f2c649110c940d11d3f521c6ec4be76c1d0763e4
KEEP-LATEST soap-invoker:223a6b4 sha256:de37965e3ae8e684fcbc960706cc198c9ed3045e423f19e79d2b8ef7d8ab9104
KEEP-LATEST soap-parser:8ad244a sha256:13b974f0c0a65bf0189ee1972be4090e16e021615c5ccd34ff9de946ccf875ad
KEEP-LATEST soft-asset-man-miniapp:4fd828a sha256:923744bcfd45283425ec52071054e59e03c60802fd51f821e841a908a53c9378
KEEP-LATEST software-identity-miniapp:1bf69da sha256:b5de488c28110d7affe3a47cb3e1e18733e6740ccb2fe794e228d600f29e99d4
KEEP-PREVIOUS software-identity-miniapp:1af723e sha256:d17a71413d55e0ffde43f5b965aee62357b67ecd5c454b84b1de1066777460c0
KEEP-LATEST sso_keycloak:23.0.3-1.0.53 sha256:b18ad3dce1631ad3bb8e7747a501dea03c363015e8027c7584d1ae2ebc347fa8
KEEP-LATEST timberio/vector:0.46.1-debian sha256:b69744ff85f898279be707312546cf6f117192659e5080ffc988c7dca341dfdd
KEEP-PREVIOUS timberio/vector:0.42.0-debian sha256:e3537b4d454b6711e10c7643268fe5e8f0cc8489d462dc6a46cd01ccea9c89dd
============================================================
Report files
============================================================
Detailed report : /opt/registry-retention/registry-retention-report.tsv
Summary report : /opt/registry-retention/registry-retention-summary.tsv
No data was deleted.
توجه کنید که این دستور ایمیجی را پاک نکرده و صرفا برای اطمینان و گزارشگیری ساخته شده است.
پاک کردن ایمیج های اضافی
برای پاک کردن ایمیجهای قابل حذف ابتدا دستور زیر را اجرا کنید:
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 خود قرار دهید.