Ansible 包含


Ansible 包含

##一、什么是 include?

在日常编程中,我们会将通用、公用的代码片段抽离出来,以便于解耦和复用。而 ansible 中也有类似的思想体现,那就是includeinclude_varsinclude_tasks 以及 import_tasks,我们先看下本文大致内容

二、include 关键字

include tasks 任务

通过 include 我们可以对剧本的部分进行抽离,比如,以下两个剧本:

playbook-1

---
- hosts: ecs[0]
  gather_facts: no
  tasks:
    - name: "打印开始信息"
      debug:
        msg: "【{{ '%Y-%m-%d %H:%M:%S' | strftime }}】脚本开始执行,当前主机:{{ inventory_hostname }}..."
    - name: "创建 t3 文件"
      file:
        path: /tmp/testdir/t3.txt
        state: touch

playbook-2

---
- hosts: ecs[0]
  gather_facts: no
  tasks:
    - name: "打印开始信息"
      debug:
        msg: "【{{ '%Y-%m-%d %H:%M:%S' | strftime }}】脚本开始执行,当前主机:{{ inventory_hostname }}..."
    - name: "创建 t4 文件"
      file:
        path: /tmp/testdir/t4.txt
        state: touch

抽离通用的 tasktasks/tasks-demo1.yml

---
- name: "打印开始信息"
  debug:
    msg: "【{{ '%Y-%m-%d %H:%M:%S' | strftime }}】脚本开始执行,当前主机:{{ inventory_hostname }}..."

剧本定义

---
- hosts: ecs[0]
  gather_facts: no
  tasks:
    # 包含引入 task 定义文件
    - include: tasks/tasks-demo1.yml
    - name: "创建 t3 文件"
      file:
        path: /tmp/testdir/t3.txt
        state: touch

当使用 include 模块引用对应的文件时,文件中的任务会在被引用处执行,就如同写在被引用处一样

执行效果

$ ansible-playbook playbook-include-demo1-0.yml
PLAY [ecs[0]] ******
TASK [打印开始信息] ******
ok: [ecs-1.aliyun.sz] => {
    "msg": "【2021-10-11 19:57:33】脚本开始执行,当前主机:ecs-1.aliyun.sz..."
}
TASK [创建 t3 文件] ******
changed: [ecs-1.aliyun.sz]
PLAY RECAP ******
ecs-1.aliyun.sz            : ok=2    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0

OK,可以看到总工作执行了两个任务,第一个任务是从 tasks 文件中 include 进来,第二个任务是剧本 tasks 关键字定义的,也就是说 include 关键字本身并不算是一个任务,这点需要注意,后续 include_tasks 时会进行对比说明~

include handlers 回调

include 除了可以用在 tasks 字段,还可以用在 handlers,关于 handler 大致内容

OK,复习完了以后开始编写剧本

handler 定义

---
- name: "create file handler"
  debug:
    msg: "【{{ '%Y-%m-%d %H:%M:%S' | strftime }}】文件创建完成,当前主机:{{ inventory_hostname }}..."

剧本定义

---
- hosts: ecs[0]
  gather_facts: no
  tasks:
    # 包含引入 task 定义文件
    - include: tasks/tasks-demo1.yml
    - name: "创建文件"
      file:
        path: /tmp/testdir/t3.txt
        state: touch
        # 直接通知 handler
      notify: "create file handler"
  handlers:
    # 包含引入 handler 定义文件
    - include: tasks/tasks-demo1-1.yml

执行效果

$ ansible-playbook playbook-include-demo1-1.yml
PLAY [ecs[0]] ******
TASK [打印开始信息] ******
ok: [ecs-1.aliyun.sz] => {
    "msg": "【2021-10-10 09:34:56】脚本开始执行,当前主机:ecs-1.aliyun.sz..."
}
TASK [创建文件] ******
changed: [ecs-1.aliyun.sz]
RUNNING HANDLER [create file handler] ******
ok: [ecs-1.aliyun.sz] => {
    "msg": "【2021-10-10 09:34:57】文件创建完成,当前主机:ecs-1.aliyun.sz..."
}
PLAY RECAP ******
ecs-1.aliyun.sz            : ok=3    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0

include 参数化包含任务

任务定义

---
- name: "变量输出"
  debug:
    msg: "【{{ '%Y-%m-%d %H:%M:%S' | strftime }}】变量输出,username:{{ username }},gender:{{ gender }}"

剧本定义

---
- hosts: ecs[0]
  gather_facts: no
  tasks:
    # 包含引入 task 定义文件
    - include: tasks/tasks-demo1-2.yml
      vars:
        username: "Da"
        gender: "Male"
    - name: "创建文件"
      file:
        path: /tmp/testdir/t3.txt
        state: touch

include 给”包含”打tags

之前我们学过 ansible tags,它主要是用来在执行剧本时,跳过或执行包含某个标识的任务,先复习下:

tags 同样可以应用于 include,如下所示:

---
- hosts: ecs[0]
  gather_facts: no
  tasks:
    # 包含引入 task 定义文件
    - include: tasks/tasks-demo1.yml
      tags: print-start-info
    - include: tasks/tasks-demo1-2.yml
      tags: print-vars
      vars:
        username: "Da"
        gender: "Male"
    - name: "创建文件"
      tags: always
      file:
        path: /tmp/testdir/t3.txt
        state: touch

执行时,我们只执行包含 print-var 标签的,由于 创建文件 任务包含 always 标签,所以即使不指明,它还是会被运行的,效果如下

$ ansible-playbook --tags=print-vars playbook-include-demo1-3.yml
PLAY [ecs[0]] ******
TASK [变量输出] ******
ok: [ecs-1.aliyun.sz] => {
    "msg": "【2021-10-10 10:03:26】变量输出,username:Da,gender:Male"
}
TASK [创建文件] ******
changed: [ecs-1.aliyun.sz]
PLAY RECAP ******
ecs-1.aliyun.sz            : ok=2    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0

跳过指定标签

$ ansible-playbook --skip-tags=print-vars,print-start-info  playbook-include-demo1-3.yml
PLAY [ecs[0]] ******
TASK [创建文件] ******
changed: [ecs-1.aliyun.sz]
PLAY RECAP ******
ecs-1.aliyun.sz            : ok=1    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0

列出标签

$ ansible-playbook --list-tags playbook-include-demo1-3.yml
playbook: playbook-include-demo1-3.yml
  play #1 (ecs[0]): ecs[0]	TAGS: []
      TASK TAGS: [always, print-start-info, print-vars]

include 条件判断

用来条件判断的 when 关键字,也可以用在 include 中,老规矩先复习下

剧本定义

---
- hosts: ecs[0]
  gather_facts: yes
  tasks:
    # 包含引入 task 定义文件
    - include: tasks/tasks-demo1.yml
      tags: print-start-info
      when: ansible_distribution == "CentOS" and ansible_distribution_major_version == "7"
    - name: "创建文件"
      tags: always
      file:
        path: /tmp/testdir/t3.txt
        state: touch

执行效果

$ ansible-playbook playbook-include-demo1-4.yml
PLAY [ecs[0]] ******
TASK [Gathering Facts] ******
ok: [ecs-1.aliyun.sz]
TASK [打印开始信息] ******
ok: [ecs-1.aliyun.sz] => {
    "msg": "【2021-10-10 10:20:36】脚本开始执行,当前主机:ecs-1.aliyun.sz..."
}
TASK [创建文件] ******
changed: [ecs-1.aliyun.sz]
PLAY RECAP ******
ecs-1.aliyun.sz            : ok=3    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0

include 循环

既然都支持了 参数化、标签、判断,那怎么少得了循环呢,复习下

复习完了后,我们先思考下,loop 在此处的发挥的作用是什么呢?

其实不难理解,那就是 “迭代 可执行对象 将参数传递给 include 循环执行” ,这句话可能有点绕,不过看了小例子就明白了

任务定义

---
- name: "Tasks-A"
  debug:
    msg: "【{{ '%Y-%m-%d %H:%M:%S' | strftime }}】变量输出,item:{{ item }}---Task-A"
