0
votes

The scenario is: I have several services running on several hosts. There is one special service - the reverseproxy/loadbalancer. Any service needs to configure that special service on the host, that runs the rp/lp service. During installation/update/deletion of a random service with an Ansible role, I need to call the ReverseProxy role on the specific host to configure the corresponding vhost.

At the moment I call a specific task file in the reverse proxy role to add or remove a vhost by the service with include_role and set some vars (very easy example without service and inventory specific vars).

- name: "Configure ReverseProxy"
  include_role:
    name: reverseproxy
    tasks_from: vhost_add
    apply:
      delegate_to: "{{ groups['reverseproxy'][0] }}"
  vars:
    reverse_proxy_url: "http://{{ ansible_fqdn }}:{{ service_port }}/"
    reverse_proxy_domain: "sub.domain.tld"

I have three problems.

  • I know, it's not a good idea to build such dependencies between roles and different hosts. I don't know a better way, especially if you think about the situation, where you need to do some extra stuff after creating the vhost (f.e. configure the service via REST API, which needs the external fqdn). In case of two separate playbooks with "backend"-service and "reverseproxy"-service - then I need a third playbook for configuring "hanging" services. Also I'm not sure, if I can retrieve the correct backend URL in the reverse proxy role (only think about the HTTP scheme or paths). That sounds not easy, or?
  • Earlier I had separate roles for adding/removing vhosts to a reverseproxy. This roles didn't have dependencies, but I needed to duplicate several defaults and templates and vars etc. which isn't nice too. Then I've changed that to a single role. Of course - in my opinion, this isn't really that, what a "role" should be. A role is something like "webserver" or "reverseproxy" (a state). But not something like "add_vhost_to_reverseproxy" (a verb). This would be something like a playbook - but is calling a parameterized playbook via a role a good idea/possible? The main problem is, that the state of reverseproxy is the sum of all services in the inventory.
  • In case of that single included role, including it, starts also all dependent roles (configure custom, firewall, etc.). Nevertheless in that case I found out, that the delegation did not use the facts of the delegated host.

I tested that with the following example - the inventory:

all:
  hosts:
    server1:
      my_var: a
    server2:
      my_var: b
  children:
    service:
      hosts:
        server1:
    reverseproxy:
      hosts:
        server2:

And playbook which assigns a role-a to the group webserver. The role-a has a task like:

- block:
    - setup:
    - name: "Include role b on delegated {{ groups['reverseproxy'][0] }}"
      include_role:
        name: role-b
  delegate_to: "{{ groups['reverseproxy'][0] }}"
  delegate_facts: true # or false or omit - it has no effect on Ansible 2.9 and 2.10

And in role-b only outputing the my_var of the inventory will output

TASK [role-b : My_Var on server1] *******************
ok: [server1 -> <ip-of-server2>] => 
  my_var: a

Which says me, that role-b that should be run on server2 has the facts of server1. So - configuring the "reverseproxy" service is done in context of the "backend"-service. Which would have several other issues - when you think about firewall-dependencies etc. I can avoid that, by using tags - but then I need to run the playbook not just with the tag of the service, but also with all tags I want to configure, and I cannot use include_tasks with args-apply-tags anymore inside a role that also includes other roles (the tags will applied to all subtasks...). I miss something like include_role but only that specific tags or ignore dependencies. This isn't a bug, but has possible side effects in case of delegate_to.

I'm not really sure, what is the question? The question is - what is a good way to handle dependencies between hosts and roles in Ansible - especially when they are not on the same host?

1
This is way too broad and your question will likely be closed. Just to try to put you on track: try to rethink the problem as if the RP/LB was not in your inventory. You will need an interface (API, file share, mailbox, carrier pigeon....) to access it.Zeitounator

1 Answers

0
votes

I am sure I do not fully understand your exact problem, but when I was dealing with load balancers I used a template. So this was my disable_workers playbook:

---
- hosts: "{{ ip_list | default( 'jboss' ) }}"
  tasks:
  - name: Tag JBoss service as 'disabled'
    ec2_tag:
      resource: "{{ ec2_id }}"
      region: "{{ region }}"
      state: present
      tags:
        State: 'disabled'
    delegate_to: localhost

  - action: setup

- hosts: httpd
  become: yes
  become_user: root
  vars:
    uriworkermap_file: "{{ httpd_conf_dir }}/uriworkermap.properties"
  tasks:
 
  - name: Refresh inventory cache
    ec2_remote_facts:
      region: "{{ region }}"
    delegate_to: localhost

  - name: Update uriworkermap.properties
    template:
      backup: yes
      dest: "{{ uriworkermap_file }}"
      mode: 0644
      src: ./J2/uriworkermap.properties.j2

Do not expect this to work as-is. It was v1.8 on AWS hosts, and things may have changed.

But the point is to set user-defined facts, on each host, for that host's desired state (enabled, disabled, stopped), reload the facts, and then run the Jinja template that uses those facts.