выполнить задачу один раз при изменении переменной - PullRequest
0 голосов
/ 09 апреля 2019

У меня есть playbook, который ищет конкретный контейнер на серверах (вся среда), а затем получает контейнер Docker ID на серверах, который имеет желаемый сервис. Последний шаг - команда exec bash внутри контейнера. Мой код:

    shell: docker ps | grep '{{service}}:' 
    register: ps
    changed_when: ps.stdout != ""

  - name: get id container with {{service}}
    shell: docker ps | grep '{{service}}:' | awk '{print $1}'
    register: id
    when:  ps is changed

  - name: alembic upgrade head exec
    shell: docker exec -i {{id.stdout}} bash -c 'pwd'
    register: pwd
    when: id is changed
  - debug: var=pwd.stdout_lines
    when: id is changed

Выход:

PLAY [dev2] ******************************************************************************************************************************************************************************************************************************

TASK [Gathering Facts] *******************************************************************************************************************************************************************************************************************
ok: [dev2_3]
ok: [dev2_4]
ok: [dev2_1]

TASK [search server with graphql] ********************************************************************************************************************************************************************************************************
ok: [dev2_1]
changed: [dev2_3]
changed: [dev2_4]

TASK [get id container with graphql] *****************************************************************************************************************************************************************************************************
skipping: [dev2_1]
changed: [dev2_3]
changed: [dev2_4]

TASK [alembic upgrade head exec] *********************************************************************************************************************************************************************************************************
skipping: [dev2_1]
changed: [dev2_3]
changed: [dev2_4]

TASK [debug] *****************************************************************************************************************************************************************************************************************************
skipping: [dev2_1]
ok: [dev2_3] => {
    "pwd.stdout_lines": [
        "/usr/src/app"
    ]
}
ok: [dev2_4] => {
    "pwd.stdout_lines": [
        "/usr/src/app"
    ]
}

Проблема в том, что если у вас есть группа хостов с 10 серверами, то желаемое обслуживание будет на 5 серверах, на вышеупомянутой конфигурации это будет выполняться пять раз.

Что мне нужно: последняя задача, которую она должна быть выполнена один раз, на любом сервере, который соответствует условию "id is change"

run_once: yes всегда выполнять задачу на первом хосте из списка, так что это случайный случай, если первый хост будет иметь желаемый статус, он будет выполнен правильно, если нет, playbook закончится с ошибкой - первый хост не имеет желаемой переменной (id .stdout)

1 Ответ

1 голос
/ 09 апреля 2019

Похоже, что вашей первой проблемой является поиск хостов, на которых работает ваш сервис.Я мог бы использовать модуль group_by Ansible для создания динамической группы хостов, которые соответствуют вашим критериям, например:

---
- hosts: all
  gather_facts: false
  tasks:
    - name: check host for target service
      become: true
      command: "docker ps --filter name={{ service }} --format '{%raw%}{{{%endraw%} .ID }}'"
      register: service_check

    - set_fact:
        has_target_service: "{{ not (not service_check.stdout) }}"
        container_id: "{{ service_check.stdout }}"

    - group_by:
        key: "has_service_{{ has_target_service }}"

Это создаст две группы, has_service_True для хостов, на которых запущена ваша целевая служба, иhas_service_False для хостов, которых нет.Он также установит факт container_id на хостах, на которых запущена ваша целевая служба.

Затем вы можете написать новую игру для обработки обновления базы данных и использовать директиву run_once, чтобы убедиться, что онаработает только на одном хосте:

- hosts: has_service_True
  gather_facts: false
  tasks:
    - name: alembic upgrade head exec
      run_once: true
      shell: docker exec -i {{container_id}} bash -c 'alembic upgrade command'

Ответы на комментарии

  1. Проблема в том, что синтаксис, используемый для форматирования в Docker, одинаковсинтаксис, используемый для выражений Jinja в Ansible.Поэтому, если вы напишите {{ something }}, Ansible попытается интерпретировать это как выражение Jinja.Использование {%raw}...{%endraw%} позволяет нам писать {{ таким образом, чтобы оно не было захвачено Ansible.

  2. Я написал {{ not (not service_check.stdout) }}, потому что service_check.stdout - строка, иЯ хотел логическое значение.Пустая строка, оцененная как логическое значение, равна false, а непустая строка - true.Следовательно, выражение not service_check.stdout будет false, если stdout имеет содержимое, и true, если оно пустое.Я хотел обратного, поэтому мы снова отрицали выражение.

    Честно говоря, я мог бы написать вместо {{ true if service_check.stdout else false }}, что, вероятно, более понятно.

...