Skip to content

Latest commit

 

History

History
413 lines (317 loc) · 16.8 KB

lab3_intro_to_playbooks.adoc

File metadata and controls

413 lines (317 loc) · 16.8 KB

Lab 3: Introduction to Playbooks

In lab three, we will focus on learning how to create and structure Ansible Playbooks. After completing this section, you should be able to write basic Ansible playbooks and use the ansible-playbook command. This information will be handy to interact programmatically against our OpenStack instances in later labs.

Ansible Playbooks

In Lab 2 you learned how to run ad-hoc commands with Ansible. Ad-hoc commands can run a single, simple task. The real power of Ansible is in how to use playbooks to run multiple, complex tasks against a system or a group of systems in an easily repeatable way.

There are two central concepts to understand before writing playbooks:

  • A play: is an ordered set of tasks which should be run against hosts selected from your inventory.

  • A playbook: is a text file that contains a list of one or more plays to run in order

Playbook format

To better understand the structure of a playbook, let us first review a simple ad-hoc command:

$ ansible web,db -m command -a "hostname"
fda4c4a2-f655-49dc-997f-29d555d15d49 | CHANGED | rc=0 >>
web1.novalocal

3c0bc0ea-ce96-4ca5-ab89-442434c34088 | CHANGED | rc=0 >>
web0.novalocal

8572e2c5-36d2-489a-a71f-7ff911958a7b| CHANGED | rc=0 >>
db0.novalocal

The previous command should report the hostname of the system or systems used as target.

This can be rewritten as a simple single-task play and saved in a playbook. The resulting playbook might appear as follows:

single-task-playbook.yml
---
- name: Execute raw command to get hostname from system
  hosts: web, db
  tasks:
    - name: Hostname command
      command: hostname
...

A playbook is a text file written in YAML format, and is normally saved with the extension yml. The playbook primarily uses indentation with space characters to indicate the structure of its data. YAML doesn’t place strict requirements on how many spaces are used for the indentation, but there are two basic rules:

  • Data elements at the same level in the hierarchy (such as items in the same list) must have the same indentation.

  • Items that are children of another item must be indented more than their parents. You can also add blank lines for readability.

The playbook begins with a line consisting of three dashes (---) as a start of document marker and it is a good practice to end each playbook with three dots (…​) as an end of document marker.

In between those markers, the playbook is defined as a list of plays. An item in a YAML list starts with a single dash (-) followed by a space. For example, a YAML list might appear as follows:

- nova
- glance
- neutron
- cinder
- keystone
- manila

The play itself is a collection (an associative array or hash/dictionary) of key: value pairs. Keys in the same play should have the same indentation. The following example describes a YAML hash/dictionary with three keys. The first two keys have simple values. The third has a list of three items as a value.

example.yml
---
- name: Example
  hosts: Fileservers
  tasks:
    - one
    - two
    - three
...

In the previous example, the play has three keys: name, hosts, and tasks. These keys all have the same indentation because they belong to the play. The first line of the example play starts with a dash and a space (indicating the play is the first item of a list), and then the first key, the name attribute.

Note
The order in which the plays and tasks are listed in a playbook is important, because Ansible runs them in the same order.

Running playbooks

Now that we know the structure of a playbook and how easy it is to write one, let’s discuss the ansible-playbook command. The ansible-playbook command is used to run playbooks and is executed on the control node. Using the ansible-playbook command run the single-task-playbook.yml from the previous example.

$ ansible-playbook single-task-playbook.yml

PLAY [Execute raw command to get hostname from system] ************************************************************************************

TASK [Gathering Facts] ********************************************************************************************************************
ok: [fda4c4a2-f655-49dc-997f-29d555d15d49]
ok: [3c0bc0ea-ce96-4ca5-ab89-442434c34088]
ok: [8572e2c5-36d2-489a-a71f-7ff911958a7b]

TASK [Hostname command] *******************************************************************************************************************
changed: [fda4c4a2-f655-49dc-997f-29d555d15d49]
changed: [3c0bc0ea-ce96-4ca5-ab89-442434c34088]
changed: [8572e2c5-36d2-489a-a71f-7ff911958a7b]

PLAY RECAP ********************************************************************************************************************************
3c0bc0ea-ce96-4ca5-ab89-442434c34088 : ok=2    changed=1    unreachable=0    failed=0
8572e2c5-36d2-489a-a71f-7ff911958a7b : ok=2    changed=1    unreachable=0    failed=0
fda4c4a2-f655-49dc-997f-29d555d15d49 : ok=2    changed=1    unreachable=0    failed=0

