简体   繁体   中英

Running ansible on the first node

I am trying to configure a series of web servers. I have the following in my inventory file:

web_servers:
  hosts:
    1.2.3.4:
    1.2.3.5:

I want to make sure the very first host (1.2.3.4) has a cron job created on it. But it needs to be that specific host.

Here's my ansible yaml:

- name: Creates a backup cron file under /etc/cron.d
  cron:
    name: App Backups
    weekday: "*"
    minute: "0"
    hour: "2"
    user: app
    job: "/scripts/backups.sh"
    cron_file: app-backup

I've tried adding run_once: true as well as when: inventory_hostname == groups['web_servers'][0] and several other variations but each time the code runs on the wrong node. I suspect its selecting a node at random - maybe the second node already runs faster?

There's got to be some way to make sure it always runs on 1.2.3.4. I don't want to hard code the hosts file in the playbook because it will be used for setting up multiple environments and the IP will be different from environment to environment.

Can someone please help me understand how I can get the task to run only on the first node?

It sounds like the first node should be in its own group, which you can then target in the play:

web_servers:
  hosts:
    1.2.3.4:
    1.2.3.5:

cron_runner:
  hosts:
    1.2.3.4:
- hosts: cron_runner

  tasks:
    - name: Creates a backup cron file under /etc/cron.d
      cron:
        name: App Backups
        weekday: "*"
        minute: "0"
        hour: "2"
        user: app
        job: "/scripts/backups.sh"
        cron_file: app-backup

By default, Ansible runs each task on all hosts affected by a play before starting the next task on any host, using 5 forks.

In your first play save a variable with the first host in your localhost and in your second play use that variable.

- hosts: localhost

  tasks:
  - name: Get the first host
    set_fact:
      ip_server: "{{ groups['webservers'][0] }}"

- hosts: "{{ hostvars['localhost']['ip_server'] }}"

  tasks:
  - name: Creates a backup cron file under /etc/cron.d
    cron:
      name: App Backups
      weekday: "*"
      minute: "0"
      hour: "2"
      user: app
      job: "/scripts/backups.sh"
      cron_file: app-backup

Q: " Get the task to run only on the first node. "

web_servers:
  hosts:
    1.2.3.4:
    1.2.3.5:

A: Playbook's keyword order controls the sorting of hosts

Quoting from Playbook Keywords

order: Controls the sorting of hosts as they are used for executing the play. Possible values are inventory (default), sorted, reverse_sorted, reverse_inventory, and shuffle.

By default, the order is inventory , ie the first host in the play is the first host in the inventory. Use run_once

Quoting from Task Keywords :

run_once: Boolean that will bypass the host loop, forcing the task to attempt to execute on the first host available and afterward apply any results and facts to all active hosts in the same batch.

For example, the playbook

- hosts: webservers
  tasks:
    - debug:
        msg: "Creates a backup cron on {{ inventory_hostname }}"
      run_once: true

gives

msg: Creates a backup cron on 1.2.3.4

Test order of the hosts and see other options. Given the inventory

shell> cat hosts
webservers:
  hosts:
    1.2.3.4:
    1.2.3.5:
    1.2.3.3:

the playbook shows the first host in the play for the selected order

- hosts: webservers
  order: inventory
  tasks:
    - debug:
        msg: "inventory: {{ inventory_hostname }}"
      run_once: true

- hosts: webservers
  order: reverse_inventory
  tasks:
    - debug:
        msg: "reverse_inventory: {{ inventory_hostname }}"
      run_once: true

- hosts: webservers
  order: sorted
  tasks:
    - debug:
        msg: "sorted: {{ inventory_hostname }}"
      run_once: true

- hosts: webservers
  order: reverse_sorted
  tasks:
    - debug:
        msg: "reverse_sorted: {{ inventory_hostname }}"
      run_once: true

- hosts: webservers
  order: shuffle
  tasks:
    - debug:
        msg: "shuffle: {{ inventory_hostname }}"
      run_once: true

gives (abridged)

  msg: 'inventory: 1.2.3.4'
  msg: 'reverse_inventory: 1.2.3.3'
  msg: 'sorted: 1.2.3.3'
  msg: 'reverse_sorted: 1.2.3.5'
  msg: 'shuffle: 1.2.3.5'

Q: " For whatever reason it's not working for me. "

A: In this case, delegate the task to the first host in the group. See Delegating tasks . For example

    - debug:
        msg: "Creates a backup cron on {{ inventory_hostname }}"
      run_once: true
      delegate_to: groups.webservers.0

gives

ok: [1.2.3.4 -> groups.webservers.0] => 
  msg: Creates a backup cron on 1.2.3.4

For the benefit of others I ended up using something completely different than the suggestions above:

- name: Creates a backup cron file under /etc/cron.d
  cron:
    name: App Backups
    weekday: "*"
    minute: "0"
    hour: "2"
    user: app
    job: "/scripts/backups.sh"
    cron_file: app-backup
  when: ansible_hostname is regex (".*[A-za-z]1$")

In my case I know that the first server will end in the number 1. So I do a regex match against the ansible_hostname variable.

I realise there's no way any of the above suggestions would have mentioned that as I made no mention of the system naming convention but given that the approaches above didn't work for me I wanted to offer this alternative to those who may be in a similar situation.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM