我有一个剧本,它在服务器(整个环境)中寻找特定的容器,然后在具有所需服务的服务器上获取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)
答案 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'
评论的答案
问题在于Docker中用于格式化的语法与Ansible中用于Jinja表达式的语法相同。因此,如果您编写{{ something }}
,Ansible将尝试将其解释为Jinja表达式。使用{%raw}...{%endraw%}
可以使我们以无法被Ansible捕获的方式编写{{
。
我写了{{ not (not service_check.stdout) }}
是因为service_check.stdout
是一个字符串,我想要一个布尔值。评估为布尔值的空字符串为false
,非空字符串为true
。因此,如果not service_check.stdout
有内容,则表达式false
为stdout
,如果为空,则表达式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'))
,这可能更清楚。