Post

Ansible All Distro Docker Install

Ansible All Distro Docker Install

As part of my SysAdmin journey, I’ve begun to play with Ansible. For a while I’ve been using very simple playbooks to update my homelab’s VMs and LXC containers, but it’s time to take that a bit further.

Desired Outcome

The goal today is to start using playbooks in a modular way, thus allowing more flexibility and the reuse of playbooks across multiple projects.

The idea here, is to have one main playbook that determines the OS on the host, namely Debian, Ubuntu, or a RedHat derivative OS (such as CentOS, Rocky, Fedora etc.)

From there, a chain of appropriate task.yml files will begin updating the host OS, installing Docker, and finally copying over a test webpage and starting up an Nginx Docker container to serve the webpage.

If we see the image below, we’re golden.

Nginx webpage test

Playbooks and task.yml files

First up we have the main playbook that triggers everything.

main playbook

os_specific_vm_setup.yml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
---
- name: Update OS and install Docker
  hosts: all
  become: true
  gather_facts: yes

  tasks:
    - name: Run update and install Docker for UBUNTU
      include_tasks: ubuntu_vm_setup.yml
      when: ansible_facts['distribution'] == 'Ubuntu'

    - name: Run update and install Docker for DEBIAN
      include_tasks: debian_vm_setup.yml
      when: ansible_facts['distribution'] == 'Debian'

    - name: Run update and install Docker for REDHAT
      include_tasks: redhat_vm_setup.yml
      when: ansible_facts['distribution'] in ['CentOS', 'RedHat', 'Rocky', 'AlmaLinux', 'Oracle', 'Scientific', 'Fedora'] #'Rocky'

OS specific tasks

debian_vm_setup.yml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# pre_tasks
- name: Update cache if needed and update all installed apps
  ansible.builtin.apt:
    update_cache: true
    cache_valid_time: 3600
    name: "*"
    state: latest

# tasks
- name: Install base apps
  import_tasks: tasks/base_apps_debian.yml

- name: Install Docker
  import_tasks: tasks/docker_install_debian.yml # Debian host

- name: Setup Nginx container
  import_tasks: tasks/docker_nginx_setup.yml

# post_tasks
- name: Do apt cleanup and autoremove dangling files
  ansible.builtin.apt:
    autoclean: yes
    autoremove: yes

ubuntu_vm_setup.yml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# pre_tasks
- name: Update cache if needed and update all installed apps
  ansible.builtin.apt:
    update_cache: true
    cache_valid_time: 3600
    name: "*"
    state: latest

# tasks
- name: Install base apps
  import_tasks: tasks/base_apps_debian.yml

- name: Install Docker
  import_tasks: tasks/docker_install_ubuntu.yml # Ubuntu host

- name: Setup Nginx container
  import_tasks: tasks/docker_nginx_setup.yml

# post_tasks
- name: Do apt cleanup and autoremove dangling files
  ansible.builtin.apt:
    autoclean: yes
    autoremove: yes

redhat_vm_setup.yml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# pre_tasks
- name: Update cache if needed and update all installed apps
  ansible.builtin.dnf:
    update_cache: true
    name: "*"
    state: latest

# tasks
- name: Install base apps
  import_tasks: tasks/base_apps_redhat.yml

- name: Install Docker
  import_tasks: tasks/docker_install_redhat.yml # Redhat;Rock Linux host

- name: Setup Nginx container
  import_tasks: tasks/docker_nginx_setup.yml

# post_tasks
- name: Do apt cleanup and autoremove dangling files
  ansible.builtin.dnf:
    autoremove: yes

OS specific base app install tasks

base_apps_debian.yml

1
2
3
4
5
6
7
8
9
10
11
12
13
- name: Install commonly used OS apps
  ansible.builtin.apt: 
    pkg:                  # install a list of packages
    - net-tools
    - curl
    - zip
    - unzip
    - git
    - fail2ban
    - tree
    - iperf3
    state: latest
    update_cache: true

base_apps_redhat.yml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
- name: Install EPEL repo
  ansible.builtin.dnf: 
    pkg:
    - epel-release        # necessary for certain dnf packages
    state: latest
    update_cache: true

- name: Install commonly used OS apps
  ansible.builtin.dnf: 
    pkg:                  # install a list of packages
    - net-tools
    - curl
    - zip
    - unzip
    - git
    - fail2ban
    - tree
    - iperf3
    - python3-pip
    state: latest
    update_cache: true

OS specfic Docker install tasks

docker_install_debian.yml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
- name: Install Docker dependencies
  ansible.builtin.apt:
    pkg:
    - apt-transport-https
    - ca-certificates
    - curl
    - software-properties-common
    - python3-pip
    - virtualenv
    - python3-setuptools
    state: latest
    update_cache: true

- name: Add Docker GPG apt Key
  apt_key:
    url: https://download.docker.com/linux/ubuntu/gpg
    state: present

