Skip to content

Commit 6cb8db4

Browse files
authored
add youki install tool for kubernetes (#3526)
Signed-off-by: Yusuke Sakurai <yusuke.sakurai@3-shake.com>
1 parent f8e9b07 commit 6cb8db4

13 files changed

Lines changed: 371 additions & 11 deletions

File tree

.dockerignore

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
# Cargo / build artifacts
2+
target/
3+
contest-target/
4+
bin/
5+
6+
# Built binaries dropped at repo root by scripts/build.sh
7+
youki
8+
runtimetest
9+
contest
10+
11+
# Test fixtures (not needed for image builds)
12+
bundle.tar.gz
13+
test.log
14+
15+
# Git / CI metadata (not used inside images)
16+
.git/
17+
.github/
18+
19+
# Docs build outputs
20+
docs/book/book/
21+
22+
# IDE / editor / OS noise
23+
.idea/
24+
.vscode/
25+
*.swp
26+
*.swo
27+
.DS_Store
28+
29+
# Misc
30+
node_modules/
31+
*.log

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
[workspace]
22
resolver = "2"
33
members = ["crates/*", "tests/contest/*", "tools/*"]
4-
exclude = ["experiment/seccomp", "experiment/selinux"]
4+
exclude = ["experiment/seccomp", "experiment/selinux", "tools/youki-deploy"]
55

66
[workspace.dependencies]
77
anyhow = "1.0.102"

docs/src/SUMMARY.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
- [liboci-cli](./user/liboci_cli.md)
1414
- [libseccomp](./user/libseccomp.md)
1515
- [Webassembly](./user/webassembly.md)
16+
- [Kubernetes](./user/kubernetes.md)
1617

1718
---
1819

docs/src/developer/e2e/kubernetes_test.md

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44

55
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).
66

7+
## Single Node deploy test
8+
79
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.
810

911
## Local
@@ -17,3 +19,30 @@ To clean up an existing Kind cluster first:
1719
```console
1820
$ just clean-test-kind
1921
```
22+
23+
## Multi Node deploy test
24+
25+
In addition to the single-node `test-kind` flow above, there is a
26+
multi-node variant that mirrors how youki would be installed on a real
27+
Kubernetes cluster: the cluster nodes themselves stay as vanilla
28+
`kindest/node` images, and a DaemonSet running on every node
29+
installs youki onto the host and registers it with containerd at
30+
runtime.
31+
32+
### Local
33+
34+
```console
35+
$ just test-kind-deploy
36+
```
37+
38+
Or to only stand up the cluster + DaemonSet without the nginx smoke test:
39+
40+
```console
41+
$ just kind-deploy
42+
```
43+
44+
Clean up:
45+
46+
```console
47+
$ just clean-test-kind-deploy
48+
```

docs/src/user/introduction.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,3 +12,4 @@ This is divided into following sub-sections :
1212
- liboci-cli
1313
- libseccomp
1414
- Webassembly : This explains how to use webassembly module with youki.
15+
- Kubernetes : This explains how to install youki on a Kubernetes cluster as the OCI runtime via the youki-deploy DaemonSet.

