Kubernetes Installation and Cluster Setup
Learn Kubernetes Installation and Cluster Setup with plain-English explanations and real examples..
20+ years shipping production infrastructure and CI/CD at scale. Drawn from code that ran under real load.
- ✓Two or more Ubuntu 22.04 LTS nodes (physical or VM), sudo access, 2 CPU cores and 2 GB RAM per node, network connectivity between nodes, basic Linux command-line knowledge, familiarity with YAML syntax.
Think of Kubernetes Installation and Cluster Setup like a tool in your DevOps toolkit — once you understand what it does and when to reach for it, managing Kubernetes clusters becomes second nature.
Your first production Kubernetes cluster will fail. Not because Kubernetes is broken, but because you skipped the boring parts: networking, storage, and security. I've seen teams spend weeks on YAML only to realize their cluster can't even pull images from a private registry. Kubernetes installation isn't just running a script—it's designing a control plane that survives a node failure, a network that doesn't drop packets, and a storage backend that doesn't corrupt data. This guide walks you through a production-grade setup using kubeadm, from prerequisites to cluster validation. No fluff, just the steps that matter.
Prerequisites and Environment Preparation
Before installing Kubernetes, your nodes must meet specific requirements. You need at least two machines (physical or virtual) running Ubuntu 22.04 LTS or CentOS 7/8. Each node should have a minimum of 2 CPU cores, 2 GB RAM, and 20 GB free disk space. All nodes must have unique hostnames, MAC addresses, and product_uuids. Disable swap: sudo swapoff -a and remove swap entries from /etc/fstab. Ensure the container runtime is installed—containerd is recommended over Docker. Install containerd from the official Docker repository. Configure the kernel modules overlay and br_netfilter: sudo modprobe overlay && sudo modprobe br_netfilter. Set sysctl params: net.bridge.bridge-nf-call-iptables = 1, net.ipv4.ip_forward = 1. Apply with sudo sysctl --system. These steps prevent common networking and resource issues during cluster initialization.
free -h and ensure Swap line shows 0.Installing kubeadm, kubelet, and kubectl
With the environment ready, install the Kubernetes components. kubeadm initializes the cluster, kubelet runs on all nodes, and kubectl is the CLI. Add the Kubernetes repository and install specific versions to avoid compatibility issues. Use version 1.28.x as of this writing. Pin the versions to prevent accidental upgrades. On all nodes, run: sudo apt-get install -y kubelet=1.28.0-00 kubeadm=1.28.0-00 kubectl=1.28.0-00. Hold the packages: sudo apt-mark hold kubelet kubeadm kubectl. This ensures the cluster remains stable. After installation, enable and start kubelet: sudo systemctl enable --now kubelet. Verify with kubeadm version and kubectl version --client.
apt-mark hold or equivalent for your package manager.Initializing the Control Plane with kubeadm
On the control plane node, initialize the cluster. kubeadm init sets up the control plane components: API server, controller manager, scheduler, and etcd. Choose a pod network CIDR that doesn't conflict with your existing network. Common choices: 10.244.0.0/16 for Flannel, 10.96.0.0/12 for services. Run: sudo kubeadm init --pod-network-cidr=10.244.0.0/16. This downloads images and starts containers. After success, you'll get a kubeconfig file and a join token. Copy the kubeconfig to your user directory: mkdir -p $HOME/.kube && sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config && sudo chown $(id -u):$(id -g) $HOME/.kube/config. Verify with kubectl get nodes—the control plane node should show as NotReady until a network plugin is installed.
Installing a Pod Network (CNI Plugin)
A Container Network Interface (CNI) plugin enables pod-to-pod communication. Without it, nodes remain NotReady and pods cannot communicate. Choose a CNI that fits your environment: Flannel (simple overlay), Calico (network policies), Weave (easy setup), or Cilium (eBPF). For this guide, install Flannel: kubectl apply -f https://raw.githubusercontent.com/flannel-io/flannel/master/Documentation/kube-flannel.yml. After applying, check nodes: kubectl get nodes should show Ready. If nodes stay NotReady, check kubelet logs: journalctl -u kubelet -f. Common issues: wrong CIDR, firewall blocking ports 8472 (VXLAN), or missing kernel modules. Flannel uses VXLAN overlay; ensure UDP port 8472 is open between nodes.
Joining Worker Nodes to the Cluster
Worker nodes run your applications. To join them, use the token and hash from the kubeadm init output. If you lost it, generate a new token on the control plane: kubeadm token create --print-join-command. This prints the full join command. On each worker node, run the command with sudo: sudo kubeadm join <control-plane-ip>:6443 --token <token> --discovery-token-ca-cert-hash sha256:<hash>. After joining, verify on the control plane: kubectl get nodes. Worker nodes will show as NotReady initially, then become Ready after the CNI plugin is installed (if not already). Ensure the worker node has the same container runtime and Kubernetes version as the control plane. If join fails, check network connectivity to port 6443 and that the token hasn't expired (default 24 hours).
Configuring kubectl for Remote Access
By default, kubectl uses the admin.conf file on the control plane. To manage the cluster from your local machine, copy the kubeconfig file and update the server IP. On your local machine, install kubectl matching the cluster version. Copy the admin.conf from the control plane: scp user@control-plane:/etc/kubernetes/admin.conf ~/.kube/config. Edit the file to change the server address from localhost to the control plane's public IP or DNS. Test with kubectl cluster-info. For security, create a dedicated user with limited permissions instead of using admin.conf. Use RBAC to grant only necessary access. For example, create a read-only user for developers.
Deploying a Test Application
Verify the cluster works by deploying a simple application. Create a deployment: kubectl create deployment nginx --image=nginx:1.25. Expose it as a service: kubectl expose deployment nginx --port=80 --type=NodePort. Get the NodePort: kubectl get svc nginx. Access the application using any node's IP and the NodePort (e.g., http://192.168.1.101:30080). If it works, your cluster is functional. Check pod logs: kubectl logs deployment/nginx. If pods are pending, check node resources: kubectl describe node. Common issues: insufficient CPU/memory, image pull errors (check registry access), or network policy blocking.
Setting Up Persistent Storage with HostPath
Stateful applications need persistent storage. For a simple setup, use HostPath volumes—they mount a directory from the node's filesystem into the pod. Create a PersistentVolume (PV) and PersistentVolumeClaim (PVC). Example PV: hostPath: path: /data. Then create a PVC that requests storage. Deploy a pod that uses the PVC. HostPath is not suitable for production because pods may be scheduled on different nodes. For production, use a network storage solution like NFS, Ceph, or cloud provider volumes. Test with a simple pod that writes to the volume and verify data persists after pod restart.
Enabling the Kubernetes Dashboard
The Kubernetes Dashboard provides a web UI for cluster management. Install it with: kubectl apply -f https://raw.githubusercontent.com/kubernetes/dashboard/v2.7.0/aio/deploy/recommended.yaml. Create a service account and cluster role binding for admin access. Then get a bearer token: kubectl -n kubernetes-dashboard create token admin-user. Start the proxy: kubectl proxy. Access the dashboard at http://localhost:8001/api/v1/namespaces/kubernetes-dashboard/services/https:kubernetes-dashboard:/proxy/. Use the token to log in. The dashboard shows cluster resources, logs, and allows basic operations. For production, secure the dashboard with RBAC and HTTPS.
Cluster Backup and Restore with etcd
etcd stores all cluster state. Backing up etcd is critical for disaster recovery. Use etcdctl (v3) to take snapshots. On the control plane, run: ETCDCTL_API=3 etcdctl --endpoints=https://127.0.0.1:2379 --cacert=/etc/kubernetes/pki/etcd/ca.crt --cert=/etc/kubernetes/pki/etcd/server.crt --key=/etc/kubernetes/pki/etcd/server.key snapshot save /backup/etcd-snapshot.db. To restore, stop the API server and etcd, then restore the snapshot to a new data directory: ETCDCTL_API=3 etcdctl snapshot restore /backup/etcd-snapshot.db --data-dir=/var/lib/etcd-restore. Update the etcd manifest to use the new data directory. Test by starting the cluster and verifying resources.
Troubleshooting Common Installation Issues
Even with careful setup, issues arise. Common problems: kubelet fails to start (check journalctl -u kubelet), nodes stay NotReady (CNI not installed or network issues), pods stuck in Pending (insufficient resources or PVC not bound), API server not responding (check firewall port 6443). Use kubectl describe to get details. For kubeadm init failures, check pre-flight checks: kubeadm init --dry-run. Ensure all nodes have synchronized clocks (install ntp). If images fail to pull, check container runtime and registry access. For network issues, verify CNI plugin logs: kubectl -n kube-system logs <cni-pod>. Keep a checklist and validate each step.
kubeadm init --dry-run to catch issues before actual initialization.Production Hardening and Next Steps
Your cluster is running, but it's not production-ready. Hardening steps: enable RBAC, use network policies, set resource limits, enable audit logging, and use a private container registry. Implement Pod Security Standards (baseline or restricted). Set up monitoring with Prometheus and Grafana. Configure logging with Fluentd or Loki. Use a service mesh like Istio for advanced traffic management. Automate deployments with CI/CD (GitLab CI, Jenkins). Finally, document your cluster architecture, backup procedures, and runbooks. Kubernetes is a platform, not a magic bullet—invest in operations.
| File | Command / Code | Purpose |
|---|---|---|
| prepare-node.sh | sudo swapoff -a | Prerequisites and Environment Preparation |
| install-k8s.sh | sudo apt-get update | Installing kubeadm, kubelet, and kubectl |
| init-control-plane.sh | sudo kubeadm init --pod-network-cidr=10.244.0.0/16 | Initializing the Control Plane with kubeadm |
| install-flannel.sh | kubectl apply -f https://raw.githubusercontent.com/flannel-io/flannel/master/Doc... | Installing a Pod Network (CNI Plugin) |
| join-worker.sh | sudo kubeadm join 192.168.1.100:6443 --token abcdef.0123456789abcdef --discovery... | Joining Worker Nodes to the Cluster |
| remote-kubectl.sh | scp user@192.168.1.100:/etc/kubernetes/admin.conf ~/.kube/config | Configuring kubectl for Remote Access |
| test-app.sh | kubectl create deployment nginx --image=nginx:1.25 | Deploying a Test Application |
| hostpath-pv.yaml | apiVersion: v1 | Setting Up Persistent Storage with HostPath |
| dashboard-setup.sh | kubectl apply -f https://raw.githubusercontent.com/kubernetes/dashboard/v2.7.0/a... | Enabling the Kubernetes Dashboard |
| etcd-backup.sh | ETCDCTL_API=3 etcdctl --endpoints=https://127.0.0.1:2379 \ | Cluster Backup and Restore with etcd |
| troubleshoot.sh | sudo systemctl status kubelet | Troubleshooting Common Installation Issues |
| network-policy.yaml | apiVersion: networking.k8s.io/v1 | Production Hardening and Next Steps |
Key takeaways
Interview Questions on This Topic
What is the minimum number of nodes required for a Kubernetes cluster?
Frequently Asked Questions
20+ years shipping production infrastructure and CI/CD at scale. Drawn from code that ran under real load.
That's Kubernetes. Mark it forged?
4 min read · try the examples if you haven't