Compare commits

...

5 Commits

Author SHA1 Message Date
42fb065afe docs: add readme 2025-11-17 15:06:17 +01:00
2279f8beba feat: add templates 2025-11-17 15:06:09 +01:00
66f09c49bd feat: add tasks 2025-11-17 15:06:06 +01:00
c5925f2a5a feat: add handlers 2025-11-17 15:06:02 +01:00
b73d283e45 feat: add default 2025-11-17 15:05:57 +01:00
11 changed files with 320 additions and 0 deletions

73
README.md Normal file
View File

@@ -0,0 +1,73 @@
# ansible-role-docker
An Ansible role to install and configure Docker.
## Requirements
- Ansible 2.10 or higher
- Debian or Ubuntu-based distribution
## Dependencies
This role doesn't have any dependencies to other roles or collections.
## Playbook Example
A pretty complete playbook example to install Docker and configure it for multiple users, expose the Docker API, configure a Docker registry and create a Docker network.
```yaml
---
- hosts: docker_servers
become: true
roles:
- ansible-role-docker
vars:
docker_users:
- titou
- antoine
docker_data_dir: /opt/docker
docker_expose_api: true
docker_registry_url: "docker.io"
docker_registry_email: "user@example.com"
docker_networks:
- name: "my-network"
driver: "bridge"
subnet: "172.20.0.0/16"
gateway: "172.20.0.1"
ip_range: "172.20.0.0/24"
```
**⚠️ SECURITY WARNING**: Exposing Docker API over TCP without TLS is insecure : consider using TLS or restrict access with firewall rules.
You can also use the role in its most simple form:
```yaml
---
- hosts: docker_servers
become: true
roles:
- ansible-role-docker
```
## Variables
### Basic Configuration
| Variable | Required | Default | Description |
| -------------------------- | -------- | ----------- | -------------------------------------------------------------------------------------------- |
| `docker_api_host` | No | `127.0.0.1` | Host address to expose Docker API on (when `docker_expose_api` is true) |
| `docker_api_port` | No | `2375` | Port to expose Docker API on (when `docker_expose_api` is true) |
| `docker_arch` | No | `amd64` | Architecture to install Docker for (e.g., "amd64", "arm64") |
| `docker_data_dir` | No | `` | Directory to store Docker volumes data |
| `docker_expose_api` | No | `false` | Whether to expose Docker API over TCP |
| `docker_networks` | No | `[]` | List of networks to create (dicts with name, driver, subnet, etc.) |
| `docker_registry_email` | No | `` | Registry email (mutually exclusive with `docker_registry_username`) |
| `docker_registry_url` | No | `` | Docker registry URL (registry authentication is skipped if not set) |
| `docker_registry_username` | No | `` | Registry username (mutually exclusive with `docker_registry_email`) |
| `docker_users` | No | `[]` | List of users to be added to the `docker` group |
**Note**: When using registry authentication, the registry password must be provided via the `DOCKER_REGISTRY_PASSWORD` environment variable.
## License
This project is licensed under the GNU General Public License v3.0 or later (GPLv3+). See the [LICENSE](LICENSE) file for details.

9
defaults/main.yml Normal file
View File

@@ -0,0 +1,9 @@
---
docker_expose_api: false
docker_api_host: 127.0.0.1
docker_api_port: 2375
docker_arch: amd64
docker_users: []
docker_networks: []

12
handlers/main.yml Normal file
View File

@@ -0,0 +1,12 @@
---
- name: reload systemd
ansible.builtin.systemd:
daemon_reload: true
- name: restart docker
ansible.builtin.systemd:
name: docker
state: restarted
daemon_reload: true
ignore_errors: false

57
tasks/configuration.yml Normal file
View File

@@ -0,0 +1,57 @@
---
- name: Add users to docker group
ansible.builtin.user:
name: "{{ item }}"
groups: docker
append: true
loop: "{{ docker_users }}"
when: docker_users | length > 0
- name: Create /etc/docker directory
ansible.builtin.file:
path: /etc/docker
state: directory
mode: "0755"
- name: Enable and start Docker
ansible.builtin.systemd:
name: docker
state: started
enabled: true
- name: Create docker data directory
ansible.builtin.file:
path: "{{ docker_data_dir }}"
state: directory
mode: "0755"
group: docker
when: docker_data_dir is defined and docker_data_dir | length > 0
- name: Create systemd override directory for Docker
ansible.builtin.file:
path: /etc/systemd/system/docker.service.d
state: directory
mode: "0755"
when: docker_expose_api
- name: Deploy Docker systemd override for API exposure
ansible.builtin.template:
src: docker-override.conf.j2
dest: /etc/systemd/system/docker.service.d/override.conf
mode: "0644"
notify: restart docker
when: docker_expose_api
- name: Remove Docker systemd override when API exposure is disabled
ansible.builtin.file:
path: /etc/systemd/system/docker.service.d/override.conf
state: absent
notify: restart docker
when: not docker_expose_api
- name: Deploy Docker daemon.json configuration file
ansible.builtin.template:
src: daemon.json.j2
dest: /etc/docker/daemon.json
mode: "0644"
notify: restart docker

12
tasks/healthcheck.yml Normal file
View File

@@ -0,0 +1,12 @@
---
- name: Wait for Docker daemon to be ready
ansible.builtin.wait_for:
path: /var/run/docker.sock
state: present
timeout: 30
- name: Verify Docker is running and healthy
ansible.builtin.command: docker info
register: docker_health
changed_when: false
failed_when: docker_health.rc != 0

38
tasks/installation.yml Normal file
View File

