How to forward your SSH key to hosts in Ansible

I prefer to work with SSH keys in order to connect to all my hosts. It makes the daily work easier. Now imagine that you need to use your SSH key in a host. For example I needed that in order to run a checkout of a SVN repo. You can’t specify SSH key with svn module.

Just edit your /etc/ansible/ansible.cfg file and search for [ssh_connection]. There you can edit all the options you want for your SSH connection. A really useful option. So just add the option ForwardAgent=yes to ssh_args.

[ssh_connection]
ssh_args = -o ForwardAgent=yes

Now in every SSH connection Ansible will forward your key.

Create a log entry for every Ansible execution

Besides of sending an email when you’re performing a task with Ansible, I also recommend the usage of log files.

- file: path=/var/log/ansible state=touch
- name: "Write into log"
   shell: echo "ANSIBLE | {{ ansible_date_time.iso8601 }} | <USER> | <ACTION> | <WHERE>" >> /var/log/ansible

Then you can easily review that log file with older executions on that server and even send it to a log manager.

How to know the amount of data that has been transferred using copy module

Ansible doesn’t have a progress bar and sometimes when you use copy module in a poor network or transferring a big file, we could be running the playbook and got stuck here:

TASK [Copy war into directory /deploy] ***************

And we don’t know what’s going on, if it’s working, if it’s going fast, slow,…

How can we know the status of our copy?

You should connect into the server where you’re copying your file and go into the temporary directory that has been created in the user home that is being used by Ansible to connect. So, if your playbook looks like:

- hosts: appserver
  user: rcastells
  become: yes

You should connect into appserver and go into temporary directory created by ansible:

/home/rcastells/.ansible/tmp/ansible-tmp-1462436406.52-187369121476281

Take in consideration that inside tmp you will find a lot of different temporary directories created by Ansible for every playbook execution. You’re only interested in the latest one.

Inside this temporary directory you can find a file called ‘source’ that will be increasing its size. This temporary file is the one that is using copy module.

56M -rw-rw-r-- 1 rcastells rcastells 56M May 5 16:34 source

Is not a really nice solution, but at least you can know what’s going on and how fast is your copy going.

Usage of serial mode for certain playbooks (like an application deployment)

Ansible runs in parallel mode, which means that every task that you define in your playbook will run by default in all hosts before to proceed to next task.

How to avoid that? With the usage of serial mode.

In a real scenario you can have two Application servers running your app and you will need to update it. Probably you will have a playbook that does all the job for you but if your playbook is as follows:

- hosts: appservers
  become: yes
  tasks:
    - name: "Deploy your new app"
      shell: <whatever to deploy>

    - name: "Restart of your service"
      shell: <whatever to restart>

And your inventory looks like:

[appservers]
app01
app02

You will restart your service in both servers at the same time. You could use wait_for module for example waiting a port to come up, but as a good practice it is good to add serial mode. So, if you define your playbook as follows:

- hosts: appservers
  serial: 1
  become: yes
  tasks:
    - name: "Deploy your new app"
      shell: <whatever to deploy>

    - name: "Restart of your service"
      shell: <whatever to restart>

Your tasks will be performed one by one. So, if you do things properly, you can avoid downtime for your application.

In our example we were talking on a simple example, but you can have more than two servers:

[appservers]
app[01:06]

And then specify the serial number that you want:

  • serial: 2: Tasks will be performed to 2 hosts at the same time.
  • serial: 3: Tasks will be performed to 3 hosts at the same time.

In another entry we will talk about the combination of max_fail_percentage and serial in order to manage our deployments in case that we want to do a rollback.

Usage of inventory file as conditional

I’ve used the name of the inventory file as conditional for some playbooks. This is a weird example where you use the same server to build the war for two different environments and using two different scripts. So imagine that you want to use the same playbook in order to do the deploy of your application.

Two (not recommended) solutions:

  1. Write two different playbooks: So if you do one change in one playbook you must remember to do the change in the other playbook. Try to avoid that.
  2. Use vars prompt: Of course you can prompt the user where the task will be performed, but you already have this information in your inventory file. So no need to ask.

Imagine that you have this task to be performed in server A:

- name: "War for Europe"
  shell: /opt/scripts/buildWarEurope.sh

And for USA another different script also in server A:

- name: "War for USA"
  shell: /opt/scripts/buildWarUSA.sh

Those scripts are on the same server, so you can’t rename them and use the same name for your Ansible task.

What I recommend you to do in that scenario is to use inventory file name in your conditional. You should have a directory called inventory with your environment files:

inventory/europe_prod
inventory/usa_prod

So, if you run your playbooks using inventory files (which you MUST do) as follows:

ansibe-playbook -i inventory/europe_prod deploy.yml

You can take this information from your gather_facts and then use it later as follows:

- name: "War for Europe"
  shell: /opt/scripts/buildWarEurope.sh
  when: ( 'europe' in inventory_file )
- name: "War for USA"
  shell: /opt/scripts/buildWarUSA.sh
  when: ( 'usa' in inventory_file )

And that’s it. One playbook and no usage of vars prompt.