Search code examples
ansibleansible-inventory

Ansible hosts preference


I am trying to run some yum commands on remote set of nodes. But they need to be run in a particular sequence and not in parallel.

---
- name: Java 1.8 upgrade for EFM on Witness
  hosts: database-witness:database-standby:database-master
  tasks:
  - name: Installating Java 1.8 on Witness
    yum: name=java-1.8.0-openjdk-1.8.0.111-2.b15.el7_3 state=present
    notify: set unlimited java 1.8 security
  - name: Remove Java 1.7
    shell: rm -f /usr/lib/jvm/$(ls /usr/lib/jvm | grep "java-1.7")/jre/lib/security/*.jar
  - name: Remove SDK
    yum: name=java-1.7.0-openjdk.x86_64 state=absent
  - name: Remove Headless
    yum: name=java-1.7.0-openjdk-headless.x86_64 state=absent

My env file is ::

[database-master]
172.X.X.1

[database-standby]
172.X.X.2

[database-witness]
172.X.X.3

In the above playbook I've specified:

hosts: database-witness:database-standby:database-master

Does this mean the set of yum commands will first be run on database-witness THEN on database-standby and THEN on database-master.


Solution

  • In the above playbook I've specified:

    hosts: database-witness:database-standby:database-master
    

    Does this mean the set of yum commands will first be run on database-witness THEN on database-standby and THEN on database-master.

    No. Ansible runs playbooks using a specified strategy. By default (linear strategy), Ansible will fork five threads and run each task in parallel on five hosts. Ansible will move to the next tasks only when all hosts in the current group finished (or failed).

    Since you want to move to the next host after all tasks on a single host finished, the default strategy will not work for you.


    You can add a serial: 1 declaration (see Rolling Update Batch Size) to the play which will cause Ansible to run all tasks on a single host before proceeding to the next one, but the question of the order remains.

    Theoretically hosts' list is ordered and once you get it empirically tested it should be repeatable, but there was a (small) number of examples in questions on SO which suggested that Ansible is not always keeping the "common-sense" order.

    ---
    - name: Java 1.8 upgrade for EFM on Witness
      hosts: database-witness:database-standby:database-master
      serial: 1
      tasks:
        # the tasks
    

    But if you have a strict requirement like in your question, the most reliable way would be to split your play into three.

    You can extract the tasks to a separate file to avoid duplication. So:

    ---
    - name: Java 1.8 upgrade for EFM on Witness
      hosts: database-witness
      tasks:
        - include: tasks.yml
    
    - name: Java 1.8 upgrade for EFM on Witness
      hosts: database-standby
      tasks:
        - include: tasks.yml
    
    - name: Java 1.8 upgrade for EFM on Witness
      hosts: database-master
      tasks:
        - include: tasks.yml
    

    And tasks.yml:

    - name: Installating Java 1.8 on Witness
      yum: name=java-1.8.0-openjdk-1.8.0.111-2.b15.el7_3 state=present
      notify: set unlimited java 1.8 security
    - name: Remove Java 1.7
      shell: rm -f /usr/lib/jvm/$(ls /usr/lib/jvm | grep "java-1.7")/jre/lib/security/*.jar
    - name: Remove SDK
      yum: name=java-1.7.0-openjdk.x86_64 state=absent
    - name: Remove Headless
      yum: name=java-1.7.0-openjdk-headless.x86_64 state=absent
    

    The latter solution also makes sure the execution will stop in case of an error on any host. With serial: 1-method you have no such guarantee, so if the tasks failed on database-witness, Ansible would proceed with the next target.