可重用Playbook
虽然可以在一个非常大的文件中编写剧本(并且您可能会以这种方式开始学习剧本),但最终您会希望重复使用文件并开始组织事情。 在Ansible中,有三种方法可以执行此操作:includes, imports, 和roles.
includes和imports(在Ansible版本2.4中添加)允许用户将大型剧本分解成较小的文件,这些文件可以在多个父剧本中使用,甚至可以在同一Playbook中多次使用。
roles不仅允许将任务打包在一起,还可以包含变量,处理程序,甚至模块和其他插件。 与includes和imports不同,角色也可以通过Ansible Galaxy上传和共享。
动态与静态
Ansible有两种可重用内容的操作模式:动态和静态。
在Ansible 2.0中,介绍了动态包含的概念。 由于以这种方式使所有包含动态有一些限制,在Ansible 2.1中引入了强制包括静态的能力。 由于include任务变得过载以包含静态和动态语法,并且由于include的默认行为可能会根据Task上设置的其他选项而发生变化,所以Ansible 2.4引入了include和import的概念。
如果你使用任何 import* 任务(如:import_playbook, import_tasks)它将是静态的。如果你使用任何include*任务(如:include_tasks, include_role)它将是动态的。
祼include任务(用于任务文件和Playbook级包含)仍然可用,但现在已被视为弃用。
动态与静态不同
这两种操作模式非常简单:
- Ansible在Playbook解析时预处理所有静态imports。
- 动态includes是在运行期间遇到该任务时处理的。
当涉及Ansible任务选项,如tags和条件语句(when:)时:
- 对于静态imports,父任务选项将被复制到导入中包含的所有子任务。
- 对于动态includes,任务选项仅在评估时应用于动态任务,并且不会复制到子任务。
注意:roles是一个有点特殊的情况。 在Ansible 2.3之前,角色总是通过特定角色静态地包含在特定role:中,在任何其他剧本任务(除非使用pre_tasks)之前总是先执行角色。 角色仍然可以以这种方式使用,但是,Ansible 2.3引入了include_role选项以允许角色与其他任务内联执行。
Includes和Imports权衡
使用include*和import*有一些优点,以及用户在选择使用时应该考虑的一些权衡:
使用include *语句的主要优点是循环。 当循环与include一起使用时,包含的任务或角色将为循环中的每个项目执行一次。
与import*语句相比,使用include*有一些限制:
- 只存在于动态包含内的标签不会显示在–list-tags输出中。
- 只存在于动态包含内的任务不会显示在–list-tasks输出中。
- 您不能使用notify来触发来自动态包含内部的处理程序名称(请参阅下面的注释)。
- 您不能使用–start-at-task开始执行动态包含内的任务。
与动态包含相比,使用import*也会有一些限制:
- 如上所述,循环不能用于imports。
- 当使用变量作为目标文件或角色名称时,不能使用来自库存源(主机/组变量等)的变量。
注意:关于notify动态任务的使用:仍然可以触发动态包含本身,这将导致包含内的所有任务正在运行。
Including and Importing
Includes vs. Imports
如上面介绍的,include和import语句非常相似,但是Ansible执行程序引擎对它们的处理方式非常不同。
- 所有import*语句在解析剧本时进行预处理。
- 所有include*语句都是在执行playbook时处理的。
Importing Playbooks
在主剧本中可以包含其它剧本,示例:
--- - import_playbook: webservers.yml - import_playbook: databases.yml
列出的每个剧本和任务将按照他们列出的顺序运行,就像他们直接在这里定义的一样。
在2.4之前,只有include可用并兼顾剧本和任务的导入和包含。
Including and Importing Task Files
使用包含的任务列表是一个定义系统将要履行的角色的好方法。 任务包含文件只包含一个简单的任务列表:
# common_tasks.yml --- - name: placeholder foo command: /bin/foo - name: placeholder bar command: /bin/bar
然后,您可以使用import_tasks或include_tasks将此文件包含在主任务列表中:
tasks: - import_tasks: common_tasks.yml # or - include_tasks: common_tasks.yml
您还可以将变量传递给import和include:
tasks: - import_tasks: wordpress.yml vars: wp_user: timmy - import_tasks: wordpress.yml vars: wp_user: alice - import_tasks: wordpress.yml vars: wp_user: bob
也可以使用替代语法将变量传递给include文件,该语法还支持像词典和列表这样的结构化变量:
tasks: - include_tasks: wordpress.yml vars: wp_user: timmy ssh_keys: - "{{ lookup('file', 'keys/one.pub') }}" - "{{ lookup('file', 'keys/two.pub') }}"
使用任何语法,传入的变量都可以在包含的文件中使用。 这些变量只能用于包含文件中的任务。 请参阅变量优先权:我应该在哪里放置变量?查看有关变量继承和优先级的更多详细信息。
任务包括语句可以在任意深度使用。
注意:静态和动态可以混合使用,但不建议,因为它可能会导致您的剧本中难以诊断的错误。
包含和导入也可用于handlers:部分; 例如,如果你想定义如何重新启动apache,你只需要为你的所有剧本做一次。 您可能会创建一个如下所示的handlers.yml:
# more_handlers.yml --- - name: restart apache service: name=apache state=restarted
然后,在你的主剧本中添加:
handlers: - include_tasks: more_handlers.yml # or - import_tasks: more_handlers.yml
注意:请务必参考上面所提到的处理程序的限制/权衡。
Roles
角色是根据已知文件结构自动加载某些vars_files,任务和处理程序的方式。 按角色分组内容还可以轻松与其他用户共享角色。
角色目录结构
示例项目结构:
site.yml webservers.yml fooservers.yml roles/ common/ tasks/ handlers/ files/ templates/ vars/ defaults/ meta/ webservers/ tasks/ defaults/ meta/
角色期望文件位于某些目录名称中。 角色必须至少包含其中一个目录,但排除任何未使用的目录是完全正确的。 在使用时,每个目录必须包含一个main.yml文件,其中包含相关的内容:
- tasks – 包含角色要执行的主要任务列表
- handlers – 包含可以由此角色,甚至在此角色以外的任何地方使用的处理程序。
- defaults – 该角色的默认变量(有关更多信息,请参阅变量)。
- vars – 角色的其它变量(有关更多信息,请参阅变量)。
- files – 包含可以通过此角色部署的文件。
- templates – 包含可以通过此角色部署的模板。
- meta – 为这个角色定义一些元数据。 请参阅下面的更多细节。
其他YAML文件可能包含在某些目录中。 例如,通常的做法是从tasks / main.yml文件中包含特定于平台的任务:
# roles/example/tasks/main.yml - name: added in 2.4, previously you used 'include' import_tasks: redhat.yml when: ansible_os_platform|lower == 'redhat' - import_tasks: debian.yml when: ansible_os_platform|lower == 'debian' # roles/example/tasks/redhat.yml - yum: name: "httpd" state: present # roles/example/tasks/debian.yml - apt: name: "apache2" state: present
使用角色
经典(原始)使用角色的方式是通过给定剧本的roles:选项:
--- - hosts: webservers roles: - common - webservers
这为每个角色’x’指定了以下行为:
- 如果 roles/x/tasks/main.yml 存在,其中列出的任务将被添加到剧本中。
- 如果 roles/x/handlers/main.yml 存在,列在其中的处理程序将被添加到该剧本中。
- 如果 roles/x/vars/main.yml 存在,列在其中的变量将被添加到剧本中。
- 如果 roles/x/defaults/main.yml 存在,列在其中的变量将被添加到剧本中。
- 如果 roles/x/meta/main.yml 存在,其中列出的任何角色依赖关系都将添加到角色列表(3和更高版本)中。
- 任何副本,脚本,模板或包含任务(在角色中)都可以在角色 /x/{files,templates,tasks}/(目录取决于任务)中引用文件,而无需相对或绝对地引导它们。
以这种方式使用时,Playbook的执行顺序如下所示:
- 剧本中定义的任何pre_tasks。
- 到目前为止触发的任何处理程序都将运行。
- 角色中列出的每个role将依次执行。 角色meta / main.yml中定义的任何角色依赖关系都将首先运行,并受到标记过滤和条件限制。
- 剧本中定义的任何tasks。
- 到目前为止触发的任何处理程序都将运行。
- 剧本中定义的任何post_tasks。
- 到目前为止触发的任何处理程序都将运行。
注意:如果在任务中使用标签(稍后描述为仅运行剧本的一部分),一定要标记pre_tasks,post_tasks和角色依赖关系,并将它们一起传递,特别是如果 pre/post 任务和角色依赖关系用于监视停机窗口控制或负载平衡。
从Ansible 2.4开始,可以通过import_role或include_role使用任何其他任务来内联角色:
--- - hosts: webservers tasks: - debug: msg: "before we run our role" - import_role: name: example - include_role: name: example - debug: msg: "after we ran our role"
用于角色的名称可以是简单的名称,也可以是完全的路径:
--- - hosts: webservers roles: - { role: '/path/to/my/roles/common' }
角色可以接受参数:
--- - hosts: webservers roles: - common - { role: foo_app_instance, dir: '/opt/a', app_port: 5000 } - { role: foo_app_instance, dir: '/opt/b', app_port: 5001 }
或者,使用更新的语法:
--- - hosts: webservers tasks: - include_role: name: foo_app_instance vars: dir: '/opt/a' app_port: 5000 ...
最后,您可能希望将标签分配给您指定的角色。 你可以这样做内联:
--- - hosts: webservers tasks: - import_role: name: foo tags: - bar - baz
注意:这会将该角色中的所有任务用指定的标记进行标记,并附加角色中指定的任何标记。 这个例子中的标签不会被添加到include_role中的任务中。 直接标记include_role任务,以将标记应用于包含角色中的任务。 如果您发现自己构建了大量标签并且您想在不同时间调用该角色的子集,那么您应该考虑将该角色分解为多个角色。
角色重复和执行
如果角色定义的参数对于每个定义都没有不同,Ansible将只允许角色执行一次,即使定义了多次。 例如:
--- - hosts: webservers roles: - foo - foo
以上示例,角色foo将只执行一次。
要让角色运行多次,有两种选择:
- 在每个角色定义中传递不同的参数。
- 将allow_duplicates: true添加到该角色的meta / main.yml文件中。
示例1,添加不同参数:
--- - hosts: webservers roles: - { role: foo, message: "first" } - { role: foo, message: "second" }
此示例中,由于每个角色定义了不同的参数,foo将会运行两次。
示例2,使用allow_duplicates: true:
# playbook.yml --- - hosts: webservers roles: - foo - foo # roles/foo/meta/main.yml --- allow_duplicates: true
此示例中,由于明确地启用foo将运行两次。
角色默认变量
角色默认变量允许您为包含角色或依赖角色设置默认变量(请参见下文)。 要创建默认值,只需在角色目录中添加一个defaults / main.yml文件即可。 这些变量将具有任何可用变量的最低优先级,并可轻松被任何其他变量(包括inventory库变量)覆盖。
Role Search Path
Ansible通过以下方式搜索角色:
- 与剧本文件同级的roles/目录。
- 默认,/etc/ansible/roles。
在Ansible 1.4及更高版本中,您可以配置可选roles_path以搜索角色。 使用它可以将所有常见角色查看到一个位置,并在多个剧本项目之间轻松共享。
Very interesting details you have noted, appreciate it for posting.