- name: Add Docker repo
  ansible.builtin.apt_repository:
    repo: deb [arch=amd64] https://download.docker.com/linux/debian bookworm stable   # update for correct Ubuntu version: jammy=22.04 LTS
    state: present

- name: Update apt and install docker-ce
  ansible.builtin.apt:
    pkg:
    - docker-ce
    - docker-ce-cli
    - containerd.io
    - docker-buildx-plugin
    - docker-compose-plugin
    state: latest
    update_cache: true

- name: Add non-root Docker user
  ansible.builtin.shell:
    cmd: usermod -aG docker testuser

docker_install_ubuntu.yml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
# Docker environment install for Ubuntu
- name: Install Docker dependencies
  ansible.builtin.apt:
    pkg:
    - apt-transport-https
    - ca-certificates
    - curl
    - software-properties-common
    - python3-pip
    - virtualenv
    - python3-setuptools
    state: latest
    update_cache: true

- name: Add Docker GPG apt Key
  apt_key:
    url: https://download.docker.com/linux/ubuntu/gpg
    state: present

- name: Add Docker repo
  ansible.builtin.apt_repository:
    repo: deb https://download.docker.com/linux/ubuntu noble stable   # update for correct Ubuntu version: noble=24.04 LTS; jammy=22.04 LTS
    state: present

- name: Update apt and install docker-ce
  ansible.builtin.apt:
    pkg:
    - docker-ce
    - docker-ce-cli
    - containerd.io
    - docker-buildx-plugin
    - docker-compose-plugin
    state: latest
    update_cache: true

- name: Add non-root Docker user
  ansible.builtin.shell:
    cmd: usermod -aG docker testuser

docker_install_redhat.yml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
# Docker environment install for Redhat
- name: Add signing key
  ansible.builtin.rpm_key:
    key: "https://download.docker.com/linux/rhel/gpg"
    state: present

- name: Add repo into repo.d list
  ansible.builtin.yum_repository:
    name: docker
    description: docker repository
    baseurl: "https://download.docker.com/linux/rhel/9/x86_64/stable"
#    enable: true
    gpgcheck: true
    gpgkey: "https://download.docker.com/linux/rhel/gpg"

- name: Install Docker
  ansible.builtin.yum:
    name:
      - docker-ce
      - docker-ce-cli
      - containerd.io
    state: present
    update_cache: true

- name: Install Python PIP dependencies
  ansible.builtin.pip:
    name:
      - virtualenv
      - requests
      #- python3-wheels
    state: present

- name: Start and Enable Docker service
  ansible.builtin.service:
    name: "docker"
    enabled: true
    state: started

- name: Add non-root Docker user
  ansible.builtin.shell:
    cmd: usermod -aG docker testuser

Nginx container setup

docker_nginx_setup.yml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
- name: Create local Nginx folder structure
  file:
    path: /home/testuser/nginx
    state: directory

- name: Copy over nginx folder
  copy:
    dest: /home/testuser/nginx/
    src: nginx/

- name: Start Nginx container
  ansible.builtin.docker_container:
    name: "nginx"
    image: "nginx:latest"
    detach: yes
    ports: "80:80"
    volumes: "/home/testuser/nginx/site:/usr/share/nginx/html"
    restart: "true"

ansible.cfg

ansible.cfg

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
[testing:children]
debian
redhat
ubuntu

[debian]
192.168.10.156 # debian

[debian:vars]
ansible_ssh_user=testuser
#ansible_ssh_password=my-fancy-password
ansible_become_password=my-fancy-password
ansible_ssh_private_key_file=/home/testuser/.ssh/testing

[redhat]
192.168.10.153 # rocky

[redhat:vars]
ansible_ssh_user=testuser
#ansible_ssh_password=my-fancy-password
ansible_become_password=my-fancy-password
ansible_ssh_private_key_file=/home/testuser/.ssh/testing

[ubuntu]
192.168.10.152 # ubuntu

[ubuntu:vars]
ansible_ssh_user=testuser
#ansible_ssh_password=my-fancy-password
ansible_become_password=my-fancy-password
ansible_ssh_private_key_file=/home/testuser/.ssh/testing

## ENABLE SSH AGENT once before testing/application to avoid retyping password
## eval "$(ssh-agent -s)"
## ssh-add ~/.ssh/testing

inventory.ini

inventory.ini

1
2
3
4
5
[defaults]
inventory = inventory.ini
deprecation_warnings=False # to stop getting annoying warnings in purple text
#host_key_checking=false     # comment out once SSH keys are in use
ansible_python_interpreter=/usr/bin/python3

Technical Difficulties

Honestly, the issues were mostly around me needing to do some trial and error, and more and more trial and error. Especially regarding the differences between Debian/Ubuntu and RedHat prerequisite apps. Much the same with the various ansible.modules. It took a lot of research, but I got there.

Lessons Learned

Time, and practice is needed. Documentation is your friend. Slowly writing commands in myself versus copy and pasting everything is the best way to learn, as it helps my brain to understand how everything interconnects.

This post is licensed under CC BY 4.0 by the author.