A reusable Ansible template for bootstrapping a single-control-plane Kubernetes cluster on Rocky Linux 9 (or a closely compatible RHEL-family distribution), using containerd and Flannel.
This repository is intentionally environment-neutral. The example inventory uses
reserved example.com names and will not work until you copy and edit it.
Important
This is a production-style learning and bootstrap template, not a claim of a production-ready cluster. Production normally requires multiple control planes, a stable API endpoint, backups, monitoring, policy, and tested upgrades.
- Required kernel modules and networking sysctls
- Optional firewalld rules for the API, kubelet, Flannel, and NodePort
- containerd with the systemd cgroup driver
- Kubernetes packages from the selected upstream minor-release repository
- One kubeadm control plane, one or more workers, and pinned-release Flannel
- Ansible Core on the control host
- SSH access with privilege escalation
- Rocky Linux 9 or a compatible RHEL-family target
- Unique hostnames, reachable addresses, and synchronized clocks
- At least one control-plane host and one worker
- Swap disabled, unless the role is explicitly allowed to disable it
Install the required collections:
ansible-galaxy collection install -r requirements.yml-
Copy the example inventory; the local copy is ignored by Git:
cp inventory/hosts.ini inventory/hosts.local.ini
-
Replace the example names and addresses:
[k8s_master] control-01 ansible_host=192.0.2.10 [k8s_workers] worker-01 ansible_host=192.0.2.21 worker-02 ansible_host=192.0.2.22 [k8s_cluster:children] k8s_master k8s_workers
-
Review
group_vars/k8s_cluster.yml:Variable Default Meaning kubernetes_version1.36Upstream minor repository pod_network_cidr10.244.0.0/16Flannel network; fixed by this template flannel_versionv0.28.8Pinned Flannel release manage_firewalltrueManage firewalld rules disable_swapfalseModify hosts to disable swap control_plane_endpointfirst master address Endpoint advertised by kubeadm fetch_admin_kubeconfigfalseCopy cluster-admin credentials locally -
Verify SSH host keys. Host-key checking is enabled. Connect interactively once or maintain a managed
known_hostsfile; do not globally disable verification. -
Verify targeting and escalation:
ansible -i inventory/hosts.local.ini k8s_cluster -m ping ansible -i inventory/hosts.local.ini k8s_cluster -b -m command -a id
Use --ask-become-pass when appropriate. Unrestricted passwordless sudo is not a
requirement and should only be granted after considering the threat model.
Preview the target set and proposed changes:
ansible-playbook -i inventory/hosts.local.ini site.yml --list-hosts
ansible-playbook -i inventory/hosts.local.ini site.yml --check --diffSome kubeadm and runtime operations cannot be fully predicted in check mode. Run:
ansible-playbook -i inventory/hosts.local.ini site.ymlIndividual phases must be run in this order:
ansible-playbook -i inventory/hosts.local.ini common.yml
ansible-playbook -i inventory/hosts.local.ini containerd.yml
ansible-playbook -i inventory/hosts.local.ini kubernetes.yml
ansible-playbook -i inventory/hosts.local.ini master.yml worker.ymlThe last two must share one invocation because the join command is held only in Ansible memory. Join tokens are credentials and are never stored in this repo.
The playbook does not overwrite the control host's kubeconfig. To opt in to a
copy in the ignored artifacts/ directory:
ansible-playbook -i inventory/hosts.local.ini site.yml \
-e fetch_admin_kubeconfig=true
export KUBECONFIG="$PWD/artifacts/admin.conf"
kubectl get nodes
kubectl get pods --all-namespacesProtect admin.conf: it grants cluster-admin privileges. Use scoped credentials
for normal operation.
- Packages use
state: present, avoiding surprise upgrades on normal reruns. - Flannel is pinned to a release rather than a mutable branch.
- The full NodePort range and trusted CNI interfaces are broad defaults. Narrow these rules when workload ports and source networks are known.
- API audit logging, encryption at rest, Pod Security admission policy, NetworkPolicies, external secrets, and CIS remediation need site-specific work.
kubeadm initis guarded by/etc/kubernetes/admin.conf. Rebuilding or changing an initialized cluster is a lifecycle operation, not a routine rerun.
ansible-playbook -i inventory/hosts.ini site.yml --syntax-check
yamllint .
ansible-lintThe last two are optional developer tools and may need local or CI installation.
MIT; see LICENSE.