Template management, list management, parallel mode

Posted by walkonet on Fri, 31 Dec 2021 20:11:06 +0100

Template management

1. Introduction to Jinja

Tempalert is a copy module. Its usage is the same as that of the copy module. It is generally used to copy the facts variables in the configuration file to the client and resolve the variables.
Format Note: template is a module with the same format as other modules

src path note: if you write the file directly without writing the path, the system will go to the current directory/ Templates / directory, so you should manually create the templates directory in advance and put the files to be copied into it

[root@server ansible]# mkdir template
[root@server ansible]# mv ja template/
[root@server ansible]# ls template/
ja
[root@server ansible]# cat template/ja 
127.0.0.1   localhost localhost.localdomain localhost4 localhost4.localdomain4
::1         localhost localhost.localdomain localhost6 localhost6.localdomain6

Example: define the ja template, copy the hosts file to the / etc / adaptive / template / directory, and check the variables
Define ja

[root@server template]# cat ja 
127.0.0.1   localhost localhost.localdomain localhost4 localhost4.localdomain4
::1         localhost localhost.localdomain localhost6 localhost6.localdomain6

{{ ansible_facts['default_ipv4'] ['address'] }} {{ ansible_facts['fqdn'] }}
## Write variable

root@server playbook]# cat tmptest.yml 
---
- hosts: httpd
  tasks: 
    - name: testing
      template:      ##Transfer using template module
        src: /etc/ansible/template/ja
        dest: /etc/hosts

implement

[root@server playbook]# ansible-playbook tmptest.yml 

PLAY [httpd] *******************************************************************

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

TASK [testing] *****************************************************************
changed: [192.168.143.20]

PLAY RECAP *********************************************************************
192.168.143.20              : ok=2    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   
##see
[root@httpd ansible]# cat /etc/hosts
127.0.0.1   localhost localhost.localdomain localhost4 localhost4.localdomain4
::1         localhost localhost.localdomain localhost6 localhost6.localdomain6

192.168.143.20 httpd

2. Build template

The jinja2 template consists of several elements: data, variables, and expressions. When rendering the ja template, these variables and expressions are replaced with corresponding values. The variables used in the template can be specified in the vars section of the playbook. The fact of the managed host can be used as a variable in the template.

Remember that you can use the ansible system_ hostname -i inventory_ Use the file - M setup command to get the facts related to the managed host.

