Skip to main content

Ansible

·1246 words·6 mins

Ansible Setup and Best Practices Guide
#

This document provides a comprehensive guide to setting up and using Ansible for managing Ubuntu servers, based on a real-world learning process. It covers everything from installing Ansible to structuring projects with roles, managing different environments, and troubleshooting common errors. I had gemini pro create this document below after the back and forth I had while setting up and getting into Ansible. It is pretty great if you have a model with lot’s of context as you can actually instruct it to create markdown files based on the interactions and learnings you had.

flowchart LR
        A["Control Server"]
        
        A --> B["Target Server 1"]
        A --> C["Target Server 2"]

How I now use it is that I create the VM’s that I want to control via ansible by hand in Proxmox. I know I can automate that too, but I do not do that enough (at this moment) to automate that part too (maybe in the future). Currently I quickly create a Ubuntu server VM on my proxmox cluster, run through the setup process, and use the steps in this document to enable control by ansible. Then running ansible makes the servers into what I need them to be, installing basic tools, docker etc. On my pages on Hugo and Gitlab, I also used it to setup those tools. It makes installations repeatable and controlled. And the neat thing is that you can actually use gemini as well to help you write the ansible playbooks!

of course there are also some awesome websites with good instructions

1. Setting Up the Ansible Control Node
#

The control node is the machine where Ansible is installed and from which you will manage your other servers.

1.1. Install Ansible on an Ubuntu Server: First, update your system and install the necessary prerequisites, then add the Ansible PPA (Personal Package Archive) to get the latest version.

# Update package list and install prerequisites
sudo apt update
sudo apt install software-properties-common

# Add the official Ansible PPA
sudo add-apt-repository --yes --update ppa:ansible/ansible

# Install Ansible
sudo apt install ansible

1.2. Verify the Installation: Check that Ansible was installed correctly.

ansible --version

1.3. Create a Project Directory: It’s best practice to keep all your Ansible files organized in a dedicated project directory.

mkdir ~/ansible_project
cd ~/ansible_project

2. Onboarding a New Target Host
#

A target host is any server you want to manage with Ansible. Before Ansible can connect, the host needs to be prepared for secure, passwordless communication.

2.1. Create a Management User: Ensure your management user (e.g., my_super_user) exists on the target host.

2.2. Set Up SSH Key Authentication (from Control Node): This allows Ansible to log in without needing a password.

# Generate an SSH key on your control node if you don't have one
ssh-keygen -t rsa -b 4096

# Copy your public key to the target host
ssh-copy-id my_super_user@<target_server_ip>

2.3. Grant Passwordless sudo Privileges (on Target Host): Ansible needs to perform administrative tasks. Granting passwordless sudo is essential for automation.

  • Command:

    sudo visudo -f /etc/sudoers.d/90-my_super_user
    
  • Content to add:

    my_super_user ALL=(ALL) NOPASSWD: ALL
    
  • Why visudo? It’s a special editor that syntax-checks your changes before saving, preventing you from getting locked out of sudo.

  • Why /etc/sudoers.d/? This directory keeps your custom rules separate from the main /etc/sudoers file, so they won’t be overwritten by system updates.

  • Why 90-? Files in this directory are read in alphabetical order. A high number like 90 ensures your rule is read last and takes precedence over any other conflicting rules.

3. Best Practice Project Structure with Roles
#

As your project grows, using roles is the best way to keep it organized, reusable, and maintainable.

3.1. Create the Directory Structure: Use the ansible-galaxy command to create a skeleton structure for your roles.

# From within your ansible_project directory
mkdir -p roles
cd roles
ansible-galaxy init common
ansible-galaxy init docker

3.2. Final Project Structure: Your project should look like this:

ansible_project/
├── hosts                  # Inventory file listing your servers
├── roles/
│   ├── common/            # For tasks common to all servers
│   │   └── tasks/
│   │       └── main.yml
│   └── docker/            # For tasks specific to setting up Docker
│       └── tasks/
│           └── main.yml
└── site.yml               # The main playbook that calls the roles

4. The hosts Inventory File
#

The inventory file lists your servers and groups them into logical environments like testing and production.

File: ~/ansible_project/hosts

[testing]
thor ansible_host=192.168.66.66

[production]
artemis ansible_host=192.168.66.166

# A parent group that contains both testing and production
[docker_servers:children]
testing
production

[all:vars]
ansible_user=my_super_user

5. The common Role: Baseline Configuration
#

This role installs packages and configures services that you want on all your servers.

File: ~/ansible_project/roles/common/tasks/main.yml

---
# tasks file for common
- name: Update apt cache and install common prerequisites
  ansible.builtin.apt:
    name:
      - apt-transport-https
      - ca-certificates
      - curl
      - gnupg
      - lsb-release
      - qemu-guest-agent # For Proxmox integration
      - btop             # A useful monitoring tool
    state: present
    update_cache: yes

- name: Ensure the QEMU Guest Agent is running
  ansible.builtin.service:
    name: qemu-guest-agent
    state: started
    enabled: yes

6. The docker Role: Application-Specific Configuration
#

This role contains all the logic required to install and configure Docker.

File: ~/ansible_project/roles/docker/tasks/main.yml

---
# tasks file for docker
- name: Create directory for Docker's GPG key
  ansible.builtin.file:
    path: /etc/apt/keyrings
    state: directory
    mode: '0755'

- name: Download and dearmor Docker's official GPG key
  ansible.builtin.shell:
    cmd: "curl -fsSL [https://download.docker.com/linux/ubuntu/gpg](https://download.docker.com/linux/ubuntu/gpg) | gpg --dearmor -o /etc/apt/keyrings/docker.gpg"
    creates: /etc/apt/keyrings/docker.gpg

- name: Add Docker repository source file
  ansible.builtin.copy:
    dest: /etc/apt/sources.list.d/docker.list
    content: "deb [arch=amd64 signed-by=/etc/apt/keyrings/docker.gpg] [https://download.docker.com/linux/ubuntu](https://download.docker.com/linux/ubuntu) {{ ansible_lsb.codename }} stable"
    owner: root
    group: root
    mode: '0644'

- name: Install or update Docker Engine and Docker Compose
  ansible.builtin.apt:
    name:
      - docker-ce
      - docker-ce-cli
      - containerd.io
      - docker-buildx-plugin
      - docker-compose-plugin
    state: latest # 'latest' ensures packages are updated on each run

- name: Add {{ ansible_user }} user to the docker group
  ansible.builtin.user:
    name: "{{ ansible_user }}"
    groups: docker
    append: yes

- name: Ensure Docker service is running and enabled on boot
  ansible.builtin.service:
    name: docker
    state: started
    enabled: yes

7. The Main Playbook (site.yml)
#

This top-level playbook is now very simple. It just specifies which hosts to target and which roles to apply.

File: ~/ansible_project/site.yml

---
- name: Configure Docker Servers
  hosts: docker_servers
  become: true
  roles:
    - common
    - docker

8. Running Playbooks Safely
#

Always test your changes before applying them to production.

  • Check Mode (Dry Run): See what would change without making any modifications.
    ansible-playbook -i hosts site.yml --check
    
  • Check Mode with Diff: See the exact file changes that would occur.
    ansible-playbook -i hosts site.yml --check --diff
    
  • Limit to a Test Server: Run the playbook only against your testing group.
    ansible-playbook -i hosts site.yml --limit testing
    
  • Run on Production: Once verified, run against the production group.
    ansible-playbook -i hosts site.yml --limit production
    

9. Troubleshooting Common Errors
#

  • GPG Key Error (NO_PUBKEY): This error occurs when apt doesn’t trust the Docker repository.

    • Cause: The GPG key was not downloaded or was in the wrong format.
    • Solution: Use the shell module with gpg --dearmor to download and convert the key to the required binary format (as shown in the docker role).
  • Errors on Reruns: An error occurs on the very first task (Update apt cache).

    • Cause: A file from a previous failed run (e.g., /etc/apt/sources.list.d/docker.list) was left on the target host, causing apt to fail before Ansible can fix the problem.
    • Solution: Manually SSH into the target host and remove the leftover configuration files before rerunning the playbook.
      sudo rm /etc/apt/sources.list.d/docker.list
      sudo rm /etc/apt/keyrings/docker.gpg
      
Daniël Spithout
Author
Daniël Spithout
Learning by doing