feat(registry-mirror): add docker.io pull-through cache (LXC 250)
ci/woodpecker/push/deploy-infrastructure Pipeline was successful
ci/woodpecker/push/security-scan Pipeline was successful

Mitigates docker.io anonymous rate limits (100/6h per IP) that cause
ImagePullBackOff during Talos+FluxCD cluster rebuilds. Local-disk-backed
distribution/distribution:3.0.0 in an external LXC so the cache survives
full cluster wipes; Talos containerd is pointed at it via
machine.registries.mirrors and falls back to docker.io if unreachable.

Only docker.io is mirrored; ghcr.io / quay.io / registry.k8s.io aren't
rate-limited.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-04-26 10:05:51 +02:00
co-authored by Claude Opus 4.7
parent 247910b5dc
commit 7a5234532b
8 changed files with 103 additions and 2 deletions
@@ -0,0 +1,8 @@
---
- name: Install and Configure registry-mirror
hosts: proxmox_registry_mirror
become: true
roles:
- role: common
- role: docker-install
- role: registry-mirror
@@ -0,0 +1,7 @@
---
registry_mirror_image: "distribution/distribution:3.0.0"
registry_mirror_container_name: "registry-mirror"
registry_mirror_listen_port: 5000
registry_mirror_data_dir: "/var/lib/registry-mirror"
registry_mirror_config_dir: "/etc/registry-mirror"
registry_mirror_upstream_url: "https://registry-1.docker.io"
@@ -0,0 +1,6 @@
---
- name: Restart registry-mirror
community.docker.docker_container:
name: "{{ registry_mirror_container_name }}"
state: started
restart: true
@@ -0,0 +1,33 @@
---
- name: Ensure config directory exists
ansible.builtin.file:
path: "{{ registry_mirror_config_dir }}"
state: directory
mode: '0755'
- name: Ensure data directory exists
ansible.builtin.file:
path: "{{ registry_mirror_data_dir }}"
state: directory
mode: '0755'
- name: Render registry config
ansible.builtin.template:
src: config.yml.j2
dest: "{{ registry_mirror_config_dir }}/config.yml"
mode: '0644'
notify: Restart registry-mirror
- name: Run registry-mirror container
community.docker.docker_container:
name: "{{ registry_mirror_container_name }}"
image: "{{ registry_mirror_image }}"
state: started
restart_policy: unless-stopped
pull: true
published_ports:
- "{{ registry_mirror_listen_port }}:5000"
volumes:
- "{{ registry_mirror_config_dir }}/config.yml:/etc/distribution/config.yml:ro"
- "{{ registry_mirror_data_dir }}:/var/lib/registry"
command: ["serve", "/etc/distribution/config.yml"]
@@ -0,0 +1,21 @@
version: 0.1
log:
level: info
fields:
service: registry
storage:
filesystem:
rootdirectory: /var/lib/registry
cache:
blobdescriptor: inmemory
http:
addr: :5000
headers:
X-Content-Type-Options: [nosniff]
health:
storagedriver:
enabled: true
interval: 10s
threshold: 3
proxy:
remoteurl: {{ registry_mirror_upstream_url }}
+4 -2
View File
@@ -61,8 +61,9 @@ if phase_runs talos; then
echo -e "${YELLOW}=== Phase: Talos bootstrap ===${NC}"
command -v talosctl >/dev/null || fail "talosctl not found"
command -v kubectl >/dev/null || fail "kubectl not found"
[[ -f "$TALOS_DIR/patches/cluster.yaml" ]] || fail "cluster.yaml patch not found"
[[ -f "$TALOS_DIR/patches/cilium-cni.yaml" ]] || fail "cilium-cni.yaml patch not found"
[[ -f "$TALOS_DIR/patches/cluster.yaml" ]] || fail "cluster.yaml patch not found"
[[ -f "$TALOS_DIR/patches/cilium-cni.yaml" ]] || fail "cilium-cni.yaml patch not found"
[[ -f "$TALOS_DIR/patches/registry-mirror.yaml" ]] || fail "registry-mirror.yaml patch not found"
step 0 "Generating Talos configs..."
TALOS_GEN_DIR=$(mktemp -d)
@@ -102,6 +103,7 @@ if phase_runs talos; then
--file "$base_config" \
--config-patch "@$TALOS_DIR/patches/cluster.yaml" \
--config-patch "@$TALOS_DIR/patches/cilium-cni.yaml" \
--config-patch "@$TALOS_DIR/patches/registry-mirror.yaml" \
--config-patch "@$TALOS_DIR/patches/${node}.yaml" 2>&1); then
echo " applied via $node_ip ${GREEN}(OK)${NC}"
return 0
@@ -0,0 +1,6 @@
machine:
registries:
mirrors:
docker.io:
endpoints:
- http://registry-mirror.srv.renner.uno:5000
+18
View File
@@ -0,0 +1,18 @@
module "registry_mirror" {
source = "./modules/proxmox_lxc"
hostname = "registry-mirror.srv.renner.uno"
vmid = 250
target_node = "jupiter"
cores = 1
memory = 1024
disk_size = "50G"
mac_address = "bc:24:11:02:50:00"
tags = "docker;registry_mirror"
storage = "local-zfs"
ssh_public_keys = local.common_ssh_keys
ansible_playbook = "plays/install-registry-mirror.yml"
}