- name: "Tasks-B"
  debug:
    msg: "【{{ '%Y-%m-%d %H:%M:%S' | strftime }}】变量输出,item:{{ item }}---Task-B"

剧本定义

---
- hosts: ecs[0]
  gather_facts: no
  tasks:
    # 包含引入 task 定义文件
    - include: tasks/tasks-demo1-5.yml
      # 迭代列表 [1, 2] 循环包含 include 默认会将 item 参数传递过去
      loop: [1, 2]

执行效果

$ ansible-playbook playbook-include-demo1-5.yml
PLAY [ecs[0]] ******
TASK [include] ******
included: /prodata/scripts/ansibleLearn/tasks/tasks-demo1-5.yml for ecs-1.aliyun.sz
included: /prodata/scripts/ansibleLearn/tasks/tasks-demo1-5.yml for ecs-1.aliyun.sz
# 第一次导入,传入 item 变量 = 1
TASK [Tasks-A] ******
ok: [ecs-1.aliyun.sz] => {
    "msg": "【2021-10-10 10:30:38】变量输出,item:1---Task-A"
}
TASK [Tasks-B] ******
ok: [ecs-1.aliyun.sz] => {
    "msg": "【2021-10-10 10:30:38】变量输出,item:1---Task-B"
}
# 第二次导入,传入 item 变量 = 2
TASK [Tasks-A] ******
ok: [ecs-1.aliyun.sz] => {
    "msg": "【2021-10-10 10:30:38】变量输出,item:2---Task-A"
}
TASK [Tasks-B] ******
ok: [ecs-1.aliyun.sz] => {
    "msg": "【2021-10-10 10:30:38】变量输出,item:2---Task-B"
}
PLAY RECAP ******
ecs-1.aliyun.sz            : ok=6    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0

看到这不禁又产生一个疑惑,loop 将元素通过 item 传递给 include 中的 task,那假如 task 本身也有 loop,出现这种 ”双层循环” 的情况时,task 中的 item 信息到底是什么呢?试试不就知道了嘛~

任务定义

---
- name: "Include-Tasks"
  debug:
    msg: "【{{ '%Y-%m-%d %H:%M:%S' | strftime }}】变量输出,item:{{ item }}"
  loop: [inner-a, inner-b]

剧本定义

---
- hosts: ecs[0]
  gather_facts: no
  tasks:
    # 包含引入 task 定义文件
    - include: tasks/tasks-demo1-6.yml
      # 迭代列表 [outer-1, outer-2] 循环包含 include 默认会将 item 参数传递过去
      loop: [outer-1, outer-2]

执行效果

$ ansible-playbook playbook-include-demo1-6.yml
PLAY [ecs[0]] ******
TASK [include] ******
included: /prodata/scripts/ansibleLearn/tasks/tasks-demo1-6.yml for ecs-1.aliyun.sz
included: /prodata/scripts/ansibleLearn/tasks/tasks-demo1-6.yml for ecs-1.aliyun.sz
TASK [Include-Tasks] ******
[WARNING]: The loop variable 'item' is already in use. You should set the `loop_var` value in the `loop_control` option for the task to something
else to avoid variable collisions and unexpected behavior.
ok: [ecs-1.aliyun.sz] => (item=inner-a) => {
    "msg": "【2021-10-10 12:45:03】变量输出,item:inner-a"
}
ok: [ecs-1.aliyun.sz] => (item=inner-b) => {
    "msg": "【2021-10-10 12:45:03】变量输出,item:inner-b"
}
TASK [Include-Tasks] ******
[WARNING]: The loop variable 'item' is already in use. You should set the `loop_var` value in the `loop_control` option for the task to something
else to avoid variable collisions and unexpected behavior.
ok: [ecs-1.aliyun.sz] => (item=inner-a) => {
    "msg": "【2021-10-10 12:45:03】变量输出,item:inner-a"
}
ok: [ecs-1.aliyun.sz] => (item=inner-b) => {
    "msg": "【2021-10-10 12:45:03】变量输出,item:inner-b"
}
PLAY RECAP ******
ecs-1.aliyun.sz            : ok=4    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0

返回信息中包含一段非常醒目的警告

[WARNING]: The loop variable ‘item’ is already in use. You should set the loop_var value in the loop_control option for the task to something else to avoid variable collisions and unexpected behavior.

提示 item 已经被使用了,建议使用 loop_control 关键字设置 loop_var 变量,以避免变量冲突产生意外的问题

OK,我们处理下剧本和任务

---
- name: "Include-Tasks"
  debug:
    msg: "【{{ '%Y-%m-%d %H:%M:%S' | strftime }}】变量输出,item:{{outer_item}}-{{ item }}"
  loop: [inner-a, inner-b]

剧本定义

---
- hosts: ecs[0]
  gather_facts: no
  tasks:
    # 包含引入 task 定义文件
    - include: tasks/tasks-demo1-6.yml
      # 迭代列表 [1, 2] 循环包含 include 默认会将 item 参数传递过去
      loop: [outer-1, outer-2]
        loop_control:
          loop_var: outer_item

执行效果

$ ansible-playbook playbook-include-demo1-6.yml
PLAY [ecs[0]] ******
TASK [include] ******
included: /prodata/scripts/ansibleLearn/tasks/tasks-demo1-6.yml for ecs-1.aliyun.sz
included: /prodata/scripts/ansibleLearn/tasks/tasks-demo1-6.yml for ecs-1.aliyun.sz
TASK [Include-Tasks] ******
ok: [ecs-1.aliyun.sz] => (item=inner-a) => {
    "msg": "【2021-10-10 12:53:08】变量输出,item:outer-1-inner-a"
}
ok: [ecs-1.aliyun.sz] => (item=inner-b) => {
    "msg": "【2021-10-10 12:53:08】变量输出,item:outer-1-inner-b"
}
TASK [Include-Tasks] ******
ok: [ecs-1.aliyun.sz] => (item=inner-a) => {
    "msg": "【2021-10-10 12:53:08】变量输出,item:outer-2-inner-a"
}
ok: [ecs-1.aliyun.sz] => (item=inner-b) => {
    "msg": "【2021-10-10 12:53:08】变量输出,item:outer-2-inner-b"
}
PLAY RECAP ******
ecs-1.aliyun.sz            : ok=4    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0

不过,include 关键字在 ansible 2.4 版本标记为 deprecated 状态,并且相关功能也于 2.8 版本后逐个移除,主要原因是 include 的功能太混杂了,什么 tasks、handlers、play,ansible 官方认为 include 的功能需要进一步的细化拆分,到目前为止,include 功能拆分为以下几个关键字:

或许你会有些疑惑,为什么出现两个看起来有些类似的关键字,“include_tasks” 与 “import_tasks” 有什么区别?这个问题涉及到了 动态导入静态导入 ,我们暂时按下不谈,后面会详细讨论二者的区别与如何选择

除此外,还有其他几个相关的用于 包含 & 导入 的关键字:

var_files、include_vars 是我们在 playbook-变量 小节已经了解过的内容,所以不再赘述,其他的关键字我们会在本节或后续的内容中一一说明

三、include_vars 关键字

详见 Ansible Playbook 变量——include_vars

四、include_tasks 关键字

include_tasks 包含任务

了解了 include 的用法,再学习 include_tasks 就很简单了,首先定义一个任务

---
- name: 打印开始信息
  debug:
    msg: "【{{ '%Y-%m-%d %H:%M:%S' | strftime }}】脚本开始执行,当前主机:{{ inventory_hostname }}..."

然后,在 playbook 中,我们可以在 tasks 字段使用 include_tasks 指令引入文件中的任务列表

---
- hosts: ecs[0]
  gather_facts: no
  tasks:
    # include_tasks 包含引入 task 定义文件
    - include_tasks: tasks/tasks-demo1.yml
    - name: "创建文件"
      file:
        path: /tmp/testdir/t3.txt
        state: touch

执行效果

