#!/sbin/openrc-run # K3s server control plane. The per-cluster config.yaml sets cluster-init, so # k3s uses the embedded etcd datastore (required for multi-server HA). Data # lives at /var/lib/rancher/k3s; subsequent starts reuse it. description="K3s Kubernetes server (Mulga EKS control plane)" command="/usr/local/bin/k3s" command_args="server --config /etc/rancher/k3s/config.yaml" pidfile="/run/k3s.pid" # Konnectivity agent image (apiserver-network-proxy). Pinned alongside the # konnectivity-server version baked by setup.sh; staged into the worker # DaemonSet by start_pre. KONN_AGENT_IMAGE="registry.k8s.io/kas-network-proxy/proxy-agent:v0.30.3" # Supervise the apiserver. A transient k3s crash must self-heal: without a # supervisor a single exit leaves the control plane permanently dead, the NLB # target never recovers, and the cluster wedges ACTIVE-but-unreachable. supervisor="supervise-daemon" respawn_delay=5 respawn_max=0 output_log="/var/log/k3s.log" error_log="/var/log/k3s.log" depend() { need net cgroups after firewall } start_pre() { # mulga-eks-k3s-recovery drops this marker when a required-snapshot restore # could not fetch its etcd snapshot. Refuse to start: a fresh DR seed with no # snapshot would cluster-init an EMPTY datastore (silent total data loss). The # marker is in /run tmpfs, cleared on reboot, so recovery retries the fetch # next boot and k3s starts once the snapshot restores. if [ -f /run/spinifex-eks/k3s-restore-block ]; then eerror "restore-snapshot could not fetch the required etcd snapshot; refusing to start k3s on an empty datastore (see mulga-eks-k3s-recovery log)" return 1 fi # config.yaml is written by k3s-first-boot from the skeleton + cloud-init # data. Refuse to start if neither exists. if [ ! -f /etc/rancher/k3s/config.yaml ]; then if [ -f /etc/rancher/k3s/config.yaml.skel ]; then cp /etc/rancher/k3s/config.yaml.skel /etc/rancher/k3s/config.yaml else eerror "no /etc/rancher/k3s/config.yaml and no .skel to derive from" return 1 fi fi # Konnectivity agent: stage the worker-side DaemonSet into the auto-deploy dir # (the apiserver `cluster` egress selector tunnels through konnectivity-server, # which the agents dial out to). __KONN_HOST__ is the NLB endpoint agents reach, # seeded by the launcher in first-boot.env; __KONN_AGENT_IMAGE__ is pinned here. # The agent learns the apiserver replica count from the server handshake # (--sync-forever), so no server-count token is substituted here. Guarded on # the baked manifest so an older image without it simply skips the agent. if [ -f /usr/share/spinifex-eks/konnectivity/konnectivity-agent.yaml ]; then konn_host=$(awk -F= '/^EKS_KONNECTIVITY_HOST=/{print $2}' \ /etc/spinifex-eks/first-boot.env 2>/dev/null) if [ -n "${konn_host}" ]; then mkdir -p /var/lib/rancher/k3s/server/manifests sed -e "s|__KONN_HOST__|${konn_host}|g" \ -e "s|__KONN_AGENT_IMAGE__|${KONN_AGENT_IMAGE}|g" \ /usr/share/spinifex-eks/konnectivity/konnectivity-agent.yaml \ > /var/lib/rancher/k3s/server/manifests/konnectivity-agent.yaml einfo "konnectivity-agent staged (host ${konn_host})" else ewarn "EKS_KONNECTIVITY_HOST empty; konnectivity-agent not staged, apiserver->pod egress will fail" fi fi # Node-local CoreDNS: suppress k3s's bundled CoreDNS (a single Deployment that # tolerates the CP taint and strands itself on the control-plane node) and # deploy the Mulga DaemonSet variant so every node resolves via its own # CoreDNS. Worker pods cannot reach a CP-resident CoreDNS — the worker<->CP # overlay does not cross the VPC boundary. Staged pre-start so the deploy # controller never applies the bundled manifest. Guarded on the baked bundle: # an older image without it keeps k3s's CoreDNS rather than losing DNS. if [ -f /usr/share/spinifex-eks/coredns/coredns-mulga.yaml ]; then mkdir -p /var/lib/rancher/k3s/server/manifests : > /var/lib/rancher/k3s/server/manifests/coredns.yaml.skip cp /usr/share/spinifex-eks/coredns/coredns-mulga.yaml \ /var/lib/rancher/k3s/server/manifests/coredns-mulga.yaml fi # The apiserver is configured with --authentication-token-webhook-config-file # pointing at the kubeconfig eks-token-webhook writes at startup. `before k3s` # orders the webhook's start but it backgrounds, so wait (bounded) for the # kubeconfig to land before letting the apiserver load a missing file. i=0 while [ ! -f /etc/spinifex-eks/token-webhook.kubeconfig ] && [ "${i}" -lt 30 ]; do sleep 1 i=$((i + 1)) done if [ ! -f /etc/spinifex-eks/token-webhook.kubeconfig ]; then ewarn "eks-token-webhook kubeconfig absent after ${i}s; IAM token auth will be unavailable until the webhook starts" fi }