[root@centos8-1 ansible]# ansible apache -m setup
192.168.143.20 | SUCCESS => {
"ansible_facts": {
"ansible_all_ipv4_addresses": [
"192.168.143.20"
],
"ansible_all_ipv6_addresses": [
"fe80::4f83:a388:3cd:89bc"
Omitted here......

We can also ansible apache -m setup | less

The following example demonstrates how to create * * / etc / SSH / sshd using variables and facts retrieved from the managed host by Ansible_ Config template. When the relevant playbook * * is executed, any fact will be replaced with the corresponding value in the configured managed host.

Note: files containing ja templates do not need to have any specific file extension (such as. j2). However, providing such a file extension will make it easier for you to remember that it is a template file.

3. Deploy jjinja2 template

The jinja2 template is a powerful tool for customizing profiles to be deployed on managed hosts. After creating a ja template for the configuration file, it can be deployed to the managed host through the template template, which supports the transfer of local files in the control node to the managed host.

To use the template module, use the following syntax. The value associated with the src key specifies the source jinja2 template, while the value associated with the dest key specifies the file to be created on the target host.

[root@server playbook]# cat tmptest.yml 
---
- hosts: httpd
  tasks: 
    - name: testing
      template: 
        src: /etc/ansible/template/ja
        dest: /etc/hosts

4. Management template file

To prevent the system administrator from modifying the file of Ansible deployment, it is best to include a comment at the top of the template to indicate that the file should not be edited manually.

Ansible can be used_ "Ansible managed" string set in the managed directive to perform this operation. This is not a normal variable, but it can be used as a variable in the template. ansible_ The managed instruction is in ansible Set in cfg file:

ansible_managed = Ansible managed

To make ansible_ If the managed string is included in the ja template, use the following syntax:

{{ ansible_managed }}   ## Be sure to space before and after

5. Use cycle

ja uses the for statement to provide loop functionality. In the following example, the user variable is replaced with all the values contained in the users variable, one value per line.

[root@server template]# cat xh.j2  
{% for user in users%}
  {{ user }}
---------------
{% endfor %}
[root@server template]# pwd
/etc/ansible/template

Write variable

[root@server hosts_facts]# cat user.yml 
users: 
  - wu
  - yi
  - fan
  - chilaofan

Write temptest yml

[root@server playbook]# cat tmptest.yml 
---
- hosts: httpd
  vars_files: 
   /etc/ansible/hosts_facts/user.yml ##Specify variable path
  tasks: 
    - name: for
      template: 
        src: /etc/ansible/template/xh.j2     ##Location of template files
        dest: /opt/    Path to target host

implement

root@server playbook]# ansible-playbook tmptest.yml 

PLAY [httpd] *******************************************************************

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

TASK [for] *********************************************************************
changed: [192.168.143.20]

PLAY RECAP *********************************************************************
192.168.143.20              : ok=2    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   

## see
[root@httpd opt]# cat xh.j2 
  wu
---------------
  yi
---------------
  fan
---------------
  chilaofan
---------------

The following example template uses the for statement to run all values in the users variable one by one, replacing myuser with individual values, except when the value is root

[root@server template]# cat xh.j2 
{% for user in users if not user == "wu" %}  ## Do not execute wu
  {{ user }}
---------------
{% endfor %}

implement

root@server playbook]# ansible-playbook tmptest.yml 

PLAY [httpd] *******************************************************************

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

TASK [for] *********************************************************************
changed: [192.168.143.20]

PLAY RECAP *********************************************************************
192.168.143.20              : ok=2    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   

see
[root@httpd opt]# cat xh.j2 
  yi
---------------
  fan
---------------
  chilaofan
---------------

Or here we remove not and execute it.

[root@server template]# cat xh.j2 
{% for user in users if  user == "wu" %} ## Remove not and make the value of user equal to wu
  {{ user }}
---------------
{% endfor %}

implement

[root@server playbook]# ansible-playbook tmptest.yml 

PLAY [httpd] *******************************************************************

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

TASK [for] *********************************************************************
changed: [192.168.143.20]

PLAY RECAP *********************************************************************
192.168.143.20              : ok=2    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   
see
[root@httpd opt]# cat xh.j2 
  wu         ## Output wu only
---------------

Number of cycles

{# for statement #}
{% for myuser in users if not myuser == "root" %}
User number {{ loop.index }} - {{ myuser }}
{% endfor %}

example

[root@server playbook]# cat /etc/ansible/template/xh.j2 
{% for user in users if not user == "wu" %}
user number {{ loop.index }} -  {{ user }}   use loop.index Statistics of variables
---------------
{% endfor %}

implement

[root@server playbook]# ansible-playbook tmptest.yml 

PLAY [httpd] *******************************************************************

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

TASK [for] *********************************************************************
changed: [192.168.143.20]

PLAY RECAP *********************************************************************
192.168.143.20              : ok=2    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   

see
[root@httpd opt]# cat xh.j2 
user number 1 -  yi
---------------
user number 2 -  fan
---------------
user number 3 -  chilaofan

loop. The index variable extends to the index number where the loop is currently located. It has a value of 1 at the first execution of the loop and increments by 1 at each iteration.

As another example, the template also uses the for statement and assumes that the myhosts variable has been defined in the manifest file used. This variable will contain a list of hosts to manage. When you use the following for statement, all hosts in the myhosts group are listed in the listing file.

{% for myhost in groups['myhosts'] %}
{{ myhost }}
{% endfor %}    

The following three lines are templates / hosts The J2 template constructs files from all hosts in the all group. (the middle line of the template is very long due to the length of the variable name.) It iterates over each host in the group to get the three facts of the * * / etc/hosts * * file.

Format:

{% for host in groups['all'] %}
{{ hostvars['host']['ansible_facts']['default_ipv4']['address'] }}      {{ hostvars['host']['ansible_facts']['fqdn'] }}     {{ hostvars['host']['ansible_facts']['hostname'] }}
{% endfor %}

6. Use conditional statements

jinja2 uses if statements to provide conditional control. If certain conditions are met, this allows the user to place a row in the deployed file.

In the following example, the value of the result variable can be placed in a deployed file only if the value of the finished variable is True.

Format:

{% if finished %}
{{ result }}
{% endif %}

example

[root@server playbook]# cat /etc/ansible/template/xh.j2 
{% if users %}
  {{ users }}}
{% endif %}

implement

[root@server playbook]# ansible-playbook tmptest.yml 

PLAY [httpd] *******************************************************************

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

TASK [for] *********************************************************************
changed: [192.168.143.20]

PLAY RECAP *********************************************************************
192.168.143.20              : ok=2    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   

View results
[root@httpd opt]# cat xh.j2 
  ['wu', 'yi', 'fan', 'chilaofan']}

