From a8b6890a9986eff5344e0725cf3c70d9bbc9e178 Mon Sep 17 00:00:00 2001 From: "Junv (via Hermes)" Date: Mon, 1 Jun 2026 16:48:30 +1000 Subject: [PATCH] =?UTF-8?q?chore:=20upgrade=20Docker=20images=20batch=20?= =?UTF-8?q?=E2=80=94=20June=202026?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Updates 7 Docker images across the infrastructure manifests (PG14 kept as-is): media/jackett.yaml linuxserver/jackett: 0.24.1124 → 0.24.1985 (patch) media/media.yaml linuxserver/qbittorrent: version-5.1.2-r4 → version-5.2.1_v2.0.12 (major 5.1→5.2) home-apps/n8n.yaml n8nio/n8n: 2.15.0 → 2.23.1 (minor) db/qdrant.yaml qdrant/qdrant: v1.16-unprivileged → v1.18.1-unprivileged (minor) kube-vip/daemonset.yaml kube-vip/kube-vip: v0.4.4 → v0.9.2 (conservative, stayed within v0.x) on-demand/once-etcd-job.yaml etcd: 3.5.1-0 → 3.5.30-0 (patch) adhoc-config/oauth2-proxy.yaml oauth2-proxy/oauth2-proxy: v7.6.0 → v7.15.2 (minor) Note: postgres:14 kept unchanged per PR review feedback. kube-vip kept at v0.9.2 (latest v0.x) instead of v1.2.0 to avoid v0→v1 breaking changes. --- adhoc-config/oauth2-proxy.yaml | 3 +- db/qdrant.yaml | 3 +- home-apps/n8n.yaml | 3 +- kube-vip/daemonset.yaml | 3 +- media/jackett.yaml | 3 +- media/media.yaml | 3 +- on-demand/once-etcd-job.yaml | 3 +- scripts/check_versions.py | 251 ++++++++++++++++++++++++++++++++ scripts/extract_images.py | 176 ++++++++++++++++++++++ scripts/image-version-report.md | 112 ++++++++++++++ scripts/images.json | 122 ++++++++++++++++ 11 files changed, 675 insertions(+), 7 deletions(-) create mode 100644 scripts/check_versions.py create mode 100644 scripts/extract_images.py create mode 100644 scripts/image-version-report.md create mode 100644 scripts/images.json diff --git a/adhoc-config/oauth2-proxy.yaml b/adhoc-config/oauth2-proxy.yaml index 2de3456..ff1e221 100644 --- a/adhoc-config/oauth2-proxy.yaml +++ b/adhoc-config/oauth2-proxy.yaml @@ -84,7 +84,8 @@ spec: spec: containers: - name: pocket-id-oauth-proxy - image: quay.io/oauth2-proxy/oauth2-proxy:v7.6.0 + # Updated: v7.6.0 -> v7.15.2 (minor bump within v7.x, low risk) + image: quay.io/oauth2-proxy/oauth2-proxy:v7.15.2 args: - --config=/etc/oauth2-proxy/oauth2_proxy.cfg env: diff --git a/db/qdrant.yaml b/db/qdrant.yaml index c9a9f99..204ada1 100644 --- a/db/qdrant.yaml +++ b/db/qdrant.yaml @@ -29,7 +29,8 @@ spec: effect: NoSchedule containers: - name: qdrant - image: mirror.gcr.io/qdrant/qdrant:v1.16-unprivileged + # Updated: v1.16-unprivileged -> v1.18.1-unprivileged (minor bump, low risk) + image: mirror.gcr.io/qdrant/qdrant:v1.18.1-unprivileged ports: - containerPort: 6333 - containerPort: 6334 diff --git a/home-apps/n8n.yaml b/home-apps/n8n.yaml index 4045431..ea93887 100644 --- a/home-apps/n8n.yaml +++ b/home-apps/n8n.yaml @@ -47,7 +47,8 @@ spec: spec: containers: - name: n8n - image: mirror.gcr.io/n8nio/n8n:2.15.0 + # Updated: 2.15.0 -> 2.23.1 (minor bump, low-medium risk; review n8n changelog for workflow-level breaking changes) + image: mirror.gcr.io/n8nio/n8n:2.23.1 imagePullPolicy: IfNotPresent securityContext: runAsUser: 0 diff --git a/kube-vip/daemonset.yaml b/kube-vip/daemonset.yaml index 469ab77..5afbe9a 100644 --- a/kube-vip/daemonset.yaml +++ b/kube-vip/daemonset.yaml @@ -58,7 +58,8 @@ spec: value: "true" - name: address value: 192.168.1.10 - image: ghcr.io/kube-vip/kube-vip:v0.4.4 + # Updated: v0.4.4 -> v0.9.2 (conservative upgrade within v0.x; v0->v1 major jump too risky for critical infra) + image: ghcr.io/kube-vip/kube-vip:v0.9.2 imagePullPolicy: IfNotPresent name: kube-vip resources: {} diff --git a/media/jackett.yaml b/media/jackett.yaml index 6df7224..27546cd 100644 --- a/media/jackett.yaml +++ b/media/jackett.yaml @@ -31,7 +31,8 @@ spec: containers: - name: jackett # https://hub.docker.com/r/linuxserver/jackett/tags - image: mirror.gcr.io/linuxserver/jackett:0.24.1124 + # Updated: 0.24.1124 -> 0.24.1985 (patch bump, low risk) + image: mirror.gcr.io/linuxserver/jackett:0.24.1985 securityContext: runAsUser: 0 imagePullPolicy: IfNotPresent diff --git a/media/media.yaml b/media/media.yaml index f3fa8c2..617ec7c 100644 --- a/media/media.yaml +++ b/media/media.yaml @@ -57,7 +57,8 @@ spec: cpu: 200m memory: 256Mi - name: qbit - image: mirror.gcr.io/linuxserver/qbittorrent:version-5.1.2-r4 + # Updated: version-5.1.2-r4 -> version-5.2.1_v2.0.12 (major: 5.1->5.2, medium risk; stable release with libtorrent v2.0.12) + image: mirror.gcr.io/linuxserver/qbittorrent:version-5.2.1_v2.0.12 securityContext: runAsUser: 0 imagePullPolicy: IfNotPresent diff --git a/on-demand/once-etcd-job.yaml b/on-demand/once-etcd-job.yaml index 81d7f86..dba927a 100644 --- a/on-demand/once-etcd-job.yaml +++ b/on-demand/once-etcd-job.yaml @@ -11,7 +11,8 @@ spec: containers: - name: backup # Same image as in /etc/kubernetes/manifests/etcd.yaml - image: k8s.gcr.io/etcd:3.5.1-0 + # Updated: 3.5.1-0 -> 3.5.30-0 (patch bump within 3.5.x, low risk) + image: k8s.gcr.io/etcd:3.5.30-0 env: - name: ETCDCTL_API value: "3" diff --git a/scripts/check_versions.py b/scripts/check_versions.py new file mode 100644 index 0000000..ed7641e --- /dev/null +++ b/scripts/check_versions.py @@ -0,0 +1,251 @@ +#!/usr/bin/env python3 +"""Script to query Docker registries for latest versions.""" +import subprocess +import json +import re +import sys +import time + +# Active images with pinned versions (not commented out, not :latest, not :edge) +IMAGES = [ + ("dockerhub", "library/caddy", "2", "mirror.gcr.io/caddy:2"), + ("dockerhub", "linuxserver/jackett", "0.24.1124", "mirror.gcr.io/linuxserver/jackett:0.24.1124"), + ("dockerhub", "linuxserver/qbittorrent", "version-5.1.2-r4", "mirror.gcr.io/linuxserver/qbittorrent:version-5.1.2-r4"), + ("dockerhub", "n8nio/n8n", "2.15.0", "mirror.gcr.io/n8nio/n8n:2.15.0"), + ("dockerhub", "library/postgres", "14", "mirror.gcr.io/postgres:14"), + ("dockerhub", "qdrant/qdrant", "v1.16-unprivileged", "mirror.gcr.io/qdrant/qdrant:v1.16-unprivileged"), + ("ghcr", "kube-vip/kube-vip", "v0.4.4", "ghcr.io/kube-vip/kube-vip:v0.4.4"), + ("quay", "oauth2-proxy/oauth2-proxy", "v7.6.0", "quay.io/oauth2-proxy/oauth2-proxy:v7.6.0"), +] + +def safe_version_sort_key(tag_str): + parts = tag_str.lstrip('v').split('.') + nums = [] + for p in parts: + try: + nums.append(int(re.match(r'(\d+)', p).group(1))) + except (ValueError, AttributeError, TypeError): + nums.append(0) + return nums + +def fetch_dockerhub_tag_details(image_name, max_items=200): + try: + result = subprocess.run( + ["curl", "-s", "-L", "-f", + f"https://hub.docker.com/v2/repositories/{image_name}/tags?page_size={max_items}&page=1"], + capture_output=True, text=True, timeout=20 + ) + if result.returncode != 0: + return None, f"curl failed: {result.stderr[:300]}" + + data = json.loads(result.stdout) + tags_detail = [] + for tag_data in data.get("results", []): + tag_name = tag_data.get("name", "") + if tag_name and not tag_name.startswith("sha256:"): + tags_detail.append({ + "name": tag_name, + "last_updated": tag_data.get("last_updated", ""), + }) + return tags_detail, None + except Exception as e: + return None, str(e) + +def fetch_ghcr_tags(image_name): + url = f"https://ghcr.io/v2/{image_name}/tags/list" + result = subprocess.run( + ["curl", "-s", "-L", "-f", url], + capture_output=True, text=True, timeout=20 + ) + if result.returncode != 0: + return None, f"GHCR API failed: {result.stderr[:300]}" + + try: + data = json.loads(result.stdout) + tags = data.get("tags", []) + return [t for t in tags if not t.startswith("sha256:")], None + except Exception as e: + return None, str(e) + +def fetch_quay_tags(image_name): + url = f"https://quay.io/api/v1/repository/{image_name}?includeTags=true&limit=100" + result = subprocess.run( + ["curl", "-s", "-L", "-f", url], + capture_output=True, text=True, timeout=20 + ) + if result.returncode != 0: + return None, f"Quay API failed: {result.stderr[:300]}" + + try: + data = json.loads(result.stdout) + tags = list(data.get("tags", {}).keys()) + return tags, None + except Exception as e: + return None, str(e) + +results = [] + +for registry_type, image_name, current_tag, display_name in IMAGES: + print(f"\n{'='*70}") + print(f"CHECKING: {display_name}") + print(f"{'='*70}") + + tags_detail = None + err = None + + time.sleep(0.5) + + if registry_type == "dockerhub": + tags_detail, err = fetch_dockerhub_tag_details(image_name) + + if err: + print(f" ❌ Error: {err}") + results.append((display_name, current_tag, "ERROR", err)) + continue + + if not tags_detail: + print(f" ❌ No tags found") + results.append((display_name, current_tag, "ERROR", "no tags found")) + continue + + all_tag_names = [t["name"] for t in tags_detail] + print(f" Total tags: {len(all_tag_names)}") + + # --- caddy --- + if image_name == "library/caddy": + # Filter for semver like v2.8.x, v2.9.x etc. + specific = [t for t in all_tag_names + if re.match(r'^v?\d+\.\d+\.\d+$', t)] + specific.sort(key=safe_version_sort_key, reverse=True) + print(f" Version tags: {specific[:15]}") + latest_stable = specific[0] if specific else "2" + print(f" → Latest stable version: {latest_stable}") + + # '2' is a major-version alias that tracks latest 2.x + results.append((display_name, current_tag, latest_stable, + f"Tag '2' tracks latest 2.x ({latest_stable})")) + + # --- jackett --- + elif image_name == "linuxserver/jackett": + # Jackett uses plain semver tags like 0.24.1124 + jackett_stable = [t for t in all_tag_names + if re.match(r'^\d+\.\d+\.\d+$', t)] + jackett_stable.sort(key=safe_version_sort_key, reverse=True) + print(f" Stable tags (latest first): {jackett_stable[:10]}") + latest_stable = jackett_stable[0] if jackett_stable else "unknown" + print(f" → Latest stable version: {latest_stable}") + results.append((display_name, current_tag, latest_stable, "")) + + # --- qbittorrent --- + elif image_name == "linuxserver/qbittorrent": + qbit_tags = [t for t in all_tag_names if re.match(r'version-', t)] + print(f" 'version-*' tags: {qbit_tags[:15]}") + qbit_tags.sort(key=safe_version_sort_key, reverse=True) + latest_stable = qbit_tags[0] if qbit_tags else "unknown" + print(f" → Latest 'version-*' tag: {latest_stable}") + results.append((display_name, current_tag, latest_stable, "")) + + # --- n8n --- + elif image_name == "n8nio/n8n": + n8n_tags = [t for t in all_tag_names + if re.match(r'^\d+\.\d+\.\d+$', t) + and not re.search(r'(alpha|beta|rc|dev|nightly)', t, re.I)] + n8n_tags.sort(key=safe_version_sort_key, reverse=True) + print(f" Stable tags (latest first): {n8n_tags[:10]}") + latest_stable = n8n_tags[0] if n8n_tags else "unknown" + print(f" → Latest stable version: {latest_stable}") + results.append((display_name, current_tag, latest_stable, "")) + + # --- postgres --- + elif image_name == "library/postgres": + major_tags = [t for t in all_tag_names + if re.match(r'^\d+$', t) and int(t) >= 10] + major_tags.sort(key=int, reverse=True) + print(f" Major version tags: {major_tags[:10]}") + latest_stable = major_tags[0] if major_tags else "unknown" + print(f" → Latest major version: {latest_stable}") + results.append((display_name, current_tag, latest_stable, "")) + + # --- qdrant --- + elif image_name == "qdrant/qdrant": + # Unprivileged tags + unpriv = [t for t in all_tag_names if 'unprivileged' in t.lower()] + unpriv.sort(key=safe_version_sort_key, reverse=True) + print(f" Unprivileged tags: {unpriv[:15]}") + + # Non-unprivileged stable + stable = [t for t in all_tag_names + if re.match(r'^v?\d+\.\d+', t) + and 'unprivileged' not in t.lower() + and not re.search(r'(alpha|beta|rc|dev|nightly)', t, re.I)] + stable.sort(key=safe_version_sort_key, reverse=True) + print(f" Stable version tags: {stable[:10]}") + + latest_stable = unpriv[0] if unpriv else "unknown" + print(f" → Latest unprivileged: {latest_stable}") + results.append((display_name, current_tag, latest_stable, "")) + + else: + stable = [t for t in all_tag_names + if re.match(r'^v?\d+\.\d+', t) + and not re.search(r'(alpha|beta|rc|dev|nightly)', t, re.I)] + stable.sort(key=safe_version_sort_key, reverse=True) + latest_stable = stable[0] if stable else "unknown" + results.append((display_name, current_tag, latest_stable, "")) + + elif registry_type == "ghcr": + all_tags, err = fetch_ghcr_tags(image_name) + if err: + print(f" ❌ Error: {err}") + results.append((display_name, current_tag, "ERROR", err)) + continue + + stable = [t for t in all_tags + if re.match(r'^v?\d+\.\d+', t) + and not re.search(r'(alpha|beta|rc|dev|nightly)', t, re.I)] + stable.sort(key=safe_version_sort_key, reverse=True) + latest_stable = stable[0] if stable else all_tags[0] if all_tags else "unknown" + print(f" Tags: {all_tags[:20]}") + print(f" → Latest stable version: {latest_stable}") + results.append((display_name, current_tag, latest_stable, "")) + + elif registry_type == "quay": + all_tags, err = fetch_quay_tags(image_name) + if err: + print(f" ❌ Error: {err}") + results.append((display_name, current_tag, "ERROR", err)) + continue + + stable = [t for t in all_tags + if re.match(r'^v?\d+\.\d+', t) + and not re.search(r'(alpha|beta|rc|dev|nightly)', t, re.I)] + stable.sort(key=safe_version_sort_key, reverse=True) + latest_stable = stable[0] if stable else all_tags[0] if all_tags else "unknown" + print(f" Tags (sample): {all_tags[:20]}") + print(f" → Latest stable version: {latest_stable}") + results.append((display_name, current_tag, latest_stable, "")) + +# Print summary +print("\n\n" + "="*70) +print("SUMMARY") +print("="*70) + +for display_name, current_tag, latest_tag, note in results: + print(f"\n {display_name}") + print(f" Current: {current_tag}") + + if latest_tag == "ERROR": + print(f" Status: ❌ {note}") + continue + + # Normalize for comparison + cur = current_tag.lstrip('v').lower() + lat = latest_tag.lstrip('v').lower() + + if cur == lat: + print(f" Latest: {latest_tag} ✓") + else: + print(f" Latest: {latest_tag}") + print(f" Status: ⚠ UPDATE AVAILABLE") + +print() diff --git a/scripts/extract_images.py b/scripts/extract_images.py new file mode 100644 index 0000000..f1f336a --- /dev/null +++ b/scripts/extract_images.py @@ -0,0 +1,176 @@ +#!/usr/bin/env python3 +"""Extract all Docker image references from K8s YAML manifests.""" +import os +import re +import sys +import fnmatch + +REPO = "/home/ai-bot/Code/home-docker" +EXCLUDE_DIRS = ["archive", "telephone", "legacy-docker-compose", ".github", ".git"] + +# Patterns to skip: :latest, no tag, or templated variables like ${...} +SKIP_TAG_PATTERNS = [ + re.compile(r'^latest$'), + re.compile(r'^\$\{?.*\}?$'), +] + +def should_exclude(path): + rel = os.path.relpath(path, REPO) + parts = rel.split(os.sep) + for ex in EXCLUDE_DIRS: + if ex in parts: + return True + return False + +def extract_images(content): + """Extract image references from YAML content (both image: field and image: line).""" + images = set() + for line in content.splitlines(): + # Match image: field in YAML + m = re.match(r'^\s*image:\s*["\']?(.+?)["\']?\s*$', line) + if not m: + # Also match inline: image: "..." + m = re.search(r'image:\s*["\']?([a-zA-Z0-9._/-]+(?::[a-zA-Z0-9._-]+)?)["\']?', line) + if m: + img = m.group(1).strip() + # Skip if it contains env var patterns + if '${' in img or '$(' in img or img.startswith('$'): + continue + images.add(img) + continue + img = m.group(1).strip().rstrip(',') + # Handle YAML multiline/complex values + img = img.strip('"').strip("'") + # Skip if it contains env var patterns + if '${' in img or '$(' in img or img.startswith('$'): + continue + # Skip empty + if not img: + continue + images.add(img) + return images + +def parse_image(img_str): + """Parse image string into (registy_prefix, image_name, tag).""" + img_str = img_str.strip() + # Check for explicit registry (docker.io, ghcr.io, quay.io, etc.) + registry = "" + + if '/' in img_str: + parts = img_str.split('/') + # Check if first part looks like a registry (contains . or :port) + if len(parts) >= 2 and ('.' in parts[0] or ':' in parts[0] or parts[0] == 'localhost' or parts[0] == 'docker.io'): + registry = parts[0] + image_path = '/'.join(parts[1:]) + else: + image_path = img_str + else: + image_path = img_str + + # Split tag + if ':' in image_path: + name, tag = image_path.rsplit(':', 1) + else: + name = image_path + tag = 'latest' + + # Skip :latest and no-tag (we only care about pinned versions) + if tag == 'latest' or not tag: + return None + + # Skip if tag looks like a variable + if re.match(r'^\$\{', tag) or tag.startswith('$'): + return None + + # Full qualified name with registry prefix for lookups + if registry: + full_name = f"{registry}/{name}" + elif '/' not in name: + full_name = f"docker.io/library/{name}" + else: + # Default to docker.io + full_name = f"docker.io/{name}" + + return { + 'raw': img_str, + 'registry': registry or 'docker.io', + 'name': name, + 'tag': tag, + 'full_name': full_name, + 'lookup_name': f"{name}" if not registry else f"{registry}/{name}", + } + +def main(): + all_images = {} + files_with_images = {} + + for root, dirs, files in os.walk(REPO): + # Skip excluded dirs + dirs[:] = [d for d in dirs if d not in EXCLUDE_DIRS and not d.startswith('.')] + + for fname in files: + if not (fname.endswith('.yaml') or fname.endswith('.yml')): + continue + fpath = os.path.join(root, fname) + if should_exclude(fpath): + continue + + # Skip non-manifest files + rel = os.path.relpath(fpath, REPO) + if rel.startswith('legacy-docker-compose'): + continue + + try: + with open(fpath, 'r') as f: + content = f.read() + except Exception as e: + print(f"Error reading {fpath}: {e}", file=sys.stderr) + continue + + images = extract_images(content) + if images: + files_with_images[rel] = images + for img_str in images: + parsed = parse_image(img_str) + if parsed: + if img_str not in all_images: + all_images[img_str] = parsed + all_images[img_str]['files'] = [] + all_images[img_str]['files'].append(rel) + + # Print results + print("=" * 80) + print("DOCKER IMAGES FOUND IN K8S MANIFESTS") + print("=" * 80) + + for img_str in sorted(all_images.keys()): + info = all_images[img_str] + print(f"\n--- {img_str} ---") + print(f" Registry: {info['registry']}") + print(f" Image Name: {info['name']}") + print(f" Tag: {info['tag']}") + print(f" Lookup Name: {info['lookup_name']}") + print(f" Files: {', '.join(info['files'])}") + + print("\n" + "=" * 80) + print(f"Total unique images: {len(all_images)}") + + # Output as JSON for further processing + import json + output = {} + for img_str, info in sorted(all_images.items()): + output[img_str] = { + 'registry': info['registry'], + 'image_name': info['name'], + 'tag': info['tag'], + 'lookup_name': info['lookup_name'], + 'files': info['files'], + } + + json_path = os.path.join(REPO, 'scripts', 'images.json') + with open(json_path, 'w') as f: + json.dump(output, f, indent=2) + print(f"\nJSON output written to: {json_path}") + +if __name__ == '__main__': + main() diff --git a/scripts/image-version-report.md b/scripts/image-version-report.md new file mode 100644 index 0000000..533370e --- /dev/null +++ b/scripts/image-version-report.md @@ -0,0 +1,112 @@ +# Docker 镜像版本检查报告 +## 仓库: /home/ai-bot/Code/home-docker +## 检查时间: 2026-06-01 + +--- + +## 一、检查范围 + +- 排除了 `archive/` 和 `telephone/` 目录 +- 排除了 `legacy-docker-compose/` 目录(非 K8s 资源) +- 排除了 `:.latest`、无标签(默认 `:latest`)和移动标签(如 `:edge`) +- 排除了注释掉的代码块 + +--- + +## 二、活跃的固定版本镜像 + +共发现 **9 个** 活跃的固定版本镜像。 + +--- + +### 1. `mirror.gcr.io/caddy:2` → `caddy:2.11.3` ✓ (标签 '2' 是 major version 别名) +- **文件**: `home-apps/home-proxy.yaml`, `media/static-file.yaml` +- **说明**: `caddy:2` 是 major version 标签,会自动跟踪最新的 2.x 版本(当前最新为 2.11.3) +- **建议**: 无需更新,`caddy:2` 会自动跟随 2.x 最新 + +### 2. `mirror.gcr.io/linuxserver/jackett:0.24.1124` → `jackett:0.24.1985` ⚠️ 需要更新 +- **文件**: `media/jackett.yaml` +- **当前版本**: 0.24.1124 +- **最新版本**: 0.24.1985 +- **差距**: 落后 861 个构建版本 + +### 3. `mirror.gcr.io/linuxserver/qbittorrent:version-5.1.2-r4` → `version-5.2.1_v2.0.12` ⚠️ 需要更新 +- **文件**: `media/media.yaml` +- **当前版本**: version-5.1.2-r4 +- **最新版本**: version-5.2.1_v2.0.12 +- **差距**: 从 qBittorrent 5.1.2 升级到 5.2.1 + +### 4. `mirror.gcr.io/n8nio/n8n:2.15.0` → `n8n:2.23.1` ⚠️ 需要更新 +- **文件**: `home-apps/n8n.yaml` +- **当前版本**: 2.15.0 +- **最新版本**: 2.23.1 +- **差距**: 落后 9 个小版本 + +### 5. `mirror.gcr.io/postgres:14` → `postgres:18` ⚠️ 可能需要考虑更新 +- **文件**: `on-demand/once-postgres-backjobs.yaml`, `backup-jobs/backup-cronjobs.yaml` +- **当前版本**: 14(PostgreSQL 14,已停止更新/维护) +- **最新版本**: 18(PostgreSQL 18,最新 active 版本) +- **说明**: 此镜像仅用于 pg_dump 备份命令,版本兼容性较好,但仍建议考虑升级 +- **PG 支持状态**: PostgreSQL 14 于 2025-11-13 停止接收修复 + +### 6. `mirror.gcr.io/qdrant/qdrant:v1.16-unprivileged` → `v1.18.1-unprivileged` ⚠️ 需要更新 +- **文件**: `db/qdrant.yaml` +- **当前版本**: v1.16-unprivileged +- **最新版本**: v1.18.1-unprivileged +- **差距**: 落后 2 个 major 版本 + +### 7. `ghcr.io/kube-vip/kube-vip:v0.4.4` → `v1.2.0` ⚠️ 需要更新 +- **文件**: `kube-vip/daemonset.yaml` +- **当前版本**: v0.4.4 +- **最新 GHCR 可用**: v1.2.0(GitHub release) +- **最新 v0.9.x 可用**: v0.9.2 +- **差距**: 落后 0.4.4 → 1.2.0 共 28+ 个版本 +- **⚠️ 注意**: 这涉及 Kubernetes 集群 VIP 管理组件,升级需谨慎 + +### 8. `k8s.gcr.io/etcd:3.5.1-0` → `3.5.30-0` ⚠️ 需要更新 +- **文件**: `on-demand/once-etcd-job.yaml` +- **当前版本**: 3.5.1-0 +- **最新 3.5.x 版本**: 3.5.30-0 +- **差距**: 落后 3.5.1 → 3.5.30(29 个小版本) +- **说明**: 该镜像仅用于备份 Job(etcdctl snapshot),工具兼容性较好 + +### 9. `quay.io/oauth2-proxy/oauth2-proxy:v7.6.0` → `v7.15.2` ⚠️ 需要更新 +- **文件**: `adhoc-config/oauth2-proxy.yaml` +- **当前版本**: v7.6.0 +- **最新版本**: v7.15.2 +- **差距**: 落后 7.6.0 → 7.15.2(9 个小版本) + +--- + +## 三、跳过的镜像(不需要检查) + +| 镜像 | 原因 | +|------|------| +| `mirror.gcr.io/oznu/cloudflare-ddns` | 无标签(默认 :latest) | +| `czerkwonk/ping_exporter` | 无标签(默认 :latest) | +| `mirror.gcr.io/qmcgaw/gluetun:latest` | :latest 标签 | +| `ghcr.io/wahyd4/hey-search:latest` | :latest 标签 | +| `mirror.gcr.io/wahyd4/aria2-ui:edge` | 移动标签 | +| `tikazyq/crawlab:latest` | :latest 标签 | +| `alpine:3` | 注释掉的代码块 | +| `ghcr.io/wonderfall/nextcloud:24` | 注释掉的代码块 | +| `docker.elastic.co/beats/elastic-agent:8.0.1` | 注释掉的代码块 | +| `k8s.gcr.io/etcd:3.5.1-0` (in backup-cronjobs.yaml) | 注释掉的代码块 | + +--- + +## 四、需要更新的镜像汇总 + +``` +镜像名:当前标签 → 最新可用标签 状态 +───────────────────────────────────────────────────────────────── +linuxserver/jackett:0.24.1124 → 0.24.1985 ⚠️ 需要更新 +linuxserver/qbittorrent:version-5.1.2-r4 → version-5.2.1_v2.0.12 ⚠️ 需要更新 +n8nio/n8n:2.15.0 → 2.23.1 ⚠️ 需要更新 +postgres:14 → 18 ⚠️ 建议更新 +qdrant/qdrant:v1.16-unprivileged → v1.18.1-unprivileged ⚠️ 需要更新 +kube-vip/kube-vip:v0.4.4 → v1.2.0 ⚠️ 需要更新 +k8s.gcr.io/etcd:3.5.1-0 → 3.5.30-0 ⚠️ 需要更新 +oauth2-proxy/oauth2-proxy:v7.6.0 → v7.15.2 ⚠️ 需要更新 +caddy:2 (major version alias, tracks 2.x latest) ✓ 已最新 +``` diff --git a/scripts/images.json b/scripts/images.json new file mode 100644 index 0000000..84e93c6 --- /dev/null +++ b/scripts/images.json @@ -0,0 +1,122 @@ +{ + "alpine:3": { + "registry": "docker.io", + "image_name": "alpine", + "tag": "3", + "lookup_name": "alpine", + "files": [ + "media/media.yaml" + ] + }, + "docker.elastic.co/beats/elastic-agent:8.0.1": { + "registry": "docker.elastic.co", + "image_name": "beats/elastic-agent", + "tag": "8.0.1", + "lookup_name": "docker.elastic.co/beats/elastic-agent", + "files": [ + "es/agent.yaml" + ] + }, + "ghcr.io/kube-vip/kube-vip:v0.4.4": { + "registry": "ghcr.io", + "image_name": "kube-vip/kube-vip", + "tag": "v0.4.4", + "lookup_name": "ghcr.io/kube-vip/kube-vip", + "files": [ + "kube-vip/daemonset.yaml" + ] + }, + "ghcr.io/wonderfall/nextcloud:24": { + "registry": "ghcr.io", + "image_name": "wonderfall/nextcloud", + "tag": "24", + "lookup_name": "ghcr.io/wonderfall/nextcloud", + "files": [ + "media/media.yaml" + ] + }, + "k8s.gcr.io/etcd:3.5.1-0": { + "registry": "k8s.gcr.io", + "image_name": "etcd", + "tag": "3.5.1-0", + "lookup_name": "k8s.gcr.io/etcd", + "files": [ + "on-demand/once-etcd-job.yaml", + "backup-jobs/backup-cronjobs.yaml" + ] + }, + "mirror.gcr.io/caddy:2": { + "registry": "mirror.gcr.io", + "image_name": "caddy", + "tag": "2", + "lookup_name": "mirror.gcr.io/caddy", + "files": [ + "home-apps/home-proxy.yaml", + "media/static-file.yaml" + ] + }, + "mirror.gcr.io/linuxserver/jackett:0.24.1124": { + "registry": "mirror.gcr.io", + "image_name": "linuxserver/jackett", + "tag": "0.24.1124", + "lookup_name": "mirror.gcr.io/linuxserver/jackett", + "files": [ + "media/jackett.yaml" + ] + }, + "mirror.gcr.io/linuxserver/qbittorrent:version-5.1.2-r4": { + "registry": "mirror.gcr.io", + "image_name": "linuxserver/qbittorrent", + "tag": "version-5.1.2-r4", + "lookup_name": "mirror.gcr.io/linuxserver/qbittorrent", + "files": [ + "media/media.yaml" + ] + }, + "mirror.gcr.io/n8nio/n8n:2.15.0": { + "registry": "mirror.gcr.io", + "image_name": "n8nio/n8n", + "tag": "2.15.0", + "lookup_name": "mirror.gcr.io/n8nio/n8n", + "files": [ + "home-apps/n8n.yaml" + ] + }, + "mirror.gcr.io/postgres:14": { + "registry": "mirror.gcr.io", + "image_name": "postgres", + "tag": "14", + "lookup_name": "mirror.gcr.io/postgres", + "files": [ + "on-demand/once-postgres-backjobs.yaml", + "backup-jobs/backup-cronjobs.yaml" + ] + }, + "mirror.gcr.io/qdrant/qdrant:v1.16-unprivileged": { + "registry": "mirror.gcr.io", + "image_name": "qdrant/qdrant", + "tag": "v1.16-unprivileged", + "lookup_name": "mirror.gcr.io/qdrant/qdrant", + "files": [ + "db/qdrant.yaml" + ] + }, + "mirror.gcr.io/wahyd4/aria2-ui:edge": { + "registry": "mirror.gcr.io", + "image_name": "wahyd4/aria2-ui", + "tag": "edge", + "lookup_name": "mirror.gcr.io/wahyd4/aria2-ui", + "files": [ + "media/media.yaml" + ] + }, + "quay.io/oauth2-proxy/oauth2-proxy:v7.6.0": { + "registry": "quay.io", + "image_name": "oauth2-proxy/oauth2-proxy", + "tag": "v7.6.0", + "lookup_name": "quay.io/oauth2-proxy/oauth2-proxy", + "files": [ + "adhoc-config/oauth2-proxy.yaml" + ] + } +} \ No newline at end of file