Automating Single Master Node Kubernetes Cluster Setup with Kubeadm using Ansible

https://medium.com/@shaikhmj012/automating-single-master-node-kubernetes-cluster-setup-with-kubeadm-using-ansible-by-shaikhmj-285bc12c23d8

Shaikh MJ Shaikh MJ

Follow 6 min read · Mar 17, 2025

In this blog post, I’ll walk you through how to automate the deployment of a single master node Kubernetes cluster using Ansible and Kubeadm. This playbook is designed to simplify the process of setting up a Kubernetes cluster, ensuring that all prerequisites are met and the necessary components are installed and configured correctly.

Prerequisites Before diving into the playbook, ensure the following prerequisites are met:

Ansible Installation: Install Ansible on a machine that will act as the Ansible controller. Ensure SSH access is configured to the nodes where Kubernetes will be installed. Internet Access: All Ansible slave nodes must have internet access to download necessary packages. Hosts File Configuration: Set up the /etc/hosts file on all Ansible slave nodes. Time Synchronisation: Set the timezone and install a time synchronisation tool like chrony or ntp. Step 1: Create a Custom Inventory File The first step is to create a custom inventory file that defines the structure of your Kubernetes cluster. This file will specify the master and worker nodes.

single-host-file.yaml

  • name: Ensure inventory file exists with correct structure hosts: localhost become: true tasks:
    • name: Ensure /opt/ansible directory exists ansible.builtin.file: path: /opt/ansible state: directory mode: ‘0755’

    • name: Create and write to single-host-file ansible.builtin.copy: dest: /opt/ansible/single-host-file content: | [k8s_cluster:children] k8s_master_sing_clus k8s_worker_sing_clus

      [k8s_master_sing_clus]
      
      [k8s_worker_sing_clus]   mode: '0644' when: not ansible_check_mode
      
    • name: Change permissions on the inventory file ansible.builtin.file: path: /opt/ansible/single-host-file mode: ‘0664’ inventory.ini Create an inventory file for local execution:

[local] localhost ansible_connection=local Run the playbook to create the inventory file:

$ ansible-playbook -i inventory.ini single-host-file.yaml This will create a file at /opt/ansible/single-host-file with the following content:

[k8s_cluster:children] k8s_master_sing_clus k8s_worker_sing_clus

[k8s_master_sing_clus]

[k8s_worker_sing_clus] Step 2: Define Variables Next, define the variables required for the Kubernetes cluster setup in a vars.yaml file:

docker_version: “26.1.4” k8s: “1.31.1” kubernetes_version: “v1.31.1”

ip_cpe: “10.150.17.42” # Control-plane-endpoint IP ip_aaa: “10.150.17.42” # API-advertise-address IP ip_pnc: “10.244.0.0/16” # Pod-network-cidr ip_sc: “10.96.0.0/16” # Service-cidr Step 3: Add Kubernetes Repository Download the Kubernetes repository file from the official site and save it as kubernetes.repo:

[kubernetes] name=Kubernetes baseurl=https://pkgs.k8s.io/core:/stable:/v1.31/rpm/ enabled=1 gpgcheck=1 gpgkey=https://pkgs.k8s.io/core:/stable:/v1.31/rpm/repodata/repomd.xml.key #exclude=kubelet kubeadm kubectl cri-tools kubernetes-cni Step 4: Insert IP Addresses into Inventory File Run the following playbook to insert the IP addresses of the master and worker nodes into the inventory file:

setup-single.yaml

  • name: Single master node set up hosts: localhost connection: local vars_prompt:
    • name: cluster_type prompt: “Which type of cluster do you want to create? Enter ‘single’ for Single Master or ‘multi’ for Multi-Master:” private: no

    • name: num_workers prompt: “How many worker nodes do you have?” private: no default: 1

    • name: master_ip prompt: “Enter the IP address of the master node” private: no

    • name: worker_ips prompt: “Enter the IP addresses of the worker nodes (comma-separated)” private: no

    tasks:

    • name: Display user choice debug: msg: “You selected {{ cluster_type }} master node cluster.”

    • name: Add master IP address to inventory file ansible.builtin.lineinfile: path: /opt/ansible/single-host-file line: “{{ master_ip }}” insertafter: “\[k8s_master_sing_clus\]”

    • name: Add worker IP addresses to inventory file ansible.builtin.lineinfile: path: /opt/ansible/single-host-file line: “{{ item }}” insertafter: “\[k8s_worker_sing_clus\]” state: present loop: “{{ worker_ips.split(‘,’) }}” when: cluster_type == “single” and num_workers|int > 0 ignore_errors: true Run the playbook:

$ ansible-playbook -i inventory.ini setup-single.yaml Step 5: Install Kubernetes Finally, run the main playbook to install and configure Kubernetes on the master and worker nodes:

single-master-node.yaml

  • name: Setting up Single Master Node Cluster including all Prerequisites. hosts: k8s_cluster become: true vars_files:
    • vars.yaml tasks:
    • name: Remove unnecessary packages ansible.builtin.yum: name: “{{ item }}” state: absent loop:
      • buildah
      • podman
      • podman-catatonit ignore_errors: yes
    • name: Install dependencies ansible.builtin.package: name: yum-utils state: present
    • name: Install required packages ansible.builtin.package: name: “{{ item }}” state: present loop:
      • vim
      • curl
      • wget
      • lsof
      • net-tools
      • telnet
      • make
      • nc
    • name: Stop and Disable Firewalld ansible.builtin.service: name: firewalld state: stopped enabled: no
    • name: Disable swap ansible.builtin.mount: path: “{{ item }}” fstype: swap state: absent loop:
      • swap
      • none
    • name: Disable IPtables ansible.builtin.command: iptables -F
    • name: Disable SELinux ansible.builtin.command: setenforce 0
    • name: Load kernel modules overlay and br_netfilter ansible.builtin.modprobe: name: “{{ item }}” loop:
      • overlay
      • br_netfilter
    • name: Set iptables policy for FORWARD chain ansible.builtin.shell: iptables -P FORWARD ACCEPT
    • name: Configure kernel parameters ansible.builtin.sysctl: name: “{{ item.name }}” value: “{{ item.value }}” state: present reload: yes loop:
      • {name: net.bridge.bridge-nf-call-iptables, value: 1}
      • {name: net.bridge.bridge-nf-call-ip6tables, value: 1}
      • {name: net.ipv4.ip_forward, value: 1}
      • {name: vm.swappiness, value: 0}
      • {name: vm.overcommit_memory, value: 1}
      • {name: vm.panic_on_oom, value: 0}
      • {name: fs.inotify.max_user_watches, value: 89100}
    • name: Add Docker repository ansible.builtin.command: yum config-manager –add-repo https://download.docker.com/linux/rhel/docker-ce.repo
    • name: Remove Docker and all related packages ansible.builtin.yum: name: - docker-ce - docker-ce-cli - containerd.io - docker-buildx-plugin - docker-ce-rootless-extras - docker-compose-plugin state: absent ignore_errors: yes
    • name: Remove Docker configuration and data ansible.builtin.file: path: “{{ item }}” state: absent loop:
      • /var/lib/docker
      • /var/lib/containerd ignore_errors: yes
    • name: Install Docker (version {{ docker_version }}) ansible.builtin.yum: name: “{{ item }}-{{ docker_version }}” state: present update_cache: yes disable_gpg_check: yes loop:
      • docker-ce
      • docker-ce-cli
      • docker-ce-rootless-extras
    • name: Start & enable Docker service ansible.builtin.service: name: docker state: started enabled: yes
    • name: Start & enable Containerd Service ansible.builtin.service: name: containerd state: started enabled: yes
    • name: Add YUM repository for Kubernetes ansible.builtin.copy: src: kubernetes.repo dest: /etc/yum.repos.d/kubernetes.repo
    • name: Remove existing Kubernetes components ansible.builtin.yum: name: “{{ item }}” state: absent loop:
      • kubelet
      • kubeadm
      • kubectl ignore_errors: yes
    • name: Install Kubernetes components (version {{ k8s }}) ansible.builtin.yum: name: - “kubelet-{{ k8s }}” - “kubeadm-{{ k8s }}” - “kubectl-{{ k8s }}” state: present update_cache: yes
    • name: Start and enable kubelet ansible.builtin.service: name: kubelet state: started enabled: yes
    • name: Stopping the kubelet ansible.builtin.service: name: kubelet state: stopped
    • name: Remove containerd configuration file ansible.builtin.file: path: /etc/containerd/config.toml state: absent
    • name: Regenerate containerd configuration file ansible.builtin.command: containerd config default > /etc/containerd/config.toml when: not ansible_check_mode
    • name: Restart containerd service ansible.builtin.systemd: name: containerd state: restarted
    • name: Reload systemd daemon ansible.builtin.systemd: daemon_reload: yes
    • name: Restart Docker service ansible.builtin.systemd: name: docker state: restarted
    • name: Initialise the Kubernetes cluster using kubeadm command ansible.builtin.command: > kubeadm init –control-plane-endpoint=”{{ ip_cpe }}:6443” –upload-certs –apiserver-advertise-address=”{{ ip_aaa }}” –pod-network-cidr=”{{ ip_pnc }}” –service-cidr=”{{ ip_sc }}” –kubernetes-version=”{{ k8s }}” –v=3

      when: inventory_hostname in groups[‘k8s_master_sing_clus’]

    • name: Set up Kubernetes for $HOME user ansible.builtin.command: “{{ item }}” when: inventory_hostname in groups[‘k8s_master_sing_clus’] loop:
      • mkdir -p $HOME/.kube
      • cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
      • chown $USER:$USER $HOME/.kube/config
    • name: Install Flannel pod network ansible.builtin.command: kubectl apply -f https://github.com/coreos/flannel/raw/master/Documentation/kube-flannel.yml when: inventory_hostname in groups[‘k8s_master_sing_clus’]
    • name: Retrieve Kubernetes join command ansible.builtin.command: kubeadm token create –print-join-command when: inventory_hostname in groups[‘k8s_master_sing_clus’] register: join_command
    • name: Attach kubeadm join command to a file on Ansible control node delegate_to: localhost # Run this task on the Ansible control node run_once: yes # Ensures this runs only once (not for each master node) ansible.builtin.copy: content: “{{ join_command.stdout_lines[0] }}” dest: /opt/ansible/join-command mode: ‘0600’
    • name: Copy the join-command file to worker node ansible.builtin.copy: src: /opt/ansible/join-command dest: /tmp/kubeadm-join.sh mode: ‘0777’ when: inventory_hostname in groups[‘k8s_worker_sing_clus’]
    • name: Join the worker node to cluster ansible.builtin.command: sh /tmp/kubeadm-join.sh when: inventory_hostname in groups[‘k8s_worker_sing_clus’]
    • name: Delete the join-command script after execution ansible.builtin.file: path: /tmp/kubeadm-join.sh state: absent when: inventory_hostname in groups[‘k8s_worker_sing_clus’]
    • name: delete join-command file from Ansible control node delegate_to: localhost run_once: yes ansible.builtin.file: path: /opt/ansible/join-command state: absent Conclusion This Ansible playbook automates the deployment of a single master node Kubernetes cluster using Kubeadm. It handles everything from setting up the inventory file to installing and configuring Kubernetes components. By following this guide, you can easily set up a Kubernetes cluster on fresh servers or troubleshoot existing setups.

Get Shaikh MJ’s stories in your inbox Join Medium for free to get updates from this writer.

Enter your email Subscribe Feel free to customise the playbook to suit your specific requirements, and happy automating!

Updated: