# Copyright 2018 The Kubernetes Authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # kind node base image # # For systemd + docker configuration used below, see the following references: # https://systemd.io/CONTAINER_INTERFACE/ # this ARG needs to be global to use it in `FROM` & is updated for new versions of debian:trixie-slim-* ARG KICBASE_IMAGE="debian:trixie-20260918-slim" # start from debian 13, this image is reasonably small as a starting point # for a kubernetes node image, it doesn't contain much we don't need. FROM ${KICBASE_IMAGE} AS kicbase ARG BUILDKIT_VERSION="v0.33.0" ARG CRIO_VERSION="v1.35" ARG CRICTL_VERSION="v1.35.0" ARG CRI_DOCKERD_VERSION="v0.4.3" ARG CRI_DOCKERD_COMMIT="d969e29f500151687337b2a896ab65e8192f3145" ARG CNI_PLUGINS_VERSION="v1.9.1" ARG TARGETARCH ARG NERDCTL_VERSION="2.3.5" ARG NERDCTLD_VERSION="0.7.0" # copy in static files (configs, scripts) COPY 10-network-security.conf /etc/sysctl.d/10-network-security.conf COPY 11-tcp-mtu-probing.conf /etc/sysctl.d/11-tcp-mtu-probing.conf COPY 02-crio.conf /etc/crio/crio.conf.d/02-crio.conf COPY containerd.toml /etc/containerd/config.toml COPY containerd_docker_io_hosts.toml /etc/containerd/certs.d/docker.io/hosts.toml COPY clean-install /usr/local/bin/clean-install COPY entrypoint /usr/local/bin/entrypoint COPY CHANGELOG ./CHANGELOG COPY nerdctld/nerdctl.socket /usr/local/lib/systemd/system/nerdctl.socket COPY nerdctld/nerdctl.service /usr/local/lib/systemd/system/nerdctl.service COPY auto-pause-${TARGETARCH} /bin/auto-pause # Install dependencies, first from apt, then from release tarballs. # NOTE: we use one RUN to minimize layers. # # First we must ensure that our util scripts are executable. # # The base image already has: ssh, apt, snapd, but we need to install more packages. # Packages installed are broken down into (each on a line): # - packages needed to run services (systemd) # - packages needed for kubernetes components # - packages needed by the container runtime # - misc packages kind uses itself # - packages that provide semi-core kubernetes functionality # After installing packages we cleanup by: # - removing unwanted systemd services # - disabling kmsg in journald (these log entries would be confusing) # # Next we ensure the /etc/kubernetes/manifests directory exists. Normally # a kubeadm debian / rpm package would ensure that this exists but we install # freshly built binaries directly when we build the node image. # # Finally we adjust tempfiles cleanup to be 1 minute after "boot" instead of 15m # This is plenty after we've done initial setup for a node, but before we are # likely to try to export logs etc. RUN echo "Ensuring scripts are executable ..." \ && chmod +x /usr/local/bin/clean-install /usr/local/bin/entrypoint \ && echo "Installing Packages ..." \ && DEBIAN_FRONTEND=noninteractive clean-install \ systemd dbus \ conntrack iptables iproute2 ethtool socat util-linux mount ebtables udev kmod \ libseccomp2 pigz \ bash ca-certificates curl rsync \ nfs-common \ iputils-ping netcat-openbsd vim-tiny \ && find /lib/systemd/system/sysinit.target.wants/ -name "systemd-tmpfiles-setup.service" -delete \ && rm -f /lib/systemd/system/multi-user.target.wants/* \ && rm -f /etc/systemd/system/*.wants/* \ && rm -f /lib/systemd/system/local-fs.target.wants/* \ && rm -f /lib/systemd/system/sockets.target.wants/*udev* \ && rm -f /lib/systemd/system/sockets.target.wants/*initctl* \ && rm -f /lib/systemd/system/basic.target.wants/* \ && echo "ReadKMsg=no" >> /etc/systemd/journald.conf \ && ln -sf /usr/lib/systemd/systemd /sbin/init \ && echo "Ensuring /etc/kubernetes/manifests" \ && mkdir -p /etc/kubernetes/manifests \ && echo "Adjusting systemd-tmpfiles timer" \ && sed -i /usr/lib/systemd/system/systemd-tmpfiles-clean.timer -e 's#OnBootSec=.*#OnBootSec=1min#' \ && echo "Disabling udev" \ && systemctl disable udev.service \ && echo "Masking systemd-binfmt to prevent host config corruption (covers all formats: python, qemu, rosetta, etc) - issue #17700" \ && systemctl mask systemd-binfmt.service \ && echo "Modifying /etc/nsswitch.conf to prefer hosts" \ && sed -i /etc/nsswitch.conf -re 's#^(hosts:\s*).*#\1dns files#' ARG COMMIT_SHA # using base image created by kind https://github.com/kubernetes-sigs/kind/blob/b6bc1125/images/base/Dockerfile # available as a docker image: docker.io/kindest/base:v20210402-3d9112b0 # which is an ubuntu 20.10 with an entry-point that helps running systemd # could be changed to any debian that can run systemd USER root # Install cri-dockerd from official GitHub releases RUN export VERSION=$(echo "${CRI_DOCKERD_VERSION}" | sed 's/^v//') && \ #TODO: add other archs after this PR is merged https://github.com/Mirantis/cri-dockerd/pull/556 if [ "${TARGETARCH}" = "amd64" ] || [ "${TARGETARCH}" = "arm64" ]; then \ echo "Installing cri-dockerd ${CRI_DOCKERD_VERSION} for ${TARGETARCH}" && \ curl -L "https://github.com/Mirantis/cri-dockerd/releases/download/${CRI_DOCKERD_VERSION}/cri-dockerd-${VERSION}.${TARGETARCH}.tgz" -o /tmp/cri-dockerd.tgz && \ tar -C /tmp -xzvf /tmp/cri-dockerd.tgz && \ mv /tmp/cri-dockerd/cri-dockerd /usr/bin/cri-dockerd && \ chmod +x /usr/bin/cri-dockerd && \ rm -rf /tmp/cri-dockerd /tmp/cri-dockerd.tgz && \ curl -L "https://raw.githubusercontent.com/Mirantis/cri-dockerd/${CRI_DOCKERD_VERSION}/packaging/systemd/cri-docker.socket" -o /usr/lib/systemd/system/cri-docker.socket && \ curl -L "https://raw.githubusercontent.com/Mirantis/cri-dockerd/${CRI_DOCKERD_VERSION}/packaging/systemd/cri-docker.service" -o /usr/lib/systemd/system/cri-docker.service; \ else \ echo "WARNING: cri-dockerd is not pre-compiled for ${TARGETARCH}, skipping installation."; \ fi # install system requirements from the regular distro repositories RUN clean-install \ lz4 \ gnupg \ sudo \ openssh-server \ dnsutils # Add support for rsa1 in sshd # modern debian-based OSs dont support rsa1 by default, so we need to enable it to support older ssh clients # TODO: remove after https://github.com/kubernetes/minikube/issues/21543 is solved RUN cat <> /etc/ssh/sshd_config PubkeyAcceptedAlgorithms +ssh-rsa HostkeyAlgorithms +ssh-rsa PubkeyAuthentication yes PasswordAuthentication no EOF # Install nerdctl and nerdctld RUN export ARCH=$(dpkg --print-architecture | sed 's/ppc64el/ppc64le/') && \ echo "Installing nerdctl and nerdctld ..." &&\ addgroup --system nerdctl &&\ mkdir -p /etc/systemd/system/nerdctl.socket.d && \ printf "[Socket]\nSocketMode=0660\nSocketGroup=nerdctl\n" \ > /etc/systemd/system/nerdctl.socket.d/override.conf && \ mkdir -p /etc/systemd/system/nerdctl.service.d && \ printf "[Service]\nEnvironment=CONTAINERD_NAMESPACE=k8s.io\n" \ > /etc/systemd/system/nerdctl.service.d/override.conf && \ curl -L --retry 5 --output /tmp/nerdctl.tgz "https://github.com/containerd/nerdctl/releases/download/v${NERDCTL_VERSION}/nerdctl-${NERDCTL_VERSION}-linux-$ARCH.tar.gz" &&\ tar -C /usr/local/bin -xzvf /tmp/nerdctl.tgz &&\ curl -L --retry 5 --output /tmp/nerdctld.tgz "https://github.com/afbjorklund/nerdctld/releases/download/v${NERDCTLD_VERSION}/nerdctld-${NERDCTLD_VERSION}-linux-$ARCH.tar.gz" &&\ tar -C /usr/local/bin -xzvf /tmp/nerdctld.tgz &&\ chmod 755 /usr/local/bin/nerdctl &&\ chmod 755 /usr/local/bin/nerdctld # install docker ## ref: https://docs.docker.com/engine/install/debian/#install-using-the-repository RUN install -m 0755 -d /etc/apt/keyrings \ && curl -fsSL https://download.docker.com/linux/debian/gpg -o /etc/apt/keyrings/docker.asc \ && chmod a+r /etc/apt/keyrings/docker.asc \ && echo \ "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/debian \ $(. /etc/os-release && echo "$VERSION_CODENAME") stable" | \ tee /etc/apt/sources.list.d/docker.list > /dev/null \ && clean-install docker-ce docker-ce-cli containerd.io docker-buildx-plugin # Set ulimit for containerd to match docker's default-ulimit setting # This ensures consistent file descriptor limits (1048576) across container runtimes RUN sed -i '/^\[Service\]/a LimitNOFILE=1048576' /lib/systemd/system/containerd.service # install buildkit RUN export ARCH=$(dpkg --print-architecture | sed 's/ppc64el/ppc64le/') \ && echo "Installing buildkit ..." \ && addgroup --system buildkit \ && export BUILDKIT_BASE_URL="https://github.com/moby/buildkit/releases/download/${BUILDKIT_VERSION}" \ && curl -sSL --retry 5 --output /tmp/buildkit.tgz "${BUILDKIT_BASE_URL}/buildkit-${BUILDKIT_VERSION}.linux-${ARCH}.tar.gz" \ && tar -C /usr/local -xzvf /tmp/buildkit.tgz \ && rm -rf /tmp/buildkit.tgz \ && mkdir -p /usr/local/lib/systemd/system \ && curl -L --retry 5 --output /usr/local/lib/systemd/system/buildkit.service "https://raw.githubusercontent.com/moby/buildkit/${BUILDKIT_VERSION}/examples/systemd/system/buildkit.service" \ && curl -L --retry 5 --output /usr/local/lib/systemd/system/buildkit.socket "https://raw.githubusercontent.com/moby/buildkit/${BUILDKIT_VERSION}/examples/systemd/system/buildkit.socket" \ && mkdir -p /etc/buildkit \ && echo "[worker.oci]\n enabled = false\n[worker.containerd]\n enabled = true\n namespace = \"k8s.io\"" > /etc/buildkit/buildkitd.toml \ && chmod 755 /usr/local/bin/buildctl \ && chmod 755 /usr/local/bin/buildkit-runc \ && chmod 755 /usr/local/bin/buildkit-qemu-* \ && chmod 755 /usr/local/bin/buildkitd \ && systemctl enable buildkit.socket # install podman RUN clean-install podman catatonit crun && \ addgroup --system podman && \ mkdir -p /etc/systemd/system/podman.socket.d && \ printf "[Socket]\nSocketMode=0660\nSocketUser=root\nSocketGroup=podman\n" \ > /etc/systemd/system/podman.socket.d/override.conf && \ mkdir -p /etc/tmpfiles.d && \ echo "d /run/podman 0770 root podman" > /etc/tmpfiles.d/podman.conf && \ systemd-tmpfiles --create # install crictl RUN export ARCH=$(dpkg --print-architecture) && \ case "$ARCH" in \ amd64) CRICTL_ARCH="amd64" ;; \ arm64) CRICTL_ARCH="arm64" ;; \ ppc64el) CRICTL_ARCH="ppc64le" ;; \ s390x) CRICTL_ARCH="s390x" ;; \ *) echo "Unsupported architecture for crictl: $ARCH" && exit 1 ;; \ esac && \ curl -fsSL --retry 5 --output /tmp/crictl.tgz "https://github.com/kubernetes-sigs/cri-tools/releases/download/${CRICTL_VERSION}/crictl-${CRICTL_VERSION}-linux-${CRICTL_ARCH}.tar.gz" && \ tar -C /usr/local/bin -xzvf /tmp/crictl.tgz && \ rm -f /tmp/crictl.tgz && \ chmod +x /usr/local/bin/crictl && \ if [ -f /usr/local/bin/critest ]; then chmod +x /usr/local/bin/critest; fi # install containernetworking-plugins RUN export ARCH=$(dpkg --print-architecture | sed 's/ppc64el/ppc64le/') && \ curl -LO "https://github.com/containernetworking/plugins/releases/download/${CNI_PLUGINS_VERSION}/cni-plugins-linux-$ARCH-${CNI_PLUGINS_VERSION}.tgz" && \ mkdir -p /opt/cni/bin && \ tar -xf "cni-plugins-linux-$ARCH-${CNI_PLUGINS_VERSION}.tgz" -C /opt/cni/bin && \ rm "cni-plugins-linux-$ARCH-${CNI_PLUGINS_VERSION}.tgz" # install cri-o from the OBS repositories RUN export ARCH=$(dpkg --print-architecture | sed 's/ppc64el/ppc64le/') && \ if [ "$ARCH" != "ppc64le" ]; then \ mkdir -p /etc/apt/sources.list.d /etc/apt/trusted.gpg.d && \ echo 'deb http://download.opensuse.org/repositories/devel:/kubic:/libcontainers:/stable/Debian_12/ /' \ > /etc/apt/sources.list.d/devel:kubic:libcontainers:stable.list && \ curl -fsSL https://download.opensuse.org/repositories/devel:/kubic:/libcontainers:/stable/Debian_12/Release.key \ | gpg --dearmor --yes -o /etc/apt/trusted.gpg.d/devel_kubic_libcontainers_stable.gpg && \ echo "deb http://download.opensuse.org/repositories/isv:/cri-o:/stable:/${CRIO_VERSION}:/build/deb/ /" \ > "/etc/apt/sources.list.d/isv:cri-o:stable:${CRIO_VERSION}:build.list" && \ curl -fsSL "https://download.opensuse.org/repositories/isv:cri-o:stable:${CRIO_VERSION}:build/deb/Release.key" \ | gpg --dearmor --yes -o "/etc/apt/trusted.gpg.d/isv_cri-o_stable_${CRIO_VERSION}_build.gpg" && \ clean-install cri-o; \ fi # install NVIDIA container toolkit RUN export ARCH=$(dpkg --print-architecture) && \ if [ "$ARCH" = 'amd64' ] || [ "$ARCH" = 'arm64' ] || [ "$ARCH" = 'ppc64el' ]; then \ curl -fsSL https://nvidia.github.io/libnvidia-container/gpgkey | sudo gpg --dearmor -o /usr/share/keyrings/nvidia-container-toolkit-keyring.gpg && \ curl -s -L https://nvidia.github.io/libnvidia-container/stable/deb/nvidia-container-toolkit.list | \ sed 's#deb https://#deb [signed-by=/usr/share/keyrings/nvidia-container-toolkit-keyring.gpg] https://#g' | \ sudo tee /etc/apt/sources.list.d/nvidia-container-toolkit.list && \ clean-install nvidia-container-toolkit; fi # install helm ARG HELM_VERSION="v4.2.3" RUN export ARCH=$(dpkg --print-architecture | sed 's/ppc64el/ppc64le/') && \ mkdir /tmp/helm && \ curl -fsSL --retry 5 -o /tmp/helm.tgz "https://get.helm.sh/helm-${HELM_VERSION}-linux-${ARCH}.tar.gz" && \ tar --strip-components=1 -xzf /tmp/helm.tgz -C /tmp/helm && \ install -D -m 0755 /tmp/helm/helm /usr/bin/helm && \ rm -rf /tmp/helm /tmp/helm.tgz # install version.json ARG VERSION_JSON RUN echo "${VERSION_JSON}" > /version.json # automount service COPY automount/minikube-automount /usr/sbin/minikube-automount COPY automount/minikube-automount.service /usr/lib/systemd/system/minikube-automount.service RUN ln -fs /usr/lib/systemd/system/minikube-automount.service \ /etc/systemd/system/multi-user.target.wants/minikube-automount.service # scheduled stop service COPY scheduled-stop/minikube-scheduled-stop /var/lib/minikube/scheduled-stop/minikube-scheduled-stop COPY scheduled-stop/minikube-scheduled-stop.service /usr/lib/systemd/system/minikube-scheduled-stop.service RUN chmod +x /var/lib/minikube/scheduled-stop/minikube-scheduled-stop # disable non-docker runtimes by default (since cri-dockerd is default in minikube) RUN systemctl disable containerd # disable crio for archs that support it RUN export ARCH=$(dpkg --print-architecture | sed 's/ppc64el/ppc64le/') && \ if [ "$ARCH" != "ppc64le" ]; then systemctl disable crio && rm -f /etc/crictl.yaml; fi # enable podman socket on archs that support it RUN export ARCH=$(dpkg --print-architecture | sed 's/ppc64el/ppc64le/') && if [ "$ARCH" != "ppc64le" ]; then systemctl enable podman.socket; fi # enable docker which is default RUN systemctl enable docker.service # minikube relies on /etc/hosts for control-plane discovery. This prevents nefarious DNS servers from breaking it. RUN sed -ri 's/dns files/files dns/g' /etc/nsswitch.conf EXPOSE 22 # create docker user for minikube ssh. to match VM using "docker" as username # Ensure 'docker' group exists; create a 'docker' user with a shell ## ref: https://docs.docker.com/engine/install/linux-postinstall/ RUN groupadd -f docker \ && useradd -m -s /bin/bash -g docker -G sudo docker # Ensure the account is usable for key-only SSH - unlock the account, and disable password RUN usermod -p '*' docker && passwd -u docker && passwd -d docker RUN echo '%sudo ALL=(ALL) NOPASSWD:ALL' > /etc/sudoers.d/20-passwordless-sudo \ && chmod 0440 /etc/sudoers.d/20-passwordless-sudo RUN export ARCH=$(dpkg --print-architecture | sed 's/ppc64el/ppc64le/') && if [ "$ARCH" != "ppc64le" ]; then adduser docker podman; fi RUN adduser docker nerdctl RUN adduser docker buildkit USER docker RUN mkdir /home/docker/.ssh USER root # kind base-image entry-point expects a "kind" folder for product_name,product_uuid # https://github.com/kubernetes-sigs/kind/blob/master/images/base/files/usr/local/bin/entrypoint RUN mkdir -p /kind # Deleting leftovers RUN rm -rf \ /usr/share/doc/* \ /usr/share/man/* \ /usr/share/local/* RUN echo "kic! Build: ${COMMIT_SHA} Time :$(date)" > "/kic.txt" # squash all layers into one FROM scratch COPY --from=kicbase / / EXPOSE 22 # tell systemd that it is in docker (it will check for the container env) # https://systemd.io/CONTAINER_INTERFACE/ ENV container=docker # systemd exits on SIGRTMIN+3, not SIGTERM (which re-executes it) # https://bugzilla.redhat.com/show_bug.cgi?id=1201657 STOPSIGNAL SIGRTMIN+3 # NOTE: this is *only* for documentation, the entrypoint is overridden later ENTRYPOINT [ "/usr/local/bin/entrypoint", "/sbin/init" ]