summaryrefslogtreecommitdiffstats
path: root/recipes-containers/k3s
diff options
context:
space:
mode:
authorBruce Ashfield <bruce.ashfield@gmail.com>2026-04-07 16:05:39 +0000
committerBruce Ashfield <bruce.ashfield@gmail.com>2026-04-07 16:05:39 +0000
commitd138800d7925a007fc61366bb20a3c90412a45c6 (patch)
treed3efabf18f940d8dfade096c40eab97dcdbd99f4 /recipes-containers/k3s
parentd1a4f655e390c76402c416714607bc3b3b85ac2f (diff)
downloadmeta-virtualization-d138800d7925a007fc61366bb20a3c90412a45c6.tar.gz
k3s: add multi-node cluster support with role-based boot
Add infrastructure for booting the same container-image-host image as either a k3s server or agent, controlled via kernel cmdline parameters (k3s.role=server|agent). k3s-role-setup.service / k3s-role-setup.sh: - Reads k3s.role, k3s.server, k3s.token, k3s.node-name, k3s.node-ip, k3s.iface from kernel cmdline - Configures cluster network interface IP via networkd drop-in - For agent role: masks k3s.service, writes agent environment file, starts k3s-agent.service - For server role: masks k3s-agent.service (default) 10-k3s-cluster.network: - Claims the cluster interface (eth1) via virt_networking bbclass - Disables DHCP to prevent systemd-networkd from interfering - Static IP added at boot by role-setup via drop-in k3s-get-token.sh: - Helper script to display the server join token - Waits for token file if k3s is still starting k3s-agent.service: - Add EnvironmentFile for /etc/default/k3s-agent (K3S_URL, K3S_TOKEN) - Add After=k3s-role-setup.service and network-online.target k3s.service: - Add After=k3s-role-setup.service packagegroup-kubernetes.bb: - k3s-host packagegroup now includes k3s-agent (both roles available) - Both k3s-host and k3s-node include k3s-net-conf Signed-off-by: Bruce Ashfield <bruce.ashfield@gmail.com>
Diffstat (limited to 'recipes-containers/k3s')
-rw-r--r--recipes-containers/k3s/k3s/10-k3s-cluster.network20
-rw-r--r--recipes-containers/k3s/k3s/k3s-agent.service6
-rw-r--r--recipes-containers/k3s/k3s/k3s-get-token.sh33
-rw-r--r--recipes-containers/k3s/k3s/k3s-role-setup.service16
-rw-r--r--recipes-containers/k3s/k3s/k3s-role-setup.sh113
-rw-r--r--recipes-containers/k3s/k3s/k3s.service2
-rw-r--r--recipes-containers/k3s/k3s_git.bb16
7 files changed, 203 insertions, 3 deletions
diff --git a/recipes-containers/k3s/k3s/10-k3s-cluster.network b/recipes-containers/k3s/k3s/10-k3s-cluster.network
new file mode 100644
index 00000000..c821a8fe
--- /dev/null
+++ b/recipes-containers/k3s/k3s/10-k3s-cluster.network
@@ -0,0 +1,20 @@
1# K3s cluster network interface configuration
2#
3# Claims the secondary interface (eth1) for k3s cluster networking,
4# preventing systemd-networkd's default catch-all from configuring
5# it with DHCP. The static IP address is configured at boot by
6# k3s-role-setup.service via a runtime drop-in based on the
7# k3s.node-ip= kernel cmdline parameter.
8#
9# If no k3s.node-ip= is set, the interface stays up with no address
10# (link-local only), which is harmless.
11
12[Match]
13Name=eth1
14
15[Network]
16DHCP=no
17LinkLocalAddressing=ipv6
18
19[Link]
20RequiredForOnline=no
diff --git a/recipes-containers/k3s/k3s/k3s-agent.service b/recipes-containers/k3s/k3s/k3s-agent.service
index 40d0564b..0304d640 100644
--- a/recipes-containers/k3s/k3s/k3s-agent.service
+++ b/recipes-containers/k3s/k3s/k3s-agent.service
@@ -3,13 +3,17 @@
3Description=Lightweight Kubernetes Agent 3Description=Lightweight Kubernetes Agent
4Documentation=https://k3s.io 4Documentation=https://k3s.io
5Requires=containerd.service 5Requires=containerd.service
6After=containerd.service 6After=containerd.service k3s-role-setup.service
7After=network-online.target
8Wants=network-online.target
7 9
8[Install] 10[Install]
9WantedBy=multi-user.target 11WantedBy=multi-user.target
10 12
11[Service] 13[Service]
12Type=notify 14Type=notify
15EnvironmentFile=-/etc/default/%N
16EnvironmentFile=-/etc/sysconfig/%N
13# Ensure CNI plugin binaries are discoverable 17# Ensure CNI plugin binaries are discoverable
14Environment=PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/opt/cni/bin:/usr/libexec/cni 18Environment=PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/opt/cni/bin:/usr/libexec/cni
15KillMode=control-group 19KillMode=control-group
diff --git a/recipes-containers/k3s/k3s/k3s-get-token.sh b/recipes-containers/k3s/k3s/k3s-get-token.sh
new file mode 100644
index 00000000..359ea2ff
--- /dev/null
+++ b/recipes-containers/k3s/k3s/k3s-get-token.sh
@@ -0,0 +1,33 @@
1#!/bin/sh
2# SPDX-FileCopyrightText: Copyright (C) 2025 Bruce Ashfield
3# SPDX-License-Identifier: MIT
4#
5# k3s-get-token — Display the k3s server join token
6#
7# Waits for the token file to be created (k3s server generates it
8# on first start) and prints it. Useful for setting up agent nodes.
9
10TOKEN_FILE="/var/lib/rancher/k3s/server/node-token"
11TIMEOUT=60
12
13if [ ! -f "$TOKEN_FILE" ]; then
14 echo "Waiting for k3s server to generate token..."
15 i=0
16 while [ ! -f "$TOKEN_FILE" ] && [ $i -lt $TIMEOUT ]; do
17 sleep 2
18 i=$((i + 2))
19 done
20fi
21
22if [ -f "$TOKEN_FILE" ]; then
23 echo ""
24 echo "=== K3s Join Token ==="
25 cat "$TOKEN_FILE"
26 echo ""
27 echo "To join an agent node:"
28 echo " run-k3s-multinode.sh agent --token \$(k3s-get-token)"
29 echo ""
30else
31 echo "Token not found. Is k3s server running?"
32 echo " systemctl status k3s"
33fi
diff --git a/recipes-containers/k3s/k3s/k3s-role-setup.service b/recipes-containers/k3s/k3s/k3s-role-setup.service
new file mode 100644
index 00000000..26d51ba3
--- /dev/null
+++ b/recipes-containers/k3s/k3s/k3s-role-setup.service
@@ -0,0 +1,16 @@
1[Unit]
2Description=K3s Role Setup from Kernel Cmdline
3Documentation=https://k3s.io
4After=local-fs.target
5Before=systemd-networkd.service network-online.target
6
7# Only run if k3s.role= is on the kernel cmdline
8ConditionKernelCommandLine=k3s.role
9
10[Service]
11Type=oneshot
12RemainAfterExit=yes
13ExecStart=/usr/local/bin/k3s-role-setup.sh
14
15[Install]
16WantedBy=multi-user.target
diff --git a/recipes-containers/k3s/k3s/k3s-role-setup.sh b/recipes-containers/k3s/k3s/k3s-role-setup.sh
new file mode 100644
index 00000000..fa4ffb05
--- /dev/null
+++ b/recipes-containers/k3s/k3s/k3s-role-setup.sh
@@ -0,0 +1,113 @@
1#!/bin/sh
2# SPDX-FileCopyrightText: Copyright (C) 2025 Bruce Ashfield
3# SPDX-License-Identifier: MIT
4#
5# k3s-role-setup.sh — Configure k3s role from kernel cmdline
6#
7# Reads k3s.role= from /proc/cmdline and configures the system
8# to run as either a k3s server or agent.
9#
10# Kernel cmdline parameters:
11# k3s.role=server — run k3s server (default if not specified)
12# k3s.role=agent — disable k3s server, enable k3s agent
13# k3s.server=IP — server address for agent mode
14# k3s.token=TOKEN — join token for agent mode
15# k3s.node-name=N — override node name (useful when multiple VMs
16# boot from the same image)
17# k3s.node-ip=IP — set node IP address
18# k3s.iface=IFACE — flannel interface (default: eth1)
19
20CMDLINE=$(cat /proc/cmdline)
21
22get_param() {
23 echo "$CMDLINE" | tr ' ' '\n' | grep "^$1=" | cut -d= -f2-
24}
25
26ROLE=$(get_param k3s.role)
27SERVER=$(get_param k3s.server)
28TOKEN=$(get_param k3s.token)
29NODE_NAME=$(get_param k3s.node-name)
30NODE_IP=$(get_param k3s.node-ip)
31IFACE=$(get_param k3s.iface)
32
33# Default role is server
34ROLE="${ROLE:-server}"
35IFACE="${IFACE:-eth1}"
36
37# Configure cluster network interface IP via networkd drop-in.
38# The base 10-k3s-cluster.network (installed by virt_networking bbclass)
39# claims eth1 and disables DHCP. This drop-in adds the static address.
40if [ -n "$NODE_IP" ] && [ -n "$IFACE" ]; then
41 echo "Configuring $IFACE with $NODE_IP/24 via networkd drop-in"
42 DROPIN_DIR="/etc/systemd/network/10-k3s-cluster.network.d"
43 mkdir -p "$DROPIN_DIR"
44 cat > "$DROPIN_DIR/address.conf" << EOF
45[Network]
46Address=${NODE_IP}/24
47EOF
48 networkctl reload 2>/dev/null || true
49fi
50
51case "$ROLE" in
52 server)
53 # Server is the default — k3s.service is already enabled.
54 # Mask the agent service to prevent it from starting.
55 systemctl mask k3s-agent.service 2>/dev/null || true
56 echo "k3s role: server"
57 ;;
58
59 agent)
60 # Mask the server service to prevent it from starting
61 systemctl mask k3s.service 2>/dev/null || true
62
63 # Wipe any server state from previous boot
64 rm -rf /var/lib/rancher/k3s/server/tls \
65 /var/lib/rancher/k3s/server/cred \
66 /var/lib/rancher/k3s/server/token \
67 /var/lib/rancher/k3s/server/agent-token \
68 /var/lib/rancher/k3s/server/node-token \
69 /var/lib/rancher/k3s/server/db \
70 /etc/rancher/k3s/k3s.yaml
71
72 # Remove server-only config (disable-cloud-controller is
73 # server-only and crashes the agent)
74 rm -f /etc/rancher/k3s/config.yaml
75
76 # Build agent environment file with cmdline parameters
77 mkdir -p /etc/default
78 {
79 echo "# Generated by k3s-role-setup from kernel cmdline"
80 [ -n "$SERVER" ] && echo "K3S_URL=https://${SERVER}:6443"
81 [ -n "$TOKEN" ] && echo "K3S_TOKEN=${TOKEN}"
82 } > /etc/default/k3s-agent
83
84 # Build extra args
85 AGENT_ARGS=""
86 [ -n "$NODE_NAME" ] && AGENT_ARGS="$AGENT_ARGS --node-name $NODE_NAME"
87 [ -n "$NODE_IP" ] && AGENT_ARGS="$AGENT_ARGS --node-ip $NODE_IP"
88 [ -n "$IFACE" ] && AGENT_ARGS="$AGENT_ARGS --flannel-iface $IFACE"
89
90 if [ -n "$AGENT_ARGS" ]; then
91 # Create systemd override for extra args
92 mkdir -p /run/systemd/system/k3s-agent.service.d
93 cat > /run/systemd/system/k3s-agent.service.d/cmdline.conf << EOF
94[Service]
95ExecStart=
96ExecStart=/usr/local/bin/k3s agent $AGENT_ARGS
97EOF
98 fi
99
100 # Reload systemd to pick up overrides, then unmask and start
101 # the agent. --no-block queues the start without waiting,
102 # avoiding deadlock with the After= ordering.
103 systemctl daemon-reload
104 systemctl unmask k3s-agent.service 2>/dev/null || true
105 systemctl start k3s-agent.service --no-block
106
107 echo "k3s role: agent (server: ${SERVER:-not set})"
108 ;;
109
110 *)
111 echo "Unknown k3s.role=$ROLE, defaulting to server"
112 ;;
113esac
diff --git a/recipes-containers/k3s/k3s/k3s.service b/recipes-containers/k3s/k3s/k3s.service
index f56110a4..e7402867 100644
--- a/recipes-containers/k3s/k3s/k3s.service
+++ b/recipes-containers/k3s/k3s/k3s.service
@@ -4,7 +4,7 @@ Description=Lightweight Kubernetes
4Documentation=https://k3s.io 4Documentation=https://k3s.io
5Requires=containerd.service 5Requires=containerd.service
6After=containerd.service 6After=containerd.service
7After=network-online.target 7After=network-online.target k3s-role-setup.service
8Wants=network-online.target 8Wants=network-online.target
9 9
10[Install] 10[Install]
diff --git a/recipes-containers/k3s/k3s_git.bb b/recipes-containers/k3s/k3s_git.bb
index e2d0a33e..1b5f3430 100644
--- a/recipes-containers/k3s/k3s_git.bb
+++ b/recipes-containers/k3s/k3s_git.bb
@@ -11,6 +11,10 @@ SRC_URI = "git://github.com/rancher/k3s.git;branch=release-1.35;name=k3s;protoco
11 file://k3s-clean \ 11 file://k3s-clean \
12 file://cni-flannel.conflist \ 12 file://cni-flannel.conflist \
13 file://k3s-killall.sh \ 13 file://k3s-killall.sh \
14 file://k3s-role-setup.sh \
15 file://k3s-role-setup.service \
16 file://k3s-get-token.sh \
17 file://10-k3s-cluster.network \
14 " 18 "
15 19
16# Traefik Helm charts — downloaded and embedded into the k3s binary 20# Traefik Helm charts — downloaded and embedded into the k3s binary
@@ -34,6 +38,11 @@ PV = "v1.35.2+k3s1+git"
34# K3s uses flannel for CNI networking, not the containerd bridge config 38# K3s uses flannel for CNI networking, not the containerd bridge config
35CNI_NETWORKING_FILES ?= "${UNPACKDIR}/cni-flannel.conflist" 39CNI_NETWORKING_FILES ?= "${UNPACKDIR}/cni-flannel.conflist"
36 40
41# Claim the cluster network interface (eth1) so systemd-networkd's
42# default catch-all doesn't configure it with DHCP. The static IP
43# is set at boot by k3s-role-setup.service via a networkd drop-in.
44VIRT_NETWORKING_FILES ?= "${UNPACKDIR}/10-k3s-cluster.network"
45
37PACKAGECONFIG ??= "traefik" 46PACKAGECONFIG ??= "traefik"
38PACKAGECONFIG[traefik] = ",,," 47PACKAGECONFIG[traefik] = ",,,"
39 48
@@ -63,6 +72,7 @@ inherit go
63inherit goarch 72inherit goarch
64inherit systemd 73inherit systemd
65inherit cni_networking 74inherit cni_networking
75inherit virt_networking
66inherit go-mod-discovery 76inherit go-mod-discovery
67 77
68BB_GIT_SHALLOW = "1" 78BB_GIT_SHALLOW = "1"
@@ -140,8 +150,11 @@ do_install() {
140 if ${@bb.utils.contains('DISTRO_FEATURES','systemd','true','false',d)}; then 150 if ${@bb.utils.contains('DISTRO_FEATURES','systemd','true','false',d)}; then
141 install -D -m 0644 "${UNPACKDIR}/k3s.service" "${D}${systemd_system_unitdir}/k3s.service" 151 install -D -m 0644 "${UNPACKDIR}/k3s.service" "${D}${systemd_system_unitdir}/k3s.service"
142 install -D -m 0644 "${UNPACKDIR}/k3s-agent.service" "${D}${systemd_system_unitdir}/k3s-agent.service" 152 install -D -m 0644 "${UNPACKDIR}/k3s-agent.service" "${D}${systemd_system_unitdir}/k3s-agent.service"
153 install -D -m 0644 "${UNPACKDIR}/k3s-role-setup.service" "${D}${systemd_system_unitdir}/k3s-role-setup.service"
143 sed -i "s#\(Exec\)\(.*\)=\(.*\)\(k3s\)#\1\2=${BIN_PREFIX}/bin/\4#g" "${D}${systemd_system_unitdir}/k3s.service" "${D}${systemd_system_unitdir}/k3s-agent.service" 154 sed -i "s#\(Exec\)\(.*\)=\(.*\)\(k3s\)#\1\2=${BIN_PREFIX}/bin/\4#g" "${D}${systemd_system_unitdir}/k3s.service" "${D}${systemd_system_unitdir}/k3s-agent.service"
144 install -m 755 "${UNPACKDIR}/k3s-agent" "${D}${BIN_PREFIX}/bin" 155 install -m 755 "${UNPACKDIR}/k3s-agent" "${D}${BIN_PREFIX}/bin"
156 install -m 755 "${UNPACKDIR}/k3s-role-setup.sh" "${D}${BIN_PREFIX}/bin/k3s-role-setup.sh"
157 install -m 755 "${UNPACKDIR}/k3s-get-token.sh" "${D}${BIN_PREFIX}/bin/k3s-get-token"
145 fi 158 fi
146 159
147 mkdir -p ${D}${datadir}/k3s/ 160 mkdir -p ${D}${datadir}/k3s/
@@ -165,9 +178,10 @@ do_install() {
165 178
166PACKAGES =+ "${PN}-server ${PN}-agent" 179PACKAGES =+ "${PN}-server ${PN}-agent"
167 180
168SYSTEMD_PACKAGES = "${@bb.utils.contains('DISTRO_FEATURES','systemd','${PN}-server ${PN}-agent','',d)}" 181SYSTEMD_PACKAGES = "${@bb.utils.contains('DISTRO_FEATURES','systemd','${PN}-server ${PN}-agent ${PN}','',d)}"
169SYSTEMD_SERVICE:${PN}-server = "${@bb.utils.contains('DISTRO_FEATURES','systemd','k3s.service','',d)}" 182SYSTEMD_SERVICE:${PN}-server = "${@bb.utils.contains('DISTRO_FEATURES','systemd','k3s.service','',d)}"
170SYSTEMD_SERVICE:${PN}-agent = "${@bb.utils.contains('DISTRO_FEATURES','systemd','k3s-agent.service','',d)}" 183SYSTEMD_SERVICE:${PN}-agent = "${@bb.utils.contains('DISTRO_FEATURES','systemd','k3s-agent.service','',d)}"
184SYSTEMD_SERVICE:${PN} = "${@bb.utils.contains('DISTRO_FEATURES','systemd','k3s-role-setup.service','',d)}"
171SYSTEMD_AUTO_ENABLE:${PN}-agent = "disable" 185SYSTEMD_AUTO_ENABLE:${PN}-agent = "disable"
172 186
173FILES:${PN}-agent = "${BIN_PREFIX}/bin/k3s-agent" 187FILES:${PN}-agent = "${BIN_PREFIX}/bin/k3s-agent"