Comprehensive Ansible with K8s
This article will guides you through using Ansible, starting from a single, simple playbook and progressing to a professional, role-based structure. We'll deploy common infrastructure including Docker, WordPress, n8n, and finally, a Kubernetes (K8s) cluster with a control plane and a worker agent.
Note: This tutorial uses modern Ansible practices, including Fully Qualified Collection Names (FQCNs) and the latest recommended methods for managing APT repositories.
Prerequisites
- A control node (your machine) with Ansible installed (
sudo apt install ansibleorpip install ansible). - Target nodes: At least two Ubuntu Linux VMs (e.g.,
192.168.1.100for master/general apps,192.168.1.101for the K8s worker agent) with SSH access configured via keys. - Basic understanding of YAML syntax.
Install required Ansible collections:
ansible-galaxy collection install community.docker kubernetes.core
Part 1: The Basics - Inventory and Ad-Hoc Commands
Ansible needs to know where to run playbooks. This is defined in an inventory file. We will define a general group, and specific groups for our K8s setup.
1. Create an Inventory
Create a file named inventory.ini:
[webservers]
192.168.1.100
[k8s_master]
192.168.1.100
[k8s_agents]
192.168.1.101
# Grouping all k8s nodes together
[k8s_cluster:children]
k8s_master
k8s_agents
[all:vars]
ansible_user=ubuntu
ansible_python_interpreter=/usr/bin/python3
2. The ansible CLI (Ad-Hoc Commands)
Use the ansible command for quick tasks.
Ping all servers to test connectivity:
ansible all -i inventory.ini -m ping
Check system uptime on the K8s agents:
ansible k8s_agents -i inventory.ini -a "uptime"
Part 2: The Single Playbook (ansible-playbook)
Playbooks (.yml files) are the core of Ansible. Let's write one to install Docker using modern APT repository standards (avoiding the deprecated apt_key module).
1. Write the Playbook
Create a file named setup_docker.yml.
---
- name: Install and Configure Docker
hosts: webservers
become: yes
tasks:
- name: Update apt cache and install prerequisites
ansible.builtin.apt:
pkg:
- ca-certificates
- curl
- gnupg
- python3-pip
state: present
update_cache: yes
- name: Create directory for Docker GPG key
ansible.builtin.file:
path: /etc/apt/keyrings
state: directory
mode: '0755'
- name: Download Docker GPG key
ansible.builtin.get_url:
url: https://download.docker.com/linux/ubuntu/gpg
dest: /etc/apt/keyrings/docker.asc
mode: '0644'
- name: Add Docker Repository
ansible.builtin.apt_repository:
repo: "deb [arch=amd64 signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/ubuntu focal stable"
state: present
- name: Install Docker CE and plugins
ansible.builtin.apt:
pkg:
- docker-ce
- docker-ce-cli
- containerd.io
- docker-compose-plugin # Modern Docker Compose V2
state: latest
update_cache: yes
- name: Ensure Docker service is running
ansible.builtin.systemd:
name: docker
state: started
enabled: yes
2. Run the Playbook
ansible-playbook -i inventory.ini setup_docker.yml
Part 3: Structuring with Roles (Apps & Automations)
As your infrastructure grows, Roles break complex playbooks into reusable components.
1. Create the Directory Structure
mkdir my-ansible-project && cd my-ansible-project
mkdir roles && cd roles
ansible-galaxy init docker
ansible-galaxy init wordpress
ansible-galaxy init n8n
cd ..
(Move the tasks from Part 2 into roles/docker/tasks/main.yml)
2. The wordpress Role (Modern Compose V2)
Let's deploy WordPress using Docker Compose V2.
# roles/wordpress/tasks/main.yml
---
- name: Create WordPress directory
ansible.builtin.file:
path: /opt/wordpress
state: directory
- name: Create docker-compose.yml for WordPress
ansible.builtin.copy:
dest: /opt/wordpress/docker-compose.yml
content: |
services:
wordpress:
image: wordpress:latest
restart: always
ports:
- 8080:80
environment:
WORDPRESS_DB_HOST: db
WORDPRESS_DB_USER: exampleuser
WORDPRESS_DB_PASSWORD: examplepass
WORDPRESS_DB_NAME: exampledb
db:
image: mysql:8.0
restart: always
environment:
MYSQL_DATABASE: exampledb
MYSQL_USER: exampleuser
MYSQL_PASSWORD: examplepass
MYSQL_RANDOM_ROOT_PASSWORD: '1'
- name: Start WordPress containers
community.docker.docker_compose_v2:
project_src: /opt/wordpress
state: present
3. The n8n Role
# roles/n8n/tasks/main.yml
---
- name: Create n8n directory
ansible.builtin.file:
path: /opt/n8n
state: directory
- name: Create docker-compose.yml for n8n
ansible.builtin.copy:
dest: /opt/n8n/docker-compose.yml
content: |
services:
n8n:
image: docker.n8n.io/n8nio/n8n
restart: always
ports:
- "5678:5678"
volumes:
- n8n_data:/home/node/.n8n
volumes:
n8n_data:
- name: Start n8n container
community.docker.docker_compose_v2:
project_src: /opt/n8n
state: present
Part 4: Deploying Kubernetes (Control Plane & Agent)
Installing Kubernetes requires configuring a base environment on all nodes, initializing the master, and passing a secure token to the worker agents to join.
Create three new roles:
cd roles
ansible-galaxy init k8s_base
ansible-galaxy init k8s_master
ansible-galaxy init k8s_agent
cd ..
1. k8s_base Role (Runs on ALL nodes)
This installs the required K8s software (kubelet, kubeadm, kubectl) and disables swap memory, which K8s requires.
# roles/k8s_base/tasks/main.yml
---
- name: Disable swap immediately
ansible.builtin.command: swapoff -a
changed_when: false
- name: Disable swap permanently in /etc/fstab
ansible.builtin.replace:
path: /etc/fstab
regexp: '^([^#].*?\sswap\s+sw\s+.*)$'
replace: '# \1'
- name: Install required packages
ansible.builtin.apt:
pkg: [apt-transport-https, curl]
state: present
- name: Download K8s GPG key
ansible.builtin.get_url:
url: https://pkgs.k8s.io/core:/stable:/v1.30/deb/Release.key
dest: /tmp/k8s.asc
- name: Dearmor K8s GPG key
ansible.builtin.command: gpg --dearmor -o /etc/apt/keyrings/kubernetes-apt-keyring.gpg /tmp/k8s.asc
args:
creates: /etc/apt/keyrings/kubernetes-apt-keyring.gpg
- name: Add K8s repository
ansible.builtin.apt_repository:
repo: "deb [signed-by=/etc/apt/keyrings/kubernetes-apt-keyring.gpg] https://pkgs.k8s.io/core:/stable:/v1.30/deb/ /"
state: present
- name: Install kubelet, kubeadm, kubectl
ansible.builtin.apt:
pkg: [kubelet, kubeadm, kubectl]
state: present
update_cache: yes
2. k8s_master Role (Runs ONLY on Master)
This initializes the cluster and saves the join token so Ansible can use it later.
# roles/k8s_master/tasks/main.yml
---
- name: Initialize Kubernetes Control Plane
ansible.builtin.command: kubeadm init --pod-network-cidr=10.244.0.0/16
args:
creates: /etc/kubernetes/admin.conf
register: kubeadm_init
- name: Create .kube directory
ansible.builtin.file:
path: /home/{{ ansible_user }}/.kube
state: directory
mode: '0755'
owner: "{{ ansible_user }}"
- name: Copy admin.conf to user's kube config
ansible.builtin.copy:
src: /etc/kubernetes/admin.conf
dest: /home/{{ ansible_user }}/.kube/config
remote_src: yes
owner: "{{ ansible_user }}"
- name: Deploy Flannel Pod Network
ansible.builtin.command: kubectl apply -f https://github.com/flannel-io/flannel/releases/latest/download/kube-flannel.yml
environment:
KUBECONFIG: /etc/kubernetes/admin.conf
- name: Generate join command for agents
ansible.builtin.command: kubeadm token create --print-join-command
register: join_command_raw
changed_when: false
- name: Set join command as a fact (so the agent can read it)
ansible.builtin.set_fact:
k8s_join_command: "{{ join_command_raw.stdout }}"
3. k8s_agent Role (Runs ONLY on Agents)
This reads the fact we saved on the master node and executes it to join the cluster.
# roles/k8s_agent/tasks/main.yml
---
- name: Join Kubernetes Cluster
ansible.builtin.command: "{{ hostvars['192.168.1.100']['k8s_join_command'] }}"
args:
creates: /etc/kubernetes/kubelet.conf
Note: Ensure 192.168.1.100 matches the exact name/IP of your master node in your inventory.
Part 5: The Master Playbook (site.yml)
Tie your entire infrastructure together in site.yml:
# site.yml
---
- name: Provision Web Server Apps
hosts: webservers
become: yes
roles:
- docker
- wordpress
- n8n
- name: Provision Kubernetes Base (All Nodes)
hosts: k8s_cluster
become: yes
roles:
- k8s_base
- name: Initialize Kubernetes Master
hosts: k8s_master
become: yes
roles:
- k8s_master
- name: Join Kubernetes Agents
hosts: k8s_agents
become: yes
roles:
- k8s_agent
Run the Full Stack
Execute the master playbook:
ansible-playbook -i inventory.ini site.yml
Ansible will deploy your Docker apps, install K8s software on all nodes, initialize the control plane, grab the secure join token, and use it to attach your worker nodes automatically!