$ ansible-playbook playbook-include_tasks-demo1-1.yml
PLAY [ecs[0]] ******
# 第一个任务:include_tasks 包含 tasks-demo1 文件中的 tasks
TASK [include_tasks] ******
included: /prodata/scripts/ansibleLearn/tasks/tasks-demo1.yml for ecs-1.aliyun.sz
# 第二个任务:执行 tasks-demo1 文件中的 “打印开始信息” 任务
TASK [打印开始信息] ******
ok: [ecs-1.aliyun.sz] => {
    "msg": "【2021-10-11 19:46:54】脚本开始执行,当前主机:ecs-1.aliyun.sz..."
}
# 第三个任务:执行 playbook 中的 “创建文件” 任务
TASK [创建文件] ******
changed: [ecs-1.aliyun.sz]
PLAY RECAP ******
ecs-1.aliyun.sz            : ok=3    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0

喏,可以看到与 include 不同,include_tasks 本身也会被当作一次 “任务” 执行,正是因为 include_tasks 关键字被当作认作 task 所以在 tags 的处理上,两者截然不同,后续在 include_tasks 的 tags 使用上详细说明

include_tasks 回调

上面我们提到了 include_tasks 关键字本身会当作任务执行,当它在 handlers 关键字中使用时也与 include 有所不同,具体如下:

此前我们的使用方式:

tasks:
  # 包含引入 task 定义文件
  - include: tasks/tasks-demo1.yml
  - name: "创建文件"
    file:
      path: /tmp/testdir/t3.txt
      state: touch
      # 直接通知文件里某个 handler task
    notify: "create file handler"
handlers:
  # 包含引入 handler 定义文件
  - include: tasks/tasks-demo1-1.yml

换做 include_tasks 关键字看看会发生什么?

---
- hosts: ecs[0]
  gather_facts: no
  tasks:
    - include_tasks: tasks/tasks-demo1.yml
    - name: "创建文件"
      file:
        path: /tmp/testdir/t3.txt
        state: touch
      notify: "create file handler"
  handlers:
    - include_tasks: tasks/tasks-demo1-1.yml

执行效果

$ ansible-playbook playbook-include_tasks-demo1-2.yml
PLAY [ecs[0]] ******
TASK [include_tasks] ******
included: /prodata/scripts/ansibleLearn/tasks/tasks-demo1.yml for ecs-1.aliyun.sz
TASK [打印开始信息] ******
ok: [ecs-1.aliyun.sz] => {
    "msg": "【2021-10-11 20:23:51】脚本开始执行,当前主机:ecs-1.aliyun.sz..."
}
TASK [创建文件] ******
ERROR! The requested handler 'create file handler' was not found in either the main handlers list nor in the listening handlers

错误的提示信息也比较醒目,既没有 handlers 列表中找到名为 create file handler 回调,也没有在 handlers 列表中找到谁在使用 listen 关键字监听 create file handler

知道错误原因,那处理思路也基本有了

  • 一、为 include_tasks task 命名
  • 二、在对应的 handler 上设置 listen 监听对应的回调事件

先看第一种处理思路:include_tasks task 命名

剧本定义

---
- hosts: ecs[0]
  gather_facts: no
  tasks:
    - include_tasks: tasks/tasks-demo1.yml
    - name: "创建文件"
      file:
        path: /tmp/testdir/t3.txt
        state: touch
      # 通知指定 handler task
      notify: "create file handler"
  handlers:
    # 为 include_tasks 这个 task 命名
    - name: "create file handler"
      include_tasks: tasks/tasks-demo1-1.yml

执行效果

$ ansible-playbook playbook-include_tasks-demo1-2.yml
PLAY [ecs[0]] ******
# 第一个任务:include_tasks 包含 tasks-demo1 文件中的 tasks
TASK [include_tasks] ******
included: /prodata/scripts/ansibleLearn/tasks/tasks-demo1.yml for ecs-1.aliyun.sz
# 第二个任务:执行 tasks-demo1 文件中的 “打印开始信息” 任务
TASK [打印开始信息] ******
ok: [ecs-1.aliyun.sz] => {
    "msg": "【2021-10-11 20:30:13】脚本开始执行,当前主机:ecs-1.aliyun.sz..."
}
# 第三个任务:执行 playbook 中的 “创建文件” 任务
TASK [创建文件] ******
changed: [ecs-1.aliyun.sz]
# 运行回调:包含任务文件
RUNNING HANDLER [create file handler] ******
included: /prodata/scripts/ansibleLearn/tasks/tasks-demo1-1.yml for ecs-1.aliyun.sz
# 运行回调:执行 create file handler 回调
RUNNING HANDLER [create file handler] ******
ok: [ecs-1.aliyun.sz] => {
    "msg": "【2021-10-11 20:30:14】文件创建完成,当前主机:ecs-1.aliyun.sz..."
}
PLAY RECAP ******
ecs-1.aliyun.sz            : ok=5    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0

第二个思路:listen 监听对应的回调事件

剧本定义

---
- hosts: ecs[0]
  gather_facts: no
  tasks:
    - include_tasks: tasks/tasks-demo1.yml
    - name: "创建文件"
      file:
        path: /tmp/testdir/t3.txt
        state: touch
      notify: "create file handler"
  handlers:
    - include_tasks: tasks/tasks-demo1-1.yml
      listen: "create file handler"

执行效果

$ ansible-playbook playbook-include_tasks-demo1-2.yml
PLAY [ecs[0]] ******
# 第一个任务:include_tasks 包含 tasks-demo1 文件中的 tasks
TASK [include_tasks] ******
included: /prodata/scripts/ansibleLearn/tasks/tasks-demo1.yml for ecs-1.aliyun.sz
# 第二个任务:执行 tasks-demo1 文件中的 “打印开始信息” 任务
TASK [打印开始信息] ******
ok: [ecs-1.aliyun.sz] => {
    "msg": "【2021-10-11 20:34:40】脚本开始执行,当前主机:ecs-1.aliyun.sz..."
}
# 第三个任务:执行 playbook 中的 “创建文件” 任务
TASK [创建文件] ******
changed: [ecs-1.aliyun.sz]
# 运行回调:包含任务文件
RUNNING HANDLER [include_tasks] ******
included: /prodata/scripts/ansibleLearn/tasks/tasks-demo1-1.yml for ecs-1.aliyun.sz
# 运行回调:执行 create file handler 回调
RUNNING HANDLER [create file handler] ******
ok: [ecs-1.aliyun.sz] => {
    "msg": "【2021-10-11 20:34:40】文件创建完成,当前主机:ecs-1.aliyun.sz..."
}
PLAY RECAP ******
ecs-1.aliyun.sz            : ok=5    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0

为了加强理解,我们看下下面的图:

include 关键字回调使用

include_tasks 关键字回调使用

当我们使用 notify 进行通知,由于使用的是 include_tasks 任务的名称,所以 include_tasks 这个任务包含进来的所有 tasks 都会顺序执行一遍

假如,我们只想执行 handler tasks 文件中某些特定任务可以吗?

可以,这就需要用到 tags 功能了~

include_tassk 参数化任务

我们先过下参数化任务,两者使用起来没区别

剧本定义

---
- hosts: ecs[0]
  gather_facts: no
  tasks:
    # 包含引入 task 定义文件
    - include_tasks: tasks/tasks-demo1-2.yml
      vars:
        username: "Da"
        gender: "Male"
    - name: "创建文件"
      file:
        path: /tmp/testdir/t3.txt
        state: touch

执行效果

$ ansible-playbook playbook-include_tasks-demo1-3.yml
PLAY [ecs[0]] ******
TASK [include_tasks] ******
included: /prodata/scripts/ansibleLearn/tasks/tasks-demo1-2.yml for ecs-1.aliyun.sz
TASK [变量输出] ******
ok: [ecs-1.aliyun.sz] => {
    "msg": "【2021-10-12 15:51:47】变量输出,username:Da,gender:Male"
}
TASK [创建文件] ******
changed: [ecs-1.aliyun.sz]
PLAY RECAP ******
ecs-1.aliyun.sz            : ok=3    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0

