• Ansible之路——第九章:Ansible Playbook


    Playbooks 是Ansible 管理配置、部署应用和编排的语言,可以使用Playbooks 来描述你想在远程主机执行的策略或者执行的一组步骤过程等。

    如果说Ansible 模块是工作中的工具的话,那么playbooks 就是方案。

    Playbooks 采用YAML 语法结构。

    9.1 Playbooks 组成

    • Target section:定义将要执行playbook 的远程主机组
    • Variable section:定义playbook 运行时需要使用的变量
    • Task section:定义将要在远程主机上执行的任务列表
    • Handler section:定义task 执行完成以后需要调用的任务

    9.1.1主机和用户

    在playbook 中的每一个play 都可以选择在哪些机器和以什么用户身份完成,hosts 一行可以是一个主机组或者主机也可以是多个,中间以冒号分隔,可以参考前面讲的通配模式;

    remote_user表示执行的用户账号,表示以什么用户来登录远程机器并执行任务。

    ---
      -hosts: webservers
        remote_user: root
    

    9.1.2每一个任务都可以定义一个用户

    ---
      - hosts: webservers
        remote_user: root
        tasks:
          - name: test connection
            ping:
            remote_user: yourname
    

     

    9.1.3在play 中支持sudo

    ---
      - hosts: webservers
        remote_user: yourname
        sudo: yes
    

      

    9.1.4在一个任务中支持sudo

    ---
      - hosts: webservers
        remote_user: yourname
        tasks:
          - service: name=nginx state=started
            sudo: yes
    

    9.1.5 登陆后sudo 到其他用户执行

    ---
      - hosts: webservers
        remote_user: yourname
        sudo: yes
        sudo_user: postgres
    

    9.2 任务列表

    每个任务建议定义一个可读性较强的名字即name,在执行playbook 时会输出,tasks 的声明格式,建议使用”module:options”的格式。

    下面以service 模块为例来定义一个任务,service: key=value 参数,请参看模块的详细介绍

    tasks:
      - name: make sure apache is running
        service: name=httpd state=running
    

    command 和shell 模块关注命令或者脚本执行后返回值,如果命令成功执行返回值不是0 的情况下,可以使用以下方法

    tasks:
      - name: run this command and ignore the result
        shell: /usr/bin/somecommand || /bin/true
    

    或者

    tasks:
      - name: run this command and ignore the result
        shell: /usr/bin/somecommand
        ignore_errors: True
    

    注:ignore_errors这里的值只要小写后为true或者yes就可以,所以即使是TRue这种也是正确的。

    如果在任务中参数过长可以回车使用空格缩进:

    ---
      - hosts: server
        tasks:
          - name: Copy ansible inventory file to client
            copy: src=/etc/ansible/hosts dest=/tmp/hosts
                  owner=root group=root mode=0644
    

    9.3 变量的使用

    1)      如何创建一个有效的变量名

    变量名应该由字母、数组和下划线组成,以字母开头

    2)      在inventory 中定义变量

    3)      在playbook 中定义变量

    4)      在角色和文件中包含变量

    5)      如何使用变量:Jinja2

    Jinja2 是一个被广泛使用的全功能的Python 模板引擎,它有完整的unicode 支持

    注释:Ansible 允许在模板中使用循环和条件判断,但是在playbook 只使用纯粹的YAML 语法

    6)      Jinja2 过滤器

    变量可以通过过滤器修改。过滤器与变量用管道符号( | )分割,并且也可以用圆括号传递可选参数。多个过滤器可以链式调用,前一个过滤器的输出会被作为后一个过滤器的输入。

    如{{ list | join(', ') }},会把一个列表用逗号连接起来。

    下面列举一些在Ansible 使用较多的Filters:

    • 格式化数据:

    {{ ansible_devices | join('| ') }}

    • 过滤器和条件一起使用:

    9.4 register

    register 关键字的作用是将命令执行的结果保存为变量,结果会因为模块不同而不同,在运行ansible-playbook 时增加-v 参数可以看到results 可能的值;注册变量就如同通过setup 模块获取facts 一样。

    执行结果:

    9.5 过滤器

    场景1:将获取到的变量的值的所有字母都变成大写。如:

     

    如上例所示,testvar变量的值中包含3个小写字母,在使用debug模块输出这个变量的时候,我们使用了一个管道符,将testvar变量传给了一个名为“upper”的东西,“upper”就是一个过滤器,执行playbook的时候将变量中的小写字母变成了大写。

    过滤器是一种能够帮助我们处理数据的工具。其实,ansible中的过滤器功能来自于jinja2模板引擎,我们可以借助jinja2的过滤器功能在ansible中对数据进行各种处理。

    当我们想要通过过滤器处理数据的时候,只需要将数据通过管道符传递给对应的过滤器即可。过滤器有很多,有些是jinja2内置的,有些是ansible特有的。除此之外,jinja2还支持自定义过滤器。

    跟字符串操作相关的过滤器,示例:

      

    跟数字操作相关的过滤器,示例:

    跟列表操作相关的过滤器,示例:

    变量未定义相关操作的过滤器,示例:

    上述的default过滤器,不仅能在变量未定义的时候返回指定的值,还能够让模块的参数变得“可有可无”。

    关于“可有可无”,通过以下示例来说明其含义。

    场景2,创建几个文件,只有个别文件需要指定权限,可以定义如下:

    这里只有第一个文件需要指定权限,其余的都是使用系统默认的权限进行创建。在实际工作中,可能需要创建几十个这样的文件,有些需要指定权限,有些不需要,因此一般都会使用条件判断和循环语句来完成,如下:

    这样的playbook确实可以很好的解决问题,但是一共循环了两遍,因为需要对文件的是否有mode属性进行判断,并根据判断结果调整参数设定。

    更好的方法有:

    这里并没有对文件是否有mode权限进行判断,而是直接调用了file模块的mode参数,将mode的值设置为了{{item.mode | default(omit)}}。这样表示的含义就是如果item有mode属性,就把file模块的mode参数设置为item的mode属性值,如果item没有mode属性,file模块就省略mode参数。‘omit’的字面意思就是省略,也就是“可有可无”。

    9.6 pre_tasks and post_tasks

    • pre_tasks: 设置playbook运行之前的tasks,一般用于准备条件或者变量自定义。
    • post_tasks: 设置playbook运行之后的tasks

    Playbook中各种task的执行顺序: 

    pre_task  >  role  >  tasks  >  post_task
    

    如果考虑到handler,则执行顺序为:

    pre_task  >  pre_handler  >  role task  >  task  >  role handler  >  task handler  >  post task  >  post handler
    

    如果你想立即执行所有的 handler 命令在1.2及以后的版本,你可以这样做:

    9.7 notify 与 handler

    notify指定handler的执行机制:“notify”这个action可用于在每个play的最后被触发,在notify中列出的操作称为handler,仅在所有的变化发生完成后一次性地执行指定操作。在notify中列出的操作称为handle,也即notify中调用handler中定义的操作。

    handler:用于当关注的资源发生变化时采取一定的操作。handler是task列表,这些task与前述的task并没有本质上的不同。

    另外,handlers 会按照声明(notify)的顺序执行。

    例如:修改ironic配置文件后需要重启相应的ironic服务。

    9.8 tags

    tags:对任务进行“打标签”操作。当任务存在标签后,我们就可以在执行playbook的时候,借助标签,指定执行哪些任务,或者不执行哪些任务。

    例如:

    9.8.1 查看所有tag

    ansible-playbook <playbook_yaml> --list-tags
    

    9.8.2 指定tag执行任务:

    可以指定某个tag:

    ansible-playbook test.yml -t/--tags "tag1"
    

    可以指定多个tag(以逗号分隔,可以加空格):

    ansible-playbook test.yml -t "tag1, tag2”
    

    9.8.3 指定tag跳过任务

    可以使用--skip-tags来跳过某些tag:

    可以指定某个tag:

    ansible-playbook test.yml --skip-tags "tag1"
    

    可以指定多个tag(以逗号分隔,可以加空格):

    ansible-playbook test.yml --skip-tags "tag1, tag2”
    

    9.9 条件判断:when

    when相当于shell脚本里的if 判断,when语句就是用来实现这个功能的,它是一个jinja2的语法,但是不需要双大括号,用法很简单。

     

     

    运算比较符有:==、!=、>、<、>=、<=、and、or、not、()、in等

    9.9.1 判断目录或文件是否存在:exists

    #exists关键字,注意检查的是ansible主机。is not exists表示不存在,也可以用not testpath is exists

    9.9.2 判断变量

    • defined:判断变量是否已经定义,已经定义则为真
    • undefined:判断变量是否已经定义,未定义则返回真
    • none:判断变量是否已经定义,如果变量值已经定义,但是变量值为空,则返回真

    9.9.3 判断执行结果

    • success或succeeded: 通过任务的返回信息判断任务的执行状态,任务执行成功则返回真
    • failure或failed: 通过任务的返回信息判断任务的执行状态,任务执行失败则返回真
    • change或changed: 通过任务的返回信息判断任务的执行状态,任务执行状态为changed则返回真
    • skip或skipped: 通过任务的返回信息判断任务的执行状态,当任务没有满足条件,而被跳过执行时,则返回真

    9.9.4 判断路径

    • file: 判断路径是否是一个文件,如果路径是一个文件则为真。
    • directory: 判断路径是否是一个目录,如果路径是一个目录则为真
    • link: 判断路径是否是一个软连接,如果路径是一个软连接则为真
    • mount: 判断路径是否是一个挂载点,如果路径是一个挂载点则为真
    • exists: 判断路径是否存在,如果路径存在则为真

    注意:某些版本之前可能需要加上“is_”前缀

    9.9.5 判断整除

    • even: 判断数值是否是偶数,偶数则为真
    • odd: 判断数值是否是奇数,奇数则为真
    • divisibleby(num): 判断是否可以整除指定的数值,如果除以指定的值以后余数为0,则返回真

    9.5.6 版本号比较

     version: 可以用于对比两个版本号的大小,或者与指定的版本号进行对比,语法version('版本号','比较操作符')。2.5版本此test从version_compare 更名为version。

    9.5.7 列表比较

    • subset: 判断一个list是不是另一个list的子集,是则为真
    • siperset: 判断一个list是不是另一个list的父集,是则为真

    注:2.5版本之前是issubset和issuperset

    9.5.8 字符串与数字判断

    • string: 判断对象是否是一个字符串,是则为真
    • number: 判断对象是否是一个数字,是则为真

    9.5.9 failed_when 

    先看个例子:

    有些时候,我们需要通过返回的字符串来判断是否failed。例如:

    也就是说,我们只有在返回结果中出现字符串‘FAILED’,我们才认为我们的task 失败了(因为command 命令即使执行成功,返回值才是我们要判断失败与否的关键)。

    9.5.10 changed_when

    与failed_when类似。示例:

    在使用command /shell 模块的时候ansible playbook 会按照自己的判断来决定是否changed了。

    有时候我们仅仅是ls 了一下, ansible-playbook 也会认为是changed了,可能这并不是我们想要的,这个时候我们就要用例子中方法来修改task的状态了:

    9.10 循环

    9.10.1 标准循环

    为了保持简洁,重复的任务可以用以下简写的方式:

    如果你在变量文件中或者 ‘vars’ 区域定义了一组YAML列表,你也可以这样做:

    with_items: "{{somelist}}"
    

    以上写法与下面是完全等同的:

    使用 ‘with_items’ 用于迭代的条目类型不仅仅支持简单的字符串列表。如果你有一个哈希列表,那么你可以用以下方式来引用子项:

    注:如果同时使用 when 和 with items (或其它循环声明), when声明会为每个条目单独执行。

    9.10.2嵌套循环

    循环也可以嵌套:

    执行一下:

    和以上介绍的with items一样,你也可以使用预定义变量:

    9.10.3 对哈希表使用循环

    哈希表:用于存储Key-Value键值对的集合。

    使用with_dict 来循环哈希表中的元素:

    9.10.4 对文件列表使用循环

    with_fileglob 可以以非递归的方式来模式匹配单个目录中的文件

    注:当在role中对 with_fileglob 使用相对路径时, Ansible会把路径映射到`roles/<rolename>/files`目录。

    9.10.5 对并行数据集使用循环(不常见)

    假设你通过某种方式加载了以下变量数据:

    ---
    alpha: [ 'a', 'b', 'c', 'd' ]
    numbers:  [ 1, 2, 3, 4 ]
    

    如果你想得到’(a, 1)’和’(b, 2)’之类的集合.可以使用with_together:

    不足的数据则为空:

    注:这种使用方式不常见。

    9.10.6 对子元素使用循环

    假设你想对一组用户做一些动作,比如创建这些用户,并且允许它们使用一组SSH key来登录。

    如何实现? 先假设你有按以下方式定义的数据,可以通过”vars_files”或”group_vars/all”文件加载:

    那么可以这样实现:

    根据mysql hosts以及预先给定的privs subkey列表,我们也可以在嵌套的subkey中迭代列表:

    9.10.7 对整数序列使用循环

    with_sequence 可以以升序数字顺序生成一组序列。你可以指定起始值、终止值,以及一个可选的步长值。

    指定参数时也可以使用key=value这种键值对的方式。如果采用这种方式,format是一个可打印的字符串。

    字值可以被指定为10进制,16进制(0x3f8)或者八进制(0600)。负数则不受支持。请看以下示例:

    9.10.8 随机选择

    random_choice功能可以用来随机获取一些值。它并不是负载均衡器(已经有相关的模块了)。它有时可以用作一个简化版的负载均衡器,比如作为条件判断:

    提供的字符串中的其中一个会被随机选中。

    9.10.9 Do-Until循环

    有时你想重试一个任务直到达到某个条件。比如:

    上面的例子递归运行shell模块,直到模块结果中的stdout输出中包含”all systems go”字符串,或者该任务按照10秒的延迟重试超过5次。retries和delay的默认值分别是3和5。

    该任务返回最后一个任务返回的结果。单次重试的结果可以使用-vv选项来查看。被注册的变量会有一个新的属性attempts,值为该任务重试的次数。

    9.10.10 查找第一个匹配的文件(不常见)

    这其实不是一个循环,但和循环很相似,如果你想引用一个文件,而该文件是从一组文件中根据给定条件匹配出来的。这组文件中部分文件名由变量拼接而成。

    针对该场景你可以这样做:

    该功能还有一个更完整的版本,可以配置搜索路径.请看以下示例:

    9.10.11 迭代程序的执行结果(不常见)

    有时你想执行一个程序,而且按行循环该程序的输出。Ansible提供了一个优雅的方式来实现这一点。但请记住,该功能始终在本地机器上执行,而不是远程机器。

    例如,find一堆文件出来,copy走。

    如果你想远程执行命令,那么以上方法则不行。但你可以这样写:

    9.10.12 使用索引循环列表(不常见)

    如果你想循环一个列表,同时得到一个数字索引来标明你当前处于列表什么位置,那么你可以这样做:

    9.10.13 循环配置文件(不常见)

    ini插件可以使用正则表达式来获取一组键值对。因此,我们可以遍历该集合。以下是我们使用的ini文件:

      

    以下是使用 with_ini 的例子:

    以下是返回的值:

    9.10.14 扁平化列表(不常见)

    在罕见的情况下,你可能有几组列表,列表中会嵌套列表。而你只是想迭代所有列表中的每个元素,比如有一个非常疯狂的假定的数据结构:

    你可以看到列表中的包到处都是。那么如果想安装两个列表中的所有包呢?

    9.10.15 循环中使用注册器

    当对处于循环中的某个数据结构使用 register 来注册变量时,结果包含一个 results 属性,这是从模块中得到的所有响应的一个列表。

    以下是在 with_items 中使用 register 的示例:

    返回的数据结构如下,与非循环结构中使用 register 的返回结果是不同的,非循环结构的返回结果类似下面:

    而循环结构的返回结果如:

    随后的任务可以用以下方式来循环注册变量,用来检查结果值:

    9.11 包含

    在编程的时候,我们一般都会将重复性的代码提取出来,作为一个逻辑单元,这个逻辑单元通常被称为“函数”或者“方法”。如果需要修改这段逻辑,只需要修改函数本身即可。而且这样还使得程序的可读性更强。

    Ansible中也有类似的功能,这种功能被称为包含,通过包含,我们可以在一个playbook中包含另一个文件,以便实现我们想要的效果。

    Ansible有两种包含的操作模式:动态和静态。Ansible 2.4引入了include和import的概念。

    • 如果您使用import*包含Task(import_playbook,import_tasks等),它将是静态的。
    • 如果您使用include*包含Task(include_tasks,include_role等),它将是动态的。

    使用include包含Task(用于task文件和Playbook级包括)仍然可用,但现在被认为已被弃用,建议使用 include_tasks和 import_tasks。

    9.11.1 include_tasks和import_tasks之间的差异

    区别一:

    • include_tasks:是动态的,在运行时展开。即在执行play之前才会加载自己变量。when只应用一次, 被include的文件名可以使用变量。
    • import_tasks:是静态的,在加载时展开。即在playbooks解析阶段将父task变量和子task变量全部读取并加载。因为是加载时展开的,文件名的变量不能是动态设定的。 when在被import的文件里的每个task,都会重新检查一次。

    例:

    b.yml

    y.yml:

    运行:

    因为 mode被改变之后, include_tasks不会重新evaluate mode, import_tasks会根据变化后的mode值重新evaluate每个task的条件。

    区别二:

    • include_tasks方法调用的文件名称可以加变量
    • import_tasks方法调用的文件名称不可以有变量

    对于include_tasks,导入文件时可以指定变量:

    当使用import_tasks方法时,执行报错。ansible也给出了错误原因,当使用static include时,是不能使用变量的:

    9.11.2 include_tasks和import_tasks的优缺点

    使用include*语句的主要优点是循环。当循环与include*一起使用时,包含的任务或角色将为循环中的每个项目执行一次。

    与import*语句相比,使用include*有一些限制:

    • 仅存在于动态包含内的标签不会显示在-list-tags输出中。
    • 仅存在于动态包含内的任务不会显示在-list-tasks输出中。
    • 您不能使用notify来触发来自动态包含内部的处理程序名称。
    • 您不能使用--start-at-task开始执行动态包含内的任务。

    与include*相比,使用import*也可能有一些限制:

    • 如上所述,循环不能用于导入。
    • 当使用目标文件或角色名称的变量时,不能使用来自库存源(主机/组变量等)的变量。

    总而言之,没有使用with的包含,就使用import,使用了with,那就用include。

    9.11.3 import_playbook

    如果想引入整个playbook,则需要使用include_playbook模块来代替include。在ansible2.8版本之后,include功能就被删除掉了。

    要求:

    • 包含一个带有要执行的play列表的文件。
    • 带有play列表的文件必须被包含在顶层
    • 不能在play中执行该操作。

    例如:

    main.yml

    otherplays.yaml

    otherplays.yaml中导入stuff.yaml,由于不是在顶层导入,而是在play中导入,所以执行会失败。

    9.11.4 include_vars

    include_vars模块可以包含JSON或YAML文件中的定义变量,覆盖已定义的主机变量和playbook变量。如:

    9.11.5 include_role 与 import_role

    后续再讲。

    9.12 Roles

    上述已经讲过tasks和handlers,那么如何组织playbook才是最好的方式呢?

    roles!

    roles是基于一个已知的文件结构,去自动加载某些vars_file,tasks以及handlers。基于roles对内容进行分组,使得我们可以容易地与其他用户分享roles。

    一个项目的结构如下:

    一个 playbook 如下:

    这个 playbook 为一个角色x指定了如下的行为:

    • 如果 roles/x/tasks/main.yml 存在, 其中列出的 tasks 将被添加到 play 中
    • 如果 roles/x/handlers/main.yml 存在, 其中列出的 handlers 将被添加到 play 中
    • 如果 roles/x/vars/main.yml 存在, 其中列出的 variables 将被添加到 play 中
    • 如果 roles/x/meta/main.yml 存在, 其中列出的 “角色依赖” 将被添加到 roles 列表中 (1.3 and later)
    • 所有 copy tasks 可以引用 roles/x/files/ 中的文件,不需要指明文件的路径。
    • 所有 script tasks 可以引用 roles/x/files/ 中的脚本,不需要指明文件的路径。
    • 所有 template tasks 可以引用 roles/x/templates/ 中的文件,不需要指明文件的路径。
    • 所有 include tasks 可以引用 roles/x/tasks/ 中的文件,不需要指明文件的路径。

    如果 roles 目录下有文件不存在,这些文件将被忽略。比如 roles 目录下面缺少了 vars/ 目录,这也没关系。

    如果你在 playbook 中同时使用 roles 和 tasks,vars_files 或者 handlers,roles 将优先执行。如果想在roles前执行一些task,可以这样:

    而且,如果你愿意,也可以使用参数化的 roles,这种方式通过添加变量来实现,比如:

    当一些事情不需要频繁去做时,你也可以为 roles 设置触发条件,像这样:

    它的工作方式是:将条件子句应用到 role 中的每一个 task 上。

    最后,你可能希望给 roles 分配指定的 tags。比如:

    9.12.1 roles各目录含义

    • tasks目录:角色需要执行的主任务文件放置在此目录中,默认的主任务文件为main.yml,当调用角色时,默认会执行main.yml文件中的任务。也可以将其他需要执行的任务通过include的方式包含在tasks/main.yml文件中。
    • handlers目录:当角色需要调用handlers时,默认会在此目录中的main.yml文件中查找对应的handler
    • defaults目录:角色会使用到的变量可以写入到此目录中的main.yml文件中,通常defaults/main.yml文件中的变量都用于设置默认值,以便在你没有设置对应变量值时,变量有默认的值可以使用,定义在defaults/main.yml文件中的变量的优先级是最低的。
    • vars目录:角色会使用到的变量可以写入到此目录中的main.yml文件中。与defaults/main.yml的区别在于,defaults/main.yml文件中的变量的优先级是最低的,而vars/main.yml文件中的变量的优先级非常高,如果只是想提供一个默认的配置,可以定义在defaults/main.yml,如果想要确保别人在调用角色时,使用的值就是你指定的值,则可以将变量定义在vars/main.yml中,因为定义在vars/main.yml文件中的变量的优先级非常高,所以其值难以覆盖。
    • meta目录:如果想要赋予这个角色一些元数据,则可以将元数据写入到meta/main.yml文件中,这些元数据用于描述角色的相关属性,比如作者信息、角色的主要作用等。也可以在meta/main.yml文件中定义这个角色依赖于哪些角色,或者改变角色的默认调用设定。
    • templates目录:角色相关的模板可以放置在此目录中。当使用角色相关的模板时,如果没有指定路径,会默认从此目录中查找对应名称模板文件。
    • files目录:角色可能会用到的一些其他文件可以放置在此目录中。

    注:上述目录并不全是必须的,如果你的角色中没有用到对应这些目录的模块,那么对应的目录就不需要包含。一般情况下,至少要有一个tasks目录。

    9.12.2 角色默认变量

    角色默认变量允许你为 included roles 或者 dependent roles 设置默认变量。要创建默认变量,只需在 roles 目录下添加 defaults/main.yml 文件。这些变量在所有可用变量中拥有最低优先级,可能被其他地方定义的变量(包括 inventory 中的变量)所覆盖。

    9.12.3 role查找

    我们可以把单个role的目录写在与入口playbook同级的目录下,也可以放在同级的roles目录下,还可以通过/etc/ansible/ansible.cfg中的roles_path(默认为家目录的.ansible/roles目录)指定。

    role查找优先级:同级roles目录 > /etc/ansible/ansible.cfg中的roles_path(默认为家目录的.ansible/roles目录) > 同级目录下

    即使role目录不处于上述目录中的任何一个,也可以使用绝对路径的方式,调用相应的角色:

    这种写法其实不算正规,标准语法应该这样:

    在roles关键字中使用role关键字指定角色对应的绝对路径,可以直接调用角色。即使不使用绝对路径,也可以使用同样的语法指定角色名。如:

    9.12.4 role变量传递

    我们可以在调用role的时候传递对应的变量。如:

    默认变量也可以定义在defaults/main.yml中,不过优先级是最低的。role使用的变量的优先级为:vars/main.yml > 调用role时传递的vars > defaults/main.yml。

    还可以写成:

    另外,角色中的变量是全局可访问的。例如:

    在调用webserver这个角色时传递的变量testvar的值,是可以传递给web这个角色的。在两个角色里面对这个变量进行debug,都可以得到这个值:

    也可以为role设置触发条件,如:

    最后,还可以给role分配指定的tags。如:

    9.12.5 重复调用

    默认情况下,我们无法多次调用同一个角色。也就是说,如下的playbook只会调用一次webserver角色:

    执行该playbook,可以发现,webserver中的debug模块只输出了一次:

    如果想要多次调用同一个角色,有两种方法。如下:

    • 方法一:设置角色的allow_duplicates属性,让其支持重复调用
    • 方法二:调用角色时,传入的参数值不同

    方法一需要为角色设置allow_duplicates属性,该属性需要设置在meta/main.yml中,如:

    方法二,当调用角色时需要传入参数,如果传入参数的值不同,也可以连续调用多次。如:

    9.12.6 role handler

    如果想在角色中使用一些handlers以便进行触发,则可以直接将对应的handlers任务写入到handlers/main.yml文件中,示例:

    9.12.7 角色依赖

    角色依赖 使你可以自动地将其他 roles 拉取到现在使用的 role 中。角色依赖保存在 roles 目录下的 meta/main.yml 文件中。这个文件应包含一列 roles 和 为之指定的参数。

    示例:

    “角色依赖” 可以通过绝对路径指定,如同顶级角色的设置:

    “角色依赖” 也可以通过源码控制仓库或者 tar 文件指定,使用逗号分隔:路径、一个可选的版本(tag, commit, branch 等等)、一个可选友好角色名(尝试从源码仓库名或者归档文件名中派生出角色名):

    “角色依赖” 总是在 role (包含”角色依赖”的role)之前执行,并且是递归地执行。默认情况下,作为 “角色依赖” 被添加的 role 只能被添加一次,如果另一个 role 将一个相同的角色列为 “角色依赖” 的对象,它不会被重复执行。但这种默认的行为可被修改,通过添加 allow_duplicates: yes 到 meta/main.yml 文件中。

    比如,一个 role 名为 ‘car’,它可以添加名为 ‘wheel’ 的 role 到它的 “角色依赖” 中:

    wheel 角色的 meta/main.yml 文件包含如下内容:

     

    最终的执行顺序是这样的:

    tire(n=1)

    brake(n=1)

    wheel(n=1)

    tire(n=2)

    brake(n=2)

    wheel(n=2)

    ...

    car

    9.12.8 在 Roles 中嵌入模块

    如果您编写一个定制模块,则可能希望将其作为角色的一部分进行分发。

    在角色的tasks和handlers结构旁边,添加一个名为‘library’的目录,然后将定制模块直接包含在其中。

    在一个role中定义的模块,可供其他role使用:

    9.12.9 include_role与import_role

    在后来版本(ansible>=2.4)中,ansible引入了 import_role(静态)和include_role(动态)方法。

    ---
    # playbooks/test.yaml
    - hosts: node1
      tasks:
        - include_role:
            name: role_A
          vars:
            name: maurice
            age: 100
        - import_role:
            name: role_B
    

    比较于 roles 语句,import_role 和 include_role 的优点如下:

    • 可以在task之间穿插导入某些role,这点是「roles」没有的特性。
    • 更加灵活,可以使用「when」语句等判断是否导入。

    关于include_role(动态)和import_role(静态)的区别,可以参考之前的include_tasks和import_tasks。

    也正是因为 include_task 是动态导入,当我们给 include_role 导入的role打tag时,实际并不会执行该roletask

    举个例子,当我们使用include导入role_A,使用import导入role_B时:

    ---
    # playbooks/test.yaml
    - hosts: node1
      tasks:
        - include_role:
            name: role_A
          tags: maurice
        - import_role:
            name: role_B
          tags: maurice
    

    role_A内容如下:

    ---
    # tasks file for role_A
    
    - debug:
        msg: "age"
    
    - debug:
        msg: "maurice"
    

    执行结果显示,role_A虽然被引用,但里面的task并没有执行:

      

    (End)

    天道酬勤
  • 相关阅读:
    顶点着色器详解 (Vertex Shaders)
    Texture Filter中的Bilinear、Trilinear以及Anistropic Filtering
    mysql远程访问1045的问题解决
    在eclipse上集成安装阿里巴巴代码规约P3C插件
    Mybatis Generator主要配置详解
    redis cluster最简配置
    springboot +redis配置
    redis cluster的conf配置文件配置
    MySQL与Oracle的主要区别
    java EE中使用PO和VO的注意事项
  • 原文地址:https://www.cnblogs.com/wangwei1/p/12966227.html
Copyright © 2020-2023  润新知