feat(registry): daily GC cronjob — keep last 10 SHA tags per repo, prune layers
This commit is contained in:
@@ -0,0 +1,104 @@
|
||||
apiVersion: batch/v1
|
||||
kind: CronJob
|
||||
metadata:
|
||||
name: registry-gc
|
||||
namespace: registry
|
||||
spec:
|
||||
# Run daily at 3am
|
||||
schedule: "0 3 * * *"
|
||||
concurrencyPolicy: Forbid
|
||||
successfulJobsHistoryLimit: 3
|
||||
failedJobsHistoryLimit: 3
|
||||
jobTemplate:
|
||||
spec:
|
||||
template:
|
||||
spec:
|
||||
restartPolicy: OnFailure
|
||||
containers:
|
||||
- name: registry-gc
|
||||
image: registry:2
|
||||
command:
|
||||
- /bin/sh
|
||||
- -c
|
||||
- |
|
||||
set -e
|
||||
echo "Starting registry garbage collection..."
|
||||
|
||||
# Keep the last 10 tags per repo; delete the rest via registry API
|
||||
REGISTRY_URL="http://registry:5000"
|
||||
|
||||
# Get all repositories
|
||||
REPOS=$(wget -qO- "$REGISTRY_URL/v2/_catalog" | sed 's/.*"repositories":\[//;s/\].*//' | tr ',' '\n' | tr -d '"')
|
||||
|
||||
for repo in $REPOS; do
|
||||
echo "Processing repo: $repo"
|
||||
|
||||
# Get all tags sorted by date (registry API returns unordered, use digest order)
|
||||
TAGS=$(wget -qO- "$REGISTRY_URL/v2/$repo/tags/list" | sed 's/.*"tags":\[//;s/\].*//' | tr ',' '\n' | tr -d '"')
|
||||
TAG_COUNT=$(echo "$TAGS" | grep -c . || true)
|
||||
|
||||
echo " $TAG_COUNT tags found"
|
||||
|
||||
# Separate SHA tags (40-char hex) from named tags (latest, etc.)
|
||||
SHA_TAGS=$(echo "$TAGS" | grep -E '^[0-9a-f]{40}$' || true)
|
||||
SHA_COUNT=$(echo "$SHA_TAGS" | grep -c . || true)
|
||||
|
||||
# Keep the 10 most recent SHA tags; delete older ones
|
||||
KEEP=10
|
||||
if [ "$SHA_COUNT" -gt "$KEEP" ]; then
|
||||
TO_DELETE=$(echo "$SHA_TAGS" | head -n "-$KEEP")
|
||||
echo " Deleting $(echo "$TO_DELETE" | grep -c .) old SHA tags..."
|
||||
|
||||
for tag in $TO_DELETE; do
|
||||
# Get the manifest digest
|
||||
DIGEST=$(wget -qO- --server-response \
|
||||
--header="Accept: application/vnd.docker.distribution.manifest.v2+json" \
|
||||
"$REGISTRY_URL/v2/$repo/manifests/$tag" 2>&1 \
|
||||
| grep -i "docker-content-digest" | awk '{print $2}' | tr -d '\r')
|
||||
|
||||
if [ -n "$DIGEST" ]; then
|
||||
# Delete by digest
|
||||
wget -q --method=DELETE \
|
||||
"$REGISTRY_URL/v2/$repo/manifests/$DIGEST" && \
|
||||
echo " Deleted $repo:$tag ($DIGEST)" || \
|
||||
echo " Failed to delete $repo:$tag"
|
||||
fi
|
||||
done
|
||||
else
|
||||
echo " Skipping (only $SHA_COUNT SHA tags, keep threshold is $KEEP)"
|
||||
fi
|
||||
done
|
||||
|
||||
# Run the built-in GC to reclaim disk space from deleted layers
|
||||
echo "Running storage garbage collection..."
|
||||
/bin/registry garbage-collect /etc/docker/registry/config.yml --delete-untagged=false
|
||||
echo "Done."
|
||||
volumeMounts:
|
||||
- name: data
|
||||
mountPath: /var/lib/registry
|
||||
- name: config
|
||||
mountPath: /etc/docker/registry/config.yml
|
||||
subPath: config.yml
|
||||
volumes:
|
||||
- name: data
|
||||
persistentVolumeClaim:
|
||||
claimName: registry-data
|
||||
- name: config
|
||||
configMap:
|
||||
name: registry-gc-config
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
name: registry-gc-config
|
||||
namespace: registry
|
||||
data:
|
||||
config.yml: |
|
||||
version: 0.1
|
||||
storage:
|
||||
filesystem:
|
||||
rootdirectory: /var/lib/registry
|
||||
delete:
|
||||
enabled: true
|
||||
http:
|
||||
addr: :5000
|
||||
Reference in New Issue
Block a user