From: Luis Chamberlain <mcgrof@kernel.org>
To: Chuck Lever <cel@kernel.org>, Daniel Gomez <da.gomez@kruces.com>,
kdevops@lists.linux.dev
Cc: Luis Chamberlain <mcgrof@kernel.org>
Subject: [PATCH] Add Docker registry mirror support for kdevops workflows
Date: Thu, 25 Sep 2025 18:32:34 -0700 [thread overview]
Message-ID: <20250926013236.3502092-1-mcgrof@kernel.org> (raw)
Adds local Docker image caching through a registry mirror at /mirror/docker/,
similar to how git repositories are mirrored. The mirror acts as a pull-through
cache for Docker Hub, significantly speeding up container deployments and
enabling offline operation.
The implementation uses Kconfig for automatic detection of /mirror/docker/
availability, eliminating the need for manual configuration in workflows. When
the mirror is detected, workflows automatically rewrite image URLs to use the
local registry at localhost:5000.
Docker dependencies are installed per-distro following kdevops patterns, with
separate ansible tasks for Debian, RedHat, and SUSE. Systemd timers update
images daily at 2 AM to handle nightly builds and latest tags.
The defconfig-mirror now includes Docker mirror support alongside git and nix
mirrors. Users can set up all mirrors with 'make defconfig-mirror && make mirror'
or just the Docker mirror with 'make docker-mirror'. Additional targets include
docker-mirror-pull to cache images and docker-mirror-status to check health.
Includes proactive support for vLLM and LMCache container images for LLM
inference deployments. The vllm/vllm-openai:latest image supports standard
single-node deployments, while lmcache/vllm-openai images provide advanced
KV cache offloading for disaggregated prefill and reduced GPU memory usage.
Nightly builds are automatically updated via systemd timers.
The MinIO workflow demonstrates the integration pattern that other container-based
workflows can follow. Configuration happens through menuconfig under Mirror
options, with automatic detection via scripts/check_docker_mirror.sh.
Generated-by: Claude AI
Signed-off-by: Luis Chamberlain <mcgrof@kernel.org>
---
Makefile.linux-mirror | 35 ++-
defconfigs/mirror | 2 +
docs/docker-mirror.md | 248 ++++++++++++++++++
kconfigs/Kconfig.mirror | 77 ++++++
playbooks/docker-mirror.yml | 20 ++
playbooks/linux-mirror.yml | 1 +
.../roles/docker-mirror/defaults/main.yml | 24 ++
.../tasks/install-deps/debian/main.yml | 60 +++++
.../docker-mirror/tasks/install-deps/main.yml | 14 +
.../tasks/install-deps/redhat/main.yml | 52 ++++
.../tasks/install-deps/suse/main.yml | 33 +++
playbooks/roles/docker-mirror/tasks/main.yml | 186 +++++++++++++
.../roles/docker-mirror/tasks/pull_images.yml | 68 +++++
.../templates/docker-images-list.txt.j2 | 23 ++
.../templates/docker-manifest.txt.j2 | 16 ++
.../templates/docker-mirror-update.service.j2 | 23 ++
.../templates/docker-mirror-update.timer.j2 | 23 ++
.../templates/docker-registry-config.yml.j2 | 38 +++
.../templates/update-docker-images.sh.j2 | 114 ++++++++
playbooks/roles/minio_setup/tasks/main.yml | 14 +-
scripts/check_docker_mirror.sh | 87 ++++++
scripts/docker-mirror-setup.sh | 197 ++++++++++++++
scripts/mirror-docker-images.sh | 247 +++++++++++++++++
23 files changed, 1600 insertions(+), 2 deletions(-)
create mode 100644 docs/docker-mirror.md
create mode 100644 playbooks/docker-mirror.yml
create mode 100644 playbooks/roles/docker-mirror/defaults/main.yml
create mode 100644 playbooks/roles/docker-mirror/tasks/install-deps/debian/main.yml
create mode 100644 playbooks/roles/docker-mirror/tasks/install-deps/main.yml
create mode 100644 playbooks/roles/docker-mirror/tasks/install-deps/redhat/main.yml
create mode 100644 playbooks/roles/docker-mirror/tasks/install-deps/suse/main.yml
create mode 100644 playbooks/roles/docker-mirror/tasks/main.yml
create mode 100644 playbooks/roles/docker-mirror/tasks/pull_images.yml
create mode 100644 playbooks/roles/docker-mirror/templates/docker-images-list.txt.j2
create mode 100644 playbooks/roles/docker-mirror/templates/docker-manifest.txt.j2
create mode 100644 playbooks/roles/docker-mirror/templates/docker-mirror-update.service.j2
create mode 100644 playbooks/roles/docker-mirror/templates/docker-mirror-update.timer.j2
create mode 100644 playbooks/roles/docker-mirror/templates/docker-registry-config.yml.j2
create mode 100644 playbooks/roles/docker-mirror/templates/update-docker-images.sh.j2
create mode 100755 scripts/check_docker_mirror.sh
create mode 100755 scripts/docker-mirror-setup.sh
create mode 100755 scripts/mirror-docker-images.sh
diff --git a/Makefile.linux-mirror b/Makefile.linux-mirror
index ccda361b..9f4c0134 100644
--- a/Makefile.linux-mirror
+++ b/Makefile.linux-mirror
@@ -22,6 +22,9 @@ LINUX_MIRROR_ARGS += mirror_stable_rc_url='$(subst ",,$(CONFIG_MIRROR_STABLE_RC_
LINUX_MIRROR_ARGS += mirror_torvalds_url='$(subst ",,$(CONFIG_MIRROR_TORVALDS_URL))'
endif
+# Docker mirror variables are now handled via Kconfig output yaml
+# No need to add them to LINUX_MIRROR_ARGS anymore
+
MIRROR_CODE := $(TOPDIR)/playbooks/roles/linux-mirror/linux-mirror-systemd/
kdevops_linux_mirror: $(KDEVOPS_EXTRA_VARS)
@@ -43,14 +46,44 @@ mirror-status: $(KDEVOPS_EXTRA_VARS)
PHONY += mirror-status
+docker-mirror: docker-mirror-setup
+
+PHONY += docker-mirror
+
+docker-mirror-setup: $(KDEVOPS_EXTRA_VARS)
+ $(Q)ansible-playbook \
+ --tags vars,docker-mirror \
+ $(KDEVOPS_PLAYBOOKS_DIR)/docker-mirror.yml \
+ --extra-vars=@./extra_vars.yaml
+
+PHONY += docker-mirror-setup
+
+docker-mirror-pull: $(KDEVOPS_EXTRA_VARS)
+ $(Q)ansible-playbook \
+ --tags vars,docker-mirror-pull \
+ $(KDEVOPS_PLAYBOOKS_DIR)/docker-mirror.yml \
+ --extra-vars=@./extra_vars.yaml
+
+PHONY += docker-mirror-pull
+
+docker-mirror-status:
+ $(Q)scripts/check_docker_mirror.sh DOCKER_MIRROR_EXISTS
+
+PHONY += docker-mirror-status
+
ANSIBLE_EXTRA_ARGS += $(LINUX_MIRROR_ARGS)
LOCALHOST_SETUP_WORK += mirror
mirror-help-menu:
@echo "Mirror options:"
- @echo "mirror - sets up systemd mirrors"
+ @echo "mirror - sets up all mirrors (git, nix, docker)"
@echo "mirror-status - checks systemd mirrors status"
@echo ""
+ @echo "Docker mirror specific targets:"
+ @echo "docker-mirror - sets up Docker registry mirror only"
+ @echo "docker-mirror-pull - pull and cache Docker images"
+ @echo "docker-mirror-status - check Docker mirror status"
+ @echo ""
HELP_TARGETS += mirror-help-menu
endif
diff --git a/defconfigs/mirror b/defconfigs/mirror
index 05d51c01..3fe7161b 100644
--- a/defconfigs/mirror
+++ b/defconfigs/mirror
@@ -3,3 +3,5 @@ CONFIG_WORKFLOWS=n
CONFIG_INSTALL_LOCAL_LINUX_MIRROR=y
CONFIG_LINUX_MIRROR_NFS=y
CONFIG_INSTALL_NIX_CACHE_MIRROR=y
+CONFIG_ENABLE_DOCKER_MIRROR=y
+CONFIG_INSTALL_DOCKER_MIRROR=y
diff --git a/docs/docker-mirror.md b/docs/docker-mirror.md
new file mode 100644
index 00000000..56105c03
--- /dev/null
+++ b/docs/docker-mirror.md
@@ -0,0 +1,248 @@
+# Docker Mirror Support for kdevops
+
+This document describes the Docker registry mirror feature in kdevops, which provides local caching of Docker images to speed up container-based workflows.
+
+## Overview
+
+The Docker mirror feature provides a local Docker registry that acts as a pull-through cache for Docker Hub and other registries. This significantly speeds up container deployments and enables offline operation with cached images.
+
+## Features
+
+- **Local Docker Registry**: Runs a Docker registry mirror on localhost:5000
+- **Pull-through Cache**: Automatically caches images as they are pulled
+- **Automatic Detection**: Workflows automatically detect and use the mirror when available
+- **NFS Server Integration**: Mirror can be shared via NFS to other systems
+- **Offline Operation**: Use cached images without internet connectivity
+
+## Configuration
+
+Enable Docker mirror support in menuconfig:
+
+```bash
+make menuconfig
+# Navigate to: Kernel development environment -> Mirror options
+# Enable: Enable Docker registry mirror support
+# Enable: Install Docker registry mirror
+```
+
+Configuration options:
+
+- `CONFIG_ENABLE_DOCKER_MIRROR`: Enable Docker mirror support (auto-detected)
+- `CONFIG_USE_DOCKER_MIRROR`: Use Docker mirror if available (auto-detected)
+- `CONFIG_INSTALL_DOCKER_MIRROR`: Install Docker registry mirror
+- `CONFIG_DOCKER_MIRROR_PORT`: Registry port (default: 5000)
+- `CONFIG_DOCKER_MIRROR_PATH`: Storage path (default: /mirror/docker)
+- `CONFIG_DOCKER_MIRROR_PULL_THROUGH_CACHE`: Enable pull-through cache mode
+
+## Usage
+
+### Setup the Docker Mirror
+
+```bash
+# Configure and install the mirror
+make menuconfig # Enable Docker mirror options
+make mirror # Sets up all mirrors with background downloads
+
+# Or setup Docker mirror only
+make docker-mirror # Quick setup, downloads happen in background
+```
+
+The setup completes immediately and downloads happen in the background via systemd:
+- Initial download starts immediately upon timer activation
+- Daily updates at 2 AM local time
+- Check progress: `journalctl -u docker-mirror-update.service -f`
+- Check status: `systemctl status docker-mirror-update.timer`
+
+### Manual Image Operations (Optional)
+
+```bash
+# Force immediate image pull (blocks until complete)
+make docker-mirror-pull
+
+# Or use the script directly with custom images
+scripts/mirror-docker-images.sh [images-list-file] [--scan] [--archive]
+```
+
+### Check Mirror Status
+
+```bash
+# Check if Docker mirror is running
+make docker-mirror-status
+
+# Or use the script
+scripts/check_docker_mirror.sh DOCKER_MIRROR_EXISTS
+```
+
+## How It Works
+
+### Mirror Detection
+
+The system automatically detects if a Docker mirror is available by:
+
+1. Checking if `/mirror/docker/` directory exists
+2. Verifying the registry service is running on the configured port
+3. Testing registry accessibility via HTTP
+
+### Workflow Integration
+
+Workflows automatically use the Docker mirror based on Kconfig detection:
+
+1. Kconfig detects if `/mirror/docker/` exists and registry is running
+2. Sets `use_docker_mirror` variable automatically
+3. Workflows rewrite image URLs when mirror is enabled
+4. Fall back to original registries if mirror is unavailable
+
+Example from MinIO workflow:
+
+```yaml
+- name: Set MinIO container image with Docker mirror if enabled
+ ansible.builtin.set_fact:
+ minio_container_image_final: "localhost:{{ docker_mirror_port }}/{{ minio_container_image | regex_replace('^[^/]+/', '') }}"
+ when:
+ - use_docker_mirror | default(false) | bool
+
+- name: Start MinIO container
+ community.docker.docker_container:
+ name: "{{ minio_container_name }}"
+ image: "{{ minio_container_image_final }}"
+```
+
+### Supported Images
+
+The Docker mirror automatically caches the following images:
+
+- **MinIO**: Object storage service
+- **Milvus**: Vector database for AI workloads
+- **vLLM**: High-performance LLM inference engine
+ - `vllm/vllm-openai:latest` for standard deployments
+- **LMCache**: Advanced KV cache offloading for vLLM
+ - `lmcache/vllm-openai:2025-05-27-v1` for stable LMCache features
+ - `lmcache/vllm-openai:latest-nightly` for experimental cache server features
+- **Registry**: Docker registry itself
+- **etcd**: Distributed key-value store
+
+#### vLLM and LMCache Images
+
+The mirror includes support for vLLM inference deployments:
+
+- **Standard deployments**: Use `vllm/vllm-openai:latest` for single-node LLM serving
+- **LMCache deployments**: Use `lmcache/vllm-openai:2025-05-27-v1` for:
+ - KV cache offloading to reduce GPU memory usage
+ - Disaggregated prefill for better resource utilization
+ - KV-aware routing and prefix caching
+- **Experimental features**: Use `lmcache/vllm-openai:latest-nightly` for testing newest cache server capabilities
+
+These images are automatically updated daily via systemd timers to ensure you have the latest optimizations and fixes.
+
+## Scripts
+
+### docker-mirror-setup.sh
+
+Sets up the Docker registry mirror:
+
+```bash
+./scripts/docker-mirror-setup.sh [MIRROR_DIR] [REGISTRY_PORT] [REGISTRY_NAME]
+```
+
+Features:
+- Creates directory structure
+- Configures registry with pull-through cache
+- Starts registry container
+- Optional Docker daemon configuration
+
+### mirror-docker-images.sh
+
+Pulls and caches Docker images:
+
+```bash
+./scripts/mirror-docker-images.sh [images-list-file] [options]
+
+Options:
+ --scan Scan kdevops configuration for Docker images
+ --archive Save images to compressed tar archives
+```
+
+### check_docker_mirror.sh
+
+Checks Docker mirror availability:
+
+```bash
+./scripts/check_docker_mirror.sh [CHECK_TYPE]
+
+CHECK_TYPE:
+ DOCKER_MIRROR_URL - Returns mirror URL if available
+ DOCKER_MIRROR_EXISTS - Returns true/false for mirror existence
+ DOCKER_MIRROR_DIR - Returns mirror directory if exists
+ IMAGE_EXISTS [image] - Check if specific image exists in mirror
+```
+
+## Architecture
+
+```
+/mirror/docker/
+├── registry/ # Registry data storage
+├── config/ # Registry configuration
+│ └── config.yml # Registry config with pull-through cache
+├── images/ # Image management
+│ ├── manifest.txt # List of cached images
+│ └── archives/ # Optional tar archives of images
+└── ...
+```
+
+## Benefits
+
+1. **Faster Deployments**: Images are served from local cache
+2. **Bandwidth Savings**: Images downloaded once, used many times
+3. **Offline Operation**: Continue working without internet access
+4. **Consistent Environments**: All systems use same image versions
+5. **Integration with NFS**: Share cached images across network
+
+## Troubleshooting
+
+### Registry Not Starting
+
+Check if port 5000 is available:
+```bash
+sudo netstat -tlnp | grep 5000
+```
+
+### Images Not Being Cached
+
+Verify registry is in pull-through mode:
+```bash
+docker exec kdevops-docker-mirror cat /etc/docker/registry/config.yml | grep proxy
+```
+
+### Workflows Not Using Mirror
+
+Check if auto-detect is enabled:
+```bash
+grep DOCKER_MIRROR_AUTO_DETECT .config
+```
+
+## Systemd Timers
+
+Docker images are automatically updated daily via systemd timers:
+
+- **Service**: `docker-mirror-update.service` - Updates Docker images
+- **Timer**: `docker-mirror-update.timer` - Runs daily at 2 AM
+- **Logs**: Stored in `/mirror/docker/logs/` with 30-day rotation
+
+Check timer status:
+```bash
+systemctl status docker-mirror-update.timer
+systemctl list-timers docker-mirror-update.timer
+```
+
+Run manual update:
+```bash
+systemctl start docker-mirror-update.service
+```
+
+## Future Enhancements
+
+- Support for multiple upstream registries
+- Image garbage collection policies
+- Web UI for registry management
+- Metrics and monitoring integration
+- Support for private registries with authentication
diff --git a/kconfigs/Kconfig.mirror b/kconfigs/Kconfig.mirror
index 067258e5..a0999c2e 100644
--- a/kconfigs/Kconfig.mirror
+++ b/kconfigs/Kconfig.mirror
@@ -740,5 +740,82 @@ config NIX_CACHE_MIRROR_PATH
This should be on a filesystem with sufficient space as the cache
can grow to several GB over time.
+config ENABLE_DOCKER_MIRROR
+ bool "Enable Docker registry mirror support"
+ default $(shell, scripts/check_docker_mirror.sh ENABLE_DOCKER_MIRROR)
+ help
+ Enable Docker registry mirror support to cache container images locally.
+ This option enables the Docker mirror infrastructure if /mirror/docker/
+ directory exists.
+
+config USE_DOCKER_MIRROR
+ bool "Use Docker registry mirror"
+ output yaml
+ default $(shell, scripts/check_docker_mirror.sh USE_DOCKER_MIRROR)
+ depends on ENABLE_DOCKER_MIRROR
+ help
+ Automatically use the Docker registry mirror if it's running and accessible.
+ This will configure workflows to use localhost:5000 as the registry mirror.
+
+config INSTALL_DOCKER_MIRROR
+ bool "Install Docker registry mirror"
+ output yaml
+ default $(shell, scripts/check_docker_mirror.sh INSTALL_DOCKER_MIRROR)
+ depends on ENABLE_DOCKER_MIRROR
+ help
+ Enable this to set up a local Docker registry mirror for all guests
+ and workflows that use Docker containers. This will significantly
+ speed up container deployments by caching Docker images locally.
+
+ When enabled, this creates:
+ - A local Docker registry mirror at /mirror/docker/
+ - A Docker registry server on port 5000 (configurable)
+ - Automatic caching of pulled images
+ - Support for offline operation with cached images
+
+ This is particularly useful for workflows that use Docker containers
+ such as:
+ - MinIO object storage
+ - Milvus vector database
+ - AI/ML workflows with containerized services
+ - Database benchmarking with sysbench
+
+ The mirror will be available at: http://localhost:5000/
+
+config DOCKER_MIRROR_PORT
+ int "Docker registry mirror port"
+ output yaml
+ default 5000
+ depends on INSTALL_DOCKER_MIRROR
+ help
+ The port for the local Docker registry mirror server.
+ Default is 5000. Ensure this port is available and not blocked by firewall.
+
+config DOCKER_MIRROR_PATH
+ string "Docker registry mirror storage path"
+ output yaml
+ default "/mirror/docker"
+ depends on INSTALL_DOCKER_MIRROR
+ help
+ Local filesystem path where the Docker registry mirror will store images.
+ This should be on a filesystem with sufficient space as Docker images
+ can consume significant storage (several GB to tens of GB depending on
+ the workflows you use).
+
+config DOCKER_MIRROR_PULL_THROUGH_CACHE
+ bool "Enable pull-through cache mode"
+ output yaml
+ default y
+ depends on INSTALL_DOCKER_MIRROR
+ help
+ Enable pull-through cache mode for the Docker registry mirror.
+ When enabled, the registry will automatically cache images as they
+ are pulled through it, acting as a transparent proxy to Docker Hub
+ and other registries.
+
+ This is the recommended mode as it requires no changes to image
+ names and transparently caches all pulled images.
+
+
endif # ENABLE_LOCAL_LINUX_MIRROR
endif # TERRAFORM
diff --git a/playbooks/docker-mirror.yml b/playbooks/docker-mirror.yml
new file mode 100644
index 00000000..9474e238
--- /dev/null
+++ b/playbooks/docker-mirror.yml
@@ -0,0 +1,20 @@
+---
+- name: Install Docker mirror
+ hosts: localhost
+ become: true
+ tasks:
+ - name: Import optional extra_args file
+ ansible.builtin.include_vars: "{{ item }}"
+ ignore_errors: true
+ with_first_found:
+ - files:
+ - "../extra_vars.yml"
+ - "../extra_vars.yaml"
+ - "../extra_vars.json"
+ skip: true
+ tags: ["vars", "docker-mirror", "docker-mirror-pull", "docker-mirror-status"]
+
+ - name: Include docker-mirror role
+ ansible.builtin.include_role:
+ name: docker-mirror
+ tags: ["docker-mirror", "docker-mirror-pull", "docker-mirror-status"]
diff --git a/playbooks/linux-mirror.yml b/playbooks/linux-mirror.yml
index 2ec39d31..36dcd79b 100644
--- a/playbooks/linux-mirror.yml
+++ b/playbooks/linux-mirror.yml
@@ -5,3 +5,4 @@
- role: linux-mirror
- role: nix-cache-mirror
when: install_nix_cache_mirror | default(false) | bool
+ - role: docker-mirror
diff --git a/playbooks/roles/docker-mirror/defaults/main.yml b/playbooks/roles/docker-mirror/defaults/main.yml
new file mode 100644
index 00000000..ef10143d
--- /dev/null
+++ b/playbooks/roles/docker-mirror/defaults/main.yml
@@ -0,0 +1,24 @@
+---
+# Docker mirror defaults
+
+# Installation settings
+install_docker_mirror: false
+use_docker_mirror: false
+docker_mirror_path: "/mirror/docker"
+docker_mirror_port: 5000
+docker_mirror_pull_through_cache: true
+
+# Registry container settings
+docker_registry_container_name: "kdevops-docker-mirror"
+docker_registry_image: "registry:2"
+
+# Additional images to cache (can be extended by workflows)
+docker_mirror_additional_images: []
+
+# Images that need regular updates (nightly/latest tags)
+docker_mirror_images_with_updates:
+ - "minio/minio:latest"
+ - "milvusdb/milvus:latest"
+ - "vllm/vllm-openai:latest"
+ - "lmcache/vllm-openai:latest-nightly"
+ - "registry:latest"
diff --git a/playbooks/roles/docker-mirror/tasks/install-deps/debian/main.yml b/playbooks/roles/docker-mirror/tasks/install-deps/debian/main.yml
new file mode 100644
index 00000000..697b1c11
--- /dev/null
+++ b/playbooks/roles/docker-mirror/tasks/install-deps/debian/main.yml
@@ -0,0 +1,60 @@
+---
+# Install Docker on Debian/Ubuntu
+
+- name: Install Docker prerequisites
+ become: true
+ ansible.builtin.apt:
+ name:
+ - apt-transport-https
+ - ca-certificates
+ - curl
+ - gnupg
+ - lsb-release
+ state: present
+ update_cache: yes
+
+- name: Add Docker GPG key
+ become: true
+ ansible.builtin.apt_key:
+ url: https://download.docker.com/linux/{{ ansible_distribution | lower }}/gpg
+ state: present
+
+- name: Add Docker repository
+ become: true
+ ansible.builtin.apt_repository:
+ repo: "deb [arch={{ ansible_architecture }}] https://download.docker.com/linux/{{ ansible_distribution | lower }} {{ ansible_distribution_release }} stable"
+ state: present
+
+- name: Install Docker
+ become: true
+ ansible.builtin.apt:
+ name:
+ - docker-ce
+ - docker-ce-cli
+ - containerd.io
+ - docker-compose-plugin
+ state: present
+ update_cache: yes
+
+- name: Install Python Docker library
+ become: true
+ ansible.builtin.apt:
+ name:
+ - python3-docker
+ state: present
+
+- name: Start and enable Docker service
+ become: true
+ ansible.builtin.systemd:
+ name: docker
+ state: started
+ enabled: yes
+ daemon_reload: yes
+
+- name: Add current user to docker group
+ become: true
+ ansible.builtin.user:
+ name: "{{ ansible_user }}"
+ groups: docker
+ append: yes
+ when: ansible_user != 'root'
diff --git a/playbooks/roles/docker-mirror/tasks/install-deps/main.yml b/playbooks/roles/docker-mirror/tasks/install-deps/main.yml
new file mode 100644
index 00000000..4b8b1377
--- /dev/null
+++ b/playbooks/roles/docker-mirror/tasks/install-deps/main.yml
@@ -0,0 +1,14 @@
+---
+# Main task file for installing Docker dependencies
+
+- name: Install Docker dependencies on Debian
+ ansible.builtin.include_tasks: debian/main.yml
+ when: ansible_os_family == "Debian"
+
+- name: Install Docker dependencies on RedHat
+ ansible.builtin.include_tasks: redhat/main.yml
+ when: ansible_os_family == "RedHat"
+
+- name: Install Docker dependencies on SUSE
+ ansible.builtin.include_tasks: suse/main.yml
+ when: ansible_os_family == "Suse"
diff --git a/playbooks/roles/docker-mirror/tasks/install-deps/redhat/main.yml b/playbooks/roles/docker-mirror/tasks/install-deps/redhat/main.yml
new file mode 100644
index 00000000..4bf62751
--- /dev/null
+++ b/playbooks/roles/docker-mirror/tasks/install-deps/redhat/main.yml
@@ -0,0 +1,52 @@
+---
+# Install Docker on RHEL/CentOS/Fedora
+
+- name: Install Docker prerequisites
+ become: true
+ ansible.builtin.yum:
+ name:
+ - yum-utils
+ - device-mapper-persistent-data
+ - lvm2
+ state: present
+
+- name: Add Docker repository
+ become: true
+ ansible.builtin.command:
+ cmd: yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo
+ changed_when: false
+ args:
+ creates: /etc/yum.repos.d/docker-ce.repo
+
+- name: Install Docker
+ become: true
+ ansible.builtin.yum:
+ name:
+ - docker-ce
+ - docker-ce-cli
+ - containerd.io
+ - docker-compose-plugin
+ state: present
+
+- name: Install Python Docker library
+ become: true
+ ansible.builtin.yum:
+ name:
+ - python3-docker
+ state: present
+
+- name: Start and enable Docker service
+ become: true
+ ansible.builtin.systemd:
+ name: docker
+ state: started
+ enabled: yes
+ daemon_reload: yes
+
+- name: Add current user to docker group
+ become: true
+ ansible.builtin.user:
+ name: "{{ ansible_user }}"
+ groups: docker
+ append: yes
+ when: ansible_user != 'root'
diff --git a/playbooks/roles/docker-mirror/tasks/install-deps/suse/main.yml b/playbooks/roles/docker-mirror/tasks/install-deps/suse/main.yml
new file mode 100644
index 00000000..346767cd
--- /dev/null
+++ b/playbooks/roles/docker-mirror/tasks/install-deps/suse/main.yml
@@ -0,0 +1,33 @@
+---
+# Install Docker on SUSE/openSUSE
+
+- name: Install Docker
+ become: true
+ ansible.builtin.zypper:
+ name:
+ - docker
+ - docker-compose
+ state: present
+
+- name: Install Python Docker library
+ become: true
+ ansible.builtin.zypper:
+ name:
+ - python3-docker-py
+ state: present
+
+- name: Start and enable Docker service
+ become: true
+ ansible.builtin.systemd:
+ name: docker
+ state: started
+ enabled: yes
+ daemon_reload: yes
+
+- name: Add current user to docker group
+ become: true
+ ansible.builtin.user:
+ name: "{{ ansible_user }}"
+ groups: docker
+ append: yes
+ when: ansible_user != 'root'
diff --git a/playbooks/roles/docker-mirror/tasks/main.yml b/playbooks/roles/docker-mirror/tasks/main.yml
new file mode 100644
index 00000000..3781adf3
--- /dev/null
+++ b/playbooks/roles/docker-mirror/tasks/main.yml
@@ -0,0 +1,186 @@
+---
+# SPDX-License-Identifier: copyleft-next-0.3.1
+
+- name: Check if Docker mirror directory exists
+ ansible.builtin.stat:
+ path: "{{ docker_mirror_path | default('/mirror/docker') }}"
+ register: docker_mirror_dir
+ tags: ["docker-mirror", "docker-mirror-status"]
+
+- name: Check if Docker is installed
+ ansible.builtin.command: docker --version
+ register: docker_installed
+ ignore_errors: true
+ changed_when: false
+ tags: ["docker-mirror"]
+
+- name: Install Docker dependencies
+ ansible.builtin.include_tasks: install-deps/main.yml
+ when:
+ - (install_docker_mirror | default(false) == 'y') or (install_docker_mirror | default(false) | bool)
+ - docker_installed.rc != 0
+ tags: ["docker-mirror"]
+
+- name: Create Docker mirror directory structure
+ become: true
+ ansible.builtin.file:
+ path: "{{ item }}"
+ state: directory
+ mode: '0755'
+ loop:
+ - "{{ docker_mirror_path | default('/mirror/docker') }}"
+ - "{{ docker_mirror_path | default('/mirror/docker') }}/registry"
+ - "{{ docker_mirror_path | default('/mirror/docker') }}/config"
+ - "{{ docker_mirror_path | default('/mirror/docker') }}/images"
+ - "{{ docker_mirror_path | default('/mirror/docker') }}/scripts"
+ - "{{ docker_mirror_path | default('/mirror/docker') }}/logs"
+ when:
+ - (install_docker_mirror | default(false) == 'y') or (install_docker_mirror | default(false) | bool)
+ tags: ["docker-mirror"]
+
+- name: Create Docker registry configuration
+ ansible.builtin.template:
+ src: docker-registry-config.yml.j2
+ dest: "{{ docker_mirror_path | default('/mirror/docker') }}/config/config.yml"
+ mode: '0644'
+ when:
+ - (install_docker_mirror | default(false) == 'y') or (install_docker_mirror | default(false) | bool)
+ tags: ["docker-mirror"]
+
+- name: Check if Docker registry container is running
+ ansible.builtin.command: docker ps -f name=kdevops-docker-mirror --format "{{ '{{' }}.Status{{ '}}' }}"
+ register: registry_status
+ changed_when: false
+ ignore_errors: true
+ tags: ["docker-mirror", "docker-mirror-status"]
+
+- name: Pull registry image
+ ansible.builtin.command: docker pull registry:2
+ when:
+ - (install_docker_mirror | default(false) == 'y') or (install_docker_mirror | default(false) | bool)
+ tags: ["docker-mirror"]
+ register: pull_registry
+ changed_when: "'Downloaded newer image' in pull_registry.stdout or 'Pulling from' in pull_registry.stdout"
+
+- name: Start Docker registry mirror container
+ ansible.builtin.command: |
+ docker run -d \
+ --name kdevops-docker-mirror \
+ --restart=always \
+ -p {{ docker_mirror_port | default(5000) }}:5000 \
+ -v {{ docker_mirror_path | default('/mirror/docker') }}/registry:/var/lib/registry \
+ -v {{ docker_mirror_path | default('/mirror/docker') }}/config/config.yml:/etc/docker/registry/config.yml \
+ -e REGISTRY_HTTP_ADDR=0.0.0.0:5000 \
+ -e REGISTRY_STORAGE_FILESYSTEM_ROOTDIRECTORY=/var/lib/registry \
+ -e REGISTRY_PROXY_REMOTEURL=https://registry-1.docker.io \
+ registry:2
+ when:
+ - (install_docker_mirror | default(false) == 'y') or (install_docker_mirror | default(false) | bool)
+ - registry_status.stdout == "" or "Exited" in registry_status.stdout
+ tags: ["docker-mirror"]
+ register: docker_run_result
+ failed_when: docker_run_result.rc != 0 and "already in use by container" not in docker_run_result.stderr
+ changed_when: docker_run_result.rc == 0
+
+- name: Wait for registry to be ready
+ ansible.builtin.uri:
+ url: "http://localhost:{{ docker_mirror_port | default(5000) }}/v2/"
+ status_code: 200
+ register: registry_ready
+ retries: 10
+ delay: 2
+ until: registry_ready.status == 200
+ when:
+ - (install_docker_mirror | default(false) == 'y') or (install_docker_mirror | default(false) | bool)
+ tags: ["docker-mirror"]
+
+- name: Display Docker mirror status
+ ansible.builtin.debug:
+ msg: |
+ Docker Mirror Status:
+ - Directory exists: {{ docker_mirror_dir.stat.exists | default(false) }}
+ - Registry running: {{ registry_status.stdout != "" and "Up" in registry_status.stdout }}
+ - Registry URL: http://localhost:{{ docker_mirror_port | default(5000) }}
+ - Mirror path: {{ docker_mirror_path | default('/mirror/docker') }}
+ tags: ["docker-mirror-status"]
+
+- name: Create Docker images list from workflows
+ ansible.builtin.template:
+ src: docker-images-list.txt.j2
+ dest: "{{ docker_mirror_path | default('/mirror/docker') }}/images/workflow-images.txt"
+ mode: '0644'
+ when:
+ - (install_docker_mirror | default(false) == 'y') or (install_docker_mirror | default(false) | bool)
+ tags: ["docker-mirror", "docker-mirror-pull"]
+
+- name: Pull and cache Docker images (manual only)
+ ansible.builtin.include_tasks: pull_images.yml
+ when:
+ - (install_docker_mirror | default(false) == 'y') or (install_docker_mirror | default(false) | bool)
+ tags: ["docker-mirror-pull"]
+
+- name: Create update script from template
+ become: true
+ ansible.builtin.template:
+ src: update-docker-images.sh.j2
+ dest: "{{ docker_mirror_path | default('/mirror/docker') }}/scripts/update-docker-images.sh"
+ mode: '0755'
+ when:
+ - (install_docker_mirror | default(false) == 'y') or (install_docker_mirror | default(false) | bool)
+ tags: ["docker-mirror"]
+
+- name: Install systemd service for Docker mirror updates
+ become: true
+ ansible.builtin.template:
+ src: docker-mirror-update.service.j2
+ dest: /etc/systemd/system/docker-mirror-update.service
+ mode: '0644'
+ when:
+ - (install_docker_mirror | default(false) == 'y') or (install_docker_mirror | default(false) | bool)
+ tags: ["docker-mirror"]
+
+- name: Install systemd timer for Docker mirror updates
+ become: true
+ ansible.builtin.template:
+ src: docker-mirror-update.timer.j2
+ dest: /etc/systemd/system/docker-mirror-update.timer
+ mode: '0644'
+ when:
+ - (install_docker_mirror | default(false) == 'y') or (install_docker_mirror | default(false) | bool)
+ tags: ["docker-mirror"]
+
+- name: Reload systemd daemon
+ become: true
+ ansible.builtin.systemd:
+ daemon_reload: yes
+ when:
+ - (install_docker_mirror | default(false) == 'y') or (install_docker_mirror | default(false) | bool)
+ tags: ["docker-mirror"]
+
+- name: Enable and start Docker mirror update timer
+ become: true
+ ansible.builtin.systemd:
+ name: docker-mirror-update.timer
+ enabled: yes
+ state: started
+ when:
+ - (install_docker_mirror | default(false) == 'y') or (install_docker_mirror | default(false) | bool)
+ tags: ["docker-mirror"]
+
+- name: Display Docker mirror setup completion
+ ansible.builtin.debug:
+ msg: |
+ Docker mirror setup complete!
+
+ Registry running at: http://localhost:{{ docker_mirror_port | default(5000) }}
+ Mirror directory: {{ docker_mirror_path | default('/mirror/docker') }}
+
+ Images are being downloaded in the background via systemd timer.
+ Check status with: systemctl status docker-mirror-update.service
+ View logs with: journalctl -u docker-mirror-update.service -f
+
+ The initial download will start immediately and may take several minutes
+ depending on your internet connection speed.
+ when:
+ - (install_docker_mirror | default(false) == 'y') or (install_docker_mirror | default(false) | bool)
+ tags: ["docker-mirror"]
diff --git a/playbooks/roles/docker-mirror/tasks/pull_images.yml b/playbooks/roles/docker-mirror/tasks/pull_images.yml
new file mode 100644
index 00000000..0a77506e
--- /dev/null
+++ b/playbooks/roles/docker-mirror/tasks/pull_images.yml
@@ -0,0 +1,68 @@
+---
+# Pull and cache Docker images for workflows
+
+- name: Define default Docker images for workflows
+ ansible.builtin.set_fact:
+ docker_mirror_images:
+ # MinIO images
+ - "minio/minio:RELEASE.2023-03-20T20-16-18Z"
+ # Milvus vector database images
+ - "milvusdb/milvus:2.3.0"
+ - "quay.io/coreos/etcd:v3.5.5"
+ # vLLM standard deployment
+ - "vllm/vllm-openai:latest"
+ # LMCache-enabled deployments
+ - "lmcache/vllm-openai:2025-05-27-v1"
+ - "lmcache/vllm-openai:latest-nightly"
+ # Registry itself
+ - "registry:2"
+
+- name: Add workflow-specific images from configuration
+ ansible.builtin.set_fact:
+ docker_mirror_images: "{{ docker_mirror_images + [item] }}"
+ with_items: "{{ docker_mirror_additional_images | default([]) }}"
+ when: docker_mirror_additional_images is defined
+
+- name: Pull Docker images
+ ansible.builtin.command: "docker pull {{ item }}"
+ loop: "{{ docker_mirror_images }}"
+ register: pull_result
+ changed_when: "'Downloaded newer image' in pull_result.stdout or 'Pulling from' in pull_result.stdout"
+ retries: 3
+ delay: 5
+ until: pull_result.rc == 0
+
+- name: Tag images for local registry
+ ansible.builtin.command: |
+ docker tag {{ item }} localhost:{{ docker_mirror_port | default(5000) }}/{{ item | regex_replace('^[^/]+/', '') }}
+ loop: "{{ docker_mirror_images }}"
+ changed_when: true
+
+- name: Push images to local registry
+ ansible.builtin.command: |
+ docker push localhost:{{ docker_mirror_port | default(5000) }}/{{ item | regex_replace('^[^/]+/', '') }}
+ loop: "{{ docker_mirror_images }}"
+ register: push_result
+ changed_when: true
+ retries: 3
+ delay: 5
+ until: push_result.rc == 0
+
+- name: Create manifest file
+ ansible.builtin.template:
+ src: docker-manifest.txt.j2
+ dest: "{{ docker_mirror_path | default('/mirror/docker') }}/images/manifest.txt"
+ mode: '0644'
+
+- name: Display cached images summary
+ ansible.builtin.debug:
+ msg: |
+ Docker images cached successfully:
+ Total images: {{ docker_mirror_images | length }}
+ Registry URL: http://localhost:{{ docker_mirror_port | default(5000) }}
+
+ To use the mirror, configure Docker daemon:
+ {
+ "registry-mirrors": ["http://localhost:{{ docker_mirror_port | default(5000) }}"],
+ "insecure-registries": ["localhost:{{ docker_mirror_port | default(5000) }}"]
+ }
diff --git a/playbooks/roles/docker-mirror/templates/docker-images-list.txt.j2 b/playbooks/roles/docker-mirror/templates/docker-images-list.txt.j2
new file mode 100644
index 00000000..7fee9227
--- /dev/null
+++ b/playbooks/roles/docker-mirror/templates/docker-images-list.txt.j2
@@ -0,0 +1,23 @@
+# Docker images list for kdevops workflows
+# This file is auto-generated from workflow configurations
+
+# MinIO workflow images
+{% if minio_container_image is defined %}
+{{ minio_container_image }}
+{% endif %}
+
+# Milvus workflow images
+{% if ai_vector_db_milvus_container_image_string is defined %}
+{{ ai_vector_db_milvus_container_image_string }}
+{% endif %}
+{% if ai_vector_db_milvus_etcd_container_image_string is defined %}
+{{ ai_vector_db_milvus_etcd_container_image_string }}
+{% endif %}
+{% if ai_vector_db_milvus_minio_container_image_string is defined %}
+{{ ai_vector_db_milvus_minio_container_image_string }}
+{% endif %}
+
+# Additional workflow images
+{% for image in docker_mirror_additional_images | default([]) %}
+{{ image }}
+{% endfor %}
diff --git a/playbooks/roles/docker-mirror/templates/docker-manifest.txt.j2 b/playbooks/roles/docker-mirror/templates/docker-manifest.txt.j2
new file mode 100644
index 00000000..67619e66
--- /dev/null
+++ b/playbooks/roles/docker-mirror/templates/docker-manifest.txt.j2
@@ -0,0 +1,16 @@
+# Docker Images Mirror Manifest
+# Generated: {{ ansible_date_time.iso8601 }}
+# Registry: localhost:{{ docker_mirror_port | default(5000) }}
+#
+# Cached Images:
+{% for image in docker_mirror_images %}
+{{ image }} -> localhost:{{ docker_mirror_port | default(5000) }}/{{ image | regex_replace('^[^/]+/', '') }}
+{% endfor %}
+
+# Total images cached: {{ docker_mirror_images | length }}
+#
+# To use these images, configure your Docker daemon with:
+# {
+# "registry-mirrors": ["http://localhost:{{ docker_mirror_port | default(5000) }}"],
+# "insecure-registries": ["localhost:{{ docker_mirror_port | default(5000) }}"]
+# }
diff --git a/playbooks/roles/docker-mirror/templates/docker-mirror-update.service.j2 b/playbooks/roles/docker-mirror/templates/docker-mirror-update.service.j2
new file mode 100644
index 00000000..963fdb18
--- /dev/null
+++ b/playbooks/roles/docker-mirror/templates/docker-mirror-update.service.j2
@@ -0,0 +1,23 @@
+[Unit]
+Description=Update Docker Mirror Images
+After=network-online.target docker.service
+Wants=network-online.target
+
+[Service]
+Type=oneshot
+ExecStart=/bin/bash {{ docker_mirror_path }}/scripts/update-docker-images.sh
+StandardOutput=journal+console
+StandardError=journal+console
+User=root
+Group=root
+
+# Restart on failure with delay
+Restart=on-failure
+RestartSec=30
+
+# Environment variables
+Environment="MIRROR_DIR={{ docker_mirror_path }}"
+Environment="REGISTRY_PORT={{ docker_mirror_port }}"
+
+[Install]
+WantedBy=multi-user.target
diff --git a/playbooks/roles/docker-mirror/templates/docker-mirror-update.timer.j2 b/playbooks/roles/docker-mirror/templates/docker-mirror-update.timer.j2
new file mode 100644
index 00000000..79b626c8
--- /dev/null
+++ b/playbooks/roles/docker-mirror/templates/docker-mirror-update.timer.j2
@@ -0,0 +1,23 @@
+[Unit]
+Description=Daily Docker Mirror Images Update
+Requires=docker-mirror-update.service
+
+[Timer]
+# Run daily at 2 AM
+OnCalendar=daily
+OnCalendar=*-*-* 02:00:00
+
+# Run immediately when timer is first activated
+OnActiveSec=0
+
+# Run on boot after 10 minutes
+OnBootSec=10min
+
+# If missed, run immediately
+Persistent=true
+
+# Randomize start time by up to 30 minutes to avoid thundering herd
+RandomizedDelaySec=30min
+
+[Install]
+WantedBy=timers.target
diff --git a/playbooks/roles/docker-mirror/templates/docker-registry-config.yml.j2 b/playbooks/roles/docker-mirror/templates/docker-registry-config.yml.j2
new file mode 100644
index 00000000..45cf8897
--- /dev/null
+++ b/playbooks/roles/docker-mirror/templates/docker-registry-config.yml.j2
@@ -0,0 +1,38 @@
+# Docker Registry Configuration for kdevops mirror
+version: 0.1
+log:
+ fields:
+ service: kdevops-docker-mirror
+ level: info
+storage:
+ cache:
+ blobdescriptor: inmemory
+ filesystem:
+ rootdirectory: /var/lib/registry
+ delete:
+ enabled: true
+ maintenance:
+ uploadpurging:
+ enabled: true
+ age: 168h
+ interval: 24h
+ dryrun: false
+http:
+ addr: :5000
+ headers:
+ X-Content-Type-Options: [nosniff]
+ Access-Control-Allow-Origin: ['*']
+ Access-Control-Allow-Methods: ['HEAD', 'GET', 'OPTIONS', 'DELETE']
+ Access-Control-Allow-Headers: ['Authorization', 'Accept']
+ Access-Control-Expose-Headers: ['Docker-Content-Digest']
+health:
+ storagedriver:
+ enabled: true
+ interval: 10s
+ threshold: 3
+{% if docker_mirror_pull_through_cache | default(true) %}
+proxy:
+ remoteurl: https://registry-1.docker.io
+ username: ""
+ password: ""
+{% endif %}
diff --git a/playbooks/roles/docker-mirror/templates/update-docker-images.sh.j2 b/playbooks/roles/docker-mirror/templates/update-docker-images.sh.j2
new file mode 100644
index 00000000..6a91c009
--- /dev/null
+++ b/playbooks/roles/docker-mirror/templates/update-docker-images.sh.j2
@@ -0,0 +1,114 @@
+#!/bin/bash
+# SPDX-License-Identifier: copyleft-next-0.3.1
+#
+# Script to update Docker mirror images
+# Called by systemd timer for regular updates
+
+set -e
+
+MIRROR_DIR="{{ docker_mirror_path }}"
+REGISTRY_PORT="{{ docker_mirror_port }}"
+LOG_FILE="${MIRROR_DIR}/logs/update-$(date +%Y%m%d-%H%M%S).log"
+
+# Create log directory if it doesn't exist
+mkdir -p "${MIRROR_DIR}/logs"
+
+# Redirect output to log file
+exec 1>"${LOG_FILE}"
+exec 2>&1
+
+echo "Starting Docker mirror update at $(date)"
+echo "================================================"
+
+# List of images to update - includes nightly/latest tags
+IMAGES=(
+{% for image in docker_mirror_images_with_updates | default([]) %}
+ "{{ image }}"
+{% endfor %}
+ # MinIO - check for updates
+ "minio/minio:latest"
+ "minio/minio:RELEASE.2023-03-20T20-16-18Z"
+
+ # Milvus - check for updates
+ "milvusdb/milvus:latest"
+ "milvusdb/milvus:2.3.0"
+
+ # vLLM - standard deployment
+ "vllm/vllm-openai:latest"
+
+ # LMCache - stable and nightly builds for KV cache offloading
+ "lmcache/vllm-openai:2025-05-27-v1"
+ "lmcache/vllm-openai:latest-nightly"
+
+ # etcd - stable version
+ "quay.io/coreos/etcd:v3.5.5"
+
+ # Registry itself
+ "registry:2"
+ "registry:latest"
+)
+
+# Function to pull and cache an image
+update_image() {
+ local image="$1"
+ local image_name="${image#*/}"
+ local local_tag="localhost:${REGISTRY_PORT}/${image_name}"
+
+ echo ""
+ echo "Updating: ${image}"
+ echo "----------------------------"
+
+ # Pull the latest version
+ if docker pull "${image}" 2>&1; then
+ echo " ✓ Successfully pulled ${image}"
+
+ # Tag for local registry
+ docker tag "${image}" "${local_tag}"
+
+ # Push to local registry
+ if docker push "${local_tag}" 2>&1; then
+ echo " ✓ Successfully pushed to local registry"
+ else
+ echo " ✗ Failed to push to local registry"
+ return 1
+ fi
+ else
+ echo " ✗ Failed to pull ${image}"
+ return 1
+ fi
+}
+
+# Check if registry is running
+if ! curl -s "http://localhost:${REGISTRY_PORT}/v2/" > /dev/null 2>&1; then
+ echo "ERROR: Docker registry is not running on port ${REGISTRY_PORT}"
+ exit 1
+fi
+
+# Update each image
+SUCCESS_COUNT=0
+FAIL_COUNT=0
+
+for image in "${IMAGES[@]}"; do
+ if update_image "${image}"; then
+ ((SUCCESS_COUNT++))
+ else
+ ((FAIL_COUNT++))
+ fi
+done
+
+# Clean up old images
+echo ""
+echo "Cleaning up old images..."
+docker image prune -f 2>&1 || true
+
+# Summary
+echo ""
+echo "================================================"
+echo "Update completed at $(date)"
+echo "Success: ${SUCCESS_COUNT} images"
+echo "Failed: ${FAIL_COUNT} images"
+
+# Rotate logs (keep last 30 days)
+find "${MIRROR_DIR}/logs" -name "update-*.log" -mtime +30 -delete 2>/dev/null || true
+
+exit 0
diff --git a/playbooks/roles/minio_setup/tasks/main.yml b/playbooks/roles/minio_setup/tasks/main.yml
index db7e3d6d..b3f6f1a5 100644
--- a/playbooks/roles/minio_setup/tasks/main.yml
+++ b/playbooks/roles/minio_setup/tasks/main.yml
@@ -66,6 +66,18 @@
Filesystem: {{ minio_fs_type.stdout }}
Storage Details: {{ minio_fs_details.stdout }}
+- name: Set MinIO container image with Docker mirror if enabled
+ ansible.builtin.set_fact:
+ minio_container_image_final: "localhost:{{ docker_mirror_port | default(5000) }}/{{ minio_container_image | regex_replace('^[^/]+/', '') }}"
+ when:
+ - use_docker_mirror | default(false) | bool
+
+- name: Use default MinIO container image if mirror not enabled
+ ansible.builtin.set_fact:
+ minio_container_image_final: "{{ minio_container_image }}"
+ when:
+ - not (use_docker_mirror | default(false) | bool)
+
- name: Create Docker network for MinIO
community.docker.docker_network:
name: "{{ minio_docker_network }}"
@@ -75,7 +87,7 @@
- name: Start MinIO container
community.docker.docker_container:
name: "{{ minio_container_name }}"
- image: "{{ minio_container_image }}"
+ image: "{{ minio_container_image_final }}"
state: started
restart_policy: unless-stopped
networks:
diff --git a/scripts/check_docker_mirror.sh b/scripts/check_docker_mirror.sh
new file mode 100755
index 00000000..5f85aea4
--- /dev/null
+++ b/scripts/check_docker_mirror.sh
@@ -0,0 +1,87 @@
+#!/bin/bash
+# SPDX-License-Identifier: copyleft-next-0.3.1
+#
+# Check if Docker mirror is available for kconfig defaults
+
+DOCKER_MIRROR_PATH="${DOCKER_MIRROR_PATH:-/mirror/docker}"
+REGISTRY_PORT="${DOCKER_REGISTRY_PORT:-5000}"
+
+# Function to check if Docker registry is running
+check_registry_running() {
+ if command -v docker >/dev/null 2>&1; then
+ docker ps --format "{{.Names}}" 2>/dev/null | grep -q "kdevops-docker-mirror"
+ return $?
+ fi
+ return 1
+}
+
+# Function to check if registry is accessible
+check_registry_accessible() {
+ curl -s -f "http://localhost:${REGISTRY_PORT}/v2/" >/dev/null 2>&1
+ return $?
+}
+
+# Main logic for kconfig
+if [[ -d "$DOCKER_MIRROR_PATH" ]]; then
+ case "$1" in
+ ENABLE_DOCKER_MIRROR)
+ echo "y"
+ exit 0
+ ;;
+
+ USE_DOCKER_MIRROR)
+ # Check if mirror has content and registry is accessible
+ if [[ -d "$DOCKER_MIRROR_PATH/registry" ]]; then
+ if check_registry_accessible; then
+ echo "y"
+ exit 0
+ fi
+ fi
+ ;;
+
+ INSTALL_DOCKER_MIRROR)
+ # Check if this is first run (mirror exists but not fully configured)
+ if [[ ! -d "$DOCKER_MIRROR_PATH/registry" ]]; then
+ echo "y"
+ exit 0
+ fi
+ # If registry exists but not running, suggest install
+ if [[ -d "$DOCKER_MIRROR_PATH/registry" ]] && ! check_registry_running; then
+ echo "y"
+ exit 0
+ fi
+ ;;
+
+ DOCKER_MIRROR_REGISTRY_RUNNING)
+ if check_registry_running; then
+ echo "y"
+ exit 0
+ fi
+ ;;
+
+ DOCKER_MIRROR_EXISTS|DOCKER_MIRROR_STATUS)
+ # Provide user-friendly status output
+ if [[ -d "$DOCKER_MIRROR_PATH" ]]; then
+ echo "Docker mirror directory exists at $DOCKER_MIRROR_PATH"
+ if check_registry_running; then
+ echo "Docker registry container is running"
+ if check_registry_accessible; then
+ echo "Docker registry is accessible at http://localhost:${REGISTRY_PORT}"
+ else
+ echo "Warning: Docker registry container is running but not accessible"
+ fi
+ else
+ echo "Docker registry container is not running"
+ echo "Run 'make docker-mirror' to start the registry"
+ fi
+ else
+ echo "Docker mirror not configured (directory $DOCKER_MIRROR_PATH does not exist)"
+ echo "Run 'make docker-mirror' to set up the Docker mirror"
+ fi
+ exit 0
+ ;;
+ esac
+fi
+
+# Default to no for Kconfig checks
+echo "n"
diff --git a/scripts/docker-mirror-setup.sh b/scripts/docker-mirror-setup.sh
new file mode 100755
index 00000000..808d53a2
--- /dev/null
+++ b/scripts/docker-mirror-setup.sh
@@ -0,0 +1,197 @@
+#!/bin/bash
+# SPDX-License-Identifier: copyleft-next-0.3.1
+#
+# Docker mirror setup script
+# This script sets up a local Docker registry mirror for kdevops workflows
+
+set -e
+
+MIRROR_DIR="${1:-/mirror/docker}"
+REGISTRY_PORT="${2:-5000}"
+REGISTRY_NAME="${3:-kdevops-mirror}"
+
+# Colors for output
+RED='\033[0;31m'
+GREEN='\033[0;32m'
+YELLOW='\033[1;33m'
+NC='\033[0m' # No Color
+
+log_info() {
+ echo -e "${GREEN}[INFO]${NC} $1"
+}
+
+log_warn() {
+ echo -e "${YELLOW}[WARN]${NC} $1"
+}
+
+log_error() {
+ echo -e "${RED}[ERROR]${NC} $1"
+}
+
+# Check if Docker is installed and running
+check_docker() {
+ if ! command -v docker &> /dev/null; then
+ log_error "Docker is not installed. Please install Docker first."
+ exit 1
+ fi
+
+ if ! docker info &> /dev/null; then
+ log_error "Docker daemon is not running or current user lacks permissions."
+ exit 1
+ fi
+}
+
+# Create mirror directory structure
+setup_directories() {
+ log_info "Setting up Docker mirror directories..."
+
+ # Create main mirror directory
+ if [ ! -d "$MIRROR_DIR" ]; then
+ sudo mkdir -p "$MIRROR_DIR"
+ log_info "Created $MIRROR_DIR"
+ fi
+
+ # Create subdirectories for registry data
+ sudo mkdir -p "$MIRROR_DIR/registry"
+ sudo mkdir -p "$MIRROR_DIR/images"
+ sudo mkdir -p "$MIRROR_DIR/config"
+
+ # Set appropriate permissions
+ sudo chmod 755 "$MIRROR_DIR"
+ sudo chmod 755 "$MIRROR_DIR/registry"
+ sudo chmod 755 "$MIRROR_DIR/images"
+ sudo chmod 755 "$MIRROR_DIR/config"
+
+ log_info "Directory structure created successfully"
+}
+
+# Create registry configuration
+create_registry_config() {
+ log_info "Creating registry configuration..."
+
+ cat > /tmp/docker-registry-config.yml << EOF
+version: 0.1
+log:
+ fields:
+ service: registry
+storage:
+ cache:
+ blobdescriptor: inmemory
+ filesystem:
+ rootdirectory: /var/lib/registry
+ delete:
+ enabled: true
+http:
+ addr: :5000
+ headers:
+ X-Content-Type-Options: [nosniff]
+health:
+ storagedriver:
+ enabled: true
+ interval: 10s
+ threshold: 3
+proxy:
+ remoteurl: https://registry-1.docker.io
+EOF
+
+ sudo mv /tmp/docker-registry-config.yml "$MIRROR_DIR/config/config.yml"
+ sudo chmod 644 "$MIRROR_DIR/config/config.yml"
+
+ log_info "Registry configuration created"
+}
+
+# Start the Docker registry mirror
+start_registry() {
+ log_info "Starting Docker registry mirror..."
+
+ # Check if registry is already running
+ if docker ps -a | grep -q "$REGISTRY_NAME"; then
+ log_warn "Registry container '$REGISTRY_NAME' already exists, removing it..."
+ docker stop "$REGISTRY_NAME" 2>/dev/null || true
+ docker rm "$REGISTRY_NAME" 2>/dev/null || true
+ fi
+
+ # Run the registry container
+ docker run -d \
+ --restart=always \
+ --name "$REGISTRY_NAME" \
+ -p "$REGISTRY_PORT:5000" \
+ -v "$MIRROR_DIR/registry:/var/lib/registry" \
+ -v "$MIRROR_DIR/config/config.yml:/etc/docker/registry/config.yml" \
+ registry:2
+
+ # Wait for registry to be ready
+ log_info "Waiting for registry to be ready..."
+ sleep 5
+
+ if curl -s "http://localhost:$REGISTRY_PORT/v2/" > /dev/null; then
+ log_info "Registry mirror is running on port $REGISTRY_PORT"
+ else
+ log_error "Failed to start registry mirror"
+ exit 1
+ fi
+}
+
+# Configure Docker daemon to use mirror
+configure_docker_daemon() {
+ log_info "Configuring Docker daemon to use mirror..."
+
+ # Check if daemon.json exists
+ DAEMON_CONFIG="/etc/docker/daemon.json"
+
+ if [ -f "$DAEMON_CONFIG" ]; then
+ log_warn "Docker daemon.json already exists, creating backup..."
+ sudo cp "$DAEMON_CONFIG" "$DAEMON_CONFIG.bak.$(date +%Y%m%d_%H%M%S)"
+ fi
+
+ # Create or update daemon configuration
+ cat > /tmp/docker-daemon.json << EOF
+{
+ "registry-mirrors": ["http://localhost:$REGISTRY_PORT"],
+ "insecure-registries": ["localhost:$REGISTRY_PORT"]
+}
+EOF
+
+ # If existing config exists, we need to merge
+ if [ -f "$DAEMON_CONFIG" ]; then
+ log_info "Merging with existing Docker daemon configuration..."
+ # This is simplified - in production you'd want proper JSON merging
+ log_warn "Manual merge may be required. New configuration saved to /tmp/docker-daemon.json"
+ else
+ sudo mv /tmp/docker-daemon.json "$DAEMON_CONFIG"
+ sudo chmod 644 "$DAEMON_CONFIG"
+
+ # Restart Docker daemon
+ log_info "Restarting Docker daemon..."
+ sudo systemctl restart docker
+ fi
+}
+
+# Main execution
+main() {
+ log_info "Docker Mirror Setup for kdevops"
+ log_info "==============================="
+
+ check_docker
+ setup_directories
+ create_registry_config
+ start_registry
+
+ log_info ""
+ log_info "Docker registry mirror setup complete!"
+ log_info "Registry URL: http://localhost:$REGISTRY_PORT"
+ log_info "Mirror directory: $MIRROR_DIR"
+ log_info ""
+ log_info "To configure Docker daemon to use this mirror, run:"
+ log_info " $0 --configure-daemon"
+ log_info ""
+ log_info "To pull and cache images, use the mirror-docker-images.sh script"
+}
+
+# Handle command line arguments
+if [ "$1" == "--configure-daemon" ]; then
+ configure_docker_daemon
+ exit 0
+fi
+
+main "$@"
diff --git a/scripts/mirror-docker-images.sh b/scripts/mirror-docker-images.sh
new file mode 100755
index 00000000..12079974
--- /dev/null
+++ b/scripts/mirror-docker-images.sh
@@ -0,0 +1,247 @@
+#!/bin/bash
+# SPDX-License-Identifier: copyleft-next-0.3.1
+#
+# Script to pull and cache Docker images for kdevops workflows
+# This script can be run periodically to keep the mirror updated
+
+set -e
+
+MIRROR_DIR="${MIRROR_DIR:-/mirror/docker}"
+REGISTRY_PORT="${REGISTRY_PORT:-5000}"
+IMAGES_LIST="${1:-}"
+
+# Default images used by kdevops workflows
+DEFAULT_IMAGES=(
+ # MinIO images
+ "minio/minio:RELEASE.2023-03-20T20-16-18Z"
+
+ # Milvus vector database images
+ "milvusdb/milvus:2.3.0"
+ "quay.io/coreos/etcd:v3.5.5"
+
+ # vLLM standard deployment
+ "vllm/vllm-openai:latest"
+
+ # LMCache-enabled deployments (KV cache offloading, disaggregated prefill)
+ "lmcache/vllm-openai:2025-05-27-v1"
+ "lmcache/vllm-openai:latest-nightly"
+
+ # Registry itself
+ "registry:2"
+)
+
+# Colors for output
+GREEN='\033[0;32m'
+YELLOW='\033[1;33m'
+RED='\033[0;31m'
+NC='\033[0m' # No Color
+
+log_info() {
+ echo -e "${GREEN}[INFO]${NC} $1"
+}
+
+log_warn() {
+ echo -e "${YELLOW}[WARN]${NC} $1"
+}
+
+log_error() {
+ echo -e "${RED}[ERROR]${NC} $1"
+}
+
+# Check if registry is running
+check_registry() {
+ if ! curl -s "http://localhost:$REGISTRY_PORT/v2/" > /dev/null 2>&1; then
+ log_error "Docker registry mirror is not running on port $REGISTRY_PORT"
+ log_info "Please run docker-mirror-setup.sh first"
+ exit 1
+ fi
+}
+
+# Pull and tag image for local registry
+mirror_image() {
+ local image="$1"
+ local image_name="${image#*/}" # Remove registry prefix if present
+ local local_tag="localhost:$REGISTRY_PORT/$image_name"
+
+ log_info "Mirroring $image..."
+
+ # Pull from Docker Hub or original registry
+ if docker pull "$image"; then
+ log_info "Successfully pulled $image"
+
+ # Tag for local registry
+ docker tag "$image" "$local_tag"
+
+ # Push to local registry
+ if docker push "$local_tag"; then
+ log_info "Successfully pushed to local registry: $local_tag"
+
+ # Save image info to manifest
+ echo "$image -> $local_tag" >> "$MIRROR_DIR/images/manifest.txt"
+ else
+ log_error "Failed to push $image to local registry"
+ return 1
+ fi
+ else
+ log_error "Failed to pull $image"
+ return 1
+ fi
+}
+
+# Save images to tar archives for offline use
+save_image_archive() {
+ local image="$1"
+ local archive_dir="$MIRROR_DIR/images/archives"
+
+ mkdir -p "$archive_dir"
+
+ local image_file=$(echo "$image" | sed 's/[:/]/_/g').tar
+ local archive_path="$archive_dir/$image_file"
+
+ log_info "Saving $image to archive..."
+
+ if docker save "$image" -o "$archive_path"; then
+ # Compress the archive
+ gzip -f "$archive_path"
+ log_info "Saved and compressed: ${archive_path}.gz"
+
+ # Generate checksum
+ sha256sum "${archive_path}.gz" > "${archive_path}.gz.sha256"
+ else
+ log_error "Failed to save $image to archive"
+ return 1
+ fi
+}
+
+# Load custom images list from file
+load_images_list() {
+ local list_file="$1"
+
+ if [ -f "$list_file" ]; then
+ log_info "Loading images from $list_file"
+ while IFS= read -r image; do
+ # Skip comments and empty lines
+ [[ "$image" =~ ^#.*$ ]] && continue
+ [[ -z "$image" ]] && continue
+
+ DEFAULT_IMAGES+=("$image")
+ done < "$list_file"
+ fi
+}
+
+# Scan kdevops configuration for Docker images
+scan_for_images() {
+ log_info "Scanning kdevops configuration for Docker images..."
+
+ local found_images=()
+
+ # Search for Docker image strings in ansible roles
+ while IFS= read -r line; do
+ if [[ "$line" =~ \"([^\"]+\/(.*):.*)\"|\'([^\']+\/(.*):.*)\' ]]; then
+ local image="${BASH_REMATCH[1]}"
+ if [ -z "$image" ]; then
+ image="${BASH_REMATCH[3]}"
+ fi
+ if [ -n "$image" ] && [[ ! " ${found_images[@]} " =~ " ${image} " ]]; then
+ found_images+=("$image")
+ log_info "Found image: $image"
+ fi
+ fi
+ done < <(grep -h "container_image\|docker_image" "$MIRROR_DIR/../playbooks/roles/"**/defaults/*.yml 2>/dev/null)
+
+ # Add found images to the list
+ for image in "${found_images[@]}"; do
+ if [[ ! " ${DEFAULT_IMAGES[@]} " =~ " ${image} " ]]; then
+ DEFAULT_IMAGES+=("$image")
+ fi
+ done
+}
+
+# Create manifest file
+create_manifest() {
+ local manifest_file="$MIRROR_DIR/images/manifest.txt"
+
+ log_info "Creating manifest file..."
+
+ echo "# Docker Images Mirror Manifest" > "$manifest_file"
+ echo "# Generated: $(date)" >> "$manifest_file"
+ echo "# Registry: localhost:$REGISTRY_PORT" >> "$manifest_file"
+ echo "" >> "$manifest_file"
+}
+
+# Main execution
+main() {
+ log_info "Docker Images Mirror for kdevops"
+ log_info "================================="
+
+ check_registry
+
+ # Create manifest
+ mkdir -p "$MIRROR_DIR/images"
+ create_manifest
+
+ # Load custom images list if provided
+ if [ -n "$IMAGES_LIST" ]; then
+ load_images_list "$IMAGES_LIST"
+ fi
+
+ # Optionally scan for images
+ if [ "$2" == "--scan" ]; then
+ scan_for_images
+ fi
+
+ # Mirror all images
+ local success=0
+ local failed=0
+
+ for image in "${DEFAULT_IMAGES[@]}"; do
+ if mirror_image "$image"; then
+ ((success++))
+
+ # Optionally save to archive
+ if [ "$2" == "--archive" ] || [ "$3" == "--archive" ]; then
+ save_image_archive "$image"
+ fi
+ else
+ ((failed++))
+ fi
+ done
+
+ log_info ""
+ log_info "Mirror operation complete!"
+ log_info "Successfully mirrored: $success images"
+
+ if [ $failed -gt 0 ]; then
+ log_warn "Failed to mirror: $failed images"
+ fi
+
+ log_info ""
+ log_info "Images are available at: localhost:$REGISTRY_PORT"
+ log_info "Manifest file: $MIRROR_DIR/images/manifest.txt"
+
+ if [ -d "$MIRROR_DIR/images/archives" ]; then
+ log_info "Archive files: $MIRROR_DIR/images/archives/"
+ fi
+}
+
+# Show usage
+show_usage() {
+ echo "Usage: $0 [images-list-file] [options]"
+ echo ""
+ echo "Options:"
+ echo " --scan Scan kdevops configuration for Docker images"
+ echo " --archive Save images to compressed tar archives"
+ echo " --help Show this help message"
+ echo ""
+ echo "Environment variables:"
+ echo " MIRROR_DIR Mirror directory (default: /mirror/docker)"
+ echo " REGISTRY_PORT Registry port (default: 5000)"
+}
+
+# Handle help option
+if [ "$1" == "--help" ]; then
+ show_usage
+ exit 0
+fi
+
+main "$@"
--
2.51.0
next reply other threads:[~2025-09-26 1:32 UTC|newest]
Thread overview: 2+ messages / expand[flat|nested] mbox.gz Atom feed top
2025-09-26 1:32 Luis Chamberlain [this message]
2025-10-03 0:48 ` [PATCH] Add Docker registry mirror support for kdevops workflows Luis Chamberlain
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20250926013236.3502092-1-mcgrof@kernel.org \
--to=mcgrof@kernel.org \
--cc=cel@kernel.org \
--cc=da.gomez@kruces.com \
--cc=kdevops@lists.linux.dev \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox