Skip to content

Commit edc7975

Browse files
wrkodeclaude
andauthored
fix(build): version-match the containerd pause image to kubeadm (C4) (#21)
containerd/config.toml hardcoded registry.k8s.io/pause:3.10 for every build, but kubeadm expects a per-minor pause: 3.10.1 (1.34/1.35) and 3.10.2 (1.36). The registry was already correct (not the dead k8s.gcr.io), but the stale tag means containerd pulls a different pause than kubeadm pre-pulled -- a duplicate image and version drift (pitfall C4). Resolve the pause image from the bundled kubeadm at build time (kubeadm config images list) and rewrite sandbox_image, so it always matches the target Kubernetes minor instead of a hardcoded tag. The config.toml value is now a placeholder the build overwrites. A CI step asserts the shipped sandbox_image equals kubeadm pause per minor, so it cannot silently regress. Verified locally: kubeadm 1.34.0 -> pause:3.10.1, 1.36.0 -> pause:3.10.2; the sed rewrite + assert pass against the real config.toml. Closes kairos-io/kairos#4200. Signed-off-by: William Rizzo <william.rizzo@gmail.com> Co-authored-by: Claude <noreply@anthropic.com>
1 parent 0860329 commit edc7975

3 files changed

Lines changed: 34 additions & 2 deletions

File tree

.github/workflows/ci.yml

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -157,6 +157,20 @@ jobs:
157157
esac
158158
echo "OK: image bundles the k8s ${{ matrix.minor }} toolchain"
159159
160+
- name: Verify containerd sandbox (pause) image is version-matched (C4)
161+
run: |
162+
set -euo pipefail
163+
img="kairos-kubeadm:ci-${{ matrix.minor }}"
164+
# The build pins containerd's sandbox_image to the pause kubeadm expects;
165+
# assert they match so a stale/hardcoded pause tag can't regress (C4).
166+
want="$(docker run --rm --entrypoint /usr/bin/kubeadm "$img" \
167+
config images list --image-repository registry.k8s.io | grep -E '/pause:[0-9]')"
168+
got="$(docker run --rm --entrypoint sh "$img" -c \
169+
"grep -oE 'registry.k8s.io/pause:[0-9.]+' /etc/containerd/config.toml")"
170+
echo "kubeadm pause: ${want}; containerd sandbox_image: ${got}"
171+
[ "$got" = "$want" ] || { echo "pause mismatch (C4): containerd ${got} != kubeadm ${want}" >&2; exit 1; }
172+
echo "OK: sandbox_image is version-matched to kubeadm"
173+
160174
- name: Verify kubeadm exposes the upgrade-path surface (ADR-12)
161175
run: |
162176
set -euo pipefail

Dockerfile

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -260,6 +260,21 @@ COPY systemd/kubelet.service.d/10-kubeadm.conf /etc/systemd/system/kubelet.servi
260260
COPY sysctl/k8s.conf /etc/sysctl.d/k8s.conf
261261
COPY modules-load/k8s.conf /etc/modules-load.d/k8s.conf
262262

263+
# Pin containerd's pod-sandbox (pause) image to the EXACT version the bundled
264+
# kubeadm expects for this Kubernetes minor, instead of a hardcoded tag. kubeadm
265+
# bumps the pause version per release (e.g. 3.10.1 in 1.34/1.35 -> 3.10.2 in 1.36);
266+
# a stale tag means containerd pulls a different pause than kubeadm pre-pulled
267+
# (duplicate image / drift -- pitfall C4). Resolved from kubeadm at build time so
268+
# it always matches the bundled toolchain. kubeadm is static and runs here on musl.
269+
RUN set -eux; \
270+
pause="$(/usr/bin/kubeadm config images list \
271+
--kubernetes-version "${KUBERNETES_VERSION}" \
272+
--image-repository registry.k8s.io | grep -E '/pause:[0-9]')"; \
273+
test -n "${pause}"; \
274+
sed -i "s#^\([[:space:]]*sandbox_image[[:space:]]*=\).*#\1 \"${pause}\"#" /etc/containerd/config.toml; \
275+
grep -q "sandbox_image = \"${pause}\"" /etc/containerd/config.toml; \
276+
echo "pinned containerd sandbox_image to ${pause}"
277+
263278
# --- Boot-time setup: enable services; modules and sysctls load via /etc -----
264279
RUN systemctl enable containerd.service kubelet.service
265280

containerd/config.toml

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,15 @@
11
# containerd configuration for kubeadm clusters built with provider-kubernetes.
2-
# - sandbox image pinned to registry.k8s.io (NOT the dead k8s.gcr.io).
2+
# - sandbox_image uses registry.k8s.io (NOT the dead k8s.gcr.io). The pause TAG
3+
# below is a placeholder: the image build overwrites it with the exact pause
4+
# version the bundled kubeadm expects for the target Kubernetes minor (see the
5+
# Dockerfile), so it is always version-matched rather than a stale hardcoded tag.
36
# - SystemdCgroup = true, matching the kubelet's default cgroup driver
47
# ("systemd"); see https://kubernetes.io/docs/setup/production-environment/container-runtimes/.
58
version = 2
69
root = "/var/lib/containerd"
710

811
[plugins."io.containerd.grpc.v1.cri"]
9-
sandbox_image = "registry.k8s.io/pause:3.10"
12+
sandbox_image = "registry.k8s.io/pause:3.10.1"
1013

1114
[plugins."io.containerd.grpc.v1.cri".containerd.runtimes.runc]
1215
runtime_type = "io.containerd.runc.v2"

0 commit comments

Comments
 (0)