Diocese0721
Diocese0721

Reputation: 11

Ansible Multi data center deployment

My dev inventory file is:

[dev]
service1 ansible_host=dev1.com ansible_port=22 ansible_user=ubuntu
service2 ansible_host=dev1.com ansible_port=22 ansible_user=ubuntu
service3 ansible_host=dev1.com ansible_port=22 ansible_user=ubuntu

I execute below roles for service:

roles:
- sync_code
- prep_local
- build
- shutdown_app
- setup_host
- start_app

I have serial: yes so when I deploy any service, each role is executed sequentially.

For prod, there are 2 datacenters, DC1 and DC2; services are deployed in both datacenters, and multiple servers in each datacenter can host that particular service.

When I start deployment of service1 in prod, I want to pick 1 server from DC1 and 1 server from DC2, run all the roles sequentially, and then pick the next 1-1 servers from each datacenter.

What's the best way to organize the inventory file and how should I set the host's value in the playbook to achieve this?

Basically, on two hosts at the same time, run a bunch of code in sequence.

Upvotes: 1

Views: 162

Answers (1)

Vladimir Botka
Vladimir Botka

Reputation: 68254

Symmetric groups

Use the module add_host and create dynamic inventory group. For example, given the inventory

shell> cat hosts
[dc1]
node_1
node_2
node_3

[dc2]
node_4
node_5
node_6

The first play will create the inventory group rolling_update. The second play will use it

shell> cat pb.yml
- hosts: all
  tasks:
    - add_host:
        name: "{{ item }}"
        groups: rolling_update
      loop: "{{ groups.dc1|zip(groups.dc2)|flatten }}"
      run_once: true
    
- hosts: rolling_update
  serial: 2
  tasks:
    - debug:
        var: inventory_hostname

gives (abridged)

shell> ansible-playbook pb.yml 

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

TASK [add_host] ******************************************************************************
changed: [node_1] => (item=node_1)
changed: [node_1] => (item=node_4)
changed: [node_1] => (item=node_2)
changed: [node_1] => (item=node_5)
changed: [node_1] => (item=node_3)
changed: [node_1] => (item=node_6)

PLAY [rolling_update] ************************************************************************

TASK [debug] *********************************************************************************
ok: [node_4] => 
  inventory_hostname: node_4
ok: [node_1] => 
  inventory_hostname: node_1

PLAY [rolling_update] ************************************************************************

TASK [debug] *********************************************************************************
ok: [node_2] => 
  inventory_hostname: node_2
ok: [node_5] => 
  inventory_hostname: node_5

PLAY [rolling_update] ************************************************************************

TASK [debug] *********************************************************************************
ok: [node_3] => 
  inventory_hostname: node_3
ok: [node_6] => 
  inventory_hostname: node_6
  ...

See:


Asymmetric groups

If the groups are not symmetric, for example

shell> cat hosts
[dc1]
node_1
node_2
node_3

[dc2]
node_4
node_5

the filter zip won't exhaust the lists. The result will be (abridged)

PLAY [rolling_update] ************************************************************************

TASK [debug] *********************************************************************************
ok: [node_1] => 
  inventory_hostname: node_1
ok: [node_4] => 
  inventory_hostname: node_4

PLAY [rolling_update] ************************************************************************

TASK [debug] *********************************************************************************
ok: [node_2] => 
  inventory_hostname: node_2
ok: [node_5] => 
  inventory_hostname: node_5

If you want to run the play for all hosts use the filter zip_longest

      loop: "{{ groups.dc1|zip_longest(groups.dc2)|flatten }}"

Then, the result will be (abridged)

PLAY [rolling_update] ************************************************************************

TASK [debug] *********************************************************************************
ok: [node_1] => 
  inventory_hostname: node_1
ok: [node_4] => 
  inventory_hostname: node_4

PLAY [rolling_update] ************************************************************************

TASK [debug] *********************************************************************************
ok: [node_2] => 
  inventory_hostname: node_2
ok: [node_5] => 
  inventory_hostname: node_5

PLAY [rolling_update] ************************************************************************

TASK [debug] *********************************************************************************
ok: [node_3] => 
  inventory_hostname: node_3

General solution

Given the hosts

shell> cat hosts
[dc1]
node_1

[dc2]
node_4
node_5
node_6

the result will be (abridged)

PLAY [rolling_update] ************************************************************************

TASK [debug] *********************************************************************************
ok: [node_1] => 
  inventory_hostname: node_1
ok: [node_4] => 
  inventory_hostname: node_4

PLAY [rolling_update] ************************************************************************

TASK [debug] *********************************************************************************
ok: [node_5] => 
  inventory_hostname: node_5
ok: [node_6] => 
  inventory_hostname: node_6

If you always want to run the play for only one host from each group create two dynamic inventory groups

- hosts: all

  vars:

    ru1: "{{ groups.dc1|zip(groups.dc2)|flatten }}"
    idx: "{{ ru1|length - 1 }}"
    ru2: "{{ groups.dc1[idx|int:]|symmetric_difference(groups.dc2[idx|int:]) }}"

  tasks:

    - block:

        - debug:
            msg: |
              ru1: {{ ru1 }}
              ru2: {{ ru2 }}

        - add_host:
            name: "{{ item }}"
            groups: rolling_update1
          loop: "{{ ru1 }}"

        - add_host:
            name: "{{ item }}"
            groups: rolling_update2
          loop: "{{ ru2 }}"

      run_once: true
    
- hosts: rolling_update1
  serial: 2
  tasks:
    - debug:
        var: inventory_hostname
    
- hosts: rolling_update2
  serial: 1
  tasks:
    - debug:
        var: inventory_hostname

Then, the result will be (abridged)

PLAY [rolling_update1] ***********************************************************************

TASK [debug] *********************************************************************************
ok: [node_4] => 
  inventory_hostname: node_4
ok: [node_1] => 
  inventory_hostname: node_1

PLAY [rolling_update2] ***********************************************************************

TASK [debug] *********************************************************************************
ok: [node_5] => 
  inventory_hostname: node_5

PLAY [rolling_update2] ***********************************************************************

TASK [debug] *********************************************************************************
ok: [node_6] => 
  inventory_hostname: node_6

If you run this playbook on symmetric groups you'll see the warning

[WARNING]: Could not match supplied host pattern, ignoring: rolling_update2

[TODO: get rid of the warning]

Upvotes: 2

Related Questions