From c9e8981349511982791e11d4c8c62573ff574f0e Mon Sep 17 00:00:00 2001 From: RomanNum3ral Date: Fri, 27 Mar 2026 07:29:59 +0000 Subject: [PATCH] Update master_node_install.sh --- master_node_install.sh | 241 +++++++++++++++++++++++++++++++++++++++-- 1 file changed, 229 insertions(+), 12 deletions(-) diff --git a/master_node_install.sh b/master_node_install.sh index be75b9f..470215a 100644 --- a/master_node_install.sh +++ b/master_node_install.sh @@ -4,14 +4,39 @@ set -Eeuo pipefail ######################################## # Arch Linux Kubernetes Control Plane # Fully automated master node installer +# + Helm +# + Traefik +# + cert-manager +# + Rancher ######################################## # ---------- Config ---------- POD_CIDR="${POD_CIDR:-192.168.0.0/16}" CALICO_VERSION="${CALICO_VERSION:-v3.31.4}" + KUBECONFIG_DIR_ROOT="/root/.kube" JOIN_COMMAND_FILE="/root/kubeadm-join-command.sh" +INSTALL_HELM="${INSTALL_HELM:-true}" +INSTALL_RANCHER="${INSTALL_RANCHER:-true}" + +# Single-node/lab convenience +ALLOW_WORKLOADS_ON_CONTROL_PLANE="${ALLOW_WORKLOADS_ON_CONTROL_PLANE:-true}" + +# Rancher settings +RANCHER_REPO_CHANNEL="${RANCHER_REPO_CHANNEL:-stable}" # stable | latest | alpha +RANCHER_BOOTSTRAP_PASSWORD="${RANCHER_BOOTSTRAP_PASSWORD:-}" +RANCHER_HOSTNAME="${RANCHER_HOSTNAME:-}" # if empty, auto-generate rancher..sslip.io +RANCHER_REPLICAS="${RANCHER_REPLICAS:-1}" +RANCHER_NAMESPACE="${RANCHER_NAMESPACE:-cattle-system}" + +# Traefik settings +TRAEFIK_NAMESPACE="${TRAEFIK_NAMESPACE:-traefik}" +TRAEFIK_INGRESS_CLASS_NAME="${TRAEFIK_INGRESS_CLASS_NAME:-traefik}" + +# cert-manager settings +CERT_MANAGER_NAMESPACE="${CERT_MANAGER_NAMESPACE:-cert-manager}" + # Detect the real invoking user when run with sudo REAL_USER="${SUDO_USER:-root}" REAL_HOME="$(getent passwd "$REAL_USER" | cut -d: -f6 || true)" @@ -37,6 +62,42 @@ die() { exit 1 } +# ---------- Helpers ---------- +require_cmd() { + command -v "$1" >/dev/null 2>&1 || die "Required command not found: $1" +} + +retry() { + local attempts="${1:-10}" + local sleep_seconds="${2:-5}" + shift 2 || true + + local n=1 + until "$@"; do + if (( n >= attempts )); then + return 1 + fi + warn "Command failed (attempt ${n}/${attempts}): $*" + sleep "${sleep_seconds}" + ((n++)) + done +} + +helm_repo_add_force() { + local name="$1" + local url="$2" + if helm repo list 2>/dev/null | awk '{print $1}' | grep -qx "${name}"; then + helm repo add "${name}" "${url}" --force-update >/dev/null + else + helm repo add "${name}" "${url}" >/dev/null + fi +} + +kubectl_ns_apply() { + local ns="$1" + kubectl create namespace "${ns}" --dry-run=client -o yaml | kubectl apply -f - +} + # ---------- Root check ---------- if [[ "${EUID}" -ne 0 ]]; then die "Run this script as root, for example: sudo ./master_node_install.sh" @@ -49,6 +110,8 @@ on_error() { warn "Useful diagnostics:" echo " journalctl -u containerd -u kubelet -b --no-pager | tail -n 200" echo " systemctl status containerd kubelet --no-pager" + echo " kubectl get nodes -o wide" + echo " kubectl get pods -A" exit "${exit_code}" } trap 'on_error $LINENO' ERR @@ -88,7 +151,12 @@ pacman -S --needed --noconfirm \ socat \ kubeadm \ kubectl \ - kubelet + kubelet \ + tar \ + gzip \ + jq \ + openssl \ + helm # ---------- Step 5: Kernel modules ---------- log "Configuring required kernel modules" @@ -123,9 +191,6 @@ fi # Ensure SystemdCgroup = true sed -ri 's/^\s*SystemdCgroup = false/SystemdCgroup = true/' /etc/containerd/config.toml -# Ensure sandbox image is left as default from containerd config or package -# No hardcoded pause image needed unless troubleshooting later - # ---------- Step 8: Enable services ---------- log "Enabling and starting containerd and kubelet" systemctl daemon-reload @@ -173,23 +238,159 @@ else warn "Could not determine invoking user's home directory; skipping user kubeconfig setup" fi -# ---------- Step 14: Install Calico ---------- +require_cmd kubectl +require_cmd kubeadm +require_cmd helm + +# ---------- Step 14: Wait for API ---------- +log "Waiting for Kubernetes API to become responsive" +retry 60 5 kubectl version --request-timeout=10s >/dev/null + +# ---------- Step 15: Optionally allow workloads on control-plane ---------- +if [[ "${ALLOW_WORKLOADS_ON_CONTROL_PLANE}" == "true" ]]; then + log "Allowing workloads on the control-plane node (single-node/lab mode)" + kubectl taint nodes --all node-role.kubernetes.io/control-plane- >/dev/null 2>&1 || true + kubectl taint nodes --all node-role.kubernetes.io/master- >/dev/null 2>&1 || true +fi + +# ---------- Step 16: Install Calico ---------- log "Installing Calico networking" kubectl apply -f "https://raw.githubusercontent.com/projectcalico/calico/${CALICO_VERSION}/manifests/calico.yaml" -# ---------- Step 15: Save worker join command ---------- +# ---------- Step 17: Wait for node readiness ---------- +log "Waiting for node(s) to become Ready" +kubectl wait --for=condition=Ready node --all --timeout=10m + +# ---------- Step 18: Wait for Calico ---------- +log "Waiting for Calico components" +kubectl -n kube-system rollout status daemonset/calico-node --timeout=10m || true +kubectl -n kube-system rollout status deployment/calico-kube-controllers --timeout=10m || true + +# ---------- Step 19: Save worker join command ---------- log "Saving worker join command" kubeadm token create --print-join-command > "${JOIN_COMMAND_FILE}" chmod 700 "${JOIN_COMMAND_FILE}" -# ---------- Step 16: Show cluster status ---------- -log "Waiting briefly for cluster components" -sleep 30 +# ---------- Step 20: Determine node info ---------- +log "Determining control-plane node information" +NODE_NAME="$(kubectl get nodes -o jsonpath='{.items[0].metadata.name}')" +NODE_IP="$(kubectl get node "${NODE_NAME}" -o jsonpath='{.status.addresses[?(@.type=="InternalIP")].address}')" +if [[ -z "${NODE_NAME}" || -z "${NODE_IP}" ]]; then + die "Failed to determine node name or node IP" +fi + +if [[ -z "${RANCHER_HOSTNAME}" ]]; then + RANCHER_HOSTNAME="rancher.${NODE_IP}.sslip.io" +fi + +if [[ -z "${RANCHER_BOOTSTRAP_PASSWORD}" ]]; then + RANCHER_BOOTSTRAP_PASSWORD="$(openssl rand -base64 24 | tr -d '\n' | tr '/+' 'AB' | cut -c1-20)" +fi + +echo "${RANCHER_BOOTSTRAP_PASSWORD}" >/root/rancher-bootstrap-password.txt +chmod 600 /root/rancher-bootstrap-password.txt + +# ---------- Step 21: Install Helm repos ---------- +if [[ "${INSTALL_HELM}" == "true" ]]; then + log "Configuring Helm repositories" + helm_repo_add_force traefik https://traefik.github.io/charts + helm_repo_add_force jetstack https://charts.jetstack.io + + case "${RANCHER_REPO_CHANNEL}" in + stable) + helm_repo_add_force rancher-stable https://releases.rancher.com/server-charts/stable + RANCHER_CHART="rancher-stable/rancher" + ;; + latest) + helm_repo_add_force rancher-latest https://releases.rancher.com/server-charts/latest + RANCHER_CHART="rancher-latest/rancher" + ;; + alpha) + helm_repo_add_force rancher-alpha https://releases.rancher.com/server-charts/alpha + RANCHER_CHART="rancher-alpha/rancher" + ;; + *) + die "Invalid RANCHER_REPO_CHANNEL: ${RANCHER_REPO_CHANNEL} (expected: stable, latest, alpha)" + ;; + esac + + helm repo update +fi + +# ---------- Step 22: Install Traefik ---------- +if [[ "${INSTALL_RANCHER}" == "true" ]]; then + log "Installing Traefik" + + kubectl_ns_apply "${TRAEFIK_NAMESPACE}" + + helm upgrade --install traefik traefik/traefik \ + --namespace "${TRAEFIK_NAMESPACE}" \ + --create-namespace \ + --set deployment.kind=DaemonSet \ + --set hostNetwork=true \ + --set dnsPolicy=ClusterFirstWithHostNet \ + --set ingressClass.enabled=true \ + --set ingressClass.isDefaultClass=true \ + --set ingressClass.name="${TRAEFIK_INGRESS_CLASS_NAME}" \ + --set providers.kubernetesIngress.enabled=true \ + --set ports.web.port=80 \ + --set ports.websecure.port=443 \ + --set service.enabled=false \ + --set logs.general.level=INFO \ + --wait \ + --timeout 15m + + log "Waiting for Traefik rollout" + kubectl -n "${TRAEFIK_NAMESPACE}" rollout status daemonset/traefik --timeout=15m +fi + +# ---------- Step 23: Install cert-manager ---------- +if [[ "${INSTALL_RANCHER}" == "true" ]]; then + log "Installing cert-manager" + + kubectl_ns_apply "${CERT_MANAGER_NAMESPACE}" + + helm upgrade --install cert-manager jetstack/cert-manager \ + --namespace "${CERT_MANAGER_NAMESPACE}" \ + --create-namespace \ + --set crds.enabled=true \ + --wait \ + --timeout 15m + + log "Waiting for cert-manager deployments" + kubectl -n "${CERT_MANAGER_NAMESPACE}" rollout status deployment/cert-manager --timeout=15m + kubectl -n "${CERT_MANAGER_NAMESPACE}" rollout status deployment/cert-manager-cainjector --timeout=15m + kubectl -n "${CERT_MANAGER_NAMESPACE}" rollout status deployment/cert-manager-webhook --timeout=15m +fi + +# ---------- Step 24: Install Rancher ---------- +if [[ "${INSTALL_RANCHER}" == "true" ]]; then + log "Installing Rancher" + + kubectl_ns_apply "${RANCHER_NAMESPACE}" + + helm upgrade --install rancher "${RANCHER_CHART}" \ + --namespace "${RANCHER_NAMESPACE}" \ + --create-namespace \ + --set hostname="${RANCHER_HOSTNAME}" \ + --set bootstrapPassword="${RANCHER_BOOTSTRAP_PASSWORD}" \ + --set replicas="${RANCHER_REPLICAS}" \ + --set ingress.ingressClassName="${TRAEFIK_INGRESS_CLASS_NAME}" \ + --wait \ + --timeout 20m + + log "Waiting for Rancher rollout" + kubectl -n "${RANCHER_NAMESPACE}" rollout status deployment/rancher --timeout=20m || true +fi + +# ---------- Step 25: Show cluster status ---------- log "Cluster status" kubectl get nodes -o wide || true echo kubectl get pods -A || true +echo +kubectl get ingress -A || true # ---------- Final output ---------- echo @@ -205,6 +406,22 @@ echo echo "To view it:" echo " sudo cat ${JOIN_COMMAND_FILE}" echo -echo "If this is a single-node lab cluster and you want to schedule normal pods on the control-plane node, run:" -echo " kubectl taint nodes --all node-role.kubernetes.io/control-plane-" -echo \ No newline at end of file + +if [[ "${INSTALL_RANCHER}" == "true" ]]; then + echo "Rancher install completed." + echo + echo "Rancher URL:" + echo " https://${RANCHER_HOSTNAME}" + echo + echo "Rancher bootstrap password saved to:" + echo " /root/rancher-bootstrap-password.txt" + echo + echo "To view it:" + echo " sudo cat /root/rancher-bootstrap-password.txt" + echo + echo "Notes:" + echo " - sslip.io is used automatically when RANCHER_HOSTNAME is not set." + echo " - Because Traefik is using host networking, access Rancher directly on this node's IP over 443." + echo " - If a local firewall is enabled, ensure ports 80 and 443 are allowed." + echo +fi \ No newline at end of file