7. Variable filter

jinja2 provides filters to change the output format of template expressions (for example, output to JSON). There are filters for languages such as JSON. to_ The JSON filter uses JSON to format the expression output.

{{ output | to_json }}

example

[root@server hosts_facts]# cat user.yml  ## Variable file
users: 
  - name: xiaoming
    age: 1
  - name: xiaohong
    age: 2
  - name: xiaogang
    age: 3

Template format

[root@server template]# cat xh.j2 
{{ users | to_json }}

implement

[root@server playbook]# ansible-playbook tmptest.yml 

PLAY [httpd] *******************************************************************

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

TASK [for] *********************************************************************
changed: [192.168.143.20]

PLAY RECAP *********************************************************************
192.168.143.20              : ok=2    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   
see
[root@httpd opt]# cat xh.j2 
[{"name": "xiaoming", "age": 1}, {"name": "xiaohong", "age": 2}, {"name": "xiaogang", "age": 3}]

There are also other filters, such as to_nice_json, which formats the expression output in JSON human readable format.

[root@server template]# cat xh.j2  ## Template format
{{ users | to_nice_json }}

implement

[root@server playbook]# ansible-playbook tmptest.yml 

PLAY [httpd] *******************************************************************

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

TASK [for] *********************************************************************
changed: [192.168.143.20]

PLAY RECAP *********************************************************************
192.168.143.20              : ok=2    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   
see
[root@httpd opt]# cat xh.j2 
[
    {
        "age": 1,
        "name": "xiaoming"
    },
    {
        "age": 2,
        "name": "xiaohong"
    },
    {
        "age": 3,
        "name": "xiaogang"
    }
]

8. Variable test

The expression used with the when clause in any playbook is a jinja2 expression. Built in Ansible tests for testing return values include failed, changed, succeeded, and skipped. The following tasks demonstrate how to use tests within conditional expressions.

tasks:
...output omitted...
  - debug: msg="the execution was aborted"
    when: returnvalue is failed

2, Select host using host mode

1. Reference list host

Host mode is used to specify the host to be the target of play or temporary commands. In its simplest form, the name of the managed host or host group in the list specifies the host mode of the host or host group.

In play, hosts specifies the managed host against which to run play. For temporary commands, the host mode is provided to the ansible command as a command line parameter.

The following example listing will be used throughout this section to demonstrate host mode.

[root@localhost ~]# cat myinventory 
web.example.com
data.example.com

[lab]
labhost1.example.com
labhost2.example.com

[test]
test1.example.com
test2.example.com

[datacenter1]
labhost1.example.com
test1.example.com

[datacenter2]
labhost2.example.com
test2.example.com

[datacenter:children]
datacenter1
datacenter2

[new]
192.168.143.20
192.168.143.30

2. Managed host

To demonstrate how to parse the host mode, we will execute the playbook of Ansible Playbook YML, using different host patterns to target different subsets of managed hosts in this sample manifest.
Managed host
The most basic host mode is that a single managed host name is listed in the list. This specifies that the host is the only host in the manifest where the ansible command will perform the operation.

When the playbook is running, the first Gathering Facts task should run on all managed hosts that match the host pattern. Failures during this task may cause the managed host to be removed from play.

If the list explicitly lists the IP address instead of the host name, you can use it as the host mode. If the IP address is not listed in the list, we cannot use it to specify the host, even if the IP address will resolve to the host name in DNS.

The following example demonstrates how to use host mode to refer to the IP address contained in the manifest.

[root@centos8-1 ansible]# cat hosts_facts/bu.yml 
---
- hosts: 192.168.58.20   //Managed host IP address
[root@server playbook]# 

[root@server  playbook]# ansible-playbook  tmptest.yml 

PLAY [192.168.143.20] *********************************************************

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

PLAY RECAP *********************************************************************
192.168.143.20           : ok=1    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   

be careful:

There is a problem in referencing the managed host through the IP address in the list, that is, it is difficult to remember which IP address is used by the host targeted by the play or temporary command. However, if there is no resolvable host name, we may have to specify the host by IP address to connect.

You can set ansible_host host variable to point an alias to a specific IP address in the list. For example, you can have a list named dummy Example, and then create a host with the following host variables_ vars/dummy.example file to point the connection with this name to the IP address 192.168.58.20

ansible_host: 192.168.143.20

3. Specify host using group

When the group name is used as host mode, it specifies that Ansible will perform operations on hosts that are members of the group.

---
- hosts: lab

Remember, there is one called all A special group that matches all managed hosts in the list.
---
- hosts: all

There is also a special group called ungrouped, which includes all managed hosts in the list that do not belong to any other group:

---
- hosts: ungrouped

4. Match multiple hosts using wildcards

Another way to achieve the same goal as the all host mode is to use the * wildcard, which will match any string. If the host pattern is just a quoted asterisk, all hosts in the list will match.

---
- hosts: '*'    //Here * refers to all hosts

[root@server playbook]# cat  tmptest.yml 
---
- name: 
  hosts: '*'    //All hosts execute
  vars_files:      
    hosts_facts/user.yml
  tasks: 
    - name: testing
      template: 
        src: hosts_facts/xh.j2     
        dest: /opt/  

a key:

Some characters used in host mode also make sense for the shell. This can be problematic when running temporary commands from the command line using host mode through ansible. It is recommended that you use single quotation marks to enclose the host modes used in the command line to prevent them from being accidentally extended by the shell.
Similarly, if any special wildcard or list character is used in Ansible Playbook, the host mode must be placed in single quotation marks to ensure that the host mode can be correctly parsed.

You can also use the * character to match a managed host or group that contains a specific substring.
For example, the following wildcard matches the host pattern to * * example. All list names at the end of COM * *:

---
- hosts: '*.example.com'

The following example uses a wildcard host pattern to match a host that starts with * * 192.168.58** Name of the host or host group:

[root@server playbook]# cat  tmptest.yml 
---
- name: 
  hosts: 192.168.143.*
  vars_files:      
    hosts_facts/user.yml
  tasks: 
    - name: testing
      template: 
        src: hosts_facts/xh.j2     
        dest: /opt/  

a key:

The wildcard host pattern matches all manifest names, hosts, and host groups. They do not distinguish whether the name is a DNS name, IP address or group, which may lead to some unexpected matches.
For example, according to the example list, compare the results of the data center host mode specified in the previous example with the results of the data host mode:

