Deploy Kubernetes Cluster from Scratch
1 | Author: Xiaohui Li |
Still Running K8s with Docker? Time to Upgrade!
This tutorial shows you how to deploy the latest Kubernetes 1.33.1 cluster on Rocky Linux 9.4, using the more native container runtime containerd 2.1.1 together with the powerful CLI tool nerdctl 2.1.2.
We’ll skip Docker entirely and go for a pure containerd adventure. The guide starts from zero, covering environment preparation, containerd configuration, nerdctl testing, Kubernetes installation and initialization, and finally cluster reset. Step by step, you’ll end up with a production‑ready, high‑quality cluster.
If you’re curious about nerdctl’s usability, or want to deploy Kubernetes without Docker, this article is for you.
Project Details
- K8s version: 1.33.1
- OS: Rocky Linux 9.4
- Master hostname:
k8s-master
- Worker hostname:
k8s-worker1
- containerd version: v2.1.1
Prepare DNS Resolution
Consistent names make kubeadm output, logs, and joins predictable. It also avoids reverse DNS surprises.
On all nodes, edit /etc/hosts
:
1 | cat >> /etc/hosts <<EOF |
Install nerdctl
Download and extract:
1 | wget https://github.com/containerd/nerdctl/releases/download/v2.1.2/nerdctl-full-2.1.2-linux-amd64.tar.gz |
Check containerd Version
1 | containerd -v |
Configure containerd
The default config lacks registry mirrors and uses upstream pause images that may be slow or blocked.
1 | mkdir /etc/containerd |
Replace pause image with a domestic mirror:
if you can access docker hub or k8s registry, you do not need do that.
1 | sed -i "s|sandbox = 'registry.k8s.io/pause:3.10'|sandbox = 'registry.cn-hangzhou.aliyuncs.com/google_containers/pause:3.10'|" /etc/containerd/config.toml |
Set registry config path:
if you can access docker hub or k8s registry, you do not need do that.
1 | sed -i '/\[plugins."io.containerd.cri.v1.images".registry\]/{n;s|.*| config_path = "/etc/containerd/certs.d"|}' /etc/containerd/config.toml |
Create registry mirror config:
1 | mkdir -p /etc/containerd/certs.d/docker.io |
Start Services
1 | systemctl daemon-reload |
Enable nerdctl Autocompletion
1 | nerdctl completion bash > /etc/bash_completion.d/nerdctl |
Create First Container
1 | nerdctl run -d -p 8000:80 --name container1 registry.myk8s.cn/library/nginx |
Enter container:
1 | nerdctl exec -it container1 /bin/bash |
Test:
1 | curl http://127.0.0.1:8000 |
Deploy Kubernetes
Disable Swap
Kubelet refuses to start when swap is enabled to ensure predictable memory accounting.
1 | swapoff -a |
Enable iptables Bridge
Kubernetes networking relies on bridged traffic being visible to iptables and routing packets between pods and services.
1 | cat <<EOF | sudo tee /etc/modules-load.d/k8s.conf |
Install kubeadm, kubelet, kubectl
Faster, consistent RPM delivery aligned with the Kubernetes version you target.
1 | cat <<EOF | sudo tee /etc/yum.repos.d/kubernetes.repo |
Enable completion:
1 | kubectl completion bash > /etc/bash_completion.d/kubectl |
Configure crictl
crictl is the primary tool to inspect pods/images directly via CRI when debugging kubelet/runtime issues.
1 | cat > /etc/crictl.yaml <<'EOF' |
Test:
1 | crictl images |
Open Firewall Ports
1 | sudo firewall-cmd --zone=public --add-protocol=ipip --permanent |
Initialize Cluster
A config file centralizes cluster parameters and makes the deployment reproducible.
1 | kubeadm config print init-defaults > kubeadm.yaml |
What these fields do:
advertiseAddress: The API server’s reachable IP for nodes.
name: Cluster name for context identification.
imageRepository: Speeds component image pulls using a regional mirror.
podSubnet: Must match your CNI configuration (Calico CIDR below).
Initialize:
1 | kubeadm init --config kubeadm.yaml |
1 |
|
Deploy Calico Network
1 | kubectl create -f https://raw.githubusercontent.com/projectcalico/calico/refs/tags/v3.30.0/manifests/tigera-operator.yaml |
Check pods:
1 | kubectl get pod -A |
Join Worker Nodes
On worker:
1 | kubeadm join 192.168.8.200:6443 --token <token> \ |
Label:
1 | kubectl label nodes k8s-worker1 node-role.kubernetes.io/worker= |
Reset Cluster (Optional)
1 | kubeadm reset --cri-socket=unix:///run/containerd/containerd.sock |
Conclusion
We have successfully deployed a Kubernetes cluster on Rocky Linux using containerd and nerdctl, integrated Calico networking, joined worker nodes, and learned how to reset the cluster. This provides a stable environment for containerized applications.