include_tasks 给”包含”打tags

上面我们在使用回调中遇到了一个问题,那就是如何执行 tasks file 中特定的任务,OK,我们现在看下处理方法

任务定义

---
- name: "Tasks-A"
  debug:
    msg: "【{{ '%Y-%m-%d %H:%M:%S' | strftime }}】Task-A"
  tags: tag-a
- name: "Tasks-B"
  debug:
    msg: "【{{ '%Y-%m-%d %H:%M:%S' | strftime }}】Task-B"
  tags: tag-b

剧本定义

---
- hosts: ecs[0]
  gather_facts: no
  tasks:
    - include_tasks: tasks/tasks-demo1-7.yml
      tags: tag1
    - name: "创建文件"
      file:
        path: /tmp/testdir/t3.txt
        state: touch
      notify: "create file handler"
  handlers:
    - name: "create file handler"
      include_tasks: tasks/tasks-demo1-1.yml
      # listen: "create file handler"

执行 task file 中的特定任务

假设我们只想执行剧本中所有任务以及 tasks-demo1-7 中 tags 为 tag-a 的任务,可以这么做

$ ansible-playbook --skip-tags=tag-b playbook-include_tasks-demo1-4.yml
PLAY [ecs[0]] ******
TASK [include_tasks] ******
included: /prodata/scripts/ansibleLearn/tasks/tasks-demo1-7.yml for ecs-1.aliyun.sz
TASK [Tasks-A] ******
ok: [ecs-1.aliyun.sz] => {
    "msg": "【2021-10-12 16:02:47】Task-A"
}
TASK [创建文件] ******
changed: [ecs-1.aliyun.sz]
RUNNING HANDLER [create file handler] ******
included: /prodata/scripts/ansibleLearn/tasks/tasks-demo1-1.yml for ecs-1.aliyun.sz
RUNNING HANDLER [create file handler] ******
ok: [ecs-1.aliyun.sz] => {
    "msg": "【2021-10-12 16:02:48】文件创建完成,当前主机:ecs-1.aliyun.sz..."
}
RUNNING HANDLER [create file handler2] ******
ok: [ecs-1.aliyun.sz] => {
    "msg": "【2021-10-12 16:02:48】文件创建完成,当前主机:ecs-1.aliyun.sz..."
}
PLAY RECAP ******
ecs-1.aliyun.sz            : ok=6    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0

跳过 tag-b 即可

执行 task file 中的所有任务

假若,我们只想运行 task file 中的所有任务,该怎么做呢?

简单,执行 playbook 时不加 tags 参数不就可以了嘛,OK,确实如此…

$ ansible-playbook  playbook-include_tasks-demo1-4.yml
PLAY [ecs[0]] ******
TASK [include_tasks] ******
included: /prodata/scripts/ansibleLearn/tasks/tasks-demo1-7.yml for ecs-1.aliyun.sz
###### 开始
# 执行所有包含进来的 task
TASK [Tasks-A] ******
ok: [ecs-1.aliyun.sz] => {
    "msg": "【2021-10-12 16:42:27】Task-A"
}
TASK [Tasks-B] ******
ok: [ecs-1.aliyun.sz] => {
    "msg": "【2021-10-12 16:42:27】Task-B"
}
###### 结束
TASK [创建文件] ******
changed: [ecs-1.aliyun.sz]
RUNNING HANDLER [create file handler] ******
included: /prodata/scripts/ansibleLearn/tasks/tasks-demo1-1.yml for ecs-1.aliyun.sz
RUNNING HANDLER [create file handler] ******
ok: [ecs-1.aliyun.sz] => {
    "msg": "【2021-10-12 16:42:27】文件创建完成,当前主机:ecs-1.aliyun.sz..."
}
PLAY RECAP ******
ecs-1.aliyun.sz            : ok=6    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0

可是试想下,如果我们使用 t1 标签,可是效果如何呢?

$ ansible-playbook --tags=tag1 playbook-include_tasks-demo1-4.yml
PLAY [ecs[0]] ******
TASK [include_tasks] ******
included: /prodata/scripts/ansibleLearn/tasks/tasks-demo1-7.yml for ecs-1.aliyun.sz
PLAY RECAP ******
ecs-1.aliyun.sz            : ok=1    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0

task-atask-b 两个任务并没有执行,这是为什么?

其实仔细想下,其中的原因不难理解,还是因为 include_tasks 本身就是一个任务,我们针对这个 task 设置 tag、使用 tag 的作用只会局限在它本身上,并不会附带执行它所 include 包含进来的 task

所以,这里引入一个参数,apply ,它接收两个参数 tagsbecome 后者是用来提升权限的,前者才是重头戏,直接看示例:

---
- hosts: ecs[0]
  gather_facts: no
  tasks:
    - include_tasks:
        file: tasks/tasks-demo1-7.yml
        apply:
          tags:
            - tag-a
            - tag-b
    - name: "创建文件"
      file:
        path: /tmp/testdir/t3.txt
        state: touch
      notify: "create file handler"
  handlers:
    - name: "create file handler"
      include_tasks: tasks/tasks-demo1-1.yml
      # listen: "create file handler"

我们尝试使用 tasks file 中的 标签进行执行

$ ansible-playbook --tags=tag-a,tag-b playbook-include_tasks-demo1-4.yml
PLAY [ecs[0]] ******
PLAY RECAP ******

还是不行,这是为什么?

再回顾下,这张复习导图

当使用 --tags 参数时,只会执行参数内的 task,这意味着 ansible 在执行时只会寻找包含标签 tag-atag-b 的任务

符合条件的任务有吗?有

包含进来了吗?包含…喔不,包含了,但没有完全包含

include_tasks 本身就是一个任务,假如它不本身没有被执行,那么内两个任务当然是没有包含进来啊,所以自然执行不了啊

OK,大致梳理清楚后,我们给 include_tasks 打上 tag

tasks:
  - include_tasks:
      file: tasks/tasks-demo1-7.yml
      apply:
        tags:
          - tag-a
          - tag-b
    tags: include-tag

再显示调用尝试下

$ ansible-playbook --tags=include-tag,tag-a,tag-b playbook-include_tasks-demo1-4.yml
PLAY [ecs[0]] ******
# 第一个任务:包含文件中的任务进来
TASK [include_tasks] ******
included: /prodata/scripts/ansibleLearn/tasks/tasks-demo1-7.yml for ecs-1.aliyun.sz
# 第二个任务:执行 Tasks-A
TASK [Tasks-A] ******
ok: [ecs-1.aliyun.sz] => {
    "msg": "【2021-10-12 16:30:58】Task-A"
}
# 第三个任务:执行 Tasks-A
TASK [Tasks-B] ******
ok: [ecs-1.aliyun.sz] => {
    "msg": "【2021-10-12 16:30:58】Task-B"
}
PLAY RECAP ******
ecs-1.aliyun.sz            : ok=3    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0

OK,达到预期效果,执行 tasks file 中所有任务

到目前,我们拎清了一个概念,如果要执行 tasks file 中的任务,那么一定要保证 include_tasks 本身是被执行的,所以,通常 include_tasks 我们都是设置为 always 的,如下所示:

tasks:
  - include_tasks:
      file: tasks/tasks-demo1-7.yml
      apply:
        tags:
          - tag-a
          - tag-b
    tags: always

这样,当我们执行时就不需要显式声明了

$ ansible-playbook --tags=tag-a,tag-b playbook-include_tasks-demo1-4.yml
PLAY [ecs[0]] ******
TASK [include_tasks] ******
included: /prodata/scripts/ansibleLearn/tasks/tasks-demo1-7.yml for ecs-1.aliyun.sz
TASK [Tasks-A] ******
ok: [ecs-1.aliyun.sz] => {
    "msg": "【2021-10-12 16:34:56】Task-A"
}
TASK [Tasks-B] ******
ok: [ecs-1.aliyun.sz] => {
    "msg": "【2021-10-12 16:34:56】Task-B"
}
PLAY RECAP ******
ecs-1.aliyun.sz            : ok=3    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0

