Update arch_install.sh
This commit is contained in:
parent
394dc98229
commit
967d168b1f
267
arch_install.sh
267
arch_install.sh
|
|
@ -4,51 +4,53 @@ set -Eeuo pipefail
|
||||||
###############################################################################
|
###############################################################################
|
||||||
# Arch Linux Kubernetes bootstrap script
|
# Arch Linux Kubernetes bootstrap script
|
||||||
#
|
#
|
||||||
|
# Purpose:
|
||||||
|
# Fully automate a single-node Kubernetes control-plane install on Arch Linux
|
||||||
|
# using:
|
||||||
|
# - containerd
|
||||||
|
# - kubeadm
|
||||||
|
# - kubectl
|
||||||
|
# - Flannel CNI
|
||||||
|
#
|
||||||
# What this script does:
|
# What this script does:
|
||||||
# 1. Updates the system
|
# 1. Updates the system
|
||||||
# 2. Replaces legacy iptables with iptables-nft in one transaction
|
# 2. Replaces legacy iptables with iptables-nft in the same pacman transaction
|
||||||
# 3. Installs Kubernetes packages and containerd
|
# 3. Installs Kubernetes packages and dependencies
|
||||||
# 4. Configures containerd to use systemd cgroups
|
# 4. Configures containerd for systemd cgroups
|
||||||
# 5. Enables required kernel modules and sysctl settings
|
# 5. Enables required kernel modules and sysctl values
|
||||||
# 6. Disables swap now and on reboot
|
# 6. Disables swap now and on boot
|
||||||
# 7. Enables containerd and kubelet
|
# 7. Enables and starts containerd and kubelet
|
||||||
# 8. Initializes a single control-plane node with kubeadm
|
# 8. Initializes the cluster with kubeadm (if not already initialized)
|
||||||
# 9. Configures kubectl for the invoking user
|
# 9. Configures kubectl for the invoking user
|
||||||
# 10. Installs Flannel CNI
|
# 10. Installs Flannel CNI
|
||||||
# 11. Optionally allows scheduling pods on the control-plane node
|
# 11. Optionally removes the control-plane taint for single-node use
|
||||||
#
|
#
|
||||||
# Notes:
|
# Usage:
|
||||||
# - This script is intended for a fresh/single-node lab setup.
|
# chmod +x arch_install.sh
|
||||||
# - Re-running is mostly safe; it skips kubeadm init if already initialized.
|
# sudo ./arch_install.sh
|
||||||
# - Run it as:
|
#
|
||||||
# sudo ./arch_k8s_bootstrap.sh
|
# Re-run behavior:
|
||||||
|
# This script is written to be mostly idempotent. If the cluster is already
|
||||||
|
# initialized, it will skip kubeadm init and preserve the existing cluster.
|
||||||
###############################################################################
|
###############################################################################
|
||||||
|
|
||||||
#######################################
|
#######################################
|
||||||
# User-tunable variables
|
# User-configurable values
|
||||||
#######################################
|
#######################################
|
||||||
|
|
||||||
# Pod CIDR required by Flannel docs
|
# Pod CIDR for Flannel
|
||||||
POD_CIDR="${POD_CIDR:-10.244.0.0/16}"
|
POD_CIDR="${POD_CIDR:-10.244.0.0/16}"
|
||||||
|
|
||||||
# Flannel manifest URL from current flannel docs/releases
|
# Flannel manifest
|
||||||
FLANNEL_MANIFEST_URL="${FLANNEL_MANIFEST_URL:-https://github.com/flannel-io/flannel/releases/latest/download/kube-flannel.yml}"
|
FLANNEL_URL="${FLANNEL_URL:-https://github.com/flannel-io/flannel/releases/latest/download/kube-flannel.yml}"
|
||||||
|
|
||||||
# Set to "true" if this is a single-node lab and you want workloads allowed
|
# Set to true if this is a single-node lab and you want normal workloads to run
|
||||||
# on the control-plane node after setup.
|
# on the control-plane node.
|
||||||
ALLOW_PODS_ON_CONTROL_PLANE="${ALLOW_PODS_ON_CONTROL_PLANE:-true}"
|
ALLOW_SINGLE_NODE_SCHEDULING="${ALLOW_SINGLE_NODE_SCHEDULING:-true}"
|
||||||
|
|
||||||
# The user who should receive ~/.kube/config.
|
# Which user should receive ~/.kube/config
|
||||||
# If script is run with sudo, prefer the original user.
|
|
||||||
TARGET_USER="${SUDO_USER:-root}"
|
TARGET_USER="${SUDO_USER:-root}"
|
||||||
|
|
||||||
# Figure out that user's home directory safely.
|
|
||||||
if [[ "${TARGET_USER}" == "root" ]]; then
|
|
||||||
TARGET_HOME="/root"
|
|
||||||
else
|
|
||||||
TARGET_HOME="$(getent passwd "${TARGET_USER}" | cut -d: -f6)"
|
|
||||||
fi
|
|
||||||
|
|
||||||
#######################################
|
#######################################
|
||||||
# Helper functions
|
# Helper functions
|
||||||
#######################################
|
#######################################
|
||||||
|
|
@ -62,42 +64,58 @@ die() {
|
||||||
exit 1
|
exit 1
|
||||||
}
|
}
|
||||||
|
|
||||||
require_root() {
|
run_as_target_user() {
|
||||||
[[ "${EUID}" -eq 0 ]] || die "Run this script with sudo or as root."
|
if [[ "$TARGET_USER" == "root" ]]; then
|
||||||
}
|
"$@"
|
||||||
|
else
|
||||||
command_exists() {
|
sudo -u "$TARGET_USER" "$@"
|
||||||
command -v "$1" >/dev/null 2>&1
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
#######################################
|
#######################################
|
||||||
# Sanity checks
|
# Root check
|
||||||
#######################################
|
#######################################
|
||||||
|
|
||||||
require_root
|
if [[ "${EUID}" -ne 0 ]]; then
|
||||||
|
die "Run this script with sudo or as root."
|
||||||
if [[ -z "${TARGET_HOME}" || ! -d "${TARGET_HOME}" ]]; then
|
|
||||||
die "Could not determine home directory for target user: ${TARGET_USER}"
|
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
#######################################
|
||||||
|
# Resolve target home directory
|
||||||
|
#######################################
|
||||||
|
|
||||||
|
if [[ "$TARGET_USER" == "root" ]]; then
|
||||||
|
TARGET_HOME="/root"
|
||||||
|
else
|
||||||
|
TARGET_HOME="$(getent passwd "$TARGET_USER" | cut -d: -f6)"
|
||||||
|
fi
|
||||||
|
|
||||||
|
[[ -n "${TARGET_HOME:-}" && -d "$TARGET_HOME" ]] || die "Could not determine home directory for $TARGET_USER"
|
||||||
|
|
||||||
log "Starting Arch Kubernetes bootstrap"
|
log "Starting Arch Kubernetes bootstrap"
|
||||||
log "Target kubectl user: ${TARGET_USER}"
|
log "Target kubectl user: $TARGET_USER"
|
||||||
log "Target home: ${TARGET_HOME}"
|
log "Target home: $TARGET_HOME"
|
||||||
|
|
||||||
#######################################
|
#######################################
|
||||||
# 1) Fully update Arch
|
# 1) Update system
|
||||||
#######################################
|
#######################################
|
||||||
|
|
||||||
log "Updating package databases and upgrading system"
|
log "Updating package databases and upgrading system"
|
||||||
pacman -Syu --noconfirm
|
pacman -Syu --noconfirm
|
||||||
|
|
||||||
#######################################
|
#######################################
|
||||||
# 2) Install required packages
|
# 2) Install packages
|
||||||
#
|
#
|
||||||
# Important:
|
# Important:
|
||||||
# - iptables-nft must replace legacy iptables in the SAME transaction.
|
# iptables-nft conflicts with legacy iptables, so we must allow pacman to
|
||||||
# - We intentionally do not remove iptables first because that can break
|
# replace iptables during the SAME transaction.
|
||||||
# dependency resolution temporarily for packages that need libxtables.
|
#
|
||||||
|
# Why we use yes | pacman here:
|
||||||
|
# pacman may ask:
|
||||||
|
# "Remove iptables? [y/N]"
|
||||||
|
# and then:
|
||||||
|
# "Proceed with installation? [Y/n]"
|
||||||
|
# Using yes feeds "y" to both prompts so the script does not stop there.
|
||||||
#######################################
|
#######################################
|
||||||
|
|
||||||
log "Installing container runtime, Kubernetes tools, networking tools, and iptables-nft"
|
log "Installing container runtime, Kubernetes tools, networking tools, and iptables-nft"
|
||||||
|
|
@ -125,45 +143,24 @@ log "Verifying iptables backend"
|
||||||
iptables --version || true
|
iptables --version || true
|
||||||
|
|
||||||
#######################################
|
#######################################
|
||||||
# 4) Enable and configure containerd
|
# 4) Persist required kernel modules
|
||||||
#
|
|
||||||
# kubelet works best with containerd configured to use systemd cgroups.
|
|
||||||
#######################################
|
#######################################
|
||||||
|
|
||||||
log "Enabling and starting containerd"
|
log "Writing required kernel modules"
|
||||||
systemctl enable --now containerd.service
|
|
||||||
|
|
||||||
log "Creating default containerd config if missing"
|
|
||||||
mkdir -p /etc/containerd
|
|
||||||
if [[ ! -f /etc/containerd/config.toml ]]; then
|
|
||||||
containerd config default > /etc/containerd/config.toml
|
|
||||||
fi
|
|
||||||
|
|
||||||
log "Setting SystemdCgroup = true in /etc/containerd/config.toml"
|
|
||||||
sed -i 's/SystemdCgroup = false/SystemdCgroup = true/' /etc/containerd/config.toml
|
|
||||||
|
|
||||||
log "Restarting containerd to apply config"
|
|
||||||
systemctl restart containerd.service
|
|
||||||
|
|
||||||
#######################################
|
|
||||||
# 5) Load required kernel modules now and on boot
|
|
||||||
#######################################
|
|
||||||
|
|
||||||
log "Persisting required kernel modules"
|
|
||||||
cat > /etc/modules-load.d/k8s.conf <<'EOF'
|
cat > /etc/modules-load.d/k8s.conf <<'EOF'
|
||||||
overlay
|
overlay
|
||||||
br_netfilter
|
br_netfilter
|
||||||
EOF
|
EOF
|
||||||
|
|
||||||
log "Loading kernel modules immediately"
|
log "Loading kernel modules now"
|
||||||
modprobe overlay
|
modprobe overlay
|
||||||
modprobe br_netfilter
|
modprobe br_netfilter
|
||||||
|
|
||||||
#######################################
|
#######################################
|
||||||
# 6) Apply required sysctl settings now and on boot
|
# 5) Persist required sysctl settings
|
||||||
#######################################
|
#######################################
|
||||||
|
|
||||||
log "Persisting required sysctl settings"
|
log "Writing required sysctl settings"
|
||||||
cat > /etc/sysctl.d/k8s.conf <<'EOF'
|
cat > /etc/sysctl.d/k8s.conf <<'EOF'
|
||||||
net.bridge.bridge-nf-call-iptables = 1
|
net.bridge.bridge-nf-call-iptables = 1
|
||||||
net.bridge.bridge-nf-call-ip6tables = 1
|
net.bridge.bridge-nf-call-ip6tables = 1
|
||||||
|
|
@ -174,138 +171,156 @@ log "Applying sysctl settings"
|
||||||
sysctl --system
|
sysctl --system
|
||||||
|
|
||||||
#######################################
|
#######################################
|
||||||
# 7) Disable swap now and comment swap entries in /etc/fstab
|
# 6) Disable swap now and on boot
|
||||||
#######################################
|
#######################################
|
||||||
|
|
||||||
log "Disabling swap immediately"
|
log "Disabling swap immediately"
|
||||||
swapoff -a || true
|
swapoff -a || true
|
||||||
|
|
||||||
log "Commenting active swap entries in /etc/fstab"
|
log "Commenting swap entries in /etc/fstab"
|
||||||
if [[ -f /etc/fstab ]]; then
|
if [[ -f /etc/fstab ]]; then
|
||||||
cp /etc/fstab /etc/fstab.bak.$(date +%s)
|
cp /etc/fstab "/etc/fstab.bak.$(date +%s)"
|
||||||
sed -i '/^[^#].*\sswap\s/s/^/# /' /etc/fstab
|
sed -i '/^[^#].*\sswap\s/s/^/# /' /etc/fstab
|
||||||
fi
|
fi
|
||||||
|
|
||||||
#######################################
|
#######################################
|
||||||
# 8) Enable kubelet
|
# 7) Configure containerd
|
||||||
#
|
#
|
||||||
# kubelet may show as active but waiting until kubeadm init finishes; that is
|
# Kubernetes works better with systemd cgroups.
|
||||||
# normal before the control plane exists.
|
|
||||||
#######################################
|
#######################################
|
||||||
|
|
||||||
|
log "Preparing /etc/containerd"
|
||||||
|
mkdir -p /etc/containerd
|
||||||
|
|
||||||
|
log "Generating default containerd config if needed"
|
||||||
|
if [[ ! -f /etc/containerd/config.toml ]]; then
|
||||||
|
containerd config default > /etc/containerd/config.toml
|
||||||
|
fi
|
||||||
|
|
||||||
|
log "Ensuring SystemdCgroup = true"
|
||||||
|
sed -i 's/SystemdCgroup = false/SystemdCgroup = true/' /etc/containerd/config.toml
|
||||||
|
|
||||||
|
#######################################
|
||||||
|
# 8) Enable and start services
|
||||||
|
#######################################
|
||||||
|
|
||||||
|
log "Enabling and starting containerd"
|
||||||
|
systemctl enable --now containerd.service
|
||||||
|
|
||||||
log "Enabling and starting kubelet"
|
log "Enabling and starting kubelet"
|
||||||
systemctl enable --now kubelet.service
|
systemctl enable --now kubelet.service
|
||||||
|
|
||||||
#######################################
|
log "Showing containerd status"
|
||||||
# 9) Preflight visibility
|
|
||||||
#######################################
|
|
||||||
|
|
||||||
log "Container runtime status"
|
|
||||||
systemctl --no-pager --full status containerd.service || true
|
systemctl --no-pager --full status containerd.service || true
|
||||||
|
|
||||||
log "Kubelet status"
|
log "Showing kubelet status"
|
||||||
systemctl --no-pager --full status kubelet.service || true
|
systemctl --no-pager --full status kubelet.service || true
|
||||||
|
|
||||||
#######################################
|
#######################################
|
||||||
# 10) Initialize cluster if not already initialized
|
# 9) Initialize cluster if needed
|
||||||
#######################################
|
#######################################
|
||||||
|
|
||||||
if [[ ! -f /etc/kubernetes/admin.conf ]]; then
|
if [[ ! -f /etc/kubernetes/admin.conf ]]; then
|
||||||
log "Initializing Kubernetes control plane with kubeadm"
|
log "Initializing Kubernetes control plane"
|
||||||
kubeadm init --pod-network-cidr="${POD_CIDR}"
|
kubeadm init --pod-network-cidr="$POD_CIDR"
|
||||||
else
|
else
|
||||||
log "Skipping kubeadm init because /etc/kubernetes/admin.conf already exists"
|
log "Cluster already initialized; skipping kubeadm init"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
#######################################
|
#######################################
|
||||||
# 11) Configure kubectl for target user
|
# 10) Configure kubectl for target user
|
||||||
#######################################
|
#######################################
|
||||||
|
|
||||||
log "Configuring kubectl for ${TARGET_USER}"
|
log "Configuring kubectl for $TARGET_USER"
|
||||||
mkdir -p "${TARGET_HOME}/.kube"
|
mkdir -p "$TARGET_HOME/.kube"
|
||||||
cp -f /etc/kubernetes/admin.conf "${TARGET_HOME}/.kube/config"
|
cp -f /etc/kubernetes/admin.conf "$TARGET_HOME/.kube/config"
|
||||||
chown -R "${TARGET_USER}:${TARGET_USER}" "${TARGET_HOME}/.kube"
|
chown -R "$TARGET_USER:$TARGET_USER" "$TARGET_HOME/.kube"
|
||||||
chmod 700 "${TARGET_HOME}/.kube"
|
chmod 700 "$TARGET_HOME/.kube"
|
||||||
chmod 600 "${TARGET_HOME}/.kube/config"
|
chmod 600 "$TARGET_HOME/.kube/config"
|
||||||
|
|
||||||
#######################################
|
#######################################
|
||||||
# 12) Wait briefly for API server to become responsive
|
# 11) Wait for API responsiveness
|
||||||
#######################################
|
#######################################
|
||||||
|
|
||||||
log "Waiting for Kubernetes API to become reachable"
|
log "Waiting for Kubernetes API server"
|
||||||
for _ in $(seq 1 60); do
|
for _ in $(seq 1 90); do
|
||||||
if sudo -u "${TARGET_USER}" kubectl --kubeconfig="${TARGET_HOME}/.kube/config" get nodes >/dev/null 2>&1; then
|
if run_as_target_user kubectl --kubeconfig="$TARGET_HOME/.kube/config" get nodes >/dev/null 2>&1; then
|
||||||
break
|
break
|
||||||
fi
|
fi
|
||||||
sleep 2
|
sleep 2
|
||||||
done
|
done
|
||||||
|
|
||||||
#######################################
|
#######################################
|
||||||
# 13) Install Flannel if not already present
|
# 12) Install Flannel if missing
|
||||||
#######################################
|
#######################################
|
||||||
|
|
||||||
log "Installing Flannel CNI"
|
log "Installing Flannel CNI if needed"
|
||||||
if ! sudo -u "${TARGET_USER}" kubectl --kubeconfig="${TARGET_HOME}/.kube/config" get namespace kube-flannel >/dev/null 2>&1; then
|
if ! run_as_target_user kubectl --kubeconfig="$TARGET_HOME/.kube/config" get namespace kube-flannel >/dev/null 2>&1; then
|
||||||
sudo -u "${TARGET_USER}" kubectl --kubeconfig="${TARGET_HOME}/.kube/config" apply -f "${FLANNEL_MANIFEST_URL}"
|
run_as_target_user kubectl --kubeconfig="$TARGET_HOME/.kube/config" apply -f "$FLANNEL_URL"
|
||||||
else
|
else
|
||||||
log "kube-flannel namespace already exists; skipping Flannel install"
|
log "Flannel already present; skipping apply"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
#######################################
|
#######################################
|
||||||
# 14) Optionally allow scheduling on single-node control plane
|
# 13) Allow single-node scheduling if requested
|
||||||
#######################################
|
#######################################
|
||||||
|
|
||||||
if [[ "${ALLOW_PODS_ON_CONTROL_PLANE}" == "true" ]]; then
|
if [[ "$ALLOW_SINGLE_NODE_SCHEDULING" == "true" ]]; then
|
||||||
log "Allowing workloads on the control-plane node (single-node lab mode)"
|
log "Removing control-plane taint for single-node scheduling"
|
||||||
sudo -u "${TARGET_USER}" kubectl --kubeconfig="${TARGET_HOME}/.kube/config" taint nodes --all node-role.kubernetes.io/control-plane- || true
|
run_as_target_user kubectl --kubeconfig="$TARGET_HOME/.kube/config" taint nodes --all node-role.kubernetes.io/control-plane- || true
|
||||||
else
|
else
|
||||||
log "Leaving default control-plane taint in place"
|
log "Leaving control-plane taint in place"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
#######################################
|
#######################################
|
||||||
# 15) Final status / useful commands
|
# 14) Final status
|
||||||
#######################################
|
#######################################
|
||||||
|
|
||||||
log "Final checks"
|
log "Final cluster checks"
|
||||||
|
|
||||||
echo
|
echo
|
||||||
echo "==== iptables backend ===="
|
echo "==== iptables backend ===="
|
||||||
iptables --version || true
|
iptables --version || true
|
||||||
|
|
||||||
echo
|
echo
|
||||||
echo "==== containerd info ===="
|
echo "==== containerd status ===="
|
||||||
crictl info >/dev/null 2>&1 && echo "crictl can talk to the runtime" || echo "crictl check did not succeed yet"
|
systemctl --no-pager --full status containerd.service || true
|
||||||
|
|
||||||
|
echo
|
||||||
|
echo "==== kubelet status ===="
|
||||||
|
systemctl --no-pager --full status kubelet.service || true
|
||||||
|
|
||||||
echo
|
echo
|
||||||
echo "==== nodes ===="
|
echo "==== nodes ===="
|
||||||
sudo -u "${TARGET_USER}" kubectl --kubeconfig="${TARGET_HOME}/.kube/config" get nodes -o wide || true
|
run_as_target_user kubectl --kubeconfig="$TARGET_HOME/.kube/config" get nodes -o wide || true
|
||||||
|
|
||||||
echo
|
echo
|
||||||
echo "==== pods (all namespaces) ===="
|
echo "==== pods (all namespaces) ===="
|
||||||
sudo -u "${TARGET_USER}" kubectl --kubeconfig="${TARGET_HOME}/.kube/config" get pods -A -o wide || true
|
run_as_target_user kubectl --kubeconfig="$TARGET_HOME/.kube/config" get pods -A -o wide || true
|
||||||
|
|
||||||
cat <<EOF
|
cat <<EOF
|
||||||
|
|
||||||
Bootstrap complete.
|
Bootstrap complete.
|
||||||
|
|
||||||
kubectl is configured for:
|
kubectl config:
|
||||||
user: ${TARGET_USER}
|
User: $TARGET_USER
|
||||||
config: ${TARGET_HOME}/.kube/config
|
Config: $TARGET_HOME/.kube/config
|
||||||
|
|
||||||
Common next commands:
|
Useful commands:
|
||||||
kubectl get nodes
|
kubectl get nodes
|
||||||
kubectl get pods -A
|
kubectl get pods -A
|
||||||
kubectl cluster-info
|
kubectl cluster-info
|
||||||
|
|
||||||
If the node is not Ready yet, wait a minute and re-run:
|
Test workload:
|
||||||
kubectl get nodes
|
|
||||||
kubectl get pods -A
|
|
||||||
|
|
||||||
To test the cluster:
|
|
||||||
kubectl create deployment nginx --image=nginx
|
kubectl create deployment nginx --image=nginx
|
||||||
kubectl expose deployment nginx --port=80 --type=NodePort
|
kubectl expose deployment nginx --port=80 --type=NodePort
|
||||||
kubectl get svc
|
kubectl get svc
|
||||||
|
|
||||||
To join worker nodes later, generate a join command with:
|
Worker join command:
|
||||||
kubeadm token create --print-join-command
|
kubeadm token create --print-join-command
|
||||||
|
|
||||||
|
If the node is still NotReady immediately after install, wait 30-60 seconds and re-check:
|
||||||
|
kubectl get nodes
|
||||||
|
kubectl get pods -A
|
||||||
|
|
||||||
EOF
|
EOF
|
||||||
Loading…
Reference in New Issue