更改变量后执行一次任务

时间:2019-04-09 14:12:13

标签: docker ansible docker-swarm

我有一个剧本,它在服务器(整个环境)中寻找特定的容器,然后在具有所需服务的服务器上获取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台服务器上,在上述配置下,它将执行5次。

我需要什么:最后一个任务应该在任何满足“标识已更改”条件的服务器上执行一次。

run_once: yes总是在列表中的第一台主机上执行任务,因此,如果第一台主机将具有理想的状态,它将可以正确执行,这将是随机的;如果没有,Playbook将以错误结尾-第一台主机没有理想的变量(id.stdout)

1 个答案:

答案 0 :(得分:1)

似乎您的第一个挑战是找到正在其上运行服务的主机。我可能会使用Ansible的group_by模块来创建符合您条件的动态主机组,如下所示:

---
- 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中用于格式化的语法与Ansible中用于Jinja表达式的语法相同。因此,如果您编写{{ something }},Ansible将尝试将其解释为Jinja表达式。使用{%raw}...{%endraw%}可以使我们以无法被Ansible捕获的方式编写{{

  2. 我写了{{ not (not service_check.stdout) }}是因为service_check.stdout是一个字符串,我想要一个布尔值。评估为布尔值的空字符串为false,非空字符串为true。因此,如果not service_check.stdout有内容,则表达式falsestdout,如果为空,则表达式true{{ true if service_check.stdout else false }}。我想要相反的内容,因此我们再次否定该表达式。

    老实说,我本来可以写ON (c.CPT = f.CPT AND c.mod = f.mod) OR (c.CPT = f.CPT AND f.mod ='xx' AND f.CPT NOT IN (SELECT CPT FROM Fees WHERE mod<>'xx')),这可能更清楚。