docs/src/user/kubernetes.md

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
# Using youki as a Kubernetes runtime
2+
3+
youki implements the OCI runtime spec, so it can be plugged into any
4+
Kubernetes cluster. Once youki is installed on each node and the
5+
node's container runtime is configured to know about it, individual
6+
Pods can opt in via `runtimeClassName: youki`.
7+
8+
## youki-deploy: ready-to-use installer for kind / containerd
9+
10+
The repository ships a small installer under
11+
[`tools/youki-deploy/`](https://github.com/youki-dev/youki/tree/main/tools/youki-deploy)
12+
which automates install the youki binary on every node, and register
13+
it as a containerd runtime. It is composed of:
14+
15+
- A Docker image that bundles the youki release binary and an
16+
`install-youki.sh` script.
17+
- A Kubernetes DaemonSet manifest that runs that image as a privileged
18+
pod on every node, mounts the host's `/usr/local/bin` and
19+
`/etc/containerd`, copies the binary into place, patches
20+
`/etc/containerd/config.toml`, and restarts containerd.
21+
22+
It is currently exercised against [kind](https://kind.sigs.k8s.io/)
23+
(Kubernetes-in-Docker) but the manifests are Kubernetes
24+
resources and should apply to any containerd-based cluster.
25+
26+
## Try it locally on kind
27+
28+
To stand up the cluster + DaemonSet:
29+
30+
```console
31+
$ just kind-deploy
32+
```
33+
34+
To tear it down:
35+
36+
```console
37+
$ just clean-test-kind-deploy
38+
```
39+
40+
See also
41+
[Developer Documentation > Kubernetes test](../developer/e2e/kubernetes_test.md).
42+
43+
## Using youki for your own Pods
44+
45+
Once the DaemonSet is installed, just add `runtimeClassName: youki` to
46+
the pod spec:
47+
48+
```yaml
49+
apiVersion: v1
50+
kind: Pod
51+
metadata:
52+
name: nginx-youki
53+
spec:
54+
runtimeClassName: youki
55+
containers:
56+
- name: nginx
57+
image: nginx:1.27-alpine
58+
```
59+
60+
Pods without `runtimeClassName` continue to use whatever the cluster's
61+
default OCI runtime is (typically `runc`), so installing youki-deploy
62+
is non-disruptive for existing workloads.
63+
64+
## Caveats
65+
66+
- The installer modifies `/usr/local/bin/youki` and
67+
`/etc/containerd/config.toml` on the host. It does not currently
68+
uninstall on DaemonSet deletion - removing youki and the containerd
69+
config patch must be done manually.
70+
- The DaemonSet manifest references the installer image as
71+
`youki-installer:latest`, which is loaded into the local kind cluster
72+
by `just kind-deploy`. To deploy on a real cluster, build the image
73+
from `tools/youki-deploy/Dockerfile` and push it to a registry that
74+
your cluster nodes can pull from, then update the image field in
75+
`tools/youki-deploy/youki-deploy.yaml`.

justfile

Lines changed: 44 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@ alias youki := youki-dev
33

44
KIND_CLUSTER_NAME := 'youki'
55
KIND_SYSTEMD_CLUSTER_NAME := 'youki-systemd'
6+
KIND_DEPLOY_CLUSTER_NAME := 'youki-deploy'
7+
YOUKI_INSTALLER_IMAGE := 'youki-installer:latest'
68

79
cwd := justfile_directory()
810

@@ -99,10 +101,10 @@ kind-cluster: bin-kind
99101
100102
# run youki with kind
101103
test-kind: kind-cluster
102-
kubectl --context=kind-{{ KIND_CLUSTER_NAME }} apply -f tests/k8s/deploy.yaml
104+
kubectl --context=kind-{{ KIND_CLUSTER_NAME }} apply -f tests/k8s/runtimeclass.yaml -f tests/k8s/deploy.yaml
103105
kubectl --context=kind-{{ KIND_CLUSTER_NAME }} wait deployment nginx-deployment --for condition=Available=True --timeout=90s
104106
kubectl --context=kind-{{ KIND_CLUSTER_NAME }} get pods -o wide
105-
kubectl --context=kind-{{ KIND_CLUSTER_NAME }} delete -f tests/k8s/deploy.yaml
107+
kubectl --context=kind-{{ KIND_CLUSTER_NAME }} delete -f tests/k8s/runtimeclass.yaml -f tests/k8s/deploy.yaml
106108

107109
[private]
108110
kind-cluster-systemd-cgroup: bin-kind
@@ -116,10 +118,10 @@ kind-cluster-systemd-cgroup: bin-kind
116118
117119
# run youki with kind and systemd cgroup enabled (regression test for dbus socket path)
118120
test-kind-systemd-cgroup: kind-cluster-systemd-cgroup
119-
kubectl --context=kind-{{ KIND_SYSTEMD_CLUSTER_NAME }} apply -f tests/k8s/deploy.yaml
121+
kubectl --context=kind-{{ KIND_SYSTEMD_CLUSTER_NAME }} apply -f tests/k8s/runtimeclass.yaml -f tests/k8s/deploy.yaml
120122
kubectl --context=kind-{{ KIND_SYSTEMD_CLUSTER_NAME }} wait deployment nginx-deployment --for condition=Available=True --timeout=90s
121123
kubectl --context=kind-{{ KIND_SYSTEMD_CLUSTER_NAME }} get pods -o wide
122-
kubectl --context=kind-{{ KIND_SYSTEMD_CLUSTER_NAME }} delete -f tests/k8s/deploy.yaml
124+
kubectl --context=kind-{{ KIND_SYSTEMD_CLUSTER_NAME }} delete -f tests/k8s/runtimeclass.yaml -f tests/k8s/deploy.yaml
123125

124126
# Bin
125127

@@ -137,6 +139,44 @@ clean-test-kind:
137139
clean-test-kind-systemd-cgroup:
138140
kind delete cluster --name {{ KIND_SYSTEMD_CLUSTER_NAME }}
139141

142+
[private]
143+
kind-cluster-multi:
144+
#!/usr/bin/env bash
145+
set -euo pipefail
146+
147+
if kind get clusters 2>/dev/null | grep -qx "{{ KIND_DEPLOY_CLUSTER_NAME }}"; then
148+
echo "kind cluster '{{ KIND_DEPLOY_CLUSTER_NAME }}' already exists, skipping creation"
149+
exit 0
150+
fi
151+
152+
kind create cluster \
153+
--name {{ KIND_DEPLOY_CLUSTER_NAME }} \
154+
--config tools/youki-deploy/kind-config.yaml
155+
156+
[private]
157+
youki-installer-image:
158+
docker buildx build \
159+
-f tools/youki-deploy/Dockerfile \
160+
-t {{ YOUKI_INSTALLER_IMAGE }} \
161+
--load .
162+
163+
# install youki on every node of a multi-node kind cluster
164+
kind-deploy: kind-cluster-multi youki-installer-image
165+
kind load docker-image {{ YOUKI_INSTALLER_IMAGE }} --name {{ KIND_DEPLOY_CLUSTER_NAME }}
166+
kubectl --context=kind-{{ KIND_DEPLOY_CLUSTER_NAME }} apply -f tools/youki-deploy/youki-deploy.yaml
167+
kubectl --context=kind-{{ KIND_DEPLOY_CLUSTER_NAME }} -n youki-system rollout status ds/youki-deploy --timeout=180s
168+
169+
# test youki on the deployed multi-node kind cluster
170+
test-kind-deploy: kind-deploy
171+
kubectl --context=kind-{{ KIND_DEPLOY_CLUSTER_NAME }} apply -f tests/k8s/deploy.yaml
172+
kubectl --context=kind-{{ KIND_DEPLOY_CLUSTER_NAME }} wait deployment nginx-deployment --for condition=Available=True --timeout=120s
173+
kubectl --context=kind-{{ KIND_DEPLOY_CLUSTER_NAME }} get pods -o wide
174+
kubectl --context=kind-{{ KIND_DEPLOY_CLUSTER_NAME }} delete -f tests/k8s/deploy.yaml
175+
176+
# Clean kind cluster
177+
clean-test-kind-deploy:
178+
kind delete cluster --name {{ KIND_DEPLOY_CLUSTER_NAME }}
179+
140180
# misc
141181

142182
# run bpftrace hack

tests/k8s/deploy.yaml

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,3 @@
1-
apiVersion: node.k8s.io/v1
2-
kind: RuntimeClass
3-
metadata:
4-
name: youki
5-
handler: youki
6-
---
71
apiVersion: apps/v1
82
kind: Deployment
93
metadata:

tests/k8s/runtimeclass.yaml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
apiVersion: node.k8s.io/v1
2+
kind: RuntimeClass
3+
metadata:
4+
name: youki
5+
handler: youki

tools/youki-deploy/Dockerfile

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
# syntax=docker/dockerfile:1.4
2+
3+
FROM rust:1-slim-bookworm AS youki-build
4+
RUN apt-get update && apt-get install -y --no-install-recommends \
5+
pkg-config \
6+
libsystemd-dev \
7+
build-essential \
8+
libelf-dev \
9+
libseccomp-dev \
10+
libclang-dev \
11+
libssl-dev \
12+
&& rm -rf /var/lib/apt/lists/*
13+
WORKDIR /youki
14+
COPY . .
15+
RUN --mount=type=cache,target=/root/.cargo/registry \
16+
--mount=type=cache,target=/root/.cargo/git \
17+
--mount=type=cache,target=/youki/target \
18+
cargo build --release -p youki --features "v2 systemd" && \
19+
cp target/release/youki /tmp/youki
20+
21+
FROM debian:bookworm-slim
22+
ARG TARGETARCH
23+
ARG KUBECTL_VERSION=v1.34.9
24+
RUN apt-get update && apt-get install -y --no-install-recommends \
25+
util-linux \
26+
&& rm -rf /var/lib/apt/lists/*
27+
# kubectl is used by install-youki.sh to label the node as runtime-ready.
28+
ADD --chmod=0755 https://dl.k8s.io/release/${KUBECTL_VERSION}/bin/linux/${TARGETARCH}/kubectl /usr/local/bin/kubectl
29+
COPY --from=youki-build /tmp/youki /opt/youki/bin/youki
30+
COPY tools/youki-deploy/install-youki.sh /opt/youki/bin/install-youki.sh
31+
RUN chmod +x /opt/youki/bin/install-youki.sh /opt/youki/bin/youki
32+
ENTRYPOINT ["/opt/youki/bin/install-youki.sh"]

0 commit comments

Comments
 (0)