I’m not sure about you, but every time I spin up a new Debian Virtual Machine with Proxmox I find myself getting annoyed with the tedious process of getting Docker and Docker Compose installed.
So in this guide, I will be walking you through my Ansible playbook for installing this amazing software! Automating this task helps reduce the risk of errors and makes it efficient for managing infrastructure.
Prerequisites
This post assumes you have these things set up already:
A new Debian server with SSH access and/or A
hosts
fileAnsible on your local machine
A playbook for updating apt
If you don’t have a playbook for updating Apt yet then you can follow this guide:
Automating APT updates with Ansible
A major part of DevOps and programming in general is automating basic tasks. A common task to automate is package updates. If you have a ton of servers (perhaps you’re using Proxmox?) that need to be updated regularly then you can use a tool like Ansible!
install-docker.yml
Playbook
The first playbook installs Docker alone. Let's take a look at it.
- hosts: "debian" | |
name: Installing Docker | |
tasks: | |
- name: Check if Docker is installed | |
become: yes | |
command: docker -v | |
register: docker_installed | |
ignore_errors: true | |
- name: Install Docker | |
become: yes | |
block: | |
- name: Add Docker's official GPG key | |
block: | |
- name: Update apt | |
include_tasks: apt.yml | |
- name: Install necessary packages | |
become: yes | |
ansible.builtin.apt: | |
name: | |
- ca-certificates | |
- curl | |
- gnupg | |
state: latest | |
update_cache: yes | |
- name: Add GPG key | |
ansible.builtin.shell: | |
cmd: | | |
sudo install -m 0755 -d /etc/apt/keyrings | |
curl -fsSL https://download.docker.com/linux/debian/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg | |
sudo chmod a+r /etc/apt/keyrings/docker.gpg | |
- name: Add the repository to Apt sources | |
block: | |
- name: Add repos | |
ansible.builtin.shell: | |
cmd: | | |
echo \ | |
"deb [arch="$(dpkg --print-architecture)" signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/debian \ | |
"$(. /etc/os-release && echo "$VERSION_CODENAME")" stable" | \ | |
sudo tee /etc/apt/sources.list.d/docker.list > /dev/null | |
- name: Update apt | |
include_tasks: apt.yml | |
- name: Install Docker packages | |
become: yes | |
ansible.builtin.apt: | |
name: | |
- docker-ce | |
- docker-ce-cli | |
- containerd.io | |
- docker-buildx-plugin | |
- docker-compose-plugin | |
state: latest | |
update_cache: yes | |
when: docker_installed.failed | |
- name: Ensure Docker group exists | |
become: yes | |
ansible.builtin.group: | |
name: docker | |
state: present | |
register: docker_group | |
- name: Add User to docker group | |
become: yes | |
ansible.builtin.user: | |
name: yourUser | |
groups: docker | |
append: true | |
when: docker_group is changed | |
I’m sure there are a few Ansible Gurus out there who can figure out ways to optimize this. If there are any, please let me know! I would love to improve.
This playbook assumes there is a hosts section in the host’s file called “debian”. Change this to whatever you need.
- name: Check if Docker is installed
become: yes
command: docker -v
register: docker_installed
ignore_errors: true
The first task, “Check if Docker is installed”, will (as the name suggests) check if Docker is first installed. I put that there so there wouldn’t be any issues with machines that already have docker installed. It ignores errors so that way the output won’t say “failed” when the playbook is run.
If the command docker -v
fails then the next task will run. You can see the conditional on line 56 ( when: docker_installed.failed
).
I split the second task into multiple, following the official documentation for installing Docker. The child task “Add Docker’s Official GPG key” is also split into a block. It will run the apt.yml
playbook from the blog post above and then install some necessary packages.
The “Add GPG key” task could probably be improved, but it simply follows the documentation by running those commands after installing the current packages.
The same is true for “Add the repository to Apt sources”. I just copied the documentation commands and ran them using the Ansible built-in shell and then updated apt again.
- name: Install Docker packages
become: yes
ansible.builtin.apt:
name:
- docker-ce
- docker-ce-cli
- containerd.io
- docker-buildx-plugin
- docker-compose-plugin
state: latest
update_cache: yes
Finally “Install Docker packages” will become sudo
and install the listed packages using apt.
The last two tasks are only there if you wish to add the user to the Docker group, so you won’t need root privileges every time you want to run a container. I enjoy the convenience so that’s what I do.
- name: Ensure Docker group exists
become: yes
ansible.builtin.group:
name: docker
state: present
register: docker_group
- name: Add User to docker group
become: yes
ansible.builtin.user:
name: yourUser
groups: docker
append: true
when: docker_group is changed
It will first check if the docker group even exists. This is just a failsafe in case the installation didn’t work properly. If it does exist then Ansible will become root again and append yourUser
to the docker group. Make sure to change yourUser
. If we don’t append the user then it will remove all other groups the user is in and add them only to Docker.
install-docker-compose.yml Playbook
If this is going on a server then you probably want Docker Compose as well. I can probably keep both playbooks inside one, but I find it easier if they are split.
- import_playbook: install-docker.yml | |
name: Installing Docker | |
- hosts: "debian" | |
name: Installing docker compose | |
tasks: | |
- name: Check if Docker Compose is installed | |
become: yes | |
command: docker-compose -v | |
register: docker_compose_installed | |
ignore_errors: true | |
- name: Install Docker Compose | |
become: yes | |
block: | |
- name: Remove old docker compose package | |
ansible.builtin.apt: | |
name: docker-compose | |
state: absent | |
ignore_errors: true | |
- name: Get latest Docker Compose version | |
uri: | |
url: https://api.github.com/repos/docker/compose/releases/latest | |
return_content: yes | |
register: docker_compose_version | |
- name: Extract Docker Compose version number | |
set_fact: | |
docker_compose_version_number: "{{ docker_compose_version.json.tag_name }}" | |
- name: Get Docker Compose based on version number | |
become: yes | |
ansible.builtin.get_url: | |
url: https://github.com/docker/compose/releases/download/{{ docker_compose_version_number }}/docker-compose-Linux-x86_64 | |
dest: /usr/bin/docker-compose | |
mode: '0755' | |
when: docker_compose_installed.failed | |
Before anything, we make sure Docker is first installed by running the playbook we just made. Make sure they are in the same directory otherwise, this would fail.
The first task, like before, checks if Docker Compose is even installed, to begin with by running the docker-compose -v
command and checking if the command passes. If it fails then the next block will run.
It will remove an old Docker Compose package as a precaution. Next we “Get the latest Docker Compose version” by calling the GitHub API and registering it as a variable. With the JSON output of the API saved, we can then query it in the next task and extract the version number. If we do it this way then there is no need to hardcode the version number and update the playbook every time a new Docker Compose version is released.
The final task “Get Docker compose based on version number” will get a URL, add it to the desired destination which is the binary folder, and then set privileges for it.
Now it should all be set! To run the playbook use this command:
ansible-playbook path/to/install-docker-comopse.yml -i path/to/hosts