@@ -0,0 +1,38 @@
---
- name: Install dependencies to use docker's repository
ansible.builtin.apt:
name:
- ca-certificates
- curl
- gnupg
state: present
update_cache: true
- name: Create /etc/apt/keyrings directory
ansible.builtin.file:
path: /etc/apt/keyrings
state: directory
mode: "0755"
- name: Import docker GPG key
ansible.builtin.get_url:
url: https://download.docker.com/linux/{{ ansible_facts['distribution'] | lower }}/gpg
dest: /etc/apt/keyrings/docker.asc
mode: "0644"
- name: Setup docker repository
ansible.builtin.apt_repository:
repo: "deb [arch={{ docker_arch }} signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/{{ ansible_facts['distribution'] | lower }} {{ ansible_facts['distribution_release'] }} stable"
state: present
filename: docker
- name: Install docker packages
ansible.builtin.apt:
name:
- docker-ce
- docker-ce-cli
- containerd.io
- docker-buildx-plugin
- docker-compose-plugin
state: present
update_cache: true

17
tasks/main.yml Normal file
View File

@@ -0,0 +1,17 @@
---
- name: Running installation
ansible.builtin.include_tasks: installation.yml
- name: Configure Docker
ansible.builtin.include_tasks: configuration.yml
- name: Verify Docker installation
ansible.builtin.include_tasks: healthcheck.yml
- name: Create Docker networks
ansible.builtin.include_tasks: networks.yml
when: docker_networks is defined
- name: Configure Docker registry authentication
ansible.builtin.include_tasks: registry.yml
when: docker_registry_url is defined

51
tasks/networks.yml Normal file
View File

@@ -0,0 +1,51 @@
---
- name: Get all Docker networks
ansible.builtin.command: docker network ls --format "{{ '{{' }}.Name{{ '}}' }}"
register: all_networks
changed_when: false
when: docker_networks is defined
- name: Get list of managed network names
ansible.builtin.set_fact:
managed_network_names: "{{ docker_networks | map(attribute='name') | list }}"
when: docker_networks is defined
- name: Check networks to remove
ansible.builtin.set_fact:
networks_to_remove: "{{ all_networks.stdout_lines | difference(managed_network_names | default([])) | difference(['bridge', 'host', 'none']) | list }}"
when: docker_networks is defined
- name: Remove Docker networks no longer in configuration
ansible.builtin.command: docker network rm {{ item }}
loop: "{{ networks_to_remove | default([]) }}"
loop_control:
label: "{{ item }}"
when:
- docker_networks is defined
- networks_to_remove | default([]) | length > 0
ignore_errors: true
failed_when: false
- name: Check if Docker network exists
ansible.builtin.command: docker network inspect {{ item.name }}
register: network_check
changed_when: false
failed_when: false
loop: "{{ docker_networks }}"
when: docker_networks | length > 0
- name: Create Docker networks
ansible.builtin.command: >
docker network create
--driver {{ item.driver | default('bridge') }}
{% if item.subnet is defined %}--subnet {{ item.subnet }}{% endif %}
{% if item.gateway is defined %}--gateway {{ item.gateway }}{% endif %}
{% if item.ip_range is defined %}--ip-range {{ item.ip_range }}{% endif %}
{{ item.name }}
loop: "{{ docker_networks }}"
loop_control:
label: "{{ item.name }}"
when:
- docker_networks | length > 0
- network_check.results | selectattr('item.name', 'equalto', item.name) | selectattr('rc', 'equalto', 1) | list | length > 0
ignore_errors: true

36
tasks/registry.yml Normal file
View File

@@ -0,0 +1,36 @@
---
- name: Validate registry credentials
ansible.builtin.assert:
that:
- docker_registry_username is defined or docker_registry_email is defined
fail_msg: "When docker_registry_url is set, either docker_registry_username or docker_registry_email must be provided"
when: docker_registry_url is defined
- name: Login to Docker registry as users
ansible.builtin.command:
cmd: >
docker login
--password-stdin
{% if docker_registry_username is defined %}-u {{ docker_registry_username }}{% elif docker_registry_email is defined %}-u {{ docker_registry_email }}{% endif %}
{{ docker_registry_url }}
stdin: "{{ lookup('env', 'DOCKER_REGISTRY_PASSWORD') | default('', true) }}"
become_user: "{{ item }}"
loop: "{{ docker_users }}"
when:
- docker_registry_url is defined
- docker_users is defined
- docker_users | length > 0
no_log: true
- name: Login to Docker registry as root
ansible.builtin.command:
cmd: >
docker login
--password-stdin
{% if docker_registry_username is defined %}-u {{ docker_registry_username }}{% elif docker_registry_email is defined %}-u {{ docker_registry_email }}{% endif %}
{{ docker_registry_url }}
stdin: "{{ lookup('env', 'DOCKER_REGISTRY_PASSWORD') | default('', true) }}"
when:
- docker_registry_url is defined
- docker_users is not defined or docker_users | length == 0
no_log: true

11
templates/daemon.json.j2 Normal file
View File

@@ -0,0 +1,11 @@
{
{% if docker_data_dir is defined and docker_data_dir | length > 0 %}
"data-root": "{{ docker_data_dir }}",
{% endif %}
"log-driver": "json-file",
"log-opts": {
"max-size": "10m",
"max-file": "3"
}
}

View File

@@ -0,0 +1,4 @@
[Service]
ExecStart=
ExecStart=/usr/bin/dockerd -H fd:// -H tcp://{{ docker_api_host }}:{{ docker_api_port }} --containerd=/run/containerd/containerd.sock