After the playbook is executed, output is generated to show the play and tasks.

In general, tasks in Ansible playbooks are idempotent, and it is safe to run the playbook multiple times. If the targeted managed hosts are already in the correct state, no changes should be made.

Verifying the Syntax before running

Before executing a playbook, it is best practice to perform a verification to ensure that the syntax of its contents is correct. The ansible-playbook command offers a --syntax-check option which can be used to verify the syntax of a playbook file. It is essential to notice that this will only check the syntax and not if the code is correct for the intended use. The following example shows the successful syntax verification of the single-task-playbook.yml playbook.

$ ansible-playbook --syntax-check single-task-playbook.yml

When syntax verification fails, a syntax error is reported. The following example shows the failed syntax verification of a playbook:

[ansible@ansiblehost ~]$ ansible-playbook --syntax-check single-task-playbook.yml
ERROR! Syntax Error while loading YAML.
The error appears to have been in '/data/ansible/single-task-playbook.yml': line 8, column 2, but
may be elsewhere in the file depending on the exact syntax problem.

Dry runs

Another best practice while executing playbooks is to perform a dry run. Dry runs are invoked with the -C option, which runs Ansible to report the changes that would have occurred if the playbook was executed, but without making actual changes to the managed hosts.

$ ansible-playbook -C single-task-playbook.yml

Guided Exercise: Ansible Playbook - Backing up a Database using Cinder

At this point, we know how to write and execute basic playbooks. In this guided exercise we will create a simple Ansible playbook that takes advantage of the command module to write an openstack command that will create a backup of the existing dbvol that resides within Cinder.

Create the initial portion of our playbook that defines the name, target and the task to create the backup:

cinder_backup.yml
---
- name: Backing up the database via the Cinder service
  hosts: localhost
  tasks:
  - name: Create a Cinder Backup of Database Volume
    command: "openstack volume backup create --force --name dbvol_backup dbvol"
...

Verify the syntax of the playbook via:

$ ansible-playbook --syntax-check cinder_backup.yml

Execute the playbook via the following command:

$ ansible-playbook cinder_backup.yml

The dbvol_backup volume can be verified using the following OpenStack command:

$ openstack volume backup list

For advanced users, the verification process can be included within the playbook itself. However, this requires knowledge of certain topics yet to be discussed such as using the register variable and debug module. For completeness, we’ve included an additional task that verifies if the dbvol_backup exists.

---
- name: Backing up the database via the Cinder service
  hosts: localhost
  tasks:
  - name: Create a Cinder Backup of Database Volume
    command: "openstack volume backup create --force --name dbvol_backup dbvol"
  ## wait for backup to complete
  - name: Run the openstack volume backup list command
    shell: "sleep 45 && openstack volume backup list"
    register: output

  - debug: var=output.stdout_lines
...

Prior to re-running this updated playbook, for simplicity, we will delete the existing dbvol_backup manually. The steps are as follows:

$ openstack volume backup delete dbvol_backup
Note
If you notice, the existing Ansible playbook is not very idempotent. By the end of all these lab exercises, you will have the knowledge and skills necessary to make the required changes.

Re-execute the playbook via the following command:

$ ansible-playbook cinder_backup.yml
PLAY [Backing up the database via the Cinder service] ************************************************************************************

TASK [Gathering Facts] *******************************************************************************************************************
ok: [localhost]

TASK [Create a Cinder Backup of Database Volume] *****************************************************************************************
changed: [localhost]

TASK [Wait for backup to complete and then run the openstack volume backup list command] *************************************************
changed: [localhost]

TASK [debug] *****************************************************************************************************************************
ok: [localhost] => {
    "output.stdout_lines": [
        "+--------------------------------------+--------------+-------------+-----------+------+",
        "| ID                                   | Name         | Description | Status    | Size |",
        "+--------------------------------------+--------------+-------------+-----------+------+",
        "| ea8f0821-a41a-4c43-bd20-2e7b08b9b972 | dbvol_backup | None        | available |   10 |",
        "+--------------------------------------+--------------+-------------+-----------+------+"
    ]
}

PLAY RECAP *******************************************************************************************************************************
localhost                  : ok=4    changed=2    unreachable=0    failed=0    skipped=0                    : ok=4    changed=2    unreachable=0    failed=0    skipped=0

In the case of MongoDB, there is no need to perform any tasks on the database itself prior to the backup. However in the case of Oracle or other vendors, the database may need to be placed in a special mode to achieve a consistent state.

For illustrative purposes, the backup playbook can be modified to include tasks to stop and start the database first. Those tasks can be replaced with something relevant to your specific database platform.

