|
| 1 | +#!/usr/bin/env bash |
| 2 | +# |
| 3 | +# install-runtimeclass.sh — provision a Kubernetes node to run RuntimeClass=a3s-box pods. |
| 4 | +# |
| 5 | +# Run as root ON each node that should host a3s-box MicroVM workloads. It installs: |
| 6 | +# * the a3s-box CLI + helpers (a3s-box, a3s-box-cri, a3s-box-guest-init, a3s-box-shim) |
| 7 | +# * libkrun / libkrunfw (the MicroVM VMM, into the system lib dir + ldconfig) |
| 8 | +# * the containerd runtime-v2 shim (containerd-shim-a3s-box-v2) |
| 9 | +# and registers the `io.containerd.a3s-box.v2` runtime with containerd, then restarts it. |
| 10 | +# |
| 11 | +# After running this on a node, label it from a control-plane so the RuntimeClass |
| 12 | +# nodeSelector (a3s-box.io/runtime=true) lets a3s-box pods schedule there: |
| 13 | +# |
| 14 | +# kubectl label node <node-name> a3s-box.io/runtime=true |
| 15 | +# |
| 16 | +# Usage: |
| 17 | +# install-runtimeclass.sh [--version vX.Y.Z] [--repo OWNER/REPO] [--from-dir DIR] |
| 18 | +# |
| 19 | +# --version release tag to install (default: v2.6.0) |
| 20 | +# --repo GitHub repo to download artifacts from (default: AI45Lab/Box) |
| 21 | +# --from-dir install from a local directory instead of downloading; the dir must |
| 22 | +# contain a3s-box-<version>-linux-<arch>.tar.gz and a containerd shim |
| 23 | +# binary (containerd-shim-a3s-box-v2[-linux-<arch>]). |
| 24 | +# |
| 25 | +# Idempotent: safe to re-run (re-installs binaries, rewrites the containerd drop-in). |
| 26 | +set -euo pipefail |
| 27 | + |
| 28 | +VERSION="v2.6.0" |
| 29 | +REPO="AI45Lab/Box" |
| 30 | +FROM_DIR="" |
| 31 | +WARMUP_IMAGE="busybox:latest" # first box on a fresh node builds a one-time cache |
| 32 | + # (~40s+); booting one here primes it so the first |
| 33 | + # real pod doesn't exceed the shim's boot poll. Best |
| 34 | + # effort — skipped silently if the image can't pull. |
| 35 | + |
| 36 | +while [ $# -gt 0 ]; do |
| 37 | + case "$1" in |
| 38 | + --version) VERSION="$2"; shift 2 ;; |
| 39 | + --repo) REPO="$2"; shift 2 ;; |
| 40 | + --from-dir) FROM_DIR="$2"; shift 2 ;; |
| 41 | + --warmup-image) WARMUP_IMAGE="$2"; shift 2 ;; |
| 42 | + --no-warmup) WARMUP_IMAGE=""; shift ;; |
| 43 | + -h|--help) sed -n '2,33p' "$0"; exit 0 ;; |
| 44 | + *) echo "unknown arg: $1" >&2; exit 2 ;; |
| 45 | + esac |
| 46 | +done |
| 47 | + |
| 48 | +log() { printf '\033[1;34m==>\033[0m %s\n' "$*"; } |
| 49 | +die() { printf '\033[1;31mERROR:\033[0m %s\n' "$*" >&2; exit 1; } |
| 50 | + |
| 51 | +# ── preflight ─────────────────────────────────────────────────────────────── |
| 52 | +[ "$(id -u)" = 0 ] || die "must run as root" |
| 53 | +command -v containerd >/dev/null || die "containerd not found on this node" |
| 54 | +[ -e /dev/kvm ] || die "/dev/kvm missing — this node has no KVM virtualization; a3s-box cannot run here" |
| 55 | + |
| 56 | +case "$(uname -m)" in |
| 57 | + x86_64) ARCH=x86_64 ;; |
| 58 | + aarch64|arm64) ARCH=arm64 ;; |
| 59 | + *) die "unsupported architecture: $(uname -m)" ;; |
| 60 | +esac |
| 61 | + |
| 62 | +TARBALL="a3s-box-${VERSION}-linux-${ARCH}.tar.gz" |
| 63 | +SHIM_ASSET="containerd-shim-a3s-box-v2-linux-${ARCH}" |
| 64 | + |
| 65 | +work="$(mktemp -d)" |
| 66 | +trap 'rm -rf "$work"' EXIT |
| 67 | + |
| 68 | +# ── obtain artifacts ──────────────────────────────────────────────────────── |
| 69 | +if [ -n "$FROM_DIR" ]; then |
| 70 | + log "Installing from local dir: $FROM_DIR" |
| 71 | + [ -f "$FROM_DIR/$TARBALL" ] || die "missing $TARBALL in $FROM_DIR" |
| 72 | + cp "$FROM_DIR/$TARBALL" "$work/$TARBALL" |
| 73 | + if [ -f "$FROM_DIR/$SHIM_ASSET" ]; then cp "$FROM_DIR/$SHIM_ASSET" "$work/shim" |
| 74 | + elif [ -f "$FROM_DIR/containerd-shim-a3s-box-v2" ]; then cp "$FROM_DIR/containerd-shim-a3s-box-v2" "$work/shim" |
| 75 | + else die "missing containerd-shim-a3s-box-v2 in $FROM_DIR"; fi |
| 76 | +else |
| 77 | + base="https://github.com/${REPO}/releases/download/${VERSION}" |
| 78 | + log "Downloading $TARBALL" |
| 79 | + curl -fsSL "$base/$TARBALL" -o "$work/$TARBALL" || die "download failed: $base/$TARBALL" |
| 80 | + log "Downloading $SHIM_ASSET" |
| 81 | + curl -fsSL "$base/$SHIM_ASSET" -o "$work/shim" || die "download failed: $base/$SHIM_ASSET" |
| 82 | +fi |
| 83 | + |
| 84 | +tar xzf "$work/$TARBALL" -C "$work" |
| 85 | +src="$work/a3s-box-${VERSION}-linux-${ARCH}" |
| 86 | +[ -d "$src" ] || die "unexpected tarball layout (no $src)" |
| 87 | + |
| 88 | +# ── install binaries + libkrun ────────────────────────────────────────────── |
| 89 | +log "Installing a3s-box binaries to /usr/local/bin" |
| 90 | +install -m0755 "$src/a3s-box" "$src/a3s-box-cri" "$src/a3s-box-guest-init" "$src/a3s-box-shim" /usr/local/bin/ |
| 91 | + |
| 92 | +log "Installing libkrun to /usr/lib + ldconfig" |
| 93 | +cp -a "$src"/lib/libkrun* /usr/lib/ |
| 94 | +ldconfig |
| 95 | + |
| 96 | +log "Installing containerd-shim-a3s-box-v2 (/usr/local/bin + /opt/containerd/bin)" |
| 97 | +install -m0755 "$work/shim" /usr/local/bin/containerd-shim-a3s-box-v2 |
| 98 | +install -d /opt/containerd/bin |
| 99 | +install -m0755 "$work/shim" /opt/containerd/bin/containerd-shim-a3s-box-v2 |
| 100 | + |
| 101 | +install -d /var/lib/a3s-box # shared A3S_HOME for the shim's a3s-box invocations |
| 102 | + |
| 103 | +# ── register the runtime with containerd ──────────────────────────────────── |
| 104 | +cfg=/etc/containerd/config.toml |
| 105 | +runtime_block() { |
| 106 | + cat <<'TOML' |
| 107 | +[plugins.'io.containerd.cri.v1.runtime'.containerd.runtimes.a3s-box] |
| 108 | + runtime_type = 'io.containerd.a3s-box.v2' |
| 109 | + [plugins.'io.containerd.cri.v1.runtime'.containerd.runtimes.a3s-box.options] |
| 110 | +TOML |
| 111 | +} |
| 112 | + |
| 113 | +if grep -qE "^imports[[:space:]]*=.*conf\.d" "$cfg" 2>/dev/null; then |
| 114 | + # containerd merges /etc/containerd/conf.d/*.toml — register via a drop-in so we |
| 115 | + # never touch the main config (clean + idempotent). |
| 116 | + install -d /etc/containerd/conf.d |
| 117 | + { echo "version = 3"; echo; runtime_block; } > /etc/containerd/conf.d/a3s-box.toml |
| 118 | + log "Registered runtime via /etc/containerd/conf.d/a3s-box.toml" |
| 119 | +elif grep -q "runtimes.a3s-box\]" "$cfg" 2>/dev/null; then |
| 120 | + log "Runtime already present in $cfg — leaving as-is" |
| 121 | +else |
| 122 | + # No conf.d imports: append the runtime table to the main config. |
| 123 | + { echo; runtime_block; } >> "$cfg" |
| 124 | + log "Registered runtime in $cfg" |
| 125 | +fi |
| 126 | + |
| 127 | +log "Restarting containerd" |
| 128 | +systemctl restart containerd |
| 129 | +sleep 2 |
| 130 | + |
| 131 | +# ── verify ────────────────────────────────────────────────────────────────── |
| 132 | +log "Verification" |
| 133 | +systemctl is-active --quiet containerd || die "containerd is not active after restart" |
| 134 | +"$src/a3s-box" --version >/dev/null 2>&1 || /usr/local/bin/a3s-box --version >/dev/null 2>&1 || die "a3s-box CLI not runnable" |
| 135 | +echo " a3s-box: $(/usr/local/bin/a3s-box --version 2>/dev/null)" |
| 136 | +echo " libkrun: $(ldconfig -p | awk '/libkrun\.so/{print $1; exit}')" |
| 137 | +echo " shim: $(command -v containerd-shim-a3s-box-v2)" |
| 138 | +echo " /dev/kvm: present" |
| 139 | +echo " containerd: active" |
| 140 | + |
| 141 | +# ── warm up (prime the one-time per-node boot cache) ──────────────────────── |
| 142 | +if [ -n "$WARMUP_IMAGE" ]; then |
| 143 | + log "Warming up with $WARMUP_IMAGE (primes first-boot cache; --no-warmup to skip)" |
| 144 | + if A3S_HOME=/var/lib/a3s-box timeout 240 /usr/local/bin/a3s-box run \ |
| 145 | + --name a3sbox-warmup "$WARMUP_IMAGE" -- true >/dev/null 2>&1; then |
| 146 | + echo " warm-up OK — first pod will boot fast" |
| 147 | + else |
| 148 | + echo " warm-up skipped (could not pull $WARMUP_IMAGE) — first pod may cold-start slowly" |
| 149 | + fi |
| 150 | + A3S_HOME=/var/lib/a3s-box /usr/local/bin/a3s-box rm -f a3sbox-warmup >/dev/null 2>&1 || true |
| 151 | +fi |
| 152 | + |
| 153 | +printf '\n\033[1;32mDone.\033[0m a3s-box runtime installed on %s.\n' "$(hostname)" |
| 154 | +cat <<EOF |
| 155 | +Final step — from a control-plane node, label this node so a3s-box pods can schedule: |
| 156 | +
|
| 157 | + kubectl label node $(hostname) a3s-box.io/runtime=true |
| 158 | +
|
| 159 | +EOF |
0 commit comments