📜 ⬆️ ⬇️

15 Things You Should Know About Ansible

I propose to readers of Habrakhabr a translation of the article “15 Things You Should Know About Ansible” published by codeheaven.io by Marlon Bernardes.

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

1 - You can pass parameters for roles


A good practice is to create roles for the organization of your playbooks. Let's say we want to create a role for installing 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 can be a main.yml file:
')
 jenkins_port: 8080 jenkins_context_path: /jenkins jenkins_home: /jenkins 

You can override variables by default by passing different 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 multiple times without changing the result of the initial application. This concept is present in most Ansible modules: you specify the desired end 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 a playbook, it will always be completed:

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

To achieve idempotency, you can use the creates attribute. When it is present, Ansible will execute the command task 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 these modules are idempotent, so you should always prefer them.

3 - Using the Ansible setup module to gather information about your hosts.


You probably saw that the first thing Ansible does when playing 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 a host and collects facts of all kinds: IP address, disk space, processor architecture, available memory, and more. It may be useful to manually call this module as a quick way to collect information about your hosts. To do this - just 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? Perform 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 keep confidential 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 clear text.

Ansible comes with the command-line utility, ansible-vault , which allows you to create and manage encrypted files. This way, you can commit the encrypted file to your version control system and only users with the decryption password can 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 “zafelitsya”, because it will not know how to read the encrypted file. You need to specify the command line argument --ask-vault-pass , which will force 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 a file (it should not be “committed”) and specify the path to the file using the argument --vault-password-file . 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 {{item}} variable 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 perform a task on your local host, instead of running it on a remote host. This can be useful if we want to wait for the server to boot (if it is just running), 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 the 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 perform a task only once.


Sometimes you may need to perform a task only once, even if there are 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 perform this task only once.

To achieve this, you can use the run_once parameter, which tells Ansible to execute a 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 be executed only if notified by 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, despite how many times they have been notified. You can declare them using handler and call with 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 - Acceleration using pipelining


A few tips on how to make Ansible run even faster:


Turning on pipelining (pipelining) reduces the number of SSH operations required to run a module on a remote server, passing scripts over a pipe (pipe) to an SSH session instead of copying them. As a result, this can lead to significant performance improvements.

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

 [ssh_connection] pipelining = True 


If you do not use Ansible facts in your tasks, you can disable the fact collection 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, the Ansible documentation covers this in detail.

11 - Ansible has several notification modules.


Do you use Ansible to automate your blue-green deployment? Are you launching playbooks to “provision” new AWS instances? Let your team know about it 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: <your>/<token>/<goes here> msg: "Hello team! I just finished updating our production environment." channel: "#general" username: "ansible-bot" 

Also available are modules for notification in irc, twillio, hipchat, jabber and many others .

12 - EC2 instances are automatically grouped by their tags.


When using Amazon Web Services and the Ansible EC2 dynamic inventory script, 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 please. Some use tags to group production / staging servers, to denote 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] 

So, if you want to perform a task on all nodes with the env = staging tag, just add this to your playbook:
  hosts: tag_env_staging tasks: - name: This task will be run on all servers with env == staging # ... 

To make it even more interesting, you can use Ansible templates , specifying which hosts should be affected. For example, if you want to perform a specific task on your production db servers (provided they are properly labeled), you can use a cross pattern (: &), like this:

  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 - You can perform tasks in the "Dry Run" mode


Ansible supports launching a playbook in dry run mode (also called Check Mode).
In this mode, Ansible will not make any changes on your host, but will simply report what changes will be made if the playbook was launched without this flag.

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

14 - Tasks can be performed step by step


Sometimes, you do not want to carry out all the tasks of your playbook.
This is a common solution when you are writing a new playbook and want to check it out.
Ansible provides a method that allows you to decide which tasks you want to run using the --step flag.
This will allow you to choose whether you want to complete the 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, simply note that you want to "tag" using the tags attribute:

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

 #      '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

Source: https://habr.com/ru/post/306998/


All Articles