Update master_node_install.sh
This commit is contained in:
parent
37fa381162
commit
c9e8981349
|
|
@ -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.<NODE_IP>.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
|
||||
|
||||
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
|
||||
Loading…
Reference in New Issue