---
- name: 
  hosts: 'data*'
  vars_files:      
    hosts_facts/user.yml
  tasks: 
    - name: testing
      template: 
        src: hosts_facts/xh.j2     
        dest: /opt/  

5. List

Multiple entries in the list can be referenced through a logical list. The comma separated list of host patterns matches all hosts that match any of these host patterns.

If you provide a comma separated list of managed hosts, all these managed hosts will be targets:

---
- hosts: labhost1.example.com,test2.example.com,192.168.2.2

If you provide a comma separated list of groups, all hosts belonging to any of these groups will be targets:

---
- hosts: lab,datacenter1    //Here we refer to the group in the ninth point reference list

You can also mix managed hosts, host groups, and wildcards, as follows:

---
- hosts: 'lab,data*,192.168.2.2'

a key:

You can also replace commas with colons (:). However, commas are the preferred delimiters, especially when using IPv6 addresses as managed host names.

If an item in the list begins with an amp ersand (&), the host must match the item to match the host pattern. It works like a logical AND.

For example, according to our example listing, the following host patterns will match computers in the lab group that also belong to the datacenter 1 group:

---
- hosts: lab,&datacenter1

We can also use the host mode * * & lab, datacenter1 or datacenter, & lab to specify that the computers in the datacenter1 group will match only when they also belong to the lab * * group.

By using an exclamation point (!) in front of the host mode Indicates that the host the first mock exam is excluded from the list. It works like logical NOT.
According to the example listing, the following example matches all hosts defined in the datacenter group, but test2 example. Except com:

---
- hosts: datacenter,!test2.example.com

You can also use the mode * * '! test2.example.com,datacenter '* * to obtain the same results.

The last example demonstrates using a host pattern that matches all hosts in the test manifest, except the managed hosts in the datacenter 1 group

---
- hosts: all,!datacenter1

6. Management dynamic list

The static list we used earlier is easy to write and easy to manage small infrastructure. However, it may be difficult to keep the static manifest file up-to-date if you want to operate many computers or work in an environment where computers change very quickly.

Most large IT environments do not have systems to track available hosts and how they are organized. For example, external directory services may be maintained through monitoring systems such as Zabbix, or located on FreeIPA or Active Directory servers. Cobbler and other installation servers or management services such as red hat satellite may track the deployed bare metal systems. Similarly, cloud services such as Amazon Web ServicesEC2 or OpenStack deployment, or virtual machine infrastructure based on Vmware or red hat virtualization may be the source of information about those replaced instances and virtual machines.

Ansible supports dynamic list scripts. These scripts retrieve current information from these types of sources whenever ansible is executed, so that the list can be updated in real time. These scripts are executable programs that can collect information from some external sources and output lists in JSON format.

Dynamic manifest scripts are used in the same way as static manifest text files. The location of the manifest can be directly in the current Ansible Specified in the cfg file or through the * * - i * * option. If the manifest file can be executed, it will be regarded as a dynamic manifest program, and Ansible will try to run it to generate the manifest. If the file is not executable, it is treated as a static manifest.

The manifest location can be in ansible The CFG configuration file is configured through the inventory parameter. By default, it is configured as * * / etc/ansible/hosts * *.

If the directory system or infrastructure used does not have a dynamic manifest script, we can write a custom manifest program. Custom programs can be written in any programming language, but the manifest information must be returned in JSON format when passing the appropriate options.

The Ansible inventory command is a useful tool for learning how to write Ansible listings in JSON format.

To display the contents of the manifest file in JSON format, run the Ansible Inventory -- list command. You can use the * * - i * * option to specify the location of the manifest file to be processed, or use only the default manifest set by the current Ansible configuration.

The following example demonstrates how to use the ansible inventory command to process INI style manifest files and output them in JSON format

[root@localhost ~]# cat inventory
workstation1.lab.example.com

