Friday, 13 February 2015

Automating IT infrastructure with Ansible- PART 2

Automating IT infrastructure with Ansible- PART 2
Writing simple Playbooks



In PART 1 of this series of tutorials on Ansible, we saw how simple and easy it is to get Ansible up and running on a host and run few simple Ansible commands here and there.

In PART 2, we are going to look at how to write simple playbooks, specifically we will learn about: Tasks, Handlers, Roles, Templates and Variables


For this scenario, the playbook that we are going to create will simply install and configure Apache web server (httpd) on the remote host. The main purpose of this tutorial however is to learn how to write playbooks effectively and correctly. So without further adieu, lets get started!



Creating the Playbook
I'm going to assume you have already installed and configured the Ansible server and a few hosts in your environment. If not, refer the PART 1 of this tutorial.

For this tutorial, we will first create a separate directory under Ansible's default directory (/etc/ansible) and perform all our tasks from here. Start off by creating a simple Master Playbook as shown below:

# cd /etc/ansible

# mkdir -p playbooks/http-playbook

# vi main.yml




Tasks
Let's create a simple play which will check and install http packages on the remote host. Here, this play will run the [task] named "Install Apache httpd" on [all] hosts listed in the hosts inventory file. 

---
## PLAYBOOK TO INSTALL AND CONFIGURE APACHE HTTP ON CENTOS

- host: all
  tasks:
   - name: Install Apache httpd
     yum: pkg=httpd state=installed

Save the file and exit the editor.



Run the playbook. you should see the following sample output as shown below:

# ansible-playbook main.yml




Handlers
Handlers are similar to tasks that we saw in the earlier step. The only difference is that a Handler will be executed only when it is called by an event. For e.g in the same main.yml file, lets add a handler that will start the httpd service. But, if you see closely, the handler will only be called AFTER the initial install [tasks] completes. The handler will be called by the [notify] directive. 


NOTE that the name of the notify and the handler must be the same.

---
## PLAYBOOK TO INSTALL AND CONFIGURE APACHE HTTP ON CENTOS

- host: all
  tasks:
   - name: Install Apache httpd
     yum: pkg=httpd state=installed
    
     notify:
       - Start Httpd

  handlers:
    - name: Start httpd
      service: name=httpd state=started    



Run the playbook again. This time, you should see a NOTIFIED message as shown below.




Roles
Roles are just a good way of organizing and redistributing multiple, related tasks. For example, you can have a web role which contains tasks to install and configure a http server, you can have a DB role for containing all your database tasks and handlers etc. 

Let's go ahead and create a role for our small playbook. Let's call the role as web. Within this role, we will create separate sub-directories each with its own purpose.

# mkdir -p roles/web

# cd /roles/web

# mkdir -p {handlers,tasks,templates,vars}

