diff options
| author | Bruce Ashfield <bruce.ashfield@gmail.com> | 2026-01-06 20:42:46 +0000 |
|---|---|---|
| committer | Bruce Ashfield <bruce.ashfield@gmail.com> | 2026-02-09 03:32:52 +0000 |
| commit | 18074e0efe255a43ab9155171d08aa6e0d736b5f (patch) | |
| tree | 25411491f3a6c3ed7b3460a0bd9f3d4d1c561bc6 /recipes-containers/vcontainer/files | |
| parent | c32db56912b06a89490fda5d554468dbc12a39a2 (diff) | |
| download | meta-virtualization-18074e0efe255a43ab9155171d08aa6e0d736b5f.tar.gz | |
vcontainer: add vdkr Docker support
Add vdkr - Docker CLI wrapper for cross-architecture container operations:
Scripts:
- vdkr.sh: Docker CLI entry point (vdkr-x86_64, vdkr-aarch64)
- vdkr-init.sh: Docker init script for QEMU guest
Recipes:
- vdkr-native: Installs vdkr CLI wrappers
- vdkr-rootfs-image: Builds Docker rootfs with containerd, runc, skopeo
- vdkr-initramfs-create: Creates bootable initramfs blob
The vdkr CLI provides Docker-compatible commands executed inside a
QEMU-emulated environment, enabling cross-architecture container
operations during Yocto builds.
Signed-off-by: Bruce Ashfield <bruce.ashfield@gmail.com>
Diffstat (limited to 'recipes-containers/vcontainer/files')
4 files changed, 315 insertions, 0 deletions
diff --git a/recipes-containers/vcontainer/files/blobs/vdkr/aarch64/README b/recipes-containers/vcontainer/files/blobs/vdkr/aarch64/README new file mode 100644 index 00000000..cabb5100 --- /dev/null +++ b/recipes-containers/vcontainer/files/blobs/vdkr/aarch64/README | |||
| @@ -0,0 +1,26 @@ | |||
| 1 | vdkr aarch64 Blobs | ||
| 2 | ==================== | ||
| 3 | |||
| 4 | This directory should contain the boot blobs for vdkr aarch64: | ||
| 5 | |||
| 6 | Required files: | ||
| 7 | - Image : Linux kernel for aarch64 | ||
| 8 | - initramfs.cpio.gz : Tiny initramfs for switch_root | ||
| 9 | - rootfs.img : Ext4 root filesystem with Docker tools | ||
| 10 | |||
| 11 | Build instructions: | ||
| 12 | 1. Set MACHINE for aarch64: | ||
| 13 | MACHINE=qemuarm64 | ||
| 14 | |||
| 15 | 2. Build the blobs: | ||
| 16 | bitbake vdkr-initramfs-create | ||
| 17 | |||
| 18 | 3. Copy from deploy directory: | ||
| 19 | cp tmp/deploy/images/qemuarm64/vdkr/aarch64/Image . | ||
| 20 | cp tmp/deploy/images/qemuarm64/vdkr/aarch64/initramfs.cpio.gz . | ||
| 21 | cp tmp/deploy/images/qemuarm64/vdkr/aarch64/rootfs.img . | ||
| 22 | |||
| 23 | Once these files are present, vdkr-native will automatically include them. | ||
| 24 | |||
| 25 | Alternatively, set VDKR_USE_DEPLOY = "1" in local.conf to use blobs | ||
| 26 | directly from DEPLOY_DIR without copying to the layer. | ||
diff --git a/recipes-containers/vcontainer/files/blobs/vdkr/x86_64/README b/recipes-containers/vcontainer/files/blobs/vdkr/x86_64/README new file mode 100644 index 00000000..6902f729 --- /dev/null +++ b/recipes-containers/vcontainer/files/blobs/vdkr/x86_64/README | |||
| @@ -0,0 +1,26 @@ | |||
| 1 | vdkr x86_64 Blobs | ||
| 2 | =================== | ||
| 3 | |||
| 4 | This directory should contain the boot blobs for vdkr x86_64: | ||
| 5 | |||
| 6 | Required files: | ||
| 7 | - bzImage : Linux kernel for x86_64 | ||
| 8 | - initramfs.cpio.gz : Tiny initramfs for switch_root | ||
| 9 | - rootfs.img : Ext4 root filesystem with Docker tools | ||
| 10 | |||
| 11 | Build instructions: | ||
| 12 | 1. Set MACHINE for x86_64: | ||
| 13 | MACHINE=qemux86-64 | ||
| 14 | |||
| 15 | 2. Build the blobs: | ||
| 16 | bitbake vdkr-initramfs-create | ||
| 17 | |||
| 18 | 3. Copy from deploy directory: | ||
| 19 | cp tmp/deploy/images/qemux86-64/vdkr/x86_64/bzImage . | ||
| 20 | cp tmp/deploy/images/qemux86-64/vdkr/x86_64/initramfs.cpio.gz . | ||
| 21 | cp tmp/deploy/images/qemux86-64/vdkr/x86_64/rootfs.img . | ||
| 22 | |||
| 23 | Once these files are present, vdkr-native will automatically include them. | ||
| 24 | |||
| 25 | Alternatively, set VDKR_USE_DEPLOY = "1" in local.conf to use blobs | ||
| 26 | directly from DEPLOY_DIR without copying to the layer. | ||
diff --git a/recipes-containers/vcontainer/files/vdkr-init.sh b/recipes-containers/vcontainer/files/vdkr-init.sh new file mode 100755 index 00000000..7cfe12af --- /dev/null +++ b/recipes-containers/vcontainer/files/vdkr-init.sh | |||
| @@ -0,0 +1,238 @@ | |||
| 1 | #!/bin/sh | ||
| 2 | # SPDX-FileCopyrightText: Copyright (C) 2025 Bruce Ashfield | ||
| 3 | # | ||
| 4 | # SPDX-License-Identifier: GPL-2.0-only | ||
| 5 | # | ||
| 6 | # vdkr-init.sh | ||
| 7 | # Init script for vdkr: execute arbitrary docker commands in QEMU | ||
| 8 | # | ||
| 9 | # This script runs on a real ext4 filesystem after switch_root from initramfs. | ||
| 10 | # The preinit script mounted /dev/vda (rootfs.img) and did switch_root to us. | ||
| 11 | # | ||
| 12 | # Drive layout (rootfs.img is always /dev/vda, mounted as /): | ||
| 13 | # /dev/vda = rootfs.img (this script runs from here, mounted as /) | ||
| 14 | # /dev/vdb = input disk (optional, OCI/tar/dir data) | ||
| 15 | # /dev/vdc = state disk (optional, persistent Docker storage) | ||
| 16 | # | ||
| 17 | # Kernel parameters: | ||
| 18 | # docker_cmd=<base64> Base64-encoded docker command + args | ||
| 19 | # docker_input=<type> Input type: none, oci, tar, dir (default: none) | ||
| 20 | # docker_output=<type> Output type: text, tar, storage (default: text) | ||
| 21 | # docker_state=<type> State type: none, disk (default: none) | ||
| 22 | # docker_network=1 Enable networking (configure eth0, DNS) | ||
| 23 | # | ||
| 24 | # Version: 2.3.0 | ||
| 25 | |||
| 26 | # Set runtime-specific parameters before sourcing common code | ||
| 27 | VCONTAINER_RUNTIME_NAME="vdkr" | ||
| 28 | VCONTAINER_RUNTIME_CMD="docker" | ||
| 29 | VCONTAINER_RUNTIME_PREFIX="docker" | ||
| 30 | VCONTAINER_STATE_DIR="/var/lib/docker" | ||
| 31 | VCONTAINER_SHARE_NAME="vdkr_share" | ||
| 32 | VCONTAINER_VERSION="2.3.0" | ||
| 33 | |||
| 34 | # Source common init functions | ||
| 35 | # When installed as /init, common file is at /vcontainer-init-common.sh | ||
| 36 | . /vcontainer-init-common.sh | ||
| 37 | |||
| 38 | # ============================================================================ | ||
| 39 | # Docker-Specific Functions | ||
| 40 | # ============================================================================ | ||
| 41 | |||
| 42 | setup_docker_storage() { | ||
| 43 | mkdir -p /run/containerd /run/lock | ||
| 44 | mkdir -p /var/lib/docker | ||
| 45 | mkdir -p /var/lib/containerd | ||
| 46 | |||
| 47 | # Handle Docker storage | ||
| 48 | if [ -n "$STATE_DISK" ] && [ -b "$STATE_DISK" ]; then | ||
| 49 | log "Mounting state disk $STATE_DISK as /var/lib/docker..." | ||
| 50 | if mount -t ext4 "$STATE_DISK" /var/lib/docker 2>&1; then | ||
| 51 | log "SUCCESS: Mounted $STATE_DISK as Docker storage" | ||
| 52 | log "Docker storage contents:" | ||
| 53 | [ "$QUIET_BOOT" = "0" ] && ls -la /var/lib/docker/ 2>/dev/null || log "(empty)" | ||
| 54 | else | ||
| 55 | log "WARNING: Failed to mount state disk, using tmpfs" | ||
| 56 | RUNTIME_STATE="none" | ||
| 57 | fi | ||
| 58 | fi | ||
| 59 | |||
| 60 | # If no state disk, use tmpfs for Docker storage | ||
| 61 | if [ "$RUNTIME_STATE" != "disk" ]; then | ||
| 62 | log "Using tmpfs for Docker storage (ephemeral)..." | ||
| 63 | mount -t tmpfs -o size=1G tmpfs /var/lib/docker | ||
| 64 | fi | ||
| 65 | } | ||
| 66 | |||
| 67 | start_containerd() { | ||
| 68 | CONTAINERD_READY=false | ||
| 69 | if [ -x "/usr/bin/containerd" ]; then | ||
| 70 | log "Starting containerd..." | ||
| 71 | mkdir -p /var/lib/containerd | ||
| 72 | mkdir -p /run/containerd | ||
| 73 | /usr/bin/containerd --log-level info --root /var/lib/containerd --state /run/containerd >/tmp/containerd.log 2>&1 & | ||
| 74 | CONTAINERD_PID=$! | ||
| 75 | # Wait for containerd socket | ||
| 76 | for i in 1 2 3 4 5 6 7 8 9 10; do | ||
| 77 | if [ -S /run/containerd/containerd.sock ]; then | ||
| 78 | log "Containerd running (PID: $CONTAINERD_PID)" | ||
| 79 | CONTAINERD_READY=true | ||
| 80 | break | ||
| 81 | fi | ||
| 82 | sleep 1 | ||
| 83 | done | ||
| 84 | if [ "$CONTAINERD_READY" != "true" ]; then | ||
| 85 | log "WARNING: Containerd failed to start, check /tmp/containerd.log" | ||
| 86 | [ -f /tmp/containerd.log ] && cat /tmp/containerd.log >&2 | ||
| 87 | fi | ||
| 88 | fi | ||
| 89 | } | ||
| 90 | |||
| 91 | start_dockerd() { | ||
| 92 | log "Starting Docker daemon..." | ||
| 93 | DOCKER_OPTS="--data-root=/var/lib/docker" | ||
| 94 | DOCKER_OPTS="$DOCKER_OPTS --storage-driver=overlay2" | ||
| 95 | DOCKER_OPTS="$DOCKER_OPTS --iptables=false" | ||
| 96 | DOCKER_OPTS="$DOCKER_OPTS --userland-proxy=false" | ||
| 97 | DOCKER_OPTS="$DOCKER_OPTS --bridge=none" | ||
| 98 | DOCKER_OPTS="$DOCKER_OPTS --host=unix:///var/run/docker.sock" | ||
| 99 | DOCKER_OPTS="$DOCKER_OPTS --exec-opt native.cgroupdriver=cgroupfs" | ||
| 100 | DOCKER_OPTS="$DOCKER_OPTS --log-level=info" | ||
| 101 | |||
| 102 | if [ "$CONTAINERD_READY" = "true" ]; then | ||
| 103 | DOCKER_OPTS="$DOCKER_OPTS --containerd=/run/containerd/containerd.sock" | ||
| 104 | fi | ||
| 105 | |||
| 106 | /usr/bin/dockerd $DOCKER_OPTS >/dev/null 2>&1 & | ||
| 107 | DOCKER_PID=$! | ||
| 108 | log "Docker daemon started (PID: $DOCKER_PID)" | ||
| 109 | |||
| 110 | # Wait for Docker to be ready | ||
| 111 | log "Waiting for Docker daemon..." | ||
| 112 | DOCKER_READY=false | ||
| 113 | |||
| 114 | sleep 5 | ||
| 115 | |||
| 116 | for i in $(seq 1 60); do | ||
| 117 | if ! kill -0 $DOCKER_PID 2>/dev/null; then | ||
| 118 | echo "===ERROR===" | ||
| 119 | echo "Docker daemon died after $i iterations" | ||
| 120 | cat /var/log/docker.log 2>/dev/null || true | ||
| 121 | dmesg | tail -20 2>/dev/null || true | ||
| 122 | sleep 2 | ||
| 123 | reboot -f | ||
| 124 | fi | ||
| 125 | |||
| 126 | if /usr/bin/docker info >/dev/null 2>&1; then | ||
| 127 | log "Docker daemon is ready!" | ||
| 128 | DOCKER_READY=true | ||
| 129 | break | ||
| 130 | fi | ||
| 131 | |||
| 132 | log "Waiting... ($i/60)" | ||
| 133 | sleep 2 | ||
| 134 | done | ||
| 135 | |||
| 136 | if [ "$DOCKER_READY" != "true" ]; then | ||
| 137 | echo "===ERROR===" | ||
| 138 | echo "Docker failed to start" | ||
| 139 | sleep 2 | ||
| 140 | reboot -f | ||
| 141 | fi | ||
| 142 | } | ||
| 143 | |||
| 144 | stop_runtime_daemons() { | ||
| 145 | # Stop Docker daemon | ||
| 146 | if [ -n "$DOCKER_PID" ]; then | ||
| 147 | log "Stopping Docker daemon..." | ||
| 148 | kill $DOCKER_PID 2>/dev/null || true | ||
| 149 | for i in $(seq 1 10); do | ||
| 150 | if ! kill -0 $DOCKER_PID 2>/dev/null; then | ||
| 151 | log "Docker daemon stopped" | ||
| 152 | break | ||
| 153 | fi | ||
| 154 | sleep 1 | ||
| 155 | done | ||
| 156 | fi | ||
| 157 | |||
| 158 | # Stop containerd | ||
| 159 | if [ -n "$CONTAINERD_PID" ]; then | ||
| 160 | log "Stopping containerd..." | ||
| 161 | kill $CONTAINERD_PID 2>/dev/null || true | ||
| 162 | sleep 2 | ||
| 163 | fi | ||
| 164 | } | ||
| 165 | |||
| 166 | handle_storage_output() { | ||
| 167 | echo "Stopping Docker gracefully..." | ||
| 168 | /usr/bin/docker system prune -f >/dev/null 2>&1 || true | ||
| 169 | kill $DOCKER_PID 2>/dev/null || true | ||
| 170 | [ -n "$CONTAINERD_PID" ] && kill $CONTAINERD_PID 2>/dev/null || true | ||
| 171 | sleep 3 | ||
| 172 | |||
| 173 | echo "Packaging Docker storage..." | ||
| 174 | cd /var/lib | ||
| 175 | tar -cf /tmp/storage.tar docker/ | ||
| 176 | |||
| 177 | STORAGE_SIZE=$(stat -c%s /tmp/storage.tar 2>/dev/null || echo "0") | ||
| 178 | echo "Storage size: $STORAGE_SIZE bytes" | ||
| 179 | |||
| 180 | if [ "$STORAGE_SIZE" -gt 1000 ]; then | ||
| 181 | dmesg -n 1 | ||
| 182 | echo "===STORAGE_START===" | ||
| 183 | base64 /tmp/storage.tar | ||
| 184 | echo "===STORAGE_END===" | ||
| 185 | echo "===EXIT_CODE=$EXEC_EXIT_CODE===" | ||
| 186 | else | ||
| 187 | echo "===ERROR===" | ||
| 188 | echo "Storage too small" | ||
| 189 | fi | ||
| 190 | } | ||
| 191 | |||
| 192 | # ============================================================================ | ||
| 193 | # Main | ||
| 194 | # ============================================================================ | ||
| 195 | |||
| 196 | # Initialize base environment | ||
| 197 | setup_base_environment | ||
| 198 | mount_base_filesystems | ||
| 199 | |||
| 200 | # Check for quiet boot mode | ||
| 201 | check_quiet_boot | ||
| 202 | |||
| 203 | log "=== vdkr Init ===" | ||
| 204 | log "Version: $VCONTAINER_VERSION" | ||
| 205 | |||
| 206 | # Mount tmpfs directories and cgroups | ||
| 207 | mount_tmpfs_dirs | ||
| 208 | setup_cgroups | ||
| 209 | |||
| 210 | # Parse kernel command line | ||
| 211 | parse_cmdline | ||
| 212 | |||
| 213 | # Detect and configure disks | ||
| 214 | detect_disks | ||
| 215 | |||
| 216 | # Set up Docker storage (Docker-specific) | ||
| 217 | setup_docker_storage | ||
| 218 | |||
| 219 | # Mount input disk | ||
| 220 | mount_input_disk | ||
| 221 | |||
| 222 | # Configure networking | ||
| 223 | configure_networking | ||
| 224 | |||
| 225 | # Start containerd and dockerd (Docker-specific) | ||
| 226 | start_containerd | ||
| 227 | start_dockerd | ||
| 228 | |||
| 229 | # Handle daemon mode or single command execution | ||
| 230 | if [ "$RUNTIME_DAEMON" = "1" ]; then | ||
| 231 | run_daemon_mode | ||
| 232 | else | ||
| 233 | prepare_input_path | ||
| 234 | execute_command | ||
| 235 | fi | ||
| 236 | |||
| 237 | # Graceful shutdown | ||
| 238 | graceful_shutdown | ||
diff --git a/recipes-containers/vcontainer/files/vdkr.sh b/recipes-containers/vcontainer/files/vdkr.sh new file mode 100755 index 00000000..818c831e --- /dev/null +++ b/recipes-containers/vcontainer/files/vdkr.sh | |||
| @@ -0,0 +1,25 @@ | |||
| 1 | #!/bin/bash | ||
| 2 | # SPDX-FileCopyrightText: Copyright (C) 2025 Bruce Ashfield | ||
| 3 | # | ||
| 4 | # SPDX-License-Identifier: GPL-2.0-only | ||
| 5 | # | ||
| 6 | # vdkr: Docker-like interface for cross-architecture container operations | ||
| 7 | # | ||
| 8 | # This provides a familiar docker-like CLI that executes commands inside | ||
| 9 | # a QEMU-emulated environment with the target architecture's Docker. | ||
| 10 | # | ||
| 11 | # Command naming convention: | ||
| 12 | # - Commands matching Docker's syntax/semantics use Docker's name (import, load, save, etc.) | ||
| 13 | # - Extended commands with non-Docker behavior use 'v' prefix (vimport) | ||
| 14 | |||
| 15 | # Set runtime-specific parameters before sourcing common code | ||
| 16 | VCONTAINER_RUNTIME_NAME="vdkr" | ||
| 17 | VCONTAINER_RUNTIME_CMD="docker" | ||
| 18 | VCONTAINER_RUNTIME_PREFIX="VDKR" | ||
| 19 | VCONTAINER_IMPORT_TARGET="docker-daemon:" | ||
| 20 | VCONTAINER_STATE_FILE="docker-state.img" | ||
| 21 | VCONTAINER_OTHER_PREFIX="VPDMN" | ||
| 22 | VCONTAINER_VERSION="3.4.0" | ||
| 23 | |||
| 24 | # Source common implementation | ||
| 25 | source "$(dirname "${BASH_SOURCE[0]}")/vcontainer-common.sh" "$@" | ||