[webservers]
web1.lab.example.com
web2.lab.example.com

[databases]
db1.lab.example.com
db2.lab.example.com


[root@localhost ~]# ansible-inventory -i inventory --list

The script starts with the appropriate interpreter line (for example, #! / usr/bin/python) and can be executed so that Ansible can run it.

When passing the – list option, the script must display JSON encoded hashes / dictionaries for all hosts and groups in the list.

In its simplest form, a group can be a list of managed hosts. In the JSON encoded output example of this listing script, webservers is a host group that contains Web1.0 lab.example. COM and web2 lab.example. Com managed host. The members of the databases host group are db1 lab.example. COM and DB2 lab.example. Com host.

[root@localhost ~]# ./inventoryscript --list
{
    "webservers": ["web1.lab.example.com","web2.lab.example.com"],
    "databases": ["db1.lab.example.com","db2.lab.example.com"]
}

In addition, the value of each group can be a JSON hash / dictionary containing a list of each managed host, any subgroup, and any group variables that may be set. The next example shows the JSON encoded output of a more complex dynamic manifest. The boston Group has two subgroups (backup and ipa), its own three managed hosts, and a set of group variables (example_host: false).

{
    "webservers": [
        "web1.lab.example.com",
        "web2.lab.example.com"
    ],
    "boston": {
        "children": [
            "backup",
            "ipa"
        ],
        "vars": {
            "example_host": false
        },
        "hosts": [
            "server1.demo.example.com",
            "server2.demo.example.com",
            "server3.demo.example.com",
        ]
    },
    "backup": [
        "server4.demo.example.com"
    ],
    "ipa": [
        "server5.demo.example.com"
    ],
    "_meta": {
        "hostvars": {
            "server5.demo.example.com": {
                "ntpserver": "ntp.demo.example.com",
                "dnsserver": "dns.demo.example.com"
            }
        }
    }
}

The script also supports the – host managed host option. This option must display a JSON hash / dictionary consisting of variables associated with the host, or a blank JSON hash / dictionary.

[root@localhost ~]# ./inventoryscript --host server5.demo.example.com
{
    "ntpserver": "ntp.demo.example.com",
    "dnsserver": "dns.demo.example.com"
}

be careful
When called through the – host hostname option, the script must display a JSON hash / dictionary of variables for the specified host. If no variables are provided, a blank JSON hash or dictionary may be displayed.

In addition, if the – list option returns a value named_ The top-level element of meta can return all host variables in one script call, so as to improve the script performance. At this point, the – host call is not made.

7. Use bifurcation to configure parallelism in ansible

When Ansible processes the playbook, each play is run sequentially. After determining the host list for play, Ansible will run each task in order. Generally, all hosts must successfully complete the task before any host starts the next task in play.

In theory, Ansible can connect to all hosts in play at the same time to perform each task. This is ideal for small host lists. However, if the play targets hundreds of hosts, it may bring a heavy burden to the control node.

The maximum number of simultaneous connections made by Ansible is controlled by the forks parameter in the Ansible configuration file. By default, it is set to 5, which can be verified in one of the following ways.

[root@server ansible]# vim ansible.cfg 
#Forks = 5 / / the default is 5

We can also view it with commands

[root@server ansible]# ansible-config dump | grep -i forks
DEFAULT_FORKS(default) = 5

For example, suppose that the ansible control node is configured with a default value of 5 forks, and play has 10 managed hosts. Ansible will perform the first task in play on the first five managed hosts, and then perform the second round of the first task on the other five managed hosts. After performing the first task on all managed hosts, ansible will continue to perform the next task on all managed hosts in the group of 5 managed hosts at a time. Ansible will perform this operation on each task in turn until the end of play.

The default value of forks is set very conservatively. If your control node is managing a Linux host, most tasks will run on the managed host and the control node will have less load. In this case, you can usually set the forks value higher, perhaps close to 100, and then the performance will improve.

If playbook runs a lot of code on the control node, it should be wise to raise the forks limit. If Ansible is used to manage network routers and switches, most modules run on control nodes rather than network devices. Since this increases the load on the control node, its ability to support an increase in the number of forks will be significantly lower than the control node that manages only Linux hosts.

You can override the default setting of forks in the ansible configuration file from the command line. Both the ansible and ansible playbook commands provide the - f or – forks option to specify the number of forks to use.

8. Manage rolling updates

Typically, when Ansible runs play, it ensures that all managed hosts have completed each task before starting any host for the next task. After all managed hosts complete all tasks, the handler for any notifications runs.

However, running all tasks on all hosts can cause unexpected behavior. For example, if play updates a load balancing Web server cluster, you may need to stop each Web server from serving when the update is made. If all servers are updated in the same play, they may all stop serving at the same time.

One way to avoid this problem is to use the serial keyword to run the host in batch through play. Before the next batch starts, each batch of hosts will run in the whole play.

In the following example, ansible performs play on two managed hosts at a time until all managed hosts have been updated. Ansible first performs the tasks in play on the first two managed hosts. If either or both of these hosts notify the handler, ansible will run the handler according to the needs of both hosts. After performing play on these two managed hosts, ansible will repeat the process on the next two managed hosts. Ansible continues to run play in this manner until all managed hosts have been updated.

---
- hosts: webservers
  serial: 2  //Use the serial keyword to batch run the host through play
  tasks:
      - name: latest apache httpd package is installed
         yum:
           name: httpd
           state: latest
         notify: restart apache
    
  handlers:
       - name: restart apache
          service:
            name: httpd
            state: restarted

Suppose the web servers group in the previous example contains five Web servers, which are located behind the load balancer. When the serial parameter is set to 2, play will run two web servers at a time. Therefore, most of the five Web servers will always be available.

On the contrary, if the serial keyword is not used, the play and generated handlers will be executed on five Web servers at the same time. This may cause a service outage because the web service will restart simultaneously on all web servers.

a key:

For some purposes, each batch of hosts counts as a complete play running on a subset of hosts. This means that if the whole batch fails, play will fail, which will cause the whole playbook to fail.

In the previous scenario with serial: 2 set, if a problem occurs and the play of the first two hosts fails, the playbook will abort and the other three hosts will not run through play. This is a useful feature because only some servers will be unavailable, degrading the service rather than interrupting it.

Ansible repeats the process on the next two managed hosts. Ansible continues to run play in this manner until all managed hosts have been updated.

---
- hosts: webservers
  serial: 2  //Use the serial keyword to batch run the host through play. Before the next batch starts, each batch of hosts will run in the whole play.
  tasks:
    - name: latest apache httpd package is installed
      yum:
        name: httpd
        state: latest
      notify: restart apache
    
  handlers:
    - name: restart apache
      service:
        name: httpd
        state: restarted

Suppose the web servers group in the previous example contains five Web servers, which are located behind the load balancer. When the serial parameter is set to 2, play will run two web servers at a time. Therefore, most of the five Web servers will always be available.

On the contrary, if the serial keyword is not used, the play and generated handlers will be executed on five Web servers at the same time. This may cause a service outage because the web service will restart simultaneously on all web servers.

a key:

For some purposes, each batch of hosts counts as a complete play running on a subset of hosts. This means that if the whole batch fails, play will fail, which will cause the whole playbook to fail.

In the previous scenario with serial: 2 set, if a problem occurs and the play of the first two hosts fails, the playbook will abort and the other three hosts will not run through play. This is a useful feature because only some servers will be unavailable, degrading the service rather than interrupting it.

The serial keyword can also be specified as a percentage. This percentage is applied to the total number of hosts in play to determine the size of the rolling update batch. Regardless of the percentage, the number of hosts in each operation is always 1 or more.