些许迷思

1. –tag 执行所有包含进来的任务

当我试着使用 --tag= 执行包含特定 tag 的任务时,我发现其他的任务也会跟着执行,示例如下:

任务定义

---
- name: "Tasks-A"
  debug:
    msg: "【{{ '%Y-%m-%d %H:%M:%S' | strftime }}】Task-A"
  tags: tag-a
- name: "Tasks-B"
  debug:
    msg: "【{{ '%Y-%m-%d %H:%M:%S' | strftime }}】Task-B"
  tags: tag-b

剧本定义

---
- hosts: ecs[0]
  gather_facts: no
  tasks:
    - include_tasks:
        file: tasks/tasks-demo1-7.yml
        apply:
          tags:
            - tag-a
            - tag-b
      tags: always
    - name: "创建文件"
      file:
        path: /tmp/testdir/t3.txt
        state: touch
      notify: "create file handler"
  handlers:
    - name: "create file handler"
      include_tasks: tasks/tasks-demo1-1.yml
      # listen: "create file handler"

执行效果

$ ansible-playbook --tags=tag-a playbook-include_tasks-demo1-4.yml
PLAY [ecs[0]] ******
# 任务一
TASK [include_tasks] ******
included: /prodata/scripts/ansibleLearn/tasks/tasks-demo1-7.yml for ecs-1.aliyun.sz
# 任务二
TASK [Tasks-A] ******
ok: [ecs-1.aliyun.sz] => {
    "msg": "【2021-10-12 16:52:58】Task-A"
}
# 任务三
TASK [Tasks-B] ******
ok: [ecs-1.aliyun.sz] => {
    "msg": "【2021-10-12 16:52:58】Task-B"
}
PLAY RECAP ******
ecs-1.aliyun.sz            : ok=3    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0

从执行返回上看,一共执行了三个任务,前两个任务都是如何预期的,include_tasks 标签为 always 所以没的说,Tasks-A 标签为 tag-a 也没的说,可为啥 Tasks-B 也跟着执行了啊?

奇了怪了,难道是因为 apply.tags 参数写了 tag-a、tag-b 吗?那我修改下

- include_tasks:
    file: tasks/tasks-demo1-7.yml
    apply:
      tags:
        - tag-a
  tags: always

再尝试执行 --tags=tag-a

$ ansible-playbook --tags=tag-a playbook-include_tasks-demo1-4.yml
PLAY [ecs[0]] ******
TASK [include_tasks] ******
included: /prodata/scripts/ansibleLearn/tasks/tasks-demo1-7.yml for ecs-1.aliyun.sz
TASK [Tasks-A] ******
ok: [ecs-1.aliyun.sz] => {
    "msg": "【2021-10-12 16:55:50】Task-A"
}
TASK [Tasks-B] ******
ok: [ecs-1.aliyun.sz] => {
    "msg": "【2021-10-12 16:55:50】Task-B"
}
PLAY RECAP ******
ecs-1.aliyun.sz            : ok=3    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0

效果依旧~ 奇了怪了

更神奇的,假如我此时执行 --tags=tag-b 居然是成功的!!!

$ ansible-playbook --tags=tag-b playbook-include_tasks-demo1-4.yml
PLAY [ecs[0]] ******
TASK [include_tasks] ******
included: /prodata/scripts/ansibleLearn/tasks/tasks-demo1-7.yml for ecs-1.aliyun.sz
TASK [Tasks-B] ******
ok: [ecs-1.aliyun.sz] => {
    "msg": "【2021-10-12 17:05:07】Task-B"
}
PLAY RECAP ******
ecs-1.aliyun.sz            : ok=2    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0

这里做一个小梳理:

  • 当 apply.tags: [tag-a, tag-b],执行 --tags=tag-a 所有包含任务都执行
  • 当 apply.tags: [tag-a] ,执行 --tags=tag-a 所有包含任务都执行
  • 当 apply.tags: [tag-a] ,执行 --tags=tag-b 只有 Tasks-B 任务都执行
2. –skip-tags 跳过所有包含进来的任务

剧本定义

---
- hosts: ecs[0]
  gather_facts: no
  tasks:
    - include_tasks:
        file: tasks/tasks-demo1-7.yml
        apply:
          tags:
            - tag-a
            - tag-b
      tags: always
    - name: "创建文件"
      file:
        path: /tmp/testdir/t3.txt
        state: touch
      notify: "create file handler"
  handlers:
    - name: "create file handler"
      include_tasks: tasks/tasks-demo1-1.yml
      # listen: "create file handler"

执行 --skip-tags=tag-a

$ ansible-playbook --skip-tags=tag-a playbook-include_tasks-demo1-4.yml
PLAY [ecs[0]] ******
TASK [include_tasks] ******
included: /prodata/scripts/ansibleLearn/tasks/tasks-demo1-7.yml for ecs-1.aliyun.sz
TASK [创建文件] ******
changed: [ecs-1.aliyun.sz]
RUNNING HANDLER [create file handler] ******
included: /prodata/scripts/ansibleLearn/tasks/tasks-demo1-1.yml for ecs-1.aliyun.sz
RUNNING HANDLER [create file handler] ******
ok: [ecs-1.aliyun.sz] => {
    "msg": "【2021-10-12 16:58:47】文件创建完成,当前主机:ecs-1.aliyun.sz..."
}
PLAY RECAP ******
ecs-1.aliyun.sz            : ok=4    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0

除非我修改 apply.tags 参数,取消掉 tag-a 标签

- include_tasks:
    file: tasks/tasks-demo1-7.yml
    apply:
      tags:
        - tag-b
  tags: always

执行 --skip-tags=tag-a

$ ansible-playbook --skip-tags=tag-a playbook-include_tasks-demo1-4.yml
PLAY [ecs[0]] ******
TASK [include_tasks] ******
included: /prodata/scripts/ansibleLearn/tasks/tasks-demo1-7.yml for ecs-1.aliyun.sz
TASK [Tasks-B] ******
ok: [ecs-1.aliyun.sz] => {
    "msg": "【2021-10-12 17:02:06】Task-B"
}
TASK [创建文件] ******
changed: [ecs-1.aliyun.sz]
RUNNING HANDLER [create file handler] ******
included: /prodata/scripts/ansibleLearn/tasks/tasks-demo1-1.yml for ecs-1.aliyun.sz
RUNNING HANDLER [create file handler] ******
ok: [ecs-1.aliyun.sz] => {
    "msg": "【2021-10-12 17:02:06】文件创建完成,当前主机:ecs-1.aliyun.sz..."
}
PLAY RECAP ******
ecs-1.aliyun.sz            : ok=5    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0

这里做一个小梳理:

  • 当 apply.tags: [tag-a, tag-b],执行 --skip-tags=tag-a 所有包含任务全都不执行
  • 当 apply.tags: [tag-b] ,执行 --skip-tags=tag-a Tasks-A 不执行

记录下当前的版本

$ ansible --version
ansible 2.9.25
  config file = /etc/ansible/ansible.cfg
  configured module search path = [u'/root/.ansible/plugins/modules', u'/usr/share/ansible/plugins/modules']
  ansible python module location = /usr/lib/python2.7/site-packages/ansible
  executable location = /usr/bin/ansible
  python version = 2.7.5 (default, Nov 16 2020, 22:23:17) [GCC 4.8.5 20150623 (Red Hat 4.8.5-44)]
目前的处理方法

不知道你是否绕晕了,反正我懵了好一阵,在一头雾水中我摸索出了另一种玩法,空列表 [],示例如下:

剧本定义

---
- hosts: ecs[0]
  gather_facts: no
  tasks:
    - include_tasks:
        file: tasks/tasks-demo1-7.yml
        apply:
          tags: []
      tags: always
    - name: "创建文件"
      file:
        path: /tmp/testdir/t3.txt
        state: touch
      notify: "create file handler"
  handlers:
    - name: "create file handler"
      include_tasks: tasks/tasks-demo1-1.yml
      # listen: "create file handler"

执行 任务文件中包含特定标签的的任务(单个)

$ ansible-playbook --tags=tag-a playbook-include_tasks-demo1-4.yml
PLAY [ecs[0]] ******
TASK [include_tasks] ******
included: /prodata/scripts/ansibleLearn/tasks/tasks-demo1-7.yml for ecs-1.aliyun.sz
TASK [Tasks-A] ******
ok: [ecs-1.aliyun.sz] => {
    "msg": "【2021-10-12 17:14:54】Task-A"
}
PLAY RECAP ******
ecs-1.aliyun.sz            : ok=2    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0

执行 任务文件中包含特定标签的的任务(多个)

$ ansible-playbook --tags=tag-a,tag-b playbook-include_tasks-demo1-4.yml
PLAY [ecs[0]] ******
TASK [include_tasks] ******
included: /prodata/scripts/ansibleLearn/tasks/tasks-demo1-7.yml for ecs-1.aliyun.sz
TASK [Tasks-A] ******
ok: [ecs-1.aliyun.sz] => {
    "msg": "【2021-10-12 17:15:03】Task-A"
}
TASK [Tasks-B] ******
ok: [ecs-1.aliyun.sz] => {
    "msg": "【2021-10-12 17:15:03】Task-B"
}
PLAY RECAP ******
ecs-1.aliyun.sz            : ok=3    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0

跳过 任务文件中包含特定标签的的任务(单个)

$ ansible-playbook --skip-tags=tag-a playbook-include_tasks-demo1-4.yml
PLAY [ecs[0]] ******
TASK [include_tasks] ******
included: /prodata/scripts/ansibleLearn/tasks/tasks-demo1-7.yml for ecs-1.aliyun.sz
TASK [Tasks-B] ******
ok: [ecs-1.aliyun.sz] => {
    "msg": "【2021-10-12 17:18:50】Task-B"
}
TASK [创建文件] ******
changed: [ecs-1.aliyun.sz]
RUNNING HANDLER [create file handler] ******
included: /prodata/scripts/ansibleLearn/tasks/tasks-demo1-1.yml for ecs-1.aliyun.sz
RUNNING HANDLER [create file handler] ******
ok: [ecs-1.aliyun.sz] => {
    "msg": "【2021-10-12 17:18:50】文件创建完成,当前主机:ecs-1.aliyun.sz..."
}
PLAY RECAP ******
ecs-1.aliyun.sz            : ok=5    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0

跳过 任务文件中包含特定标签的的任务(多个)

$ ansible-playbook --skip-tags=tag-a,tag-b playbook-include_tasks-demo1-4.yml
PLAY [ecs[0]] ******
TASK [include_tasks] ******
included: /prodata/scripts/ansibleLearn/tasks/tasks-demo1-7.yml for ecs-1.aliyun.sz
TASK [创建文件] ******
changed: [ecs-1.aliyun.sz]
RUNNING HANDLER [create file handler] ******
included: /prodata/scripts/ansibleLearn/tasks/tasks-demo1-1.yml for ecs-1.aliyun.sz
RUNNING HANDLER [create file handler] ******
ok: [ecs-1.aliyun.sz] => {
    "msg": "【2021-10-12 17:19:12】文件创建完成,当前主机:ecs-1.aliyun.sz..."
}
PLAY RECAP ******
ecs-1.aliyun.sz            : ok=4    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0

随着我进一步的尝试与删减,最后与上述效果等同的剧本定义如下:

---
- hosts: ecs[0]
  gather_facts: no
  tasks:
    - include_tasks: tasks/tasks-demo1-7.yml
      tags: always
    - name: "创建文件"
      file:
        path: /tmp/testdir/t3.txt
        state: touch
      notify: "create file handler"
  handlers:
    - name: "create file handler"
      include_tasks: tasks/tasks-demo1-1.yml
      # listen: "create file handler"

是的,你没看错,一切都回去了,笑哭…也是醉了

真是摸不透 apply.tags,还是暂时敬而远之吧~

include_tasks 条件判断

include 没啥不同的

剧本定义

---
- hosts: ecs[0]
  gather_facts: yes
  tasks:
    # 包含引入 task 定义文件
    - include_tasks: tasks/tasks-demo1.yml
      tags: print-start-info
      when: ansible_distribution == "CentOS" and ansible_distribution_major_version == "7"
    - name: "创建文件"
      tags: always
      file:
        path: /tmp/testdir/t3.txt
        state: touch

执行效果

$ ansible-playbook playbook-include_tasks-demo1-5.yml
PLAY [ecs[0]] ******
TASK [Gathering Facts] ******
ok: [ecs-1.aliyun.sz]
TASK [include_tasks] ******
included: /prodata/scripts/ansibleLearn/tasks/tasks-demo1.yml for ecs-1.aliyun.sz
TASK [打印开始信息] ******
ok: [ecs-1.aliyun.sz] => {
    "msg": "【2021-10-12 17:41:59】脚本开始执行,当前主机:ecs-1.aliyun.sz..."
}
TASK [创建文件] ******
changed: [ecs-1.aliyun.sz]
PLAY RECAP ******
ecs-1.aliyun.sz            : ok=4    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0

include_tasks 循环

同样与 include 没啥区别

剧本定义

---
- hosts: ecs[0]
  gather_facts: no
  tasks:
    # 包含引入 task 定义文件
    - include_tasks: tasks/tasks-demo1-6.yml
      # 迭代列表 [1, 2] 循环包含 include_tasks 默认会将 item 参数传递过去
      loop: [outer-1, outer-2]
      loop_control:
        loop_var: outer_item

执行效果

$ ansible-playbook playbook-include_tasks-demo1-6.yml
PLAY [ecs[0]] ******
TASK [include_tasks] ******
included: /prodata/scripts/ansibleLearn/tasks/tasks-demo1-6.yml for ecs-1.aliyun.sz
included: /prodata/scripts/ansibleLearn/tasks/tasks-demo1-6.yml for ecs-1.aliyun.sz
TASK [Include-Tasks] ******
ok: [ecs-1.aliyun.sz] => (item=inner-a) => {
    "msg": "【2021-10-12 17:44:10】变量输出,item:outer-1-inner-a"
}
ok: [ecs-1.aliyun.sz] => (item=inner-b) => {
    "msg": "【2021-10-12 17:44:10】变量输出,item:outer-1-inner-b"
}
TASK [Include-Tasks] ******
ok: [ecs-1.aliyun.sz] => (item=inner-a) => {
    "msg": "【2021-10-12 17:44:10】变量输出,item:outer-2-inner-a"
}
ok: [ecs-1.aliyun.sz] => (item=inner-b) => {
    "msg": "【2021-10-12 17:44:10】变量输出,item:outer-2-inner-b"
}
PLAY RECAP ******
ecs-1.aliyun.sz            : ok=4    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0

五、import_tasks 关键字

前面我们看过了 include_tasks,现在我们看下另一个用来 包含(导入) tasks 的 关键字 import_tasks

import_tasks 包含任务

剧本定义

---
- hosts: ecs[0]
  gather_facts: no
  tasks:
    - import_tasks: tasks/tasks-demo1-7.yml
    - debug:
      msg: "Done."

执行效果

$ ansible-playbook playbook-import_tasks-demo1-1.yml
PLAY [ecs[0]] ******
TASK [Tasks-A] ******
ok: [ecs-1.aliyun.sz] => {
    "msg": "【2021-10-12 17:40:04】Task-A"
}
TASK [Tasks-B] ******
ok: [ecs-1.aliyun.sz] => {
    "msg": "【2021-10-12 17:40:04】Task-B"
}
TASK [debug] ******
ok: [ecs-1.aliyun.sz] => {
    "msg": "Done"
}
PLAY RECAP ******
ecs-1.aliyun.sz            : ok=3    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0

OK,可以看到, import_tasks 关键字本身并没有产生 tasks,这一点与 include_tasks 截然不同!

import_tasks handlers 回调

import_tasksinclud_tasks 一样,当使用 notify 通知时,要么有同样名称的 handler,要么有 handler 监听特定名称的事件

剧本定义

---
- hosts: ecs[0]
  gather_facts: no
  tasks:
    - import_tasks: tasks/tasks-demo1.yml
    - name: "创建文件"
      file:
        path: /tmp/testdir/t3.txt
        state: touch
      notify: "create file handler"
  handlers:
    - name: "create file handler"
      include_tasks: tasks/tasks-demo1-1.yml
      # listen: "create file handler"

执行效果

$ ansible-playbook playbook-import_tasks-demo1-2.yml
PLAY [ecs[0]] ******
TASK [打印开始信息] ******
ok: [ecs-1.aliyun.sz] => {
    "msg": "【2021-10-12 17:46:53】脚本开始执行,当前主机:ecs-1.aliyun.sz..."
}
TASK [创建文件] ******
changed: [ecs-1.aliyun.sz]
RUNNING HANDLER [include_tasks] ******
included: /prodata/scripts/ansibleLearn/tasks/tasks-demo1-1.yml for ecs-1.aliyun.sz
RUNNING HANDLER [create file handler] ******
ok: [ecs-1.aliyun.sz] => {
    "msg": "【2021-10-12 17:46:53】文件创建完成,当前主机:ecs-1.aliyun.sz..."
}
PLAY RECAP ******
ecs-1.aliyun.sz            : ok=4    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0

import_tasks 参数化任务

与 include_tasks 使用方式向相同

剧本定义

---
- hosts: ecs[0]
  gather_facts: no
  tasks:
    # 包含引入 task 定义文件
    - import_tasks: tasks/tasks-demo1-2.yml
      vars:
        username: "Da"
        gender: "Male"
    - name: "创建文件"
      file:
        path: /tmp/testdir/t3.txt
        state: touch

执行效果

$ ansible-playbook playbook-import_tasks-demo1-3.yml
PLAY [ecs[0]] ******
TASK [变量输出] ******
ok: [ecs-1.aliyun.sz] => {
    "msg": "【2021-10-12 17:51:35】变量输出,username:Da,gender:Male"
}
TASK [创建文件] ******
changed: [ecs-1.aliyun.sz]
PLAY RECAP ******
ecs-1.aliyun.sz            : ok=2    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0

import_tasks 给”包含”打tags

它与 include_tasks 不同,但与 include 是相同的,如下所示:

---
- hosts: ecs[0]
  gather_facts: no
  tasks:
    # include: tasks/tasks-demo1-7.yml
    # include_tasks: tasks/tasks-demo1-7.yml
    - import_tasks: tasks/tasks-demo1-7.yml
    - debug:
        msg: "Done."

首先,假设我们什么标签参数都不加,三个关键字的效果是相同的,唯一的区别就是 include_tasks 会产生一次 task,包含进来的任务都会顺序执行

  • include

    TASK [Tasks-A] ******
    ok: [ecs-1.aliyun.sz] => {
        "msg": "【2021-10-12 18:01:51】Task-A"
    }
    TASK [Tasks-B] ******
    ok: [ecs-1.aliyun.sz] => {
        "msg": "【2021-10-12 18:01:51】Task-B"
    }
    TASK [debug] ******
    ok: [ecs-1.aliyun.sz] => {
        "msg": "Done."
    }
  • include_tasks

    TASK [include_tasks] ******
    included: /prodata/scripts/ansibleLearn/tasks/tasks-demo1-7.yml for ecs-1.aliyun.sz
    TASK [Tasks-A] ******
    ok: [ecs-1.aliyun.sz] => {
        "msg": "【2021-10-12 18:03:24】Task-A"
    }
    TASK [Tasks-B] ******
    ok: [ecs-1.aliyun.sz] => {
        "msg": "【2021-10-12 18:03:24】Task-B"
    }
    TASK [debug] ******
    ok: [ecs-1.aliyun.sz] => {
        "msg": "Done."
    }
  • import_tasks

    TASK [Tasks-A] ******
    ok: [ecs-1.aliyun.sz] => {
        "msg": "【2021-10-12 18:03:46】Task-A"
    }
    TASK [Tasks-B] ******
    ok: [ecs-1.aliyun.sz] => {
        "msg": "【2021-10-12 18:03:46】Task-B"
    }
    TASK [debug] ******
    ok: [ecs-1.aliyun.sz] => {
        "msg": "Done."
    }

OK,假如我们执行指定包含进来的任务:

  • include

    $ ansible-playbook --tags=tag-a playbook-import_tasks-demo1-4.yml
    PLAY [ecs[0]] ******
    TASK [Tasks-A] ******
    ok: [ecs-1.aliyun.sz] => {
        "msg": "【2021-10-12 18:06:18】Task-A"
    }
    PLAY RECAP ******
    ecs-1.aliyun.sz            : ok=1    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0
  • include_tasks

    $ ansible-playbook --tags=tag-a playbook-import_tasks-demo1-4.yml
    PLAY [ecs[0]] ******
    PLAY RECAP ******
  • import_tasks

    $ ansible-playbook --tags=tag-a playbook-import_tasks-demo1-4.yml
    PLAY [ecs[0]] ******
    TASK [Tasks-A] ******
    ok: [ecs-1.aliyun.sz] => {
        "msg": "【2021-10-12 18:07:43】Task-A"
    }
    PLAY RECAP ******
    ecs-1.aliyun.sz            : ok=1    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0
  • 可以看到 includeimport_tasks 是相同的,而 include_tasks 因其本身并未被执行,所有没有任何任务包含进来,从而无任务执行

跳过指定包含进来的任务:

  • include

    $ ansible-playbook --skip-tags=tag-a playbook-import_tasks-demo1-4.yml
    PLAY [ecs[0]] ******
    TASK [Tasks-B] ******
    ok: [ecs-1.aliyun.sz] => {
        "msg": "【2021-10-12 18:11:00】Task-B"
    }
    TASK [debug] ******
    ok: [ecs-1.aliyun.sz] => {
        "msg": "Done."
    }
    PLAY RECAP ******
    ecs-1.aliyun.sz            : ok=2    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0
  • include_tasks

    $ ansible-playbook --skip-tags=tag-a playbook-import_tasks-demo1-4.yml
    PLAY [ecs[0]] ******
    TASK [include_tasks] ******
    included: /prodata/scripts/ansibleLearn/tasks/tasks-demo1-7.yml for ecs-1.aliyun.sz
    TASK [Tasks-B] ******
    ok: [ecs-1.aliyun.sz] => {
        "msg": "【2021-10-12 18:11:18】Task-B"
    }
    TASK [debug] ******
    ok: [ecs-1.aliyun.sz] => {
        "msg": "Done."
    }
    PLAY RECAP ******
    ecs-1.aliyun.sz            : ok=3    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0
  • import_tasks

    $ ansible-playbook --skip-tags=tag-a playbook-import_tasks-demo1-4.yml
    PLAY [ecs[0]] ******
    TASK [Tasks-B] ******
    ok: [ecs-1.aliyun.sz] => {
        "msg": "【2021-10-12 18:11:32】Task-B"
    }
    TASK [debug] ******
    ok: [ecs-1.aliyun.sz] => {
        "msg": "Done."
    }
    PLAY RECAP ******
    ecs-1.aliyun.sz            : ok=2    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0
  • 三者效果基本也都相同,唯一差别还是 include_tasks 产生了一次 tasks 执行

import_tasks 条件判断

三者都没啥区别

剧本定义

---
- hosts: ecs[0]
  gather_facts: yes
  tasks:
    # 包含引入 task 定义文件
    - import_tasks: tasks/tasks-demo1.yml
      tags: print-start-info
      when: ansible_distribution == "CentOS" and ansible_distribution_major_version == "7"
    - name: "创建文件"
      tags: always
      file:
        path: /tmp/testdir/t3.txt
        state: touch

执行效果

$ ansible-playbook playbook-import_tasks-demo1-5.yml
PLAY [ecs[0]] ******
TASK [Gathering Facts] ******
ok: [ecs-1.aliyun.sz]
TASK [打印开始信息] ******
ok: [ecs-1.aliyun.sz] => {
    "msg": "【2021-10-12 18:15:02】脚本开始执行,当前主机:ecs-1.aliyun.sz..."
}
TASK [创建文件] ******
changed: [ecs-1.aliyun.sz]
PLAY RECAP ******
ecs-1.aliyun.sz            : ok=3    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0

import_tasks 循环

import_tasks 不支持 loop

剧本定义

---
- hosts: ecs[0]
  gather_facts: no
  tasks:
    # 包含引入 task 定义文件
    - import_tasks: tasks/tasks-demo1-6.yml
      # 迭代列表 [1, 2] 循环包含 include 默认会将 item 参数传递过去
      loop: [outer-1, outer-2]
      loop_control:
        loop_var: outer_item

执行效果

$ ansible-playbook playbook-import_tasks-demo1-6.yml
ERROR! You cannot use loops on 'import_tasks' statements. You should use 'include_tasks' instead.
The error appears to be in '/prodata/scripts/ansibleLearn/playbook-import_tasks-demo1-6.yml': line 6, column 7, but may
be elsewhere in the file depending on the exact syntax problem.
The offending line appears to be:
    # 包含引入 task 定义文件
    - import_tasks: tasks/tasks-demo1-6.yml
      ^ here

不过,import_tasks 包含进来的 task 本身是可以执行 loop 的,如下所示:

任务定义

---
- name: "Include-Tasks"
  debug:
    msg: "【{{ '%Y-%m-%d %H:%M:%S' | strftime }}】变量输出,item:{{outer_item | default('')}}-{{ item }}"
  loop: [inner-a, inner-b]

剧本定义

---
- hosts: ecs[0]
  gather_facts: no
  tasks:
    # 包含引入 task 定义文件
    - import_tasks: tasks/tasks-demo1-6.yml

执行效果

$ ansible-playbook playbook-import_tasks-demo1-6.yml
PLAY [ecs[0]] ******
TASK [Include-Tasks] ******
ok: [ecs-1.aliyun.sz] => (item=inner-a) => {
    "msg": "【2021-10-12 18:20:24】变量输出,item:inner-a"
}
ok: [ecs-1.aliyun.sz] => (item=inner-b) => {
    "msg": "【2021-10-12 18:20:24】变量输出,item:inner-b"
}
PLAY RECAP ******
ecs-1.aliyun.sz            : ok=1    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0

六、var_files 关键字

详见 Ansible Playbook 变量——var_files

七、import_playbook 关键字

之前 include 关键字也可以,但是,自 2.8 版本以后,这部分功能剥离到 import_playbook 关键字上来,具体使用如下:

剧本定义 playbook-import_playbook-demo1-0.yml

---
- name: "play-1"
  hosts: ecs[0]
  gather_facts: no
  tasks:
    - debug:
        msg: "我是 play-1,等下我会包含 play-2 进来..."
- import_playbook: playbook-import_playbook-demo1-1.yml

剧本定义 playbook-import_playbook-demo1-1.yml

---
- name: "play-2"
  hosts: ecs[0]
  gather_facts: no
  tasks:
    - debug:
        msg: "我是 play-2,我被人包含过去了..."

执行效果

$ ansible-playbook playbook-import_playbook-demo1-0.yml
PLAY [play-1] ******
TASK [debug] ******
ok: [ecs-1.aliyun.sz] => {
    "msg": "我是 play-1,等下我会包含 play-2 进来..."
}
PLAY [play-2] ******
TASK [debug] ******
ok: [ecs-1.aliyun.sz] => {
    "msg": "我是 play-2,我被人包含过去了..."
}
PLAY RECAP ******
ecs-1.aliyun.sz            : ok=2    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0

八、include_role

从 ansible 2.4 开始支持通过 include_roleimport_role 两种方式将 Role 角色 内联到其他任务中,我们接下来分别演示下

Role 示例来自于笔记 Ansible Role,具体详情见这里

首先,修改 ansible 配置,设置 role 搜索目录

$ vim /etc/ansible/ansible.cfg
#roles_path    = /etc/ansible/roles
roles_path    = /etc/ansible/roles

拷贝 Role 到对应目录中

$ tree /etc/ansible/roles
/etc/ansible/roles
└── test-role
    ├── defaults
    │   └── main.yml
    ├── files
    │   ├── index.html
    │   └── nginx.conf
    ├── filter_plugins
    │   ├── my_filters.py
    │   └── my_filters.pyc
    ├── group_vars
    │   └── all
    ├── handlers
    │   └── main.yml
    ├── host_vars
    │   └── ecs-1.aliyun.sz
    ├── library
    │   └── docker_sh
    ├── meta
    │   ├── __init__.py
    │   └── main.yml
    ├── tasks
    │   └── main.yml
    ├── templates
    │   └── test.j2
    └── vars
        └── main.yml
12 directories, 14 files

模版定义

---
- hosts: ecs[0]
  remote_user: root
  tasks:
    - name: "include_role 引用 Role"
      debug:
          msg: "通过 include_role 指令引用 Role 角色"
    - include_role:
        name: test-role

执行命令

$ ansible-playbook playbook-tasks-link-role.yml
PLAY [ecs[0]] ******
TASK [Gathering Facts] ******
ok: [ecs-1.aliyun.sz]
TASK [include_role 引用 Role] ******
ok: [ecs-1.aliyun.sz] => {
    "msg": "通过 include_role 指令引用 Role 角色"
}
TASK [include_role : test-role] ******
TASK [test-role : debug] ******
ok: [ecs-1.aliyun.sz] => {
    "msg": "hello, Yo!"
}
TASK [test-role : generate config] ******
ok: [ecs-1.aliyun.sz]
TASK [test-role : Copy HTML] ******
ok: [ecs-1.aliyun.sz]
TASK [test-role : Run Container] ******
ok: [ecs-1.aliyun.sz]
TASK [test-role : Custom filter demo1] ******
ok: [ecs-1.aliyun.sz] => {
    "msg": "Da | a_filter"
}
TASK [test-role : Custom filter demo2] ******
ok: [ecs-1.aliyun.sz] => {
    "msg": "args: ('Yo', 'Yo', 'like', 'Python') --- kwargs: {'username': 'yo', 'gender': 'female'}"
}
PLAY RECAP ******
ecs-1.aliyun.sz            : ok=8    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0

九、import_role

import_role 与 include_role 在使用上差别不大

模版定义

---
- hosts: ecs[0]
  remote_user: root
  tasks:
    - name: "import_role 引用 Role"
      debug:
          msg: "通过 import_role 指令引用 Role 角色"
    - import_role:
        name: test-role

执行命令

$ ansible-playbook playbook-tasks-link-role2.yml
PLAY [ecs[0]] ******
TASK [Gathering Facts] ******
ok: [ecs-1.aliyun.sz]
TASK [import_role 引用 Role] ******
ok: [ecs-1.aliyun.sz] => {
    "msg": "通过 import_role 指令引用 Role 角色"
}
TASK [test-role : debug] ******
ok: [ecs-1.aliyun.sz] => {
    "msg": "hello, Yo!"
}
TASK [test-role : generate config] ******
ok: [ecs-1.aliyun.sz]
TASK [test-role : Copy HTML] ******
ok: [ecs-1.aliyun.sz]
TASK [test-role : Run Container] ******
ok: [ecs-1.aliyun.sz]
TASK [test-role : Custom filter demo1] ******
ok: [ecs-1.aliyun.sz] => {
    "msg": "Da | a_filter"
}
TASK [test-role : Custom filter demo2] ******
ok: [ecs-1.aliyun.sz] => {
    "msg": "args: ('Yo', 'Yo', 'like', 'Python') --- kwargs: {'username': 'yo', 'gender': 'female'}"
}
PLAY RECAP ******
ecs-1.aliyun.sz            : ok=8    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0

文章作者: Da
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 Da !
  目录