Ansible is an open-source software provisioning, configuration management, and application deployment tool. It uses declarative language to describe system configuration, making it easy to use.
Ansible uses a simple syntax written in YAML called playbooks. It is agentless, meaning it doesn't require any software installed on remote nodes to perform its tasks. Instead, it uses SSH to execute commands and copy resources to remote machines. Ansible is also idempotent, ensuring that the same playbook can be run multiple times without changing the final state beyond the first run. It's widely used for IT automation tasks.
Preparing the Ansible Dev Environment
Before getting started, we need to install Ansible and its dependencies on our server. As Ansible is based on Python, the latest versions are available on the PIP package repository. Note that the package repository of our host system may not have an up-to-date package, so pip is the recommended platform for getting the latest versions.
To install Ansible, simply run,
pip install ansible
If you don't have pip in your system, then simply run the following:
Debian/Ubuntu
apt install python3-pip
CentOs/RHEL
yum install epel-release
yum install python3-pip
Note: Use sudo for package installation if the install fails with a lack of privilege/permission errors.
Windows Systems
# Download the get-pip.py script
Invoke-WebRequest -Uri https://bootstrap.pypa.io/get-pip.py -OutFile get-pip.py
# Install pip
python3 get-pip.py
Provisioning the Server
We will now install Docker and Start up a Docker Stack with Ansible.
Place the configuration files provided below in the same directory. Name them according to their codeblock captions.
All the required configuration is down in the Jinja Template for Ansible. This makes it easy to generate the playbook with the necessary variables placed into it in its final form. The passwords can be encrypted using ansible-vault
which is the recommended way to do so.
---
- hosts: all
become: yes
vars:
docker_users:
- "{{ ansible_user }}"
tasks:
- name: Install required packages
dnf:
name:
- dnf-utils
- device-mapper-persistent-data
- lvm2
- python3-pip
state: latest
update_cache: true
- name: Install Docker SDK for Python
pip:
name:
- docker
state: present
- name: Add Docker repository
get_url:
url: https://download.docker.com/linux/centos/docker-ce.repo
dest: /etc/yum.repos.d/docker-ce.repo
- name: Install Docker
dnf:
name:
- docker-ce
state: latest
- name: Install Docker Compose
get_url:
url: https://github.com/docker/compose/releases/download/v2.27.0/docker-compose-linux-x86_64
dest: /usr/local/bin/docker-compose
mode: 'u+x,g+x'
- name: Add users to docker group
user:
name: "{{ ansible_user }}"
groups: docker
append: yes
with_items: "{{ docker_users }}"
- name: Start Docker service
systemd:
name: docker
state: started
enabled: yes
- name: Init Swarm
community.docker.docker_swarm:
state: present
advertise_addr: "{{ ansible_default_ipv4.address }}"
- name: Login to Docker Registry
docker_login:
username: "{{ docker_username }}"
password: "{{ docker_password }}"
registry_url: "{{ docker_registry_url }}"
reauthorize: yes
- name: Transfer Docker Compose file to remote host
copy:
src: docker-compose.yaml
dest: /tmp/docker-compose.yaml
- name: Deploy Swarm
community.docker.docker_stack:
state: present
name: mystack
compose: /tmp/docker-compose.yaml
playbook.yaml.j2
This generate_playbook.yaml
file is used to generate the main playbook from the playbook.yaml.j2
template file above.
---
- name: Generate Playbook
hosts: all
connection: local
gather_facts: true
tasks:
- name: Include Variables
include_vars:
file: variables.yaml
- name: Include Docker Credentials
include_vars:
file: docker_credentials.txt
- name: Generate playbook
template:
src: playbook.yaml.j2
dest: playbook.yaml
run_once: true
generate_playbook.yaml
This file contains credentials to the docker registry. Although it's not required for public packages in dockerhub, it has been used here to encourage the use of a private registry for hosting docker images.
docker_username: asd
docker_password: asd
docker-credentials.txt
Run ansible-vault encrypt docker_credentials.txt
, you will be asked to enter a password which will be used to encrypt the password file. Once completed the username/passwords will be replaced with the encrypted form of the same.
This is the main docker-compose file, which will be used to create the docker stack containing the nginx web service.
version: '3.9'
services:
web:
image: nginx:22-alpine
ports:
- "80:80"
networks:
- nginx
networks:
nginx:
name: nginx
docker-compose.yaml
The hosts.ini
file consists of the server host IP address and username with which to access the server.
[all]
192.168.2.22 ansible_user=root
hosts.ini
The variables.yaml
file is used to offload all values that require frequent changes
docker_registry_url: my-private.registry.example.com
ansible_user: root
docker_users:
- "{{ ansible_user }}"
variables.yaml
The password used to encrypt the vault is saved as vault_password.txt
. Instead of providing the password on every run, we can point Ansible to this file to ensure faster execution of playbooks.
asd-asd
vault_password.txt
Once all the files are available and placed in the directory, we can run the following command to set up our server and start the docker swarm stack.
ansible-playbook --vault-password-file vault_password.txt generate_playbook.yaml -i hosts.ini
This will generate the playbook file according to the Jinja template and the provided variables. Remember that the provided passwords will also be available in the playbook file in cleartext. Don't commit this file to version control.
ansible-playbook playbook.yaml -i hosts.ini
Finally, now the server will be provisioned according to the instructions we provided in the playbook.
If you SSH into the server, you should see that docker has been installed, the nginx container has been deployed, and the necessary packages, like pip
, etc, are also available in the server.
Conclusion
In this way we learned about Ansible, provisioned a server with Ansible and deployed a Docker Swarm Stack with Ansible.
Thank you for reading, I periodically try to update my articles to ensure legibility.