From 1fc2506cf4eeb4cff0a4f0dc3f718d4ec7a35fea Mon Sep 17 00:00:00 2001 From: Kevin Midboe Date: Wed, 5 Aug 2026 20:48:24 +0200 Subject: [PATCH] update docker role & add separate play --- plays/docker.yml | 5 ++ roles/docker/README.md | 76 ++++++++++++++++++++++++ roles/docker/defaults/main.yml | 35 ++++++++++- roles/docker/handlers/main.yml | 2 +- roles/docker/meta/main.yml | 15 ++++- roles/docker/tasks/check_distro.yml | 43 ++++++-------- roles/docker/tasks/install.yml | 55 ++++++++++++++--- roles/docker/tasks/main-distro-check.yml | 11 ---- roles/docker/tasks/main.yml | 60 ++++++------------- roles/docker/tasks/service.yml | 14 +---- roles/docker/tasks/users.yml | 12 ++++ 11 files changed, 225 insertions(+), 103 deletions(-) create mode 100644 plays/docker.yml create mode 100644 roles/docker/README.md delete mode 100644 roles/docker/tasks/main-distro-check.yml create mode 100644 roles/docker/tasks/users.yml diff --git a/plays/docker.yml b/plays/docker.yml new file mode 100644 index 0000000..e3eb465 --- /dev/null +++ b/plays/docker.yml @@ -0,0 +1,5 @@ +--- +- name: Docker on host + hosts: all + roles: + - role: roles/docker diff --git a/roles/docker/README.md b/roles/docker/README.md new file mode 100644 index 0000000..6ea7e86 --- /dev/null +++ b/roles/docker/README.md @@ -0,0 +1,76 @@ +# docker + +Installs Docker CE from the official Docker apt repository on Ubuntu/Debian. + +## What changed from the original role + +The original role had several bugs that would have made it fail or behave +unpredictably: + +- **`tasks/main.yml` was dead code that duplicated `install.yml` incorrectly.** + Nothing ever included it (only `main-distro-check.yml` was wired up), and + it referenced undefined variables (`install_packages`, + `clean_install_remove_packages` had no `become`, etc). It's been replaced + by a single real entrypoint. +- **`install.yml` added the apt repo but never fetched the GPG key**, so + `apt-get update` would fail signature verification. Key download and repo + registration are now in one idempotent flow using `get_url` + + `apt_repository` instead of raw `shell` commands. +- **`check_distro.yml` used `meta: end_play`**, which ends the *entire + Ansible run* for *all* hosts in the play, not just this role/host. This is + almost never what you want in a multi-host play. It's replaced with a + `docker_distro_supported` fact that gates subsequent tasks, with an + optional hard `fail` (`docker_fail_on_unsupported_distro`, default `true`). +- **`service.yml` had the exact same task listed twice** (one with a `when` + guard, one without). Deduplicated into a single task. +- **Package state `latest`** was used for prerequisites and Docker itself, + which causes unnecessary upgrades (and potential breakage) on every run. + Changed to `present`; use `docker_install_packages` + your own + update strategy if you want upgrades. +- **No handler was ever notified.** Installing/upgrading Docker packages now + notifies the `Restart Docker` handler. +- **`docker` group creation used the `user` module** to create a "user" named + docker, which is not the same as a group and wasn't added to any actual + user. Replaced with a proper `group` task plus a `docker_users` list you + populate to add real users to the group. +- All tasks now use fully-qualified module names (`ansible.builtin.*`) and + `name:`/`state:` keyword syntax instead of the deprecated + `key=value` shorthand. + +## Role variables + +See `defaults/main.yml` for the full list. Commonly overridden: + +```yaml +docker_users: + - deploy + - ci + +docker_install_packages: + - docker-ce + - docker-ce-cli + - containerd.io + - docker-buildx-plugin + - docker-compose-plugin + +docker_fail_on_unsupported_distro: true +``` + +## Example playbook + +```yaml +- hosts: docker_hosts + become: true + roles: + - role: docker + vars: + docker_users: + - "{{ ansible_user }}" +``` + +## Tags + +- `docker_check_distro` +- `docker_install` +- `docker_users` +- `docker_service` diff --git a/roles/docker/defaults/main.yml b/roles/docker/defaults/main.yml index 95034e4..3190494 100644 --- a/roles/docker/defaults/main.yml +++ b/roles/docker/defaults/main.yml @@ -1,4 +1,12 @@ -clean_install_remove_packages: +--- +# Distros this role knows how to configure the Docker apt repo for. +docker_supported_distros: + - ubuntu + - debian + +# Packages to purge before installing Docker CE, to avoid conflicts +# with distro-provided / older Docker packages. +docker_clean_install_remove_packages: - docker.io - docker-doc - docker-compose @@ -6,10 +14,31 @@ clean_install_remove_packages: - containerd - runc -install_packages: +# Packages installed from the official Docker apt repository. +docker_install_packages: - docker-ce - docker-ce-cli - containerd.io - docker-buildx-plugin - - docker-compose - docker-compose-plugin + +# Base URL for the Docker apt repository. {{ '%s' }} is filled in with the +# lowercased distro name (ubuntu/debian). +docker_apt_repo_url: "https://download.docker.com/linux" + +# Where the Docker GPG signing key is stored. +docker_apt_keyring_dir: /etc/apt/keyrings +docker_apt_keyring_file: "{{ docker_apt_keyring_dir }}/docker.asc" + +# apt cache freshness, in seconds, before Ansible refreshes it. +docker_apt_cache_valid_time: 3600 + +# Users to add to the "docker" group (passwordless docker CLI access). +docker_users: [] + +# Whether to start and enable the docker service. +docker_service_enabled: true +docker_service_state: started + +# Fail the play if the distro isn't supported, instead of silently skipping. +docker_fail_on_unsupported_distro: true diff --git a/roles/docker/handlers/main.yml b/roles/docker/handlers/main.yml index eeab2a2..303ef11 100644 --- a/roles/docker/handlers/main.yml +++ b/roles/docker/handlers/main.yml @@ -1,5 +1,5 @@ --- - name: Restart Docker - systemd: + ansible.builtin.systemd: name: docker state: restarted diff --git a/roles/docker/meta/main.yml b/roles/docker/meta/main.yml index 7822c41..89fbc81 100644 --- a/roles/docker/meta/main.yml +++ b/roles/docker/meta/main.yml @@ -1,13 +1,22 @@ --- galaxy_info: + role_name: docker author: Your Name - description: Ansible role to install and manage Docker on Debian + description: Installs and manages Docker CE from the official Docker apt repository license: MIT - min_ansible_version: "2.9" + min_ansible_version: "2.14" platforms: + - name: Ubuntu + versions: + - focal + - jammy + - noble - name: Debian versions: - - all + - bullseye + - bookworm galaxy_tags: - docker + - containers + dependencies: [] diff --git a/roles/docker/tasks/check_distro.yml b/roles/docker/tasks/check_distro.yml index fa91c16..3c6bdfe 100644 --- a/roles/docker/tasks/check_distro.yml +++ b/roles/docker/tasks/check_distro.yml @@ -1,27 +1,22 @@ --- -- name: Check if the current distro is supported (Ubuntu or Debian) - set_fact: - distro_supported: "{{ ansible_facts['distribution'].lower() in supported_distros }}" - tags: - - check_distro +- name: Determine if the current distro is supported + ansible.builtin.set_fact: + docker_distro_supported: "{{ ansible_facts['distribution'] | lower in docker_supported_distros }}" -- name: Set installation URL based on the distro - set_fact: - install_url: "https://download.docker.com/linux/{{ ansible_facts['distribution'].lower() }} {{ ansible_distribution_release }} stable" - when: distro_supported - tags: - - set_url - -- name: Log Unsupported Distro - debug: - msg: "The {{ ansible_facts['distribution'] }} distribution is not supported. Skipping Docker installation." - when: not distro_supported - tags: - - unsupported_distro - -- name: Skip Docker installation task if distro is unsupported - meta: end_play - when: not distro_supported - tags: - - end_play +- name: Fail on unsupported distro + ansible.builtin.fail: + msg: >- + {{ ansible_facts['distribution'] }} is not a supported distribution for + the docker role. Supported: {{ docker_supported_distros | join(', ') }}. + when: + - not docker_distro_supported + - docker_fail_on_unsupported_distro +- name: Notify unsupported distro is being skipped + ansible.builtin.debug: + msg: >- + {{ ansible_facts['distribution'] }} is not supported by the docker role. + Skipping Docker installation on this host. + when: + - not docker_distro_supported + - not docker_fail_on_unsupported_distro diff --git a/roles/docker/tasks/install.yml b/roles/docker/tasks/install.yml index 9053896..ebbe478 100644 --- a/roles/docker/tasks/install.yml +++ b/roles/docker/tasks/install.yml @@ -1,12 +1,49 @@ --- -- name: Add Docker repository - apt_repository: - repo: deb {{ install_url }} - state: present - when: distro_supported +- name: Remove conflicting/legacy Docker packages + ansible.builtin.package: + name: "{{ docker_clean_install_remove_packages }}" + state: absent -- name: Install Docker - apt: - name: "{{ docker_package }}" +- name: Install prerequisite packages + ansible.builtin.package: + name: + - ca-certificates + - curl + - gnupg state: present - when: distro_supported + update_cache: true + +- name: Ensure apt keyrings directory exists + ansible.builtin.file: + path: "{{ docker_apt_keyring_dir }}" + state: directory + mode: "0755" + +- name: Download Docker's official GPG key + ansible.builtin.get_url: + url: "{{ docker_apt_repo_url }}/{{ ansible_facts['distribution'] | lower }}/gpg" + dest: "{{ docker_apt_keyring_file }}" + mode: "0644" + force: true + +- name: Add Docker apt repository + ansible.builtin.apt_repository: + repo: >- + deb [arch={{ 'amd64' if ansible_facts['architecture'] == 'x86_64' else ansible_facts['architecture'] }} + signed-by={{ docker_apt_keyring_file }}] + {{ docker_apt_repo_url }}/{{ ansible_facts['distribution'] | lower }} + {{ ansible_facts['distribution_release'] }} stable + state: present + filename: docker + update_cache: false + +- name: Update apt cache + ansible.builtin.apt: + update_cache: true + cache_valid_time: "{{ docker_apt_cache_valid_time }}" + +- name: Install Docker packages + ansible.builtin.package: + name: "{{ docker_install_packages }}" + state: present + notify: Restart Docker diff --git a/roles/docker/tasks/main-distro-check.yml b/roles/docker/tasks/main-distro-check.yml deleted file mode 100644 index 5dbc11f..0000000 --- a/roles/docker/tasks/main-distro-check.yml +++ /dev/null @@ -1,11 +0,0 @@ ---- -- name: Include distro check tasks - include_tasks: check_distro.yml - tags: - - check_distro - -- name: Include Docker installation tasks if distro is supported - include_tasks: install.yml - when: distro_supported - tags: - - install diff --git a/roles/docker/tasks/main.yml b/roles/docker/tasks/main.yml index a6b9352..d5120c6 100644 --- a/roles/docker/tasks/main.yml +++ b/roles/docker/tasks/main.yml @@ -1,45 +1,23 @@ --- -- name: Clean install by removing any docker package - package: name={{ item }} state=absent - with_items: "{{ clean_install_remove_packages }}" +- name: Assert supported distribution + ansible.builtin.include_tasks: check_distro.yml + tags: + - docker_check_distro -- name: Ensure curl & ca-certs are installed - package: - name: - - ca-certificates - - curl - - gnupg - state: latest +- name: Install Docker + ansible.builtin.include_tasks: install.yml + when: docker_distro_supported + tags: + - docker_install -- name: Ensure docker keyring file exists - file: - path: /etc/apt/keyrings/docker.gpg - state: touch - -- name: Download docker gpg key and add to keyrings - shell: | - install -m 0755 -d /etc/apt/keyrings - curl -fsSL https://download.docker.com/linux/ubuntu/gpg | gpg --dearmor --yes -o /etc/apt/keyrings/docker.gpg - chmod a+r /etc/apt/keyrings/docker.gpg +- name: Configure Docker group membership + ansible.builtin.include_tasks: users.yml + when: docker_distro_supported and docker_users | length > 0 + tags: + - docker_users -- name: Sign and add docker deb source - shell: | - echo \ - "deb [arch="$(dpkg --print-architecture)" signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu \ - "$(. /etc/os-release && echo "$VERSION_CODENAME")" stable" | \ - tee /etc/apt/sources.list.d/docker.list > /dev/null - -- name: Update apt sources - become: true - apt: - update_cache: yes - cache_valid_time: 1 - -- name: Install docker packages - package: name={{ item }} state=latest - with_items: "{{ install_packages }}" - -- name: Ensure group docker exists - user: - name: docker - state: present +- name: Manage Docker service + ansible.builtin.include_tasks: service.yml + when: docker_distro_supported + tags: + - docker_service diff --git a/roles/docker/tasks/service.yml b/roles/docker/tasks/service.yml index 5038f96..252f149 100644 --- a/roles/docker/tasks/service.yml +++ b/roles/docker/tasks/service.yml @@ -1,14 +1,6 @@ --- - name: Start and enable Docker service - systemd: + ansible.builtin.systemd: name: docker - enabled: yes - state: started - -- name: Start and enable Docker service - systemd: - name: docker - enabled: yes - state: started - when: distro_supported - + enabled: "{{ docker_service_enabled }}" + state: "{{ docker_service_state }}" diff --git a/roles/docker/tasks/users.yml b/roles/docker/tasks/users.yml new file mode 100644 index 0000000..09824d3 --- /dev/null +++ b/roles/docker/tasks/users.yml @@ -0,0 +1,12 @@ +--- +- name: Ensure docker group exists + ansible.builtin.group: + name: docker + state: present + +- name: Add users to the docker group + ansible.builtin.user: + name: "{{ item }}" + groups: docker + append: true + loop: "{{ docker_users }}"