homelab/manifests/multus/02-daemonset.yaml
Nik Afiq e980fcb755
Some checks failed
validate / lint (push) Failing after 1s
feat: add Multus manifests for VPN VLAN 50 (manual sync)
CRD, RBAC, thick-plugin DaemonSet (nik-debian only, k3s CNI paths,
digest-pinned), the VLAN 50 NAD (macvlan bridge on enp1s0.50), the
shared pod egress-guard script, a temporary canary pod, and the
not-yet-deployed browser-vpn-proxy workload (kept in reserved/, outside
this Application's non-recursive source path).

Referenced by argocd/apps/multus.yaml (already pushed) but not deployed
by it — that Application has no syncPolicy.automated, so this still
needs an explicit selective sync per resource.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-24 18:07:40 +09:00

217 lines
8.3 KiB
YAML

# Apply: kubectl apply -f manifests/multus/02-daemonset.yaml
# Description: Multus thick-plugin DaemonSet, restricted by nodeSelector
# to nik-debian only (the sole node with a VLAN 50 trunk — deliberately
# not cluster-wide, so minisforum and nik-gpu's CNI chain is never
# touched by this at all). sync-wave 0 — after RBAC.
#
# Rebuilt from k8snetworkplumbingwg/multus-cni's official
# deployments/multus-daemonset-thick.yml, commit
# 95a0932350f0060cd232096876e24672a1de83fa (master, fetched and read in
# full 2026-08-24 — https://raw.githubusercontent.com/
# k8snetworkplumbingwg/multus-cni/master/deployments/
# multus-daemonset-thick.yml). An earlier version of this file was NOT
# a faithful copy of that manifest — it used an invented entrypoint
# (/entrypoint/cni-installer.sh, which does not exist in the real image)
# and an incomplete set of mounts/config keys, reconstructed from memory
# rather than the real source. This version changes only what k3s and
# this topology actually require, everything else (entrypoints, daemon
# config keys, volume set, RBAC-adjacent mounts) is verbatim:
#
# 1. cni/cnibin volumes' hostPath: k3s's real CNI conf/bin dirs
# (/var/lib/rancher/k3s/agent/etc/cni/net.d,
# /var/lib/rancher/k3s/data/cni — confirmed LIVE on nik-debian
# 2026-08-23; there is no /opt/cni/bin or /etc/cni/net.d on this
# host, upstream's own defaults would silently no-op on k3s
# without this). The container-side mountPaths upstream defines
# (/host/etc/cni/net.d, /host/opt/cni/bin) are unchanged — only
# which host directory backs them moves.
# 2. All OTHER hostPath volumes (host-run, host-var-lib-cni-multus,
# host-var-lib-kubelet, host-run-k8s-cni-cncf-io, host-run-netns,
# hostroot, multus-conf-dir) are left at upstream's standard paths
# — k3s does not relocate /run, /var/lib/kubelet, /run/netns, or /
# itself, only the CNI-specific directories. This assumption (that
# kubelet's own root-dir is standard on k3s) is NOT independently
# verified against a live k3s node the way the CNI paths are —
# confirm before applying if this cluster ever changes
# --root-dir on the kubelet.
# 3. nodeSelector restricting it to nik-debian (upstream is
# cluster-wide by default).
# 4. Image pinned to the v4.3.0-thick digest (upstream's own quickstart
# file uses the "snapshot-thick" moving tag, which is explicitly a
# dev/nightly build, not something to run unpinned in production).
# Digest confirmed live via the GHCR OCI Distribution API
# 2026-08-24: v4.3.0-thick and stable-thick both resolve to
# sha256:2b9671447f3ea4e7e56730843dbf59445b9307246f393b61386b896
# d56ae51c9, i.e. v4.3.0 is genuinely the current stable release,
# not just a same-named coincidence.
#
# Do not hand-edit the daemon-config.json keys, the container
# command/args, or the mount set below without re-diffing against the
# URL above — none of that was invented this time, it's copied.
apiVersion: v1
kind: ConfigMap
metadata:
name: multus-daemon-config
namespace: kube-system
labels:
tier: node
app: multus
annotations:
argocd.argoproj.io/sync-wave: "0"
data:
daemon-config.json: |
{
"chrootDir": "/hostroot",
"cniVersion": "0.3.1",
"logLevel": "verbose",
"logToStderr": true,
"cniConfigDir": "/host/etc/cni/net.d",
"multusAutoconfigDir": "/host/etc/cni/net.d",
"multusConfigFile": "auto",
"socketDir": "/host/run/multus/"
}
---
apiVersion: apps/v1
kind: DaemonSet
metadata:
name: kube-multus-ds
namespace: kube-system
labels:
tier: node
app: multus
name: multus
annotations:
argocd.argoproj.io/sync-wave: "0"
spec:
selector:
matchLabels:
name: multus
updateStrategy:
type: RollingUpdate
template:
metadata:
labels:
tier: node
app: multus
name: multus
spec:
# Not upstream — restricts this DaemonSet to the one node with a
# VLAN 50 trunk. minisforum and nik-gpu never run Multus at all.
nodeSelector:
kubernetes.io/hostname: nik-debian
hostNetwork: true
hostPID: true
tolerations:
- operator: Exists
effect: NoSchedule
- operator: Exists
effect: NoExecute
serviceAccountName: multus
containers:
- name: kube-multus
# VERIFY the digest still resolves before applying — see
# header comment for how it was obtained and when.
image: "ghcr.io/k8snetworkplumbingwg/multus-cni@sha256:2b9671447f3ea4e7e56730843dbf59445b9307246f393b61386b896d56ae51c9" # v4.3.0-thick
command: ["/usr/src/multus-cni/bin/multus-daemon"]
resources:
requests:
cpu: "100m"
memory: "50Mi"
limits:
cpu: "100m"
memory: "50Mi"
securityContext:
privileged: true
terminationMessagePolicy: FallbackToLogsOnError
volumeMounts:
- name: cni
mountPath: /host/etc/cni/net.d
# multus-daemon expects that cnibin path must be identical between pod and container host.
# e.g. if the cni bin is in '/opt/cni/bin' on the container host side, then it should be mount to '/opt/cni/bin' in multus-daemon,
# not to any other directory, like '/opt/bin' or '/usr/bin'.
- name: cnibin
mountPath: /opt/cni/bin
- name: host-run
mountPath: /host/run
- name: host-var-lib-cni-multus
mountPath: /var/lib/cni/multus
- name: host-var-lib-kubelet
mountPath: /var/lib/kubelet
mountPropagation: HostToContainer
- name: host-run-k8s-cni-cncf-io
mountPath: /run/k8s.cni.cncf.io
- name: host-run-netns
mountPath: /run/netns
mountPropagation: HostToContainer
- name: multus-daemon-config
mountPath: /etc/cni/net.d/multus.d
readOnly: true
- name: hostroot
mountPath: /hostroot
mountPropagation: HostToContainer
- mountPath: /etc/cni/multus/net.d
name: multus-conf-dir
env:
- name: MULTUS_NODE_NAME
valueFrom:
fieldRef:
fieldPath: spec.nodeName
initContainers:
- name: install-multus-binary
# Same image/digest as the main container — see header comment.
image: "ghcr.io/k8snetworkplumbingwg/multus-cni@sha256:2b9671447f3ea4e7e56730843dbf59445b9307246f393b61386b896d56ae51c9" # v4.3.0-thick
command:
- "/usr/src/multus-cni/bin/install_multus"
- "-d"
- "/host/opt/cni/bin"
- "-t"
- "thick"
resources:
requests:
cpu: "10m"
memory: "15Mi"
securityContext:
privileged: true
terminationMessagePolicy: FallbackToLogsOnError
volumeMounts:
- name: cnibin
mountPath: /host/opt/cni/bin
mountPropagation: Bidirectional
terminationGracePeriodSeconds: 30
volumes:
- name: cni
hostPath:
# k3s CNI conf dir — NOT the standard /etc/cni/net.d.
path: /var/lib/rancher/k3s/agent/etc/cni/net.d
- name: cnibin
hostPath:
# k3s CNI bin dir — NOT the standard /opt/cni/bin.
path: /var/lib/rancher/k3s/data/cni
- name: hostroot
hostPath:
path: /
- name: multus-daemon-config
configMap:
name: multus-daemon-config
items:
- key: daemon-config.json
path: daemon-config.json
- name: host-run
hostPath:
path: /run
- name: host-var-lib-cni-multus
hostPath:
path: /var/lib/cni/multus
- name: host-var-lib-kubelet
hostPath:
path: /var/lib/kubelet
- name: host-run-k8s-cni-cncf-io
hostPath:
path: /run/k8s.cni.cncf.io
- name: host-run-netns
hostPath:
path: /run/netns/
- name: multus-conf-dir
hostPath:
path: /etc/cni/multus/net.d