-Handlers: Will store all handlers for our web role
-Tasks: Will store the tasks that are required to be performed on the web role
-Templates: Will contain a .j2 file (Python's Jinja2 template engine)
-Vars: Will contain some custom variables that will be used ONLY for the web role



here's what the directory structure now looks like:



IMP NOTEWithin each directory, Ansible will search for and read any Yaml file called main.yml automatically.

Let's first edit our main.yml and add the web role in it. Notice that we have wiped out all the rest of the content as now that content will be placed in separate files and folders.

---
## PLAYBOOK TO INSTALL AND CONFIGURE APACHE HTTP ON CENTOS

- host: all
  roles:
     - web

Save the file and exit the editor.



let us go ahead and populate the tasks and handlers now. First up, the tasks:

# vi roles/web/tasks/main.yml

---
- include: install-httpd.yml

The [include] directive is used to call other Yaml files. In this case, we will use our main.yml to call another yaml file that will actually install the http packages.



Create the install-httpd.yml file and populate it with the following contents:

# vi roles/web/tasks/install-httpd.yml

---
# these tasks install http and the php modules.

   - name: Install httpd packages
     yum: name={{ item }} state=installed
     with_items:
        - httpd
        - php    

     notify:
       - Start Httpd

Notice here, we did not use the [hosts] or the [tasks] directive. We directly go ahead and create the play. Towards the end, we have also mentioned the [notify] directive. As you must have guessed, this will call the corresponding handler which will start the httpd service.



Create the corresponding handler in the handlers directory.

# vi roles/web/handlers/main.yml

---
# handler to start httpd service

- name: Start Httpd
  service: name:httpd state=started



By now, your playbook should look something like this:



Run the playbook. You should see now that both the http and php packages were installed and the httpd service was started by the handler as well.




Templates
Templates are files that are based on Python's Jinja2 template engine. Each template file ends with a .j2 extension. You can put any content in these files for example a index.html files content etc, but the real power of these files comes when you use variables in it. You can use Ansible's [facts] and even call custom variables in these template files. 

In the below example, I have used two facts/ variables:
{{ ansible_eth0.ipv4.address }} ~~> IP Address of the remote host
{{ ansible_nodename }} ~~> Hostname of the remote host

NOTE: Ansible by default gathers “facts” about the machines under management, and these facts can be accessed in Playbooks and in templates. To see a list of all of the facts that are available about a machine, you can run the “setup” module as an ad-hoc action:
# ansible -m setup hostname

# vi roles/web/templates/index.html.j2


Save and exit the editor once you have created your template file.




Next, we will need a mechanism by which we can transfer these template files from their source directory (/roles/web/templates) to a destination directory on the remote host.

For that, let's create another yaml file in the tasks directory as follows:

# vi roles/web/tasks/copy-template.yml

---

- name: Create the index.html file over at the httpd server
  template: src=index.html.j2 dest=/var/www/html/index.html

save and exit the editor. Here, we have used a [template] module that will copy the .j2 file from the source templates directory over to the destination server. 



Dont forget to add our copy-template.yml file in the main.yml tasks file. 

# vi roles/web/tasks/main.yml

- include: copy-template.yml



Run the playbook. You should see the task of creating the index.html file over at the httpd server as shown below:



Want to see what got populated in the index.html file? Simply open a browser up and type in the IP address of the Httpd Server, you should see the hostname ({{ ansible_nodename }}) and IP address ({{ ansible_eth0.ipv4.address }}) printed as shown below:




Variables
As the name implies, you can use both Ansible facts and custom made variables in your playbooks. However, the important thing to note here is how to declare the variables?? Variables can be defined in 5 different ways:

1) Variables defined in the play under vars_files attribute:

vars_files: 
   - "/path/to/var/file"

2) Variables defined in <role>/vars/main.yml

3) Variables passed in on the command line

# ansible-playbook main.yml -e "http-port=80"

4) Variables defined in the play under vars
vars: http_port: 80 

5) Variables defined in group_vars/ directory

Let's have a look at this with an example. In this case, I'm using a httpd.conf file as a template stored at roles/web/templates directory. In this file, I'll call a custom variable {{ http_port }}. This variable will be defined in the /group_vars directory as explained:

NOTEVariable names should never include dashes, they will be interpreted as a subtraction. Use underscores instead!

# vi roles/web/templates/httpd.conf.j2



In the conf file, I have replaced the Listening port value with the custom variable {{ http_port }}



I have also used the custom variable and Ansible Facts in the ServerName and ServerAdmin fields as shown



To declare the variable, create a group_vars directory outside the roles folder. 

# mkdir group_vars

# vi group_vars/all.yml



in the all.yml file, declare the custom variable as shown. This Variable will now be available to ALL the roles that are included in this playbook. If you wish to keep this variable restricted only to the web role, then create the all.yml file in the roles/web/vars directory.

---
# Global Custom Variables
http_port: 80



Don't forget to call the [template] module for the httpd.conf file as well.

# vi roles/web/tasks/copy-template.yml

- name: Create the httpd.conf file
  template: src=httpd.conf.j2 dest=/etc/httpd/conf/httpd.conf



Run the playbook. You should see the httpd.conf task as shown below:



by the end of this tutorial, your directory structure should look something like this.



Using this similar style, you can create roles such as Database, Loadbalancer etc and create plays and templates for each of them. Ansible truly has a lot of potential and is really easy and quick to learn. Hope this tutorial helps you out the way it helped me! Stay tuned for a lot more coming your way soon..


Cheers!!






No comments :

Post a Comment