15 Things You Should Know About Ansible

I offer readers of Habrahabr a translation of the article “15 Things You Should Know About Ansible” published on codeheaven.io by Marlon Bernardes.

Recently, I worked a lot with Ansible and decided to share some of the things that I learned along the way. Below you will find a list of 15 things that I think you should know about Ansible. Did you miss something? Just leave a comment and share your personal tips.

1 - You can pass parameters for roles


It’s good practice to create roles to organize your playbooks. Let's say we want to create a role to install Jenkins. The directory structure for this role will look something like this:

jenkins/
   files/
   templates/
   tasks/
   handlers/
   defaults/

The defaults directory is used to store default variables for a role. Inside it may be the main.yml file :

jenkins_port: 8080
jenkins_context_path: /jenkins
jenkins_home: /jenkins

You can override the default variables by passing various parameters for the role, like this:

roles:
   - { role: jenkins, jenkins_port: 8181, jenkins_home: '/jenkins1' }
   - { role: jenkins, jenkins_port: 8080, jenkins_home: '/jenkins2' }

2 - How to make the command module idempotent


Idempotency is a property of certain operations that can be performed repeatedly without changing the result of the initial application. This concept is present in most Ansible modules: you specify the desired final state and Ansible decides whether the task should be completed. By default, this principle does not apply to the command module . If you have a task in the playbook, it will always be completed:

- command: /usr/bin/create-database.sh

To achieve idempotency, you can use the creates attribute . When present, Ansible will execute the command only if the template specified in the file does not exist. Alternatively, you can use removes , which will perform the task only if the specified file exists.

- command: /usr/bin/create-database.sh creates=/path/to/database

Always keep in mind that Ansible has many modules and the most common operations do not require the use of the command module . For example, there are modules for creating file systems , changing iptables rules, and managing cron jobs . By default, all of these modules are idempotent, so you should always give preference to them.

3 - Using Ansible setup module to collect information about your hosts


You've probably seen that the first thing Ansible does when it does a playbook is something like this:

TASK [setup] *******************************************************************
ok: [servername]

This is because Ansible calls the special setup module before performing the first task. The setup module connects to the host and collects all kinds of facts: IP address, disk space, processor architecture, available memory, and much more. Manually calling this module as a quick way to collect information about your hosts can be useful. To do this, simply run the command:

$ ansible localhost -m setup
localhost | SUCCESS => {
  "ansible_facts": {
    "ansible_all_ipv4_addresses": [
        "10.27.12.77",
        "192.168.33.1"
    ],
    (множество других фактов)
  }

4 - You can display all the tasks of the playbook


Want to remember what a playbook does? Run ansible-playbook using the --list-tasks flag and Ansible will display all available tasks:

$ ansible-playbook install-jenkins.yml --list-tasks
PLAY: #1
  tasks:
    TASK: meta
    TASK: open-jdk : Install open jdk 1.8
    TASK: mount-partition : Creating the filesystem for the device {{ device }}  (if needed)
    TASK: mount-partition : Mounting the device {{ device }} on path {{ path }}
    TASK: jenkins : Ensure Jenkins repo is installed.
    TASK: jenkins : Add Jenkins repo GPG key.
    TASK: jenkins : Ensure Jenkins is present.
    TASK: jenkins : Ensures that the home directory exists
    TASK: jenkins : include
    TASK: jenkins : Ensure Jenkins is started and runs on startup.
    TASK: jenkins : Wait for Jenkins to start up before proceeding.
    TASK: jenkins : Get the jenkins-cli jarfile from the Jenkins server.

5 - Use ansible-vault if you want to store sensitive information


If one of your tasks requires confidential information (say, access to a database), it is good practice to store this information in encrypted form, instead of storing it in plain text.

Ansible comes with the ansible-vault command-line utility , which allows you to create and manage encrypted files. Thus, you can “commit” the encrypted file to your version control system and only users with the decryption password will be able to read it.

# Зашифровать существующий файл. Вам необходимо создать пароль шифрования.
ansible-vault encrypt secrets.yml
# Создать новый зашифрованный файл. Вам необходимо создать пароль шифрования.
ansible-vault create secrets.yml
# Расшифровать файл. Вам необходимо ввести пароль, используемый для шифрования.
# Используйте с осторожностью! Не оставляйте ваши файлы не зашифрованными.
ansible-vault decrypt secrets.yml
# Редактировать зашифрованный файл
# (по-умолчанию используется vim, переопределяется переменной окружения $EDITOR)
ansible-vault edit secrets.yml
# Отобразить содержимое зашифрованного файла
ansible-vault edit secrets.yml

If you import the secrets.yml file into your playbook, Ansible will “snap” because it will not know how to read the encrypted file. You need to specify the command line argument --ask-vault-pass , which will prompt Ansible to offer a password for the encrypted file.

ansible-playbook playbook.yml -i hosts --ask-vault-password

Another way is to store the password in the file (it should not be “locked”) and specify the path to the file using the --vault-password-file argument . If this file is executable, Ansible will run it and use the output as a password.

Learn more about ansible-vault here .

6 - Using with_items might be a good idea


When using with_items , Ansible will create a variable {{item}} containing the value of the current iteration. Some modules handle collections very well, and much faster than running the same task several times with different parameters.

# Установка всех пакетов за раз (быстрее)
  - name: install required packages using the apt module
    apt: package={{ item }}  update_cache=yes
    sudo: True
    with_items:
      - git
      - memcached
      - nginx
  # Установка пакетов по отдельности (медленнее)
  - name: install git
    apt: package=git update_cache=yes
    sudo: True
  - name: install memcached
    apt: package=memcached update_cache=yes
    sudo: True
  - name: install nginx
    apt: package=nginx update_cache=yes
    sudo: True

7 - How Local Actions work


Sometimes you may need to run a task on your local host, instead of running it on a remote host. This can be useful if we want to wait until the server boots up (if it has just started), or when we want to add some nodes to the balancer pool (or delete them):

tasks:
 - name: take out of load balancer pool
   local_action: >
      command /usr/bin/take_out_of_pool {{ inventory_hostname }}
 - name: update application
   yum: name=acme-web-stack state=latest
 - name: add back to load balancer pool
   local_action: >
      command /usr/bin/take_out_of_pool {{ inventory_hostname }}

The example below shows how to start an EC2 instance and wait for it to become available:

- name: Launching EC2 Instance
    # instance options here
  register: ec2
- name: Waiting for ec2 instances to listen on port 22
  wait_for:
    state=started
    host={{ item.public_dns_name }}
    port=22
  with_items: ec2.instances

8 - You can tell Ansible to complete the task only once


Sometimes you may need to complete a task only once, even if you have multiple hosts. As an example, let's say you have several servers with applications that connect to the same database and you have a task that migrates the database. In this case, you need to complete this task only once.

To achieve this, you can use the run_once parameter that tells Ansible to execute the command only once:

- name: run the database migrations
  command: bundle exec rake db:migrate
  run_once: true

9 - handlers are special types of tasks


Handlers are tasks with unique names that will only be executed if notified from another task. They are very convenient for restarting services or rebooting the system.

Handlers that have been notified will be executed once at the end of the playbook, regardless of how many times they have been notified. You can declare them using handler and call using notify .

Here is an example of how to restart 2 services when the contents of a file change, but only if the file changes (an example is taken from Ansible docs ).

- name: template configuration file
  template: src=template.j2 dest=/etc/foo.conf
  notify:
     - restart memcached
     - restart apache

Handlers must be declared somewhere in your playbook:

handlers:
    - name: restart memcached
      # Используется модуль service, но вы можете использовать любой другой модуль
      service: name=memcached state=restarted
    - name: restart apache
      service: name=apache state=restarted

10 - Pipelining Acceleration


Some tips on making Ansible run even faster:

  • Enable pipelining

Enabling pipelining reduces the number of SSH operations required to execute the module on the remote server by passing scripts via pipe to the SSH session instead of copying them. As a result, this can lead to a significant increase in productivity.

However, you must be careful. Piping will only work if the requiretty option is disabled on all remote hosts in the sudoers file (/ etc / sudoers).

[ssh_connection]
pipelining = True

  • Disable Fact Collection or Enable Fact Caching

If you do not use Ansible Facts in your tasks, you can turn off the fact-gathering step to increase speed. To do this, simply add the gather_facts: False option to your playbook :

- hosts: servername
  gather_facts: False
  tasks:
    - name: ...
    # ...

On the other hand, if you need to use Ansible facts (automatically collected by the setup module), you can cache them so that subsequent executions are faster. If you want to know more, Ansible documentation covers this in detail here .

11 - Ansible has several notification modules


Using Ansible to automate your blue-green deployment? Running playbooks to provide new AWS instances? Let your team know about this using one of the notification modules. As an example, the task below will send a notification to Slack:

- hosts: servername
  tasks:
    - name: Send notification message via Slack
      local_action:
        module: slack
        # To retrieve your slack token, open your team settings and look for the
        # Incoming Webhooks plugin
        token: //
        msg: "Hello team! I just finished updating our production environment."
        channel: "#general"
        username: "ansible-bot"

Notification modules are also available in irc, twillio, hipchat, jabber and many others .

12 - EC2 instances are automatically grouped by their tags


When using the Amazon Web Services and Ansible EC2 dynamic inventory scripts, all instances will be grouped based on their characteristics, such as type, keypairs, and tags. EC2 tags are just a key = value associated with your instances, which you can use as you like. Some use labels to group production / staging servers to indicate web servers or “active” servers during blue-green deployments.

The EC2 Dynamic Inventory script uses the following template (without parentheses) when grouping hosts by tag:
tag_[TAG_NAME]_[TAG_VALUE]

Так что, если вы хотите выполнить задачу на всех узлах с тегом env=staging, просто добавьте это в ваш плейбук:
  hosts: tag_env_staging
  tasks:
    - name: This task will be run on all servers with env == staging
    # ...

Чтобы сделать это еще более интересным, вы можете использовать шаблоны Ansible, конкретизировав, какие хосты должны быть затронуты. Например, если вы хотите выполнить определенную задачу на ваших production db серверах (при условии, что они обозначены должным образом), вы можете использовать перекрестный шаблон ( :& ), как здесь:

  hosts: tag_env_production&:tag_type_db
  tasks:
    - name: This task will be run on all servers with tags 'env=production' and 'type=db'
    # ...

13 — Вы можете выполнять задачи в режиме «Dry Run»


Ansible supports running a playbook in dry run mode (also called Check Mode).
In this mode, Ansible will not make any changes to your host, but will simply tell you what changes would be made if the playbook were launched without this flag.

Although useful in some scenarios, it may not work properly if conditions are used for your tasks.

14 - Tasks can be completed step by step


Sometimes, you do not want to complete all the tasks of your playbook.
This is a common solution when you write a new playbook and want to check it out.
Ansible provides a way to let you decide which tasks you want to run using the --step flag .
This will allow you to choose whether you want to complete task (y), skip it (n) or (c) continue without asking.

15 - You can perform tasks based on their tags


You can add one or more tags to a task or playbook.
To do this, just note that you want to “tag” using the tags attribute :

Later you can decide which tags to execute or skip using the
--tags tagname (or just -t ) and --skip-tags tagnames flag :

# выполнить только задачу с тегом 'dependencies'
$ ansible-playbook --tags=dependencies playbook.yml
# выполнит все задачи, кроме тех, что содержат тег 'optional'
$ ansible-playbook --skip-tags=optional playbook.yml

You can specify more than one tag, separated by commas.

Recommendations


Ansible Docs
Ansible Up & Running Book, by Lorin Hochstein

Also popular now: