Installing Kubernetes is like nailing jelly to the ceiling. I might write a tutorial on what’s going on here, but right now I’m just going to dump a script that works.
Realistically, there’s no point in running Kubernetes on a single host – its for clustering. But just to prove it’s possible, this will do it. You can’t normally run a pod on a control node, but with one node you can remove the taint and do it anyway.
I had so many goes at doing this that I wrote this script so I could automate it until I got it right. You can run this script, or do it a command at a time (probably better) as this is only known to work on one particular configuration.
I’m NOT using dnf – I’m getting the packages direct from github because that way I’ll get versions that work. There are plenty of versions in repos that don’t play nice together. I’ve defined the versions up at the top to make it easy to fiddle.
And as a bonus there’s a “hello” pod installed, running nginx. You can pull its “welcome” page using curl.
#!/bin/bash
set -euo pipefail
K8S_VERSION="v1.29.15"
CONTAINERD_VERSION="1.7.5"
APISERVER_IP=$(hostname -I | awk '{print $1}')
# Choose a published version (e.g., v1.32.0) as 1.29 isn't.
CRICTL_VERSION="v1.32.0"
CNI_VER="v1.1.1" # stable version
echo "=== Disable swap ==="
swapoff -a
sed -i '/swap/d' /etc/fstab
echo "=== Install dependencies ==="
dnf install -y curl tar wget socat conntrack iptables iproute-tc git
echo "=== Install containerd from GitHub ==="
curl -LO https://github.com/containerd/containerd/releases/download/v${CONTAINERD_VERSION}/containerd-${CONTAINERD_VERSION}-linux-amd64.tar.gz
tar Cxzvf /usr/local containerd-${CONTAINERD_VERSION}-linux-amd64.tar.gz
cat <<EOF > /etc/systemd/system/containerd.service
[Unit]
Description=containerd container runtime
After=network.target
[Service]
ExecStart=/usr/local/bin/containerd
Restart=always
RestartSec=5
Delegate=yes
KillMode=process
OOMScoreAdjust=-999
[Install]
WantedBy=multi-user.target
EOF
mkdir -p /etc/containerd
/usr/local/bin/containerd config default > /etc/containerd/config.toml
# Edit /etc/containerd/config.toml
sed -i 's/SystemdCgroup = false/SystemdCgroup = true/' /etc/containerd/config.toml
systemctl daemon-reload
systemctl enable --now containerd
systemctl restart containerd
echo "=== Install kubeadm/kubelet/kubectl from dl.k8s.io ==="
mkdir -p /usr/local/bin
cd /usr/local/bin
for BIN in kubeadm kubelet kubectl; do
curl -LO https://dl.k8s.io/release/${K8S_VERSION}/bin/linux/amd64/${BIN}
chmod +x ${BIN}
done
# --- Set up kubelet systemd service (default just doesn't work) ---
cat <<EOF > /etc/systemd/system/kubelet.service
[Unit]
Description=Kubernetes Kubelet
After=network.target containerd.service
Requires=containerd.service
[Service]
ExecStart=/usr/local/bin/kubelet \
--kubeconfig=/etc/kubernetes/kubelet.conf \
--config=/var/lib/kubelet/config.yaml \
--container-runtime-endpoint=unix:///run/containerd/containerd.sock \
--cgroup-driver=systemd \
--v=2
Restart=always
RestartSec=5
[Install]
WantedBy=multi-user.target
EOF
#mkdir -p /etc/systemd/system/kubelet.service.d
#cat <<EOF > /etc/systemd/system/kubelet.service.d/10-containerd.conf
#[Service]
#Environment="KUBELET_EXTRA_ARGS=--container-runtime=remote --container-runtime-endpoint=unix:///run/containerd/containerd.sock --runtime-request-timeout=15m"
#EOF
systemctl daemon-reload
systemctl enable --now kubelet
systemctl restart kubelet
# Fix firewall (or turn it off, as it's one variable too many!)
echo "=== Open firewall ports ==="
firewall-cmd --permanent --add-port=6443/tcp
firewall-cmd --permanent --add-port=10250/tcp
firewall-cmd --reload
# Download and extract crictl
curl -LO https://github.com/kubernetes-sigs/cri-tools/releases/download/${CRICTL_VERSION}/crictl-${CRICTL_VERSION}-linux-amd64.tar.gz
tar zxvf crictl-${CRICTL_VERSION}-linux-amd64.tar.gz -C /usr/local/bin
# Make it executable
chmod +x /usr/local/bin/crictl
# Clean up
rm -f crictl-${CRICTL_VERSION}-linux-amd64.tar.gz
# Fix preflight network requirements
modprobe br_netfilter
echo 1 > /proc/sys/net/bridge/bridge-nf-call-iptables
echo 1 > /proc/sys/net/ipv4/ip_forward
# Make persistent if you want
cat <<EOF > /etc/sysctl.d/99-kubernetes.conf
net.bridge.bridge-nf-call-iptables = 1
net.ipv4.ip_forward = 1
net.bridge.bridge-nf-call-ip6tables = 1
EOF
sysctl --system
# echo "Get the pause image and check it's worked using grep"
ctr images pull k8s.gcr.io/pause:3.10
ctr images ls | grep pause
echo "=== Pull the k8s images ==="
kubeadm config images pull
echo "=== Initialize Kubernetes cluster ==="
kubeadm init \
--pod-network-cidr=10.244.0.0/16 \
--cri-socket /run/containerd/containerd.sock \
--apiserver-advertise-address=${APISERVER_IP}
echo "=== Configure kubectl for root user ==="
mkdir -p /root/.kube
cp /etc/kubernetes/admin.conf /root/.kube/config
chmod 600 /root/.kube/config
export KUBECONFIG=/root/.kube/config
echo "=== Install Flannel CNI ==="
kubectl apply -f https://raw.githubusercontent.com/flannel-io/flannel/master/Documentation/kube-flannel.yml
# Get the rest of the CNI plugins (because they will have disappeared!)
mkdir -p /opt/cni/bin
curl -L https://github.com/containernetworking/plugins/releases/download/$CNI_VER/cni-plugins-linux-amd64-$CNI_VER.tgz | tar -xz -C /opt/cni/bin
echo "=== Remove control-plane taint for single-node ==="
kubectl taint nodes --all node-role.kubernetes.io/control-plane-
echo "=== Deploy Hello World NodePort ==="
kubectl create deployment hello --image=nginx --replicas=1
kubectl expose deployment hello --type=NodePort --port=80
kubectl get svc
echo " To get the nginx hello page use curl APISERVER_IP:<port from above after :>"
exit
As a bonus, here’s a script to “clean up” after a bad attempt at kubeadm init. kumeadm reset doesn’t do enough!
#!/bin/sh
# Nuclear reset
# This code cleans up after a bad attempt at configuration (kubeadm init)
systemctl stop kubelet
systemctl stop containerd
systemctl disable kubelet
systemctl disable containerd
kubeadm reset -f
rm -rf /etc/kubernetes
rm -rf /var/lib/kubelet/*
rm -rf /var/lib/etcd
systemctl stop containerd
rm -rf /var/lib/containerd/*
echo Checking ports
ss -lntp | grep -E "6443|10250|10251|10252|10257|10258|10259"
echo Anything come up? Please kill -9 the PID







