diff --git a/worker_node_install.sh b/worker_node_install.sh index b9f3ce1..2e313c8 100644 --- a/worker_node_install.sh +++ b/worker_node_install.sh @@ -2,10 +2,25 @@ set -Eeuo pipefail ######################################## -# Arch Linux Kubernetes Worker -# Prepares worker node for manual join +# Arch Linux Kubernetes Worker Node +# Fully automated worker node preparation +# + Official Kubernetes binaries pinned to 1.34.x +# Ready for manual kubeadm join ######################################## +# ---------- Config ---------- +K8S_VERSION="${K8S_VERSION:-v1.34.6}" +K8S_SERIES_REGEX='^v1\.34\.[0-9]+$' +K8S_ARCH="${K8S_ARCH:-amd64}" + +# Binary locations +KUBEADM_BIN="/usr/local/bin/kubeadm" +KUBECTL_BIN="/usr/local/bin/kubectl" +KUBELET_BIN="/usr/local/bin/kubelet" + +# Optional output file for your later manual join command +JOIN_HINT_FILE="${JOIN_HINT_FILE:-/root/kubeadm-join-example.txt}" + # ---------- Logging ---------- log() { echo @@ -25,6 +40,83 @@ die() { exit 1 } +# ---------- Helpers ---------- +require_cmd() { + command -v "$1" >/dev/null 2>&1 || die "Required command not found: $1" +} + +download_k8s_binary() { + local name="$1" + local tmpdir + tmpdir="$(mktemp -d)" + + curl -fsSL -o "${tmpdir}/${name}" \ + "https://dl.k8s.io/release/${K8S_VERSION}/bin/linux/${K8S_ARCH}/${name}" + + curl -fsSL -o "${tmpdir}/${name}.sha256" \ + "https://dl.k8s.io/release/${K8S_VERSION}/bin/linux/${K8S_ARCH}/${name}.sha256" + + ( + cd "${tmpdir}" + echo "$(cat "${name}.sha256") ${name}" | sha256sum --check --status + ) || die "Checksum verification failed for ${name} ${K8S_VERSION}" + + install -o root -g root -m 0755 "${tmpdir}/${name}" "/usr/local/bin/${name}" + rm -rf "${tmpdir}" +} + +install_kubelet_service() { + log "Installing kubelet systemd service" + + mkdir -p /etc/systemd/system/kubelet.service.d + touch /etc/default/kubelet + + cat >/etc/systemd/system/kubelet.service <<'EOF' +[Unit] +Description=kubelet: The Kubernetes Node Agent +Documentation=https://kubernetes.io/docs/ +After=containerd.service network-online.target +Wants=network-online.target +Requires=containerd.service + +[Service] +ExecStart=/usr/local/bin/kubelet +Restart=always +StartLimitInterval=0 +RestartSec=10 + +[Install] +WantedBy=multi-user.target +EOF + + cat >/etc/systemd/system/kubelet.service.d/10-kubeadm.conf <<'EOF' +[Service] +Environment="KUBELET_KUBECONFIG_ARGS=--bootstrap-kubeconfig=/etc/kubernetes/bootstrap-kubelet.conf --kubeconfig=/etc/kubernetes/kubelet.conf" +Environment="KUBELET_CONFIG_ARGS=--config=/var/lib/kubelet/config.yaml" +EnvironmentFile=-/var/lib/kubelet/kubeadm-flags.env +EnvironmentFile=-/etc/default/kubelet +ExecStart= +ExecStart=/usr/local/bin/kubelet $KUBELET_KUBECONFIG_ARGS $KUBELET_CONFIG_ARGS $KUBELET_KUBEADM_ARGS $KUBELET_EXTRA_ARGS +EOF +} + +cleanup_old_k8s_state() { + log "Cleaning up any previous Kubernetes worker state" + + kubeadm reset -f >/dev/null 2>&1 || true + + rm -rf /etc/cni/net.d \ + /var/lib/cni \ + /etc/kubernetes \ + /var/lib/kubelet/pki \ + /var/lib/kubelet/config.yaml \ + /var/lib/kubelet/kubeadm-flags.env + + ip link delete cni0 2>/dev/null || true + ip link delete flannel.1 2>/dev/null || true + ip link delete kube-ipvs0 2>/dev/null || true +} + # ---------- Root check ---------- if [[ "${EUID}" -ne 0 ]]; then die "Run this script as root, for example: sudo ./worker_node_prepare.sh" @@ -41,6 +133,10 @@ on_error() { } trap 'on_error $LINENO' ERR +# ---------- Version guard ---------- +[[ "${K8S_VERSION}" =~ ${K8S_SERIES_REGEX} ]] || die \ + "This worker script is intended for Kubernetes 1.34.x to match your master node. Current K8S_VERSION=${K8S_VERSION}" + # ---------- Step 1: Disable swap ---------- log "Disabling swap immediately" swapoff -a || true @@ -62,8 +158,8 @@ if pacman -Q iptables >/dev/null 2>&1; then pacman -Rdd --noconfirm iptables || true fi -# ---------- Step 4: Install required packages ---------- -log "Installing Kubernetes worker packages" +# ---------- Step 4: Install required Arch packages ---------- +log "Installing runtime and support packages from Arch" pacman -S --needed --noconfirm \ ca-certificates \ curl \ @@ -74,11 +170,30 @@ pacman -S --needed --noconfirm \ iptables-nft \ conntrack-tools \ socat \ - kubeadm \ - kubectl \ - kubelet + tar \ + gzip \ + jq \ + openssl -# ---------- Step 5: Kernel modules ---------- +# ---------- Step 5: Remove Arch Kubernetes packages if present ---------- +log "Removing Arch-provided kubeadm/kubectl/kubelet if present" +for pkg in kubeadm kubectl kubelet; do + if pacman -Q "${pkg}" >/dev/null 2>&1; then + pacman -Rdd --noconfirm "${pkg}" || true + fi +done + +# ---------- Step 6: Install pinned Kubernetes binaries ---------- +log "Installing Kubernetes binaries ${K8S_VERSION}" +download_k8s_binary kubeadm +download_k8s_binary kubectl +download_k8s_binary kubelet + +require_cmd "${KUBEADM_BIN}" +require_cmd "${KUBECTL_BIN}" +require_cmd "${KUBELET_BIN}" + +# ---------- Step 7: Kernel modules ---------- log "Configuring required kernel modules" cat >/etc/modules-load.d/k8s.conf <<'EOF' overlay @@ -88,7 +203,7 @@ EOF modprobe overlay modprobe br_netfilter -# ---------- Step 6: Sysctl ---------- +# ---------- Step 8: Sysctl ---------- log "Configuring Kubernetes sysctl settings" cat >/etc/sysctl.d/99-kubernetes-cri.conf <<'EOF' net.bridge.bridge-nf-call-iptables = 1 @@ -98,7 +213,7 @@ EOF sysctl --system -# ---------- Step 7: Configure containerd ---------- +# ---------- Step 9: Configure containerd ---------- log "Configuring containerd" mkdir -p /etc/containerd @@ -108,16 +223,21 @@ else cp /etc/containerd/config.toml /etc/containerd/config.toml.bak.$(date +%Y%m%d%H%M%S) fi -# Ensure kubelet/containerd use systemd cgroups sed -ri 's/^\s*SystemdCgroup = false/SystemdCgroup = true/' /etc/containerd/config.toml -# ---------- Step 8: Enable services ---------- +# ---------- Step 10: Install kubelet service ---------- +install_kubelet_service + +# ---------- Step 11: Clean previous worker state ---------- +cleanup_old_k8s_state + +# ---------- Step 12: Enable services ---------- log "Enabling and starting containerd and kubelet" systemctl daemon-reload systemctl enable --now containerd systemctl enable --now kubelet -# ---------- Step 9: Wait for containerd ---------- +# ---------- Step 13: Wait for containerd ---------- log "Waiting for containerd to become active" for i in {1..20}; do if systemctl is-active --quiet containerd; then @@ -127,32 +247,49 @@ for i in {1..20}; do done systemctl is-active --quiet containerd || die "containerd did not start successfully" -# ---------- Step 10: Pre-pull Kubernetes images ---------- -log "Pulling Kubernetes node images" -kubeadm config images pull +# ---------- Step 14: Verify pinned versions ---------- +log "Verifying installed Kubernetes component versions" +KUBEADM_VERSION="$("${KUBEADM_BIN}" version -o short 2>/dev/null || true)" +KUBECTL_VERSION="$("${KUBECTL_BIN}" version --client -o json 2>/dev/null | jq -r '.clientVersion.gitVersion // empty')" +KUBELET_VERSION="$("${KUBELET_BIN}" --version 2>/dev/null | awk '{print $2}')" -# ---------- Step 11: Clean notice if already joined ---------- -if [[ -f /etc/kubernetes/kubelet.conf ]]; then - warn "This node appears to have already been joined before." - warn "If you want to re-join it cleanly, run:" - echo " sudo kubeadm reset -f" - echo " sudo rm -rf /etc/cni/net.d /var/lib/cni /var/lib/kubelet /etc/kubernetes" - echo " sudo systemctl restart containerd kubelet" -fi +[[ "${KUBEADM_VERSION}" =~ ${K8S_SERIES_REGEX} ]] || die "kubeadm version mismatch: ${KUBEADM_VERSION}" +[[ "${KUBECTL_VERSION}" =~ ${K8S_SERIES_REGEX} ]] || die "kubectl version mismatch: ${KUBECTL_VERSION}" +[[ "${KUBELET_VERSION}" =~ ${K8S_SERIES_REGEX} ]] || die "kubelet version mismatch: ${KUBELET_VERSION}" -# ---------- Step 12: Final status ---------- -log "Worker node runtime status" -systemctl status containerd kubelet --no-pager || true +# ---------- Step 15: Write join hint ---------- +log "Writing manual join hint" +cat >"${JOIN_HINT_FILE}" <<'EOF' +Run your worker join command manually, for example: +sudo kubeadm join :6443 --token \ + --discovery-token-ca-cert-hash sha256: +EOF +chmod 600 "${JOIN_HINT_FILE}" + +# ---------- Final output ---------- echo echo "Worker node preparation is complete." echo -echo "This node is ready for you to run your join command manually." +echo "Pinned Kubernetes version:" +echo " ${K8S_VERSION}" +echo +echo "Installed binaries:" +echo " ${KUBEADM_BIN}" +echo " ${KUBECTL_BIN}" +echo " ${KUBELET_BIN}" +echo +echo "Services:" +echo " containerd: $(systemctl is-active containerd || true)" +echo " kubelet: $(systemctl is-active kubelet || true)" +echo +echo "Next step:" +echo " Run your kubeadm join command manually on this worker." +echo +echo "Example hint saved to:" +echo " ${JOIN_HINT_FILE}" echo echo "Example:" -echo " kubeadm join 10.28.24.43:6443 --token \\" -echo " --discovery-token-ca-cert-hash sha256:" -echo -echo "After joining, verify on the control-plane node with:" -echo " kubectl get nodes" +echo " sudo kubeadm join :6443 --token \\" +echo " --discovery-token-ca-cert-hash sha256:" echo \ No newline at end of file