DevOps: "Making httpd service idempotent using Conditionals in Ansible Playbook"?

DevOps: "Making httpd service idempotent using Conditionals in Ansible Playbook"

Ansible, A powerful IT automation tool as well as the revolutionary invention of the Industry. It can solve almost every task and can reduce time complexity by running a single task on thousands of machines simultaneously. According to Wikipedia "Ansible is an open-source software provisioning, configuration management, and application-deployment tool enabling infrastructure as code. It runs on many Unix-like systems and can configure both Unix-like systems as well as Microsoft Windows.

Ansible Playbooks offer a repeatable, re-usable, simple configuration management, and multi-machine deployment system, one that is well suited to deploying complex applications. If we need to execute a task with Ansible more than once, write a playbook and put it under source control.

In httpd (Apache HTTP Server), we don't need to restart the httpd service again - n - again when no changes are making, when a file has the same content. In Ansible Playbook, we have the following YAML file for the problem statement

- hosts: all

  vars_prompt:
  - name: Test
    private: no
    prompt: "Enter the content, which you want to see in web page"
  tasks:
  - package:
      name: "httpd"
      state: present
    become: true

  - copy:
      content: "<h1>{{ Test }}</h1>"
      dest: "/var/www/html/shobhit.html"
    become: true

  - service:
      name: "httpd"
      state: restarted
    become: true

In this Playbook, when content is being copied or created on the managed node, the "httpd" service is always restarting according to the YAML syntax. That means, restarting HTTPD Service is not idempotence in nature and also consume more resources. To rectify this challenge in the Ansible playbook, the "Conditionals" concept is being used for this scenario. Let's see, How can we make httpd service idempotent using Conditionals in Ansible Playbook, using the steps and concepts, which are being followed below.

Improving the existing Playbook

In this section, we need to improve the above playbook. And before this, we need to first understand the following terminologies.

Essential Terminologies

Conditionals

In a playbook, we may want to execute different tasks, or have different goals, depending on the value of a fact (data about the remote system), a variable, or the result of a previous task. We may want the value of some variables to depend on the value of other variables. Or we may want to create additional groups of hosts based on whether the hosts match other criteria. We can do all of these things with conditionals.

when

"when" is the simplest conditional statement that applies to a single task. We can create the task, then add a when the statement that applies a test.

register

The task keyword "register" can create variables from the output of an Ansible task. We can use registered variables in any later tasks in your play.

Do you know: The Ansible Playbook with YAML syntax uses Jinja2 expressions

Return Values

Ansible modules normally return a data structure that can be registered into a variable, or seen directly when output by the ansible program. Each module can optionally document its own unique return values.

changed

A boolean indicating if the task had to make changes.

"changed": true

That's all

Now, we are going to improve the above playbook to solve the challenge. First, we need to create or open the existing managed node and control node. In my case, I have already set up a managed node and a control node with the following requirement.

Managed Node's Public IP Address - 18.189.184.111

In my case, I am doing these operations on top of the AWS Cloud.

Pinging the Managed Node

Let's try to ping the managed node from the target node to check whether it is able to connect or not. It's a good practice, we all should follow this. To ping the managed node, we need to run the following Ansible Ad-Hoc command

ansible all -m ping

This will ping all the hosts & the result in the output can be seen as something like this

18.189.184.111 | SUCCESS => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/libexec/plaorm-python"
    },
    "changed": false,
    "ping": "pong"
}

If the ping status is pong, then the managed node can be accessible. It's working in this case.

This host is written in the inventory file which looks like something inside as follows

18.189.184.111 ansible_user=ec2-user ansible_ssh_private_key_file=/root/SSHKeys/shobhitKeyNov2020.pem ansible_connection=ssh

In this file, the ansible_user is ec2-user (which is by default user in AWS EC2 Instance) and instead of using a password, I am using SSH Private Key using the keyword ansible_ssh_private_key_file and the value is the location of the private key file which has the extension .pem and finally, the connection type is SSH.

Now, let's improve the existing playbook as follows

Existing Playbook

- hosts: all

  vars_prompt:
  - name: Test
    private: no
    prompt: "Enter the content, which you want to see in web page"
  tasks:
  - package:
      name: "httpd"
      state: present
    become: true

  - copy:
      content: "<h1>{{ Test }}</h1>"
      dest: "/var/www/html/shobhit.html"
    become: true

  - service:
      name: "httpd"
      state: restarted
    become: true

Improved Playbook

- hosts: all

  vars_prompt:
  - name: Test
    private: no
    prompt: "Enter the content, which you want to see in web page"
  tasks:
  - package:
      name: "httpd"
      state: present
    become: true

  - copy:
      content: "<h1>{{ Test }}</h1>"
      dest: "/var/www/html/shobhit.html"
    become: true
    register: copystatus

  - service:
      name: "httpd"
      state: restarted
    become: true
    when: copystatus.changed

Now, let's understand the newly updated playbook

In the copy module, I've used an extra or new keyword called a register. As I mention, what is the register keyword, it will return the copy module status in the variable called "copystatus". The "copystatus" variable is defined by me which means you can define the variable with any name. And at last, in the service module, I've used the when keyword which is the conditional statement, and for the when keyword the condition which I've specified is "copystatus.changed" that means, whatever the copy module return keyword "copystatus" store, it will match the boolean value with when condition. If the copy module returns the changed status "False" that means no changes are made in the copy module. But, if the copy module returns the changed status False and it is stored in copystatus variable, then in the service module, when the condition will check the statement, if copystatus.changed is "False" then don't execute the service module or ignore it at the time of Ansible Playbook runs. But, if copystatus is True that means something or any changes are made in the copy module, then in this case, when the condition matches the copystatus.changed is True then it executes the service module.

Finally, what I mean, if no changes are made in the copy module, that means the copy module changed return status is false, which means it will not execute the service module as specified in the when conditional statement.

Running the Ansible Playbook

To run ansible-playbook, we need to run the following Ansible Ad-Hoc command with the path specified for ansible-playbook file as follows

ansible-playbook Playbook/web-server.yml

& the output will be

Enter the content, which you want to see in web page: Hello Shobhit

PLAY [all] *******************************************************************

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

TASK [package] ***************************************************************
ok: [18.189.184.111]

TASK [copy] ******************************************************************
changed: [18.189.184.111]

TASK [service] ***************************************************************
changed: [18.189.184.111]

PLAY RECAP *******************************************************************
18.189.184.111             : ok=4    changed=2    unreachae=0    failed=0    skipped=0    rescued=0    ignored=0

It's the first time the ansible-playbook is executed then obviously changes will be made and the service will also restart according to the YAML Syntax written inside the ansible-playbook.

Let's watch the following video for the live output

That's all

So, the final conclusion is that using conditional in Ansible playbook, we can rectify the challenge of restarting HTTPD Service is not idempotence in nature and also consume more resources. Using the above scenario, the challenge has been solved and the ansible-playbook is also improved or optimized.

***

Thanks for reading this!

This article is written, edited, and published by Shobhit Sharma

Connect With Me On Twitter | Instagram | LinkedIn | Facebook | WhatsApp

Email Me @ [email protected]

要查看或添加评论,请登录

Shobhit Sharma的更多文章

社区洞察

其他会员也浏览了