From 1a1b4500d67aec1ffe9be6e40dec60e30da4fa4a Mon Sep 17 00:00:00 2001 From: duffyduck Date: Tue, 9 Jun 2026 13:06:18 +0200 Subject: [PATCH] Korrupte/verwaiste Repos sichtbar machen + Live-GC sicher Zwei Probleme nach dem alten --delete-untagged-Schaden: 1) Repos mit Tags aber ohne _layers (verwaist) tauchen nicht im Registry-Katalog auf -> waren in der Web-Oberflaeche unsichtbar und nicht loeschbar. images() listet jetzt zusaetzlich direkt aus dem Dateisystem (scan_fs_repositories/repo_tags), Vereinigung mit dem Katalog. So sind auch korrupte Repos sichtbar und ueber den Loeschen-Button entfernbar. 2) Die GC laeuft als separater Prozess auf dem gemounteten Speicher. Der In-Memory-Blob-Cache der laufenden Registry hielt geloeschte Blobs faelschlich fuer vorhanden -> erneuter Pull legte keine _layers-Verknuepfung an (Image fehlte im Katalog). Cache via REGISTRY_STORAGE_CACHE_BLOBDESCRIPTOR="" deaktiviert -> Live-GC sicher. Verifiziert: korruptes Repo -> in Liste sichtbar -> Loeschen -> GC -> erneut pushen -> Katalog korrekt und docker pull erfolgreich, ohne Registry-Neustart. Co-Authored-By: Claude Opus 4.8 (1M context) --- auth-app/app.py | 40 +++++++++++++++++++++++++++++++++++----- docker-compose.yml | 6 ++++++ 2 files changed, 41 insertions(+), 5 deletions(-) diff --git a/auth-app/app.py b/auth-app/app.py index c166a41..ee74ee3 100644 --- a/auth-app/app.py +++ b/auth-app/app.py @@ -236,6 +236,36 @@ def _safe_repo_path(*parts): return target +def scan_fs_repositories(): + """Listet alle Repositories direkt aus dem Speicher (Dateisystem). + + Erfasst auch Repositories, die der Registry-Katalog (/v2/_catalog) nicht + zeigt - etwa nach Korruption, wenn das _layers-Verzeichnis fehlt. + """ + base = _repositories_base() + repos = [] + if not os.path.isdir(base): + return repos + for root, dirs, _files in os.walk(base): + if "_manifests" in dirs: + name = os.path.relpath(root, base).replace(os.sep, "/") + if name != ".": + repos.append(name) + dirs[:] = [] # innerhalb eines Repos nicht weiter absteigen + return repos + + +def repo_tags(name): + """Tags eines Repositories - aus dem Dateisystem, mit API als Fallback.""" + tags_dir = _safe_repo_path(*name.split("/"), "_manifests", "tags") + if tags_dir and os.path.isdir(tags_dir): + return sorted( + d for d in os.listdir(tags_dir) if os.path.isdir(os.path.join(tags_dir, d)) + ) + data = query_registry(f"/v2/{name}/tags/list") + return sorted(data.get("tags") or []) if data else [] + + def remove_repository_storage(name): """Entfernt das leere Repository-Verzeichnis aus dem Registry-Speicher. @@ -389,13 +419,13 @@ def users(): @app.route("/images") @login_required def images(): - repos = [] + # Repos aus dem Dateisystem UND dem Katalog (Vereinigung), damit auch + # Repositories sichtbar/loeschbar sind, die der Katalog nicht zeigt. + names = set(scan_fs_repositories()) data = query_registry("/v2/_catalog") if data: - for name in sorted(data.get("repositories", [])): - tags_data = query_registry(f"/v2/{name}/tags/list") - tags = sorted(tags_data.get("tags", [])) if tags_data and tags_data.get("tags") else [] - repos.append({"name": name, "tags": tags}) + names.update(data.get("repositories", [])) + repos = [{"name": name, "tags": repo_tags(name)} for name in sorted(names)] return render_template("images.html", repos=repos) diff --git a/docker-compose.yml b/docker-compose.yml index 7a0525f..09ff86a 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -34,6 +34,12 @@ services: REGISTRY_AUTH_HTPASSWD_REALM: Docker Registry REGISTRY_AUTH_HTPASSWD_PATH: /auth/htpasswd REGISTRY_STORAGE_DELETE_ENABLED: "true" + # Blob-Cache deaktivieren: Die Garbage Collection laeuft als separater + # Prozess auf dem gemounteten Speicher. Mit aktivem In-Memory-Cache wuerde + # die laufende Registry geloeschte Blobs faelschlich fuer vorhanden halten + # und beim erneuten Pull keine Layer-Verknuepfung anlegen (Image fehlt dann + # im Katalog / "manifest unknown"). Ohne Cache ist die Live-GC sicher. + REGISTRY_STORAGE_CACHE_BLOBDESCRIPTOR: "" volumes: - ./data/registry:/var/lib/registry - ./data/htpasswd:/auth:ro