I recently made some tutorials about Ansible, which is an awesome tool to automate infrastructure. And also Docker, which is a containerizing engine to make deployments easier and more secure. In this tutorial, we will bring those two amazing tools together. Because we’re using Ansible and Docker to automate web server deployments.
I always like easy management for all my Docker Servers and automatic updates. Therefore, we’re deploying Portainer, which is a nice tool to manage Docker containers. And also Watchtower to update them all automatically.
I’m using a Linux Server, based on Ubuntu 20.04 LTS in this tutorial. But you can also use any other Distribution if you like.
Pre-Requisites for Ansible and Docker
Before we can start, it’s important to run the latest version of Ansible. Because you won’t always get the newest version on all Linux Distributions. On Ubuntu 20.04 LTS for example, you only get version 2.9.6 of Ansible. But because the Docker extension requires at least Ansible in version 2.10. We need to update it!
Update Ansible to the latest version
If you have installed Ansible in previous versions and want to update version 2.10, you need to remove the old version first! Because Ansible changed the update procedure fundamentally from version 2.9.x to 2.10. I needed to remove the old packages with apt.
sudo apt remove ansible
When you removed all previously installed versions of ansible, you need to install it with the python package manager (pip), to get the latest version! Also, make sure you’re not running this command with sudo.
python3 -m pip install ansible
Also make sure you add this line to your .bashrc or .zshrc file!
Install the Docker Galaxy Extension for Ansible
Now, we can install the Ansible Galaxy Extension to manage Docker containers on remote servers. Simply execute the following command.
ansible-galaxy collection install community.docker
Configure our Ansible Environment
Let’s start configuring our Ansible Environment. Because we need to set up an ansible.cfg and inventory file in our project folder to tell Ansible how to connect to our remote server. The inventory file is a simple text file that just contains the IP address of our server.
This is a template for the ansible configuration file.
[defaults] inventory = inventory host_key_checking = False # optional: removes the SSH prompt deprecation_warnings=False # optional: removes deprecation warning in playbooks remote_user = <remote-user> private_key_file = <remote-user-private-key-file>
If everything is configured correctly, you can test the connection with the following command.
ansible all -m ping
Install Docker on our remote Server
If we have configured our Ansible Environment, we can install all necessary components on our remote server. Because I installed a fresh new Ubuntu 20.04 LTS server, we need to install Docker first. And also the Docker Python SDK is required by Ansible to run containers on remote servers. Therefore, you have two options to install everything on the remote server.
Option 1: Manual installation of all components
When you want to install the components manually, just have a look at the Docker Installation Documentation. You also need to install Python, the Python Package Manager, and the Docker SDK.
Option 2: Install Docker with an Ansible Playbook
Since we already have configured Ansible to manage our remote server, why shouldn’t we use it? Because I’ve already prepared an Ansible Playbook, you can just download and run it. You will find this Playbook in my GitHub Repository Ansible-Boilerplates, and it will install Docker and the Python Docker SDK for you.
Run Portainer with Ansible
Now we’re ready to deploy our first Docker container with Ansible! Create a new Ansible Playbook YAML file in your project folder, that should look like this.
--- - hosts: all become: yes tasks: - name: Deploy Portainer community.docker.docker_container: name: portainer image: portainer/portainer-ce ports: - "9000:9000" - "8000:8000" volumes: - /var/run/docker.sock:/var/run/docker.sock - portainer_data:/data restart_policy: always
To run the Ansible Playbook, simply execute the following command in the shell.
If everything runs successful, you should see something like this.
And if we try to access our remote server on port 9000, we can see that Portainer is now running succesful.
Install Watchtower with Ansible
To run our second Docker container, we simply can just add another task inside the same Ansible Playbook. Because Ansible will take care of which Containers are already deployed and if there are any changes to be made. And it only re-deploys containers if there are changes being made.
- name: Deploy Watchtower community.docker.docker_container: name: watchtower image: containrrr/watchtower command: --schedule "0 0 4 * * *" --debug volumes: - /var/run/docker.sock:/var/run/docker.sock restart_policy: always
Simply run the Playbook with the same command above again. You can see that the task “Portainer” is not executed again, only our new task “Watchtower”.
Deploy a WordPress Blog with Ansible and Docker
Let’s also deploy two more containers, to automate the deployment of my webserver. Because I want to run a WordPress Blog on this server, we execute the following Playbook.
We also need to create a new Network before running the Containers. Otherwise, WordPress will not be able to connect to the Database Container. Therefore we also need to attach them to the same Network.
--- - hosts: all become: yes tasks: - name: Create Network community.docker.docker_network: name: wordpress - name: Deploy Wordpress community.docker.docker_container: name: wordpress image: wordpress:latest ports: - "80:80" networks: - name: wordpress volumes: - wordpress:/var/www/html env: WORDPRESS_DB_HOST: "db" WORDPRESS_DB_USER: "exampleuser" WORDPRESS_DB_PASSWORD: "examplepass" WORDPRESS_DB_NAME: "exampledb" restart_policy: always - name: Deploy MYSQL community.docker.docker_container: name: db image: mysql:5.7 networks: - name: wordpress volumes: - db:/var/lib/mysql env: MYSQL_DATABASE: "exampledb" MYSQL_USER: "exampleuser" MYSQL_PASSWORD: "examplepass" MYSQL_RANDOM_ROOT_PASSWORD: '1' restart_policy: always
If everything was successful, you can see that our WordPress Blog is running!