8 min to read
Installing Kubernetes with Kubespray and Adding Worker Nodes (2025V.)
A comprehensive guide to automated Kubernetes cluster deployment and scaling using Kubespray with Cilium CNI

Reference: Kubespray Deployment Guide
Overview
In this post, we’ll explore how to automatically install and configure a Kubernetes cluster using Kubespray, and further expand it by adding Worker Nodes through hands-on practice. Kubespray is an Ansible-based provisioning tool that allows flexible installation of Kubernetes clusters including HA configurations across various environments (GCP, AWS, on-premises, etc.) by simply defining YAML-based inventory files.
This practical guide covers the following key areas:
- On-premises Control/Worker Node VM configuration
- Kubespray installation and Ansible virtual environment setup
- Cluster inventory definition including Helm, Cilium, and Ingress settings
- Cluster installation through Ansible playbooks (cluster.yml)
- Adding one worker node and scaling configuration through scale.yml
Additionally, we’ll cover essential Kubernetes operational settings including kubectl, bashrc, autocompletion, and context configuration.
Kubespray is a composition of Ansible playbooks, inventory, provisioning tools, and domain knowledge for deploying a production-ready Kubernetes cluster. It allows for highly customizable deployments and supports multiple cloud providers, bare metal installations, and virtualized environments.
Key features include:
- Composable attributes
- Multiple network plugin support (Calico, Flannel, Cilium, etc.)
- HA cluster setup
- Configurable addons
- Support for most popular Linux distributions
System Configuration
Based on July 30, 2025 standards.
Environment Details
Component | Specification |
---|---|
Operating System | Ubuntu 24.04 LTS (Noble) |
Infrastructure | On-premises Server |
Kubernetes Version | v1.33.3 (deployed by Kubespray) |
CNI Plugin | Cilium |
Container Runtime | containerd v2.1.3 |
Python Version | 3.10 |
Node Specifications
Node | Hostname | IP Address | CPU | Memory | Role |
---|---|---|---|---|---|
Control Plane Node | k8s-control-01 | 10.10.10.17 | 4 cores | 24GB | Control Plane + etcd |
Worker Node 1 | k8s-compute-01 | 10.10.10.18 | 12 cores | 48GB | Worker (application) |
Worker Node 2 | k8s-compute-02 | 10.10.10.19 | 12 cores | 48GB | Worker (application) |
Prerequisites
Before starting the Kubespray installation, you need to prepare your environment.
VM Creation
VMs were created using Cockpit for this deployment.
SSH Key Setup
# Generate SSH key
ssh-keygen
# Update /etc/hosts for easier node access
sudo vi /etc/hosts
10.10.10.17 k8s-control-01
10.10.10.18 k8s-compute-01
10.10.10.19 k8s-compute-02
# Copy SSH key to all nodes
ssh-copy-id k8s-control-01
ssh-copy-id k8s-compute-01
ssh-copy-id k8s-compute-02
# Verify SSH connections
ssh k8s-control-01
ssh k8s-compute-01
ssh k8s-compute-02
Package Installation
# Update package lists and install required packages
sudo add-apt-repository ppa:deadsnakes/ppa
sudo apt-get -y update
sudo apt install -y python3.10 python3-pip git python3.10-venv
# Verify Python version
python3.10 --version
Kubespray Installation and Setup
1. Clone Repository and Setup Environment
# Clone the Kubespray repository
git clone https://github.com/kubernetes-sigs/kubespray.git
cd kubespray
# Copy sample inventory
cp -rfp inventory/sample inventory/somaz-cluster
# Create and activate virtual environment
python3.10 -m venv venv
source venv/bin/activate
# Install dependencies
pip install -U -r requirements.txt
2. Configure Inventory
First, we’ll deploy only k8s-compute-01
, then later add k8s-compute-02
using scale.yml
.
inventory/somaz-cluster/inventory.ini
[kube_control_plane]
k8s-control-01 ansible_host=10.10.10.17 ip=10.10.10.17 etcd_member_name=etcd1
[etcd:children]
kube_control_plane
[kube_node]
k8s-compute-01 ansible_host=10.10.10.18 ip=10.10.10.18
3. Customize Cluster Configuration
inventory/somaz-cluster/group_vars/k8s_cluster/k8s-cluster.yml
# Choose network plugin (cilium, calico, kube-ovn, weave or flannel)
kube_network_plugin: cilium
# Cluster name
cluster_name: somaz-cluster.local
# Ownership configuration (important for permission issues)
kube_owner: root # Change from default 'kube' to 'root' to avoid permission issues
inventory/somaz-cluster/group_vars/k8s_cluster/addons.yml
# Helm deployment
helm_enabled: true
# Metrics Server deployment
metrics_server_enabled: true
# The plugin manager for kubectl
krew_enabled: true
krew_root_dir: "/usr/local/krew"
4. Verify Ansible Connectivity
# Test connection to all nodes (run from ~/kubespray directory)
ansible all -i inventory/somaz-cluster/inventory.ini -m ping
5. Deploy Kubernetes Cluster
# Deploy the cluster (run from ~/kubespray directory)
ansible-playbook -i inventory/somaz-cluster/inventory.ini cluster.yml --become
# For background execution
nohup ansible-playbook -i inventory/somaz-cluster/inventory.ini cluster.yml --become &
6. Configure kubectl
# Create kubectl config directory and copy credentials
mkdir ~/.kube
sudo cp /etc/kubernetes/admin.conf ~/.kube/config
sudo chown $USER:$USER ~/.kube/config
7. Setup kubectl Completion and Aliases
# Add kubectl completion and aliases to bashrc
echo '# kubectl completion and alias' >> ~/.bashrc
echo 'source <(kubectl completion bash)' >> ~/.bashrc
echo 'alias k=kubectl' >> ~/.bashrc
echo 'complete -F __start_kubectl k' >> ~/.bashrc
# Apply bashrc changes
source ~/.bashrc
8. Verify Installation
# Check nodes
k get nodes
NAME STATUS ROLES AGE VERSION
k8s-compute-01 Ready <none> 2d4h v1.33.3
k8s-control-01 Ready control-plane 2d4h v1.33.3
# Check versions
k version
containerd --version
# Check Cilium image
k get ds cilium -n kube-system -o=jsonpath='{.spec.template.spec.containers[0].image}'
Troubleshooting Cilium Permission Issues
Currently, there’s a known permission error with Cilium. Here’s the fix:
# Fix CNI binary permissions
chown -R root:root /opt/cni/bin
For more information about this issue, see: Cilium GitHub Issue #23838
Alternatively, you can modify the configuration:
inventory/somaz-cluster/group_vars/k8s_cluster/k8s-cluster.yml
# Change from default
# kube_owner: kube
# To
kube_owner: root
Adding Worker Nodes
One of the key advantages of Kubernetes is its scalability. Let’s add k8s-compute-02
to our cluster using the scale.yml
playbook.
Reference: Kubespray Scaling Documentation
1. Update Inventory Configuration
inventory/somaz-cluster/inventory.ini
# This inventory describes a HA topology with stacked etcd
[kube_control_plane]
k8s-control-01 ansible_host=10.10.10.17 ip=10.10.10.17 etcd_member_name=etcd1
[etcd:children]
kube_control_plane
[kube_node]
k8s-compute-01 ansible_host=10.10.10.18 ip=10.10.10.18
k8s-compute-02 ansible_host=10.10.10.19 ip=10.10.10.19
[add_node]
k8s-compute-02
2. Run Scale Playbook
# Add new nodes to the cluster using scale.yml
nohup ansible-playbook -i inventory/somaz-cluster/inventory.ini scale.yml --limit add_node --become &
3. Verify Scaled Cluster
# Check all nodes
k get nodes
NAME STATUS ROLES AGE VERSION
k8s-compute-01 Ready <none> 2d5h v1.33.3
k8s-compute-02 Ready <none> 2d4h v1.33.3
k8s-control-01 Ready control-plane 2d5h v1.33.3
Error Recovery
If you encounter errors during scaling, run the facts playbook first, then retry:
# Run facts playbook to refresh node information
ansible-playbook -i inventory/somaz-cluster/inventory.ini playbooks/facts.yml --become
# Then retry the scale operation
nohup ansible-playbook -i inventory/somaz-cluster/inventory.ini scale.yml --limit add_node --become &
Advanced Operations
Scaling Options
# Scale with all new nodes
nohup ansible-playbook -i inventory/somaz-cluster/inventory.ini scale.yml --become &
# Scale with specific node limit
nohup ansible-playbook -i inventory/somaz-cluster/inventory.ini scale.yml --limit add_node --become &
Node Removal
# Remove specific node
ansible-playbook -i inventory/somaz-cluster/inventory.ini remove-node.yml -b --extra-vars='node=node3' --extra-vars reset_nodes=true
Verification and Monitoring
Cluster Status Verification
# Check all nodes with detailed information
kubectl get nodes -o wide
# Check system pods
kubectl get pods -n kube-system
# Check Cilium status
kubectl get pods -n kube-system -l k8s-app=cilium
# Verify Cilium connectivity
kubectl exec -n kube-system ds/cilium -- cilium status
Sample Application Deployment
# Deploy a test application
kubectl create deployment nginx --image=nginx
kubectl expose deployment nginx --port=80 --type=NodePort
# Check deployment
kubectl get pods
kubectl get services
Conclusion: “Production-Ready Kubernetes Installation Automation with Kubespray”
Kubespray goes beyond simple installation automation to enable production-level Kubernetes cluster configuration. It’s particularly useful in the following scenarios:
- IaC-based infrastructure management through Terraform + Ansible integration
- Automated HA cluster, MetalLB/Ingress configuration
- Quick deployment needs in on-premises or private cloud environments
- Repeated creation and removal of test clusters in CI environments
Through this hands-on practice, we’ve learned how to control clusters using declarative inventory files and Ansible playbooks instead of manually repeating Kubernetes installation processes.
For future advanced functionality practice, consider:
- Multi Control Plane configuration (HA setup)
- External etcd cluster integration
- GitOps tools (ArgoCD, Flux) integration
- Monitoring integration with Prometheus/Grafana, Loki
- Worker Node auto-scaling configuration
Establishing the flow of Ansible-based automation → Cluster construction → GitOps-based app deployment can elevate operational automation to the next level.
Comments