Files
JimsGarage/Kubernetes/K3S-Deploy
cyberops7 fd3d414edb K3S-Deploy: rename readme.md -> README.md; align options table
- match the global README.md convention; update the reference in k3s.sh
- pad the options table so it reads cleanly in raw and rendered views,
  kept under the 80-col markdownlint limit
2026-07-05 23:29:24 -06:00
..

K3S HA Deploy (k3sup + kube-vip + MetalLB)

Deploys a highly-available k3s cluster over SSH with k3sup, a floating control-plane VIP via kube-vip, and type: LoadBalancer support via MetalLB.

Following the YouTube tutorial? This script has been modernized since the video — see What changed below.

What it builds

  • A 3-server (control-plane) + 2-agent (worker) k3s cluster with embedded etcd.
  • kube-vip advertises a single virtual IP for the Kubernetes API across all control-plane nodes (survives a node failure).
  • MetalLB hands out real IPs to type: LoadBalancer services.

Why both kube-vip and MetalLB?

They solve different problems. kube-vip provides the control-plane VIP (one address for the API server, HA across masters). MetalLB provides service load balancing (type: LoadBalancer for your apps). The older kube-vip cloud-provider that overlapped MetalLB has been removed to avoid duplicate LoadBalancer controllers.

Prerequisites

  • Ubuntu/Debian nodes (the script installs prerequisites with apt; on a non-apt distro it exits with a clear message).

  • Passwordless sudo for the SSH user on every node. Example cloud-init:

    #cloud-config
    users:
      - name: <your-user>
        sudo: ["ALL=(ALL) NOPASSWD:ALL"]
        groups: [sudo]
    
  • An SSH key pair you can use to reach the nodes (the script distributes the public key and never clobbers your ~/.ssh/config).

  • At least 3 control-plane nodes for real HA (etcd needs a quorum).

  • Run the script from an Ubuntu/Debian admin box (it installs k3sup and kubectl locally if missing).

Usage

  1. Snapshot your VMs.
  2. Copy your SSH key into your home directory (or into ~/.ssh).
  3. Edit the "YOU SHOULD ONLY NEED TO EDIT THIS SECTION" block in k3s.sh — node IPs, user, interface, vip, lbrange, certName.
  4. chmod +x k3s.sh && ./k3s.sh
  5. Review the pre-flight summary and confirm. Grab a coffee.

Options (environment variables)

Variable Purpose
ASSUME_YES=1 Skip the pre-flight [y/N] prompt (unattended runs).
NO_COLOR=1 Disable colored output.
RAW_BASE=<url> Base URL for the sibling manifests (default: main).

Tracking the latest k3s instead of a pinned version

In the config block set k3sChannel="stable" and leave k3sVersion="". The script then always installs the current stable k3s release.

Upgrading kube-vip

The kube-vip manifest is generated from the pinned image (its env schema changes between releases). To bump:

docker run --rm ghcr.io/kube-vip/kube-vip:<version> manifest daemonset \
  --interface eth0 --address 10.0.0.254 \
  --controlplane --arp --leaderElection --taint --inCluster > kube-vip
sed -i 's/value: eth0/value: REPLACE_INTERFACE/' kube-vip
sed -i 's/value: 10.0.0.254/value: REPLACE_VIP/' kube-vip

--inCluster is required on k3s: it makes kube-vip use the kube-vip ServiceAccount (created by the RBAC the script applies) instead of the kubeadm /etc/kubernetes/admin.conf kubeconfig, which does not exist on k3s. Then update KVVERSION in k3s.sh to match.

What changed from the video

  • Versions: k3s v1.35.6+k3s1, kube-vip v1.2.1, MetalLB v0.16.0.
  • kube-vip now runs on all control-plane nodes, and your kubeconfig points at the VIP (not master1) — fixes localhost:8080/API errors. A readiness check confirms the VIP is answering before the script continues.
  • Removed the redundant kube-vip cloud-provider; MetalLB alone handles type: LoadBalancer.
  • MetalLB installs from a single native manifest (which creates its own metallb-system namespace) — the old separate namespace apply and its mismatched MetalLB versions are gone.
  • k3sup: the join token is fetched once and reused across all nodes, and k3sup ready waits for the cluster instead of a hand-rolled poll loop.
  • Safer SSH: host keys are added via ssh-keyscan; the script no longer overwrites ~/.ssh/config.
  • Hardening: set -euo pipefail, per-node time sync, apt prerequisite install with a clear message on unsupported distros, arch-aware kubectl, and it is safe to re-run.
  • Cleaner output: non-blinking step-by-step logging (only the banner still blinks — for old times' sake), a pre-flight config summary with a [y/N] confirmation (ASSUME_YES=1 to skip), and a final summary listing the API VIP and the assigned LoadBalancer IP.