include_tasks和import_tasks之间有什么区别?

在Ansible 2.4中,include模块已被弃用。取而代之的是两个替代模块:import_tasksinclude_tasks。但它们的描述非常相似:
  • include_tasks:在当前playbook中包含一个任务列表文件以供执行。
  • import_tasks:导入一个任务列表以添加到当前playbook中以供后续执行。
什么时候应该使用前者,什么时候应该使用后者?

(另外:警告中提到的“动态”和“静态”任务已被弃用。我阅读了相关文档,但并不理解。) - Ben S
2个回答

文档中对这个主题有很多内容:

主要的区别是:

所有的 import* 语句在解析playbook时进行预处理。
所有的 include* 语句在playbook执行过程中被逐个处理。

因此,import 是静态的,include 是动态的。

根据我的经验,在处理逻辑“单元”时,应该使用 import。例如,将长列表的任务拆分为子任务文件:

main.yml:

- import_tasks: prepare_filesystem.yml
- import_tasks: install_prerequisites.yml
- import_tasks: install_application.yml
但是你可以使用include来处理不同的工作流程,并根据一些动态收集到的事实做出决策: 安装先决条件:
- include_tasks: prerequisites_{{ ansible_os_family | lower }}.yml

18我发现这个链接非常有用:http://docs.ansible.com/ansible/latest/playbooks_conditionals.html#applying-when-to-roles-imports-and-includes 它提到了导入(import)和包含(include)的行为不同的情况 - 当文件中的任务可能改变确定导入条件时的'when'条件。使用import_tasks时,每个任务都会检查条件,所以当条件发生变化时,行为也会改变。而使用include_tasks时,任务的存在与否取决于在执行include_tasks语句时条件是否被评估为true。如果我理解得正确的话... - Ethel Evans
include的行为是什么?如果我们使用includeimport_tasks是否相当于它? - Andy Shinn
1includestatic: yes(像import_tasks一样)和static: no(像include_tasks一样)的行为。 - Konstantin Suvorov
static 的默认值是什么? - Andy Shinn
1static 默认情况下是 None:自 Ansible 2.0 起,任务的包含是动态的,并且表现得更像真正的任务。这意味着它们可以被循环执行、跳过,并且可以使用来自任何来源的变量。Ansible 尝试自动检测这一点,但你可以使用静态指令(在 Ansible 2.1 中添加)来绕过自动检测。 - Konstantin Suvorov
那么在本来不会有任何区别的情况下,是否应该默认使用import_tasks呢? - Felipe Alvarez
@FelipeAlvarez 自从我问了这个问题以来,我一直在使用 import_tasks,效果很好。 - Ben S
1随着Ansible中的文档结构和链接不断变化:/由@EthelEvans提到的新链接为: https://docs.ansible.com/ansible/latest/user_guide/playbooks_conditionals.html#conditionals-with-re-use - Kepi

导入是静态的,包含是动态的。导入发生在解析时,包含发生在运行时。

导入基本上用文件中的任务替换任务。运行时没有导入任务。因此,像tagswhen(以及很可能其他属性)会被复制到每个导入的任务中。

包含确实会被执行。包含的任务的tagswhen只适用于任务本身。

如果导入任务未标记,则导入文件中的带标记的任务将被执行。如果包含任务未标记,则不会执行包含文件中的任何任务。

如果导入任务被标记,则将执行导入文件中的所有任务。如果包含任务被标记,则只会执行包含文件中的带标记的任务。

导入的限制:

  • 不能与with_*loop属性一起使用
  • 不能导入依赖于变量名称的文件

包含的限制:

  • --list-tags不显示来自包含文件的标签
  • --list-tasks不显示来自包含文件的任务
  • 无法使用notify来触发来自动态包含文件的处理程序名称
  • 无法使用--start-at-task在动态包含文件中开始执行任务

更多相关信息,请查看此处此处

对我来说,这基本上归结为导入不能与loop属性一起使用。

导入在像这样的情况下肯定会失败:

# playbook.yml
- import_tasks: set-x.yml
  when: x is not defined

# set-x.yml
- set_fact
  x: foo
- debug:
  var: x

debug不会被执行,因为它从import_tasks任务继承了when。所以,在导入任务文件中改变用于导入任务的when属性的变量时,不要导入任务文件。

我曾经有一个规则是从导入开始,但是一旦我需要一个包含(include),我确保被包含文件或其子文件中没有导入任何东西。但是这非常难以维护。而且目前还不清楚这是否会保护我免受麻烦。我的意思是,混合使用包含和导入是不推荐的。

我不能仅使用导入,因为偶尔我需要循环。我可能可以完全转换为仅使用包含。但是我决定在除了需要循环的情况下,到处都使用导入。我决定首先亲自体验所有那些棘手的边界情况。也许在我的剧本中不会有任何问题。或者希望我能找到一种让它工作的方法。

更新:一个可能有用的技巧是创建一个可以被多次导入但只执行一次的任务文件:

- name: ...
  ...
  when: not _file_executed | default(False)

- name: ...
  ...
  when: not _file_executed | default(False)

...

- name: Set _file_executed
  set_fact:
    _file_executed: True
UPD 混合使用 include 和 import 的一个意外效果是,include 任务的变量会覆盖导入任务的变量。

playbook.yml:

- hosts: all
  tasks:
    - import_tasks: 2.yml
      vars:
        v1: 1
    - include_tasks: 2.yml
      vars:
        v1: 1

2.yml:

- import_tasks: 3.yml
  vars:
    v1: 2

3.yml:

- debug:
    var: v1    # 2 then 1
еҸҜиғҪжҳҜеӣ дёә`include_tasks`йҰ–е…ҲеҜје…Ҙж–Үд»¶пјҢз„¶еҗҺеҶҚеә”з”Ёе…¶`vars`жҢҮд»ӨгҖӮ е®һйҷ…дёҠпјҢд№ҹеҸҜд»Ҙиҝҷж ·еӨҚзҺ°пјҡ

playbook.yml:

- hosts: all
  tasks:
    - import_tasks: 2.yml
      vars:
        v1: 1
    - include_tasks: 2.yml
      vars:
        v1: 1

2.yml:

- debug:
    var: v1    # 2 then 1
  vars:
    v1: 2
UPD包含和导入混合的又一个案例。

playbook.yml:

- hosts: all
  tasks:
    # say, you're bound to use include here (because you need a loop)
    - include_tasks: 2.yml
      vars:
        https: yes

2.yml:

- import_tasks: 3.yml
  when: https

3.yml:

- import_tasks: 4.yml
  vars:
    https: no  # here we're trying to temporarily override the https var
- import_tasks: 4.yml

4.yml:

- debug:
    var: https
我们得到了truetrue,参见前面的案例(include_tasks的变量优先于import_tasks的变量)。为了避免这种情况,我们可以在3.yml中切换到includes。但是然后3.yml中的第一个include被跳过了。由于它从父任务继承了when: https,所以第一个任务基本上是这样的:
- import_tasks: 4.yml
  vars:
    https: no  # here we're trying to temporarily override the https var
  when: https
解决方案是在2.yml中也切换到includes。这样可以防止when: https传播到子任务中。

15太棒了!我对网络上的每个人都只是重复文档内容感到沮丧。谢谢你。 - Sergio Acosta
1Sergio Acosta确切地说,最终答案是import是静态的,而include是动态的,就好像它应该能够解释给你关于它如何工作的任何事情一样。:-P - Frank
我认为我会默认包含一些大部分代码。我有一些开源模块(例如ansible-consul)使用导入来处理它们的处理程序,甚至一些带有条件的任务,然后其他开源模块(ansible-nomad)也使用导入,所以我不确定这是否算是混合,因为它们是不同的角色,对吗? - Brian Thomas
1@BrianThomas我猜导入使用include的文件或反之亦然被视为混用。只要你不这样做,就应该是安全的。 - x-yuri