Skip to content

Repository files navigation

ansible-k8s-cluster

Ansible playbooks to provision and configure a production-style Kubernetes cluster on Rocky Linux using containerd as the container runtime and Flannel as the CNI plugin.

Built with Ansible | Kubernetes 1.32.x | containerd | Flannel CNI


Cluster Layout

Machine Hostname Role
Ansible Control Node sam.example.com Ansible, kubectl
Control Plane kube-master01.example.com Kubernetes Control Plane
Worker Node 1 kube-worker01.example.com Kubernetes Worker
Worker Node 2 kube-worker02.example.com Kubernetes Worker

Stack

Component Choice Reason
Kubernetes 1.32.x Latest stable
Container Runtime containerd Recommended for K8s 1.32
CNI Plugin Flannel Simple, reliable for small clusters
OS Rocky Linux 9 RHEL-compatible, enterprise grade

Project Structure

k8s-cluster/
├── ansible.cfg
├── site.yml                         # Master playbook — runs everything in order
├── common.yml
├── containerd.yml
├── kubernetes.yml
├── master.yml
├── worker.yml
├── inventory/
│   └── hosts.ini
├── group_vars/
│   └── k8s_cluster.yml
└── roles/
    ├── common/tasks/main.yml
    ├── containerd/tasks/main.yml
    ├── containerd/handlers/main.yml
    ├── kubernetes/tasks/main.yml
    ├── master/tasks/main.yml
    └── worker/tasks/main.yml

Prerequisites

Passwordless Sudo

On each Kubernetes node, configure passwordless sudo for the Ansible user:

echo "ansible ALL=(ALL) NOPASSWD:ALL" | sudo tee /etc/sudoers.d/ansible
sudo chmod 0440 /etc/sudoers.d/ansible

SSH Key Setup

Copy the SSH key from the control node to each Kubernetes node:

ssh-copy-id ansible@kube-master01.example.com
ssh-copy-id ansible@kube-worker01.example.com
ssh-copy-id ansible@kube-worker02.example.com

Verify Connectivity

ansible k8s_cluster -m ping

Expected: pong from all nodes with no errors.


Configuration

inventory/hosts.ini

Update with your actual hostnames or IPs:

[k8s_master]
kube-master01.example.com

[k8s_workers]
kube-worker01.example.com
kube-worker02.example.com

[k8s_cluster:children]
k8s_master
k8s_workers

The group names in [k8s_cluster:children] must exactly match the group names defined above.

group_vars/k8s_cluster.yml

---
kubernetes_version: "1.32"
pod_network_cidr: "10.244.0.0/16"
flannel_manifest_url: "https://raw.githubusercontent.com/flannel-io/flannel/master/Documentation/kube-flannel.yml"

pod_network_cidr must be 10.244.0.0/16 for Flannel. Do not change this value without also reconfiguring Flannel itself.

ansible.cfg

Option Purpose
remote_user SSH user on all target hosts
host_key_checking = False Skips SSH fingerprint prompts
result_format = yaml Cleaner, more readable output
callbacks_enabled timer prints total runtime; profile_tasks shows time per task
pipelining = True Combines SSH operations for faster runs
ssh_args Reuses SSH connections for 60 seconds per task

Usage

Full cluster build

ansible-playbook site.yml

Individual phases

ansible-playbook common.yml        # Phase 1 — baseline OS config
ansible-playbook containerd.yml    # Phase 2 — container runtime
ansible-playbook kubernetes.yml    # Phase 3 — K8s packages
ansible-playbook master.yml worker.yml  # Phase 4 — init cluster and join workers

master.yml and worker.yml must be run together so the join token fact is available to the worker role.

Verify the cluster

kubectl get nodes

Expected output:

NAME                            STATUS   ROLES           AGE   VERSION
kube-master01                   Ready    control-plane   Xm    v1.32.x
kube-worker01.example.com       Ready    worker          Xm    v1.32.x
kube-worker02.example.com       Ready    worker          Xm    v1.32.x
kubectl get pods -A

Role Reference

common

Runs on all nodes. Loads required kernel modules (overlay, br_netfilter), sets sysctl parameters, confirms swap is disabled, opens firewall ports, adds cluster nodes to /etc/hosts, and configures firewalld masquerading and trusted CNI interfaces.

containerd

Adds the Docker CE repository, installs containerd.io, generates the default config, and enables SystemdCgroup = true — required for Kubernetes 1.32 on Rocky Linux.

kubernetes

Adds the Kubernetes yum repository, installs kubelet, kubeadm, and kubectl, enables kubelet, and configures crictl to use the containerd socket.

master

Initializes the control plane with kubeadm init, sets up kubeconfig for root and the Ansible user, fetches kubeconfig to the local control node, applies the Flannel CNI manifest, waits for the node to become Ready, and generates the worker join token.

worker

Joins each worker node to the cluster using the join command stored as a fact on the master, confirms kubelet is running, and labels the node with node-role.kubernetes.io/worker=worker.


Firewall Port Reference

Master Node

Port Protocol Purpose
6443 TCP Kubernetes API Server
2379 TCP etcd client
2380 TCP etcd peer
10250 TCP Kubelet API
10251 TCP kube-scheduler
10252 TCP kube-controller-manager
8472 UDP Flannel VXLAN

Worker Nodes

Port Protocol Purpose
10250 TCP Kubelet API
8472 UDP Flannel VXLAN
30000-32767 TCP NodePort Services

Appendix: Firewall Troubleshooting

If pods cannot reach other pods on different nodes, DNS lookups time out from inside pods, or the kube-dns service IP is unreachable, the most common causes are:

Masquerade disabled — firewalld NAT was off. Without it return traffic from the service network cannot get back to the originating pod:

ansible k8s_cluster -m command -a "firewall-cmd --add-masquerade --permanent" --become
ansible k8s_cluster -m command -a "firewall-cmd --reload" --become

CNI interfaces not in trusted zonecni0 and flannel.1 were unassigned, causing firewalld to block pod traffic crossing those interfaces:

ansible k8s_cluster -m command -a "firewall-cmd --zone=trusted --add-interface=cni0 --permanent" --become
ansible k8s_cluster -m command -a "firewall-cmd --zone=trusted --add-interface=flannel.1 --permanent" --become
ansible k8s_cluster -m command -a "firewall-cmd --reload" --become

Verify DNS resolution:

kubectl run dns-test --image=busybox:1.36 --rm -it --restart=Never -- nslookup kubernetes.default.svc.cluster.local

Both fixes are now incorporated into the common role and run automatically.


License

MIT

About

Ansible playbooks to provision and configure a multi-node Kubernetes cluster on Rocky Linux — automates containerd installation, control plane initialization, worker node joining, and Flannel CNI setup.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors