变量更改时执行任务一次



我有剧本,在服务器(整个环境)上寻找特定的容器,然后在具有理想服务的服务器上获取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已更改"条件的服务器上执行一次

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

看起来您的第一个挑战是查找运行服务的主机。我可能会使用 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有内容,则表达式stdoutfalse,如果为空,则true表达式。 我想要相反的,所以我们再次否定了这个表达。

    老实说,我本可以写{{ true if service_check.stdout else false }},这可能更清楚。

最新更新