Ansible To Install Docker

Ansible To Install Docker

Reading time1 min
#Automation#DevOps#Cloud#Ansible#Docker#Linux

Automated Docker Engine Deployment with Ansible: The Reliable Path

Manual Docker installation across multiple environments inevitably leads to drift—dependencies missed on some nodes, obsolete package caches, or version mismatches. A single, idempotent Ansible playbook eliminates these inconsistencies and delivers predictable results on every server, regardless of scale.

Below is a minimal, production-oriented playbook for Ubuntu 20.04 LTS and 22.04 LTS hosts. This approach uses native package management (APT), handles GPG keys securely, and configures user permissions to drop the requirement for sudo in daily Docker usage.


Core Advantages of Ansible-based Docker Provisioning

  • Uniformity: Aligns package versions and configurations at scale.
  • Idempotence: Repeated runs introduce no side effects or cumulative changes.
  • Recoverability: Cleanly recreate or recover node states via playbook replay.
  • Parallelization: Scale from a single test VM to a full production cluster with negligible effort.
  • Update Simplicity: Centralized control for Docker upgrades, rollbacks, and configuration changes.

Prerequisites

  • Control machine running Ansible (tested with Ansible ≥2.10).
  • Inventory with SSH connectivity to Ubuntu targets (20.04 or 22.04).
  • Sudo-enabled user on each managed host.
  • At least 2GB RAM and ≥10GB free disk on target systems (production containers may require more).

Example Playbook: install-docker.yml

---
- name: Docker Engine installation and configuration (Ubuntu)
  hosts: all
  become: true

  tasks:
    - name: Refresh apt cache (always)
      ansible.builtin.apt:
        update_cache: yes
        cache_valid_time: 3600

    - name: Install prerequisite packages for APT over HTTPS
      ansible.builtin.apt:
        name:
          - ca-certificates
          - curl
          - gnupg
          - lsb-release
        state: present

    - name: Ensure /etc/apt/keyrings directory exists
      ansible.builtin.file:
        path: /etc/apt/keyrings
        state: directory
        mode: '0755'

    - name: Add Docker’s official GPG key (saved in /etc/apt/keyrings)
      ansible.builtin.get_url:
        url: https://download.docker.com/linux/ubuntu/gpg
        dest: /etc/apt/keyrings/docker.gpg
        mode: '0644'
      register: docker_gpg_key

    # Prevent repeated re-adding of the apt source file
    - name: Add Docker repository for current Ubuntu release
      ansible.builtin.apt_repository:
        repo: "deb [arch=amd64 signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu {{ ansible_lsb.codename }} stable"
        filename: docker
        state: present

    - name: Update apt cache after adding Docker repo
      ansible.builtin.apt:
        update_cache: yes

    - name: Install Docker Engine and CLI (latest stable)
      ansible.builtin.apt:
        name:
          - docker-ce
          - docker-ce-cli
          - containerd.io
        state: latest

    - name: Ensure Docker is started and enabled
      ansible.builtin.systemd:
        name: docker
        state: started
        enabled: true

    - name: Add main user to docker group (skip root)
      ansible.builtin.user:
        name: "{{ ansible_user_id }}"
        groups: docker
        append: true
      when: ansible_user_id != 'root'

Note: If you see E: Unable to locate package docker-ce, double-check the repository line—{{ ansible_lsb.codename }} must resolve (e.g., focal for Ubuntu 20.04, jammy for 22.04).


How to Apply the Playbook

  1. Populate your Ansible inventory

Example hosts file:

[docker_hosts]
192.168.2.10
192.168.2.11
  1. Run the playbook (from your Ansible control node):
ansible-playbook -i hosts install-docker.yml

Expect standard Ansible output. On failure, typical SSH-related permission errors look like:

fatal: [192.168.2.10]: FAILED! => {"msg": "Authentication or permission failure...", ...}

Verification Steps

On any managed host, verify Docker:

docker --version   # Expect: Docker version 24.0.x, build ...
docker run --rm hello-world

Output should include:

Hello from Docker!
This message shows that your installation appears to be working correctly.

If you observe Cannot connect to the Docker daemon, the user may need to log out and back in for docker group changes to apply.


Beyond the Basics

  • Non-root execution: This configuration enables the specified user to run Docker commands sans sudo. Security policy may require more granular user management; consult your compliance guidelines.
  • Version pinning: In production, pin Docker versions for stability (e.g., docker-ce=5:20.10.21~3-0~ubuntu-focal). Be prepared to update pins regularly as CVEs emerge.
  • Compose and plugins: Extend with docker-compose-plugin or additional Ansible roles, e.g., geerlingguy.docker, for enterprise workloads.
  • OS variants: For RHEL/CentOS, substitute yum/dnf modules and maintain separate repo setup blocks.
  • Idempotence gotcha: If the playbook is interrupted during package install, re-running is safe. However, repository GPG key changes (rare) may require repo cache purges or manual intervention.

Practical Considerations

  • System resource limits: Default Docker installations do not set cgroup or ulimit parameters; for high-density workloads, tune /etc/docker/daemon.json.
  • Firewalls: Docker manipulates iptables. If you're using UFW or custom firewalls, review rules post-install.
  • Uninstallation: Ansible’s apt task can remove Docker—but dangling volumes and /var/lib/docker content remain unless explicitly wiped.

Example: Custom Daemon Configuration

To configure Docker with an internal registry mirror and log driver, add a task:

    - name: Configure Docker daemon.json
      ansible.builtin.copy:
        dest: /etc/docker/daemon.json
        content: |
          {
            "log-driver": "json-file",
            "log-opts": { "max-size": "10m", "max-file": "3" },
            "registry-mirrors": ["https://my.mirror.local"]
          }
      notify: Restart Docker

  handlers:
    - name: Restart Docker
      ansible.builtin.systemd:
        name: docker
        state: restarted

In Summary

Ansible-based Docker installation ensures uniform environments and eliminates fragile, error-prone manual steps—essential for resilient build or test server fleets. While production deployments may justify more advanced playbook modularization (roles, tags, molecule tests), the above template is robust for both exploratory and real-world adoption.

There are alternative approaches—cloud-native container services, baked-in machine images—but direct Ansible automation remains broadly compatible and infrastructure-agnostic.


Side note: For air-gapped environments, pre-stage the Docker deb packages and adapt installation to use apt’s deb parameter.