diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000000..c69991456a --- /dev/null +++ b/.dockerignore @@ -0,0 +1,31 @@ +# Cargo / build artifacts +target/ +contest-target/ +bin/ + +# Built binaries dropped at repo root by scripts/build.sh +youki +runtimetest +contest + +# Test fixtures (not needed for image builds) +bundle.tar.gz +test.log + +# Git / CI metadata (not used inside images) +.git/ +.github/ + +# Docs build outputs +docs/book/book/ + +# IDE / editor / OS noise +.idea/ +.vscode/ +*.swp +*.swo +.DS_Store + +# Misc +node_modules/ +*.log diff --git a/Cargo.toml b/Cargo.toml index 515544ecc0..7f1731dcea 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,7 +1,7 @@ [workspace] resolver = "2" members = ["crates/*", "tests/contest/*", "tools/*"] -exclude = ["experiment/seccomp", "experiment/selinux"] +exclude = ["experiment/seccomp", "experiment/selinux", "tools/youki-deploy"] [workspace.dependencies] anyhow = "1.0.102" diff --git a/docs/src/SUMMARY.md b/docs/src/SUMMARY.md index c5c6563c17..213a76e7cc 100644 --- a/docs/src/SUMMARY.md +++ b/docs/src/SUMMARY.md @@ -13,6 +13,7 @@ - [liboci-cli](./user/liboci_cli.md) - [libseccomp](./user/libseccomp.md) - [Webassembly](./user/webassembly.md) + - [Kubernetes](./user/kubernetes.md) --- diff --git a/docs/src/developer/e2e/kubernetes_test.md b/docs/src/developer/e2e/kubernetes_test.md index 95bfe1e29e..d71080b8fe 100644 --- a/docs/src/developer/e2e/kubernetes_test.md +++ b/docs/src/developer/e2e/kubernetes_test.md @@ -4,6 +4,8 @@ This test verifies that youki works correctly as a container runtime in a Kubernetes environment using [Kind](https://kind.sigs.k8s.io/) (Kubernetes in Docker). +## Single Node deploy test + The test builds a custom Kind node image with youki, creates a cluster, and deploys nginx pods using a RuntimeClass that specifies youki as the runtime. ## Local @@ -17,3 +19,30 @@ To clean up an existing Kind cluster first: ```console $ just clean-test-kind ``` + +## Multi Node deploy test + +In addition to the single-node `test-kind` flow above, there is a +multi-node variant that mirrors how youki would be installed on a real +Kubernetes cluster: the cluster nodes themselves stay as vanilla +`kindest/node` images, and a DaemonSet running on every node +installs youki onto the host and registers it with containerd at +runtime. + +### Local + +```console +$ just test-kind-deploy +``` + +Or to only stand up the cluster + DaemonSet without the nginx smoke test: + +```console +$ just kind-deploy +``` + +Clean up: + +```console +$ just clean-test-kind-deploy +``` diff --git a/docs/src/user/introduction.md b/docs/src/user/introduction.md index 964ad8244d..01fe2a10d8 100644 --- a/docs/src/user/introduction.md +++ b/docs/src/user/introduction.md @@ -12,3 +12,4 @@ This is divided into following sub-sections : - liboci-cli - libseccomp - Webassembly : This explains how to use webassembly module with youki. +- Kubernetes : This explains how to install youki on a Kubernetes cluster as the OCI runtime via the youki-deploy DaemonSet. diff --git a/docs/src/user/kubernetes.md b/docs/src/user/kubernetes.md new file mode 100644 index 0000000000..807dc7443f --- /dev/null +++ b/docs/src/user/kubernetes.md @@ -0,0 +1,75 @@ +# Using youki as a Kubernetes runtime + +youki implements the OCI runtime spec, so it can be plugged into any +Kubernetes cluster. Once youki is installed on each node and the +node's container runtime is configured to know about it, individual +Pods can opt in via `runtimeClassName: youki`. + +## youki-deploy: ready-to-use installer for kind / containerd + +The repository ships a small installer under +[`tools/youki-deploy/`](https://github.com/youki-dev/youki/tree/main/tools/youki-deploy) +which automates install the youki binary on every node, and register +it as a containerd runtime. It is composed of: + +- A Docker image that bundles the youki release binary and an + `install-youki.sh` script. +- A Kubernetes DaemonSet manifest that runs that image as a privileged + pod on every node, mounts the host's `/usr/local/bin` and + `/etc/containerd`, copies the binary into place, patches + `/etc/containerd/config.toml`, and restarts containerd. + +It is currently exercised against [kind](https://kind.sigs.k8s.io/) +(Kubernetes-in-Docker) but the manifests are Kubernetes +resources and should apply to any containerd-based cluster. + +## Try it locally on kind + +To stand up the cluster + DaemonSet: + +```console +$ just kind-deploy +``` + +To tear it down: + +```console +$ just clean-test-kind-deploy +``` + +See also +[Developer Documentation > Kubernetes test](../developer/e2e/kubernetes_test.md). + +## Using youki for your own Pods + +Once the DaemonSet is installed, just add `runtimeClassName: youki` to +the pod spec: + +```yaml +apiVersion: v1 +kind: Pod +metadata: + name: nginx-youki +spec: + runtimeClassName: youki + containers: + - name: nginx + image: nginx:1.27-alpine +``` + +Pods without `runtimeClassName` continue to use whatever the cluster's +default OCI runtime is (typically `runc`), so installing youki-deploy +is non-disruptive for existing workloads. + +## Caveats + +- The installer modifies `/usr/local/bin/youki` and + `/etc/containerd/config.toml` on the host. It does not currently + uninstall on DaemonSet deletion - removing youki and the containerd + config patch must be done manually. +- The DaemonSet manifest references the installer image as + `youki-installer:latest`, which is loaded into the local kind cluster + by `just kind-deploy`. To deploy on a real cluster, build the image + from `tools/youki-deploy/Dockerfile` and push it to a registry that + your cluster nodes can pull from, then update the image field in + `tools/youki-deploy/youki-deploy.yaml`. diff --git a/justfile b/justfile index 2bf450cb96..dd5cb1eaba 100644 --- a/justfile +++ b/justfile @@ -2,6 +2,8 @@ alias build := youki-release alias youki := youki-dev KIND_CLUSTER_NAME := 'youki' +KIND_DEPLOY_CLUSTER_NAME := 'youki-deploy' +YOUKI_INSTALLER_IMAGE := 'youki-installer:latest' cwd := justfile_directory() @@ -115,6 +117,39 @@ bin-kind: clean-test-kind: kind delete cluster --name {{ KIND_CLUSTER_NAME }} +[private] +kind-cluster-multi: + #!/usr/bin/env bash + set -euo pipefail + + kind create cluster \ + --name {{ KIND_DEPLOY_CLUSTER_NAME }} \ + --config tools/youki-deploy/kind-config.yaml + +[private] +youki-installer-image: + docker buildx build \ + -f tools/youki-deploy/Dockerfile \ + -t {{ YOUKI_INSTALLER_IMAGE }} \ + --load . + +# install youki on every node of a multi-node kind cluster +kind-deploy: kind-cluster-multi youki-installer-image + kind load docker-image {{ YOUKI_INSTALLER_IMAGE }} --name {{ KIND_DEPLOY_CLUSTER_NAME }} + kubectl --context=kind-{{ KIND_DEPLOY_CLUSTER_NAME }} apply -f tools/youki-deploy/youki-deploy.yaml + kubectl --context=kind-{{ KIND_DEPLOY_CLUSTER_NAME }} -n youki-system rollout status ds/youki-deploy --timeout=180s + +# test youki on the deployed multi-node kind cluster +test-kind-deploy: kind-deploy + kubectl --context=kind-{{ KIND_DEPLOY_CLUSTER_NAME }} apply -f tests/k8s/deploy.yaml + kubectl --context=kind-{{ KIND_DEPLOY_CLUSTER_NAME }} wait deployment nginx-deployment --for condition=Available=True --timeout=120s + kubectl --context=kind-{{ KIND_DEPLOY_CLUSTER_NAME }} get pods -o wide + kubectl --context=kind-{{ KIND_DEPLOY_CLUSTER_NAME }} delete -f tests/k8s/deploy.yaml + +# Clean kind cluster +clean-test-kind-deploy: + kind delete cluster --name {{ KIND_DEPLOY_CLUSTER_NAME }} + # misc # run bpftrace hack diff --git a/tools/youki-deploy/Dockerfile b/tools/youki-deploy/Dockerfile new file mode 100644 index 0000000000..e71f398172 --- /dev/null +++ b/tools/youki-deploy/Dockerfile @@ -0,0 +1,24 @@ +# syntax=docker/dockerfile:1.4 + +FROM rust:1-slim-bookworm AS youki-build +RUN apt-get update && apt-get install -y --no-install-recommends \ + pkg-config \ + libsystemd-dev \ + build-essential \ + libelf-dev \ + libseccomp-dev \ + libclang-dev \ + libssl-dev \ + && rm -rf /var/lib/apt/lists/* +WORKDIR /youki +COPY . . +RUN cargo build --release -p youki --features "v2 systemd" + +FROM debian:bookworm-slim +RUN apt-get update && apt-get install -y --no-install-recommends \ + util-linux \ + && rm -rf /var/lib/apt/lists/* +COPY --from=youki-build /youki/target/release/youki /opt/youki/bin/youki +COPY tools/youki-deploy/install-youki.sh /opt/youki/bin/install-youki.sh +RUN chmod +x /opt/youki/bin/install-youki.sh /opt/youki/bin/youki +ENTRYPOINT ["/opt/youki/bin/install-youki.sh"] diff --git a/tools/youki-deploy/install-youki.sh b/tools/youki-deploy/install-youki.sh new file mode 100644 index 0000000000..22c2654332 --- /dev/null +++ b/tools/youki-deploy/install-youki.sh @@ -0,0 +1,34 @@ +#!/usr/bin/env bash +# Install youki onto a kind node and register it with containerd. +# Designed to run inside a privileged DaemonSet pod that mounts the host's +# /usr/local/bin and /etc/containerd as hostPath volumes, with hostPID enabled. +set -euo pipefail + +HOST_BIN_DIR=${HOST_BIN_DIR:-/host/usr/local/bin} +HOST_CONTAINERD_CONFIG=${HOST_CONTAINERD_CONFIG:-/host/etc/containerd/config.toml} +RUNTIME_HANDLER=${RUNTIME_HANDLER:-youki} +MARKER="# youki-deploy:${RUNTIME_HANDLER}" + +echo "[youki-deploy] Installing youki on $(hostname)" + +install -m 0755 -D /opt/youki/bin/youki "${HOST_BIN_DIR}/youki" + +if grep -qF "${MARKER}" "${HOST_CONTAINERD_CONFIG}"; then + echo "[youki-deploy] containerd already configured, skipping config patch" +else + echo "[youki-deploy] Patching ${HOST_CONTAINERD_CONFIG}" + cat >>"${HOST_CONTAINERD_CONFIG}" <