← All Articles

Automated server deployments with Ansible

Ansible is an essential tool for server deployments, enabling quick repeatable deployments across multiple servers. It is similar to other tools such as:

Key Concepts for using Ansible

Once you have installed Ansible, here's a rundown of the key concepts you'll use:

  1. Playbooks are configuration files which specify which roles (specific deployment tasks) are run on which hosts (specific server instances listed in inventory files), and using which vars (variables which can be customised for each group of hosts or individual host).

  2. Roles are collections of specific deployment tasks, such as installing Nginx, creating log directories, and anything else you require on a server. These typically also include default vars to be used, any template files that will be customised for each host, and any meta information which may require installing other roles as dependencies.

  3. Hosts are specific server instances, often grouped into separate inventory files. For example, your "production" inventory file may list a web server host, database host, etc. I have used static inventory files in this article for simplicity, you may also consider Dynamic Inventory Configuration.

  4. Vars are variables which may be customised for either groups of hosts (ie Group Vars) or a specific host (ie Host Vars). An example may be deploying a "staging" branch from your git repo to all hosts in the "staging" inventory file. These may be encrypted using Ansible Vault to protect any sensitive information from being included in your code repo.

Ansible Folder Organisation

I generally keep my ansible files in a similar structure advocated in the official Best Practices Guidelines, namely:

        
ansible /
  playbooks /
  start_servers.yml             # Playbook to start servers

  group_vars /
    production                  # Group vars for the production environment
    staging                     # Group vars for the staging environment

  inventory /
    production                  # List of hosts for production
    staging                     # List of hosts for staging

  roles /
    webserver /                 # The webserver role, included in start_servers.yml playbook
      defaults /
        main.yml                # Default vars which may be overridden by group_vars
      files /
        id_rsa                  # A static file copied to the server. These are not customized with vars.
      meta /
        main.yml                # List any dependencies for this role
      templates /
        nginx.conf.j2           # A dynamic file customized with vars for each hosts. Always ends in .j2 extension.
      tasks /
        main.yml                # Main task for this role, which typically includes other subtasks listed here.
        01_create_dirs.yml      # Subtask to create directories
        02_install_nginx.yml    # Subtask to install nginx
        03_logging.yml          # Subtask to configure logging
        04_start_services.yml   # Subtask to start all services

        
    

Example Ansible Files

Here are some simplistic sample files from the tree structure listed previously.

Playbook

        
# ansible/playbooks/start_servers.yml
---
- name: MySite | Webserver Playbook
  hosts: webservers
  sudo: true

  roles:
    - webserver
    - caching       # Another role, not shown in this article
    - backup        # Another role, not shown in this article

        
    

Group Vars

        
# ansible/group_vars/production
---
git:
  branch: master

  domain: my-site.com
  use_pagerduty: True

        
    

Inventory

        
# ansible/inventory/production

[webservers]
webserver01.mydomain.aws ansible_ssh_host=10.11.12.123
webserver02.mydomain.aws ansible_ssh_host=10.11.12.124

[database]
rds.master.mydomain.aws ansible_ssh_host=10.11.12.125
rds.slave.mydomain.aws ansible_ssh_host=10.11.12.126

        
    

Roles

        
# ansible/roles/webserver/defaults/main.yml
---
git:
  branch: development                           # This gets overridden in group_vars
  repo: ssh://git@github.com/my-git/my-repo.git

domain: my-site.com
use_pagerduty: False                            # This gets overridden in group_vars

nginx:
  user: myuser
  worker_processes: 4

        
    
        
# ansible/roles/webserver/meta/main.yml
---
dependencies:
  - { role: logging }   # Requires another role, which is not shown in this article

        
    
        
# ansible/roles/webserver/templates/nginx.conf.j2

user  {{ nginx.user }};

worker_processes  {{ nginx.worker_processes }};

pid        /var/run/nginx.pid;

### Rest of nginx config file not shown ... #

        
    
        
# ansible/roles/webserver/tasks/main.yml
---
- name: Clone/pull from github
  git: repo={{ git.url }} dest={{ path }} version="{{ git.branch }}" key_file="/home/{{ nginx.user }}/.ssh/id_rsa" accept_hostkey=yes force=yes
  sudo_user: "{{ nginx.user }}"
  tags:
    - update

### Rest of task not shown ... #

        
    

Executing an Ansible Playbook

With my configuration setup as described previously, I can now deploy a cluster of webservers in seconds by running:

        
ansible-playbook start_servers.yml -i inventory/production --ask-vault-pass
        
    

Note: The --ask-vault-pass option will prompt the user to enter the Ansible Vault password used for any encrypted vars files.

Made with JoyBird