---
- name: Backing up the database via the Cinder service
  hosts: db
  tasks:
  - name: Stop the database
    systemd:
      name: mongod
      state: stopped
    become: true
  - name: Create a Cinder Backup of Database Volume
    command: "openstack volume backup create --force --name dbvol_backup dbvol"
    delegate_to: localhost
  - name: Wait for backup to complete and then run the openstack volume backup list command
    shell: "sleep 45 && openstack volume backup list"
    register: output
    delegate_to: localhost
  - name: Start the database
    systemd:
      name: mongod
      state: started
    become: true

  - debug: var=output.stdout_lines
...

Note the the hosts field has changed, since we now must interact with the instance itself, as well as perform local OpenStack commands. The delegate_to keyword is used to assign the specific task to be run on a specific server. In this particular case, we want our OVH instance to handle any OpenStack commands and we do this by delegating it to our localhost which is also our control node.

Prior to re-running this updated playbook, for simplicity, we will delete the existing dbvol_backup manually. The steps are as follows:

$ openstack volume backup delete dbvol_backup
Note
If you notice, the existing Ansible playbook is not very idempotent. By the end of all these lab exercises, you will have the knowledge and skills necessary to make the required changes.

Run the playbook one final time:

$ ansible-playbook cinder_backup.yml

PLAY [Backing up the database via the Cinder service] ********************************************************************************************************

TASK [Gathering Facts] ***************************************************************************************************************************************
ok: [6c4cdbf2-bbe2-4ee9-8dfa-1eaf1a776c7f]

TASK [Stop the database] *************************************************************************************************************************************
changed: [6c4cdbf2-bbe2-4ee9-8dfa-1eaf1a776c7f]

TASK [Create a Cinder Backup of Database Volume] *************************************************************************************************************
changed: [6c4cdbf2-bbe2-4ee9-8dfa-1eaf1a776c7f -> localhost]

TASK [Wait for backup to complete and then run the openstack volume backup list command] *********************************************************************
changed: [6c4cdbf2-bbe2-4ee9-8dfa-1eaf1a776c7f -> localhost]

TASK [Start the database] ************************************************************************************************************************************
changed: [6c4cdbf2-bbe2-4ee9-8dfa-1eaf1a776c7f]

TASK [debug] *************************************************************************************************************************************************
ok: [6c4cdbf2-bbe2-4ee9-8dfa-1eaf1a776c7f] => {
    "output.stdout_lines": [
        "+--------------------------------------+--------------+-------------+-----------+------+",
        "| ID                                   | Name         | Description | Status    | Size |",
        "+--------------------------------------+--------------+-------------+-----------+------+",
        "| 5cc8e54d-0f2b-4538-bb4f-078054451ca6 | dbvol_backup | None        | available |    1 |",
        "+--------------------------------------+--------------+-------------+-----------+------+"
    ]
}

PLAY RECAP ***************************************************************************************************************************************************
6c4cdbf2-bbe2-4ee9-8dfa-1eaf1a776c7f : ok=6    changed=4    unreachable=0    failed=0

Guided Exercise: Breaking the existing Database

Now that we have a working database and it has been properly backed up, we can access this database information via http://localhost:1234 in our browser. Below is a image of what you should be seeing once you access the link.

Note
If you can not access the website, ensure you set up SSH forwarding as outlined in the overview lab:
$ ssh -XL 1234:<LOAD BALANCER IP>:80 stack@<OVH IP>

The loadbalancer IP can be found out via the command:

$ openstack loadbalancer list
+--------------------------------------+-------+----------------------------------+-------------+---------------------+----------+
| id                                   | name  | project_id                       | vip_address | provisioning_status | provider |
+--------------------------------------+-------+----------------------------------+-------------+---------------------+----------+
| cc5b4311-6181-43e7-8051-0e705aa8c321 | weblb | b0796a9f0938466b9e9771c01d5bd2ba | 172.24.4.27 | ACTIVE              | amphora  |
+--------------------------------------+-------+----------------------------------+-------------+---------------------+----------+
Selection 246
Figure 1. Database Graphs

 

In the next lab, we will be restoring the existing database back to what you are currently seeing on our existing browser. In order to do this, we need to break the existing database.

We have added a simple blinking link labeled "DON’T CLICK ME" that breaks the current database. For this simple exercise, click on the link to break the database and in the following sections we will create an Ansible playbook that restores the database back to it’s original state of when the backup was taken.

Note
When accessing via browser for the first time, it may take some time to initially load, please be patient.