• Ansible最佳实践之Playbook高级循环任务如何操作


    写在前面


    • 今天和小伙伴分享一些ansible剧本中数据迭代的笔记
    • 博文内容比较简单
    • 主要介绍的常见的迭代对比
    • 使用过滤器和查找插件在复杂数据结构上实施迭代循环
    • 食用方式:了解Ansible基础语法
    • 理解不足小伙伴帮忙指正

    傍晚时分,你坐在屋檐下,看着天慢慢地黑下去,心里寂寞而凄凉,感到自己的生命被剥夺了。当时我是个年轻人,但我害怕这样生活下去,衰老下去。在我看来,这是比死亡更可怕的事。--------王小波


    循环和查找插件

    Ansible 在 2.5 中引入了loop关键字。以前任务迭代通过使用with_开头并以查找的名称结尾的关键字的方法。与 loop 等效的是 with_list,设计用于在简单的扁平列表中进行迭代,对于简单的列表来讲,loop 是最佳语法。

    以下三种语法具有相同的结果,其中第一个使用的loop是首选:

    $ cat loop_demo.yaml
    ---
    - name: loop Play
      hosts: servera
      gather_facts: no
      vars:
        - mylist:
            - li
            - rui
            - long
      tasks:
        - name: using loop
          debug: msg={{ item }}
          loop: "{{ mylist }}"
        - name: using with_list
          debug: msg={{ item }}
          with_list: "{{ mylist }}"
        - name: using lookup plugin
          debug: msg={{ item }}
          loop: "{{ lookup('list',mylist) }}"
    $
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    运行剧本是一样的效果,这里第三种方式通过,lookup插件的的方式实现的,lookup 插件是 Jinja2 模板引擎的 Ansible 扩展。通过插件使 Ansible 能够使用外部来源的数据,我们这里使用lookup来将一个数据转化为list

    $ ansible-playbook  loop_demo.yaml
    
    PLAY [loop Play] *********************************************************************************************
    
    TASK [using loop] ********************************************************************************************
    ok: [servera] => (item=li) => {
        "msg": "li"
    }
    ok: [servera] => (item=rui) => {
        "msg": "rui"
    }
    ok: [servera] => (item=long) => {
        "msg": "long"
    }
    
    TASK [using with_list] ***************************************************************************************
    ok: [servera] => (item=li) => {
        "msg": "li"
    }
    ok: [servera] => (item=rui) => {
        "msg": "rui"
    }
    ok: [servera] => (item=long) => {
        "msg": "long"
    }
    
    TASK [using lookup plugin] ***********************************************************************************
    ok: [servera] => (item=li) => {
        "msg": "li"
    }
    ok: [servera] => (item=rui) => {
        "msg": "rui"
    }
    ok: [servera] => (item=long) => {
        "msg": "long"
    }
    
    PLAY RECAP ***************************************************************************************************
    servera                    : ok=3    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0
    
    $
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41

    使用 loop 关键字替代 with_* 样式的循环具有以下优点:

    • 无需记住或查找 with_* 样式关键字来满足当前迭代场景。
    • 专注于学习 Ansible 中提供的插件和过滤器,适用性比单纯的迭代更广泛。
    • 可以通过ansible-doc -t lookup命令访问查找插件文档,结合插件文档实现复杂遍历

    使用loop关键字和dict插件替换"with_dict"关键字

    ---
    - hosts: testB
      remote_user: root
      gather_facts: no
      vars:
        users:
          alice: female
          bob: male
      tasks:
      - debug:
          msg: "{{item.key}} is {{item.value}}"
        loop: "{{ lookup('dict',users) }}"
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    在2.6版本的官网手册中,官方开始推荐使用 loop加filter 的方式来替代"loop加lookup"的方式,

    ---
    - hosts: testB
      remote_user: root
      gather_facts: no
      vars:
        users:
          alice: female
          bob: male
      tasks:
      - debug:
          msg: "{{item.key}} is {{item.value}}"
        loop: "{{ users | dict2items }}"
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    loop 方式还提供了 loop_control属性

    可以用于控制循环的行为,添加索引之类,比如,使用loop_control的index_var选项,就能在遍历列表时,将元素对应的索引写入到指定的变量中,除了index_var选项,loop_control还有一些其他的选项可用,此处我们就来总结一下这些选项。

    pause选项能够让我们设置每次循环之后的暂停时间,以秒为单位,换句话说就是设置每次循环之间的间隔时间,示例如下

    迭代场景示例

    在列表的列表上迭代

    对于需要多级迭代的嵌套数据,使用传统的循环方式,往往获取不到子数据,即不能实现数据的扁平化处理。

    ---
    - name: loop Play
      hosts: servera
      gather_facts: no
      vars:
        - mylist:
            - ['l','i']
            - rui
            - long
      tasks:
        - name: using loop
          debug: msg={{ item }}
          loop: "{{ mylist }}"
        - name: using with_list
          debug: msg={{ item }}
          with_list: "{{ mylist }}"
        - name: using lookup plugin
          debug: msg={{ item }}
          loop: "{{ lookup('list',mylist) }}"
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    上面迭代的数据为一个嵌套的list,使用前面所讲的迭代方式不能对嵌套的子数组进行迭代。

    三种不同方式的测试,都不能做的扁平化的迭代

    $ ansible-playbook  loop_demo.yaml
    
    PLAY [loop Play] *********************************************************************************************
    
    TASK [using loop] ********************************************************************************************
    ok: [servera] => (item=['l', 'i']) => {
        "msg": [
            "l",
            "i"
        ]
    }
    ok: [servera] => (item=rui) => {
        "msg": "rui"
    }
    ok: [servera] => (item=long) => {
        "msg": "long"
    }
    
    TASK [using with_list] ***************************************************************************************
    ok: [servera] => (item=['l', 'i']) => {
        "msg": [
            "l",
            "i"
        ]
    }
    ok: [servera] => (item=rui) => {
        "msg": "rui"
    }
    ok: [servera] => (item=long) => {
        "msg": "long"
    }
    
    TASK [using lookup plugin] ***********************************************************************************
    ok: [servera] => (item=['l', 'i']) => {
        "msg": [
            "l",
            "i"
        ]
    }
    ok: [servera] => (item=rui) => {
        "msg": "rui"
    }
    ok: [servera] => (item=long) => {
        "msg": "long"
    }
    
    PLAY RECAP ***************************************************************************************************
    servera                    : ok=3    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48

    这个时候,我们可以使用 with_items 关键字来迭代复杂的列表,实现列表数据的扁平化处理

    $ cat loop_demos.yaml
    ---
    - name: loop Play
      hosts: servera
      gather_facts: no
      tasks:
        - name: using with_list
          debug:
            msg: "{{ item }}"
          with_items:
            - [ 1,2,4 ]
            - [ r,u ]
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    $ ansible-playbook  loop_demos.yaml
    
    PLAY [loop Play] *********************************************************************************************
    
    TASK [using with_list] ***************************************************************************************
    ok: [servera] => (item=1) => {
        "msg": 1
    }
    ok: [servera] => (item=2) => {
        "msg": 2
    }
    ok: [servera] => (item=4) => {
        "msg": 4
    }
    ok: [servera] => (item=r) => {
        "msg": "r"
    }
    ok: [servera] => (item=u) => {
        "msg": "u"
    }
    
    PLAY RECAP ***************************************************************************************************
    servera                    : ok=1    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    获取序列

    有时候希望在剧本里获取一些序列,可以通过 whit_sequence 关键字实现

    $ cat loop_demo_sq.yaml
    ---
    - name: liruilong demo
      hosts: servera
      gather_facts: n
      tasks:
        - name: test whit_sequence
          debug: msg={{ item }}
          with_sequence:
            start=1
            end=5
            stride=1
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    $ ansible-playbook  loop_demo_sq.yaml
    
    PLAY [liruilong demo] ****************************************************************************************
    
    TASK [test whit_sequence] ************************************************************************************
    ok: [servera] => (item=1) => {
        "msg": "1"
    }
    ok: [servera] => (item=2) => {
        "msg": "2"
    }
    ok: [servera] => (item=3) => {
        "msg": "3"
    }
    ok: [servera] => (item=4) => {
        "msg": "4"
    }
    ok: [servera] => (item=5) => {
        "msg": "5"
    }
    
    PLAY RECAP ***************************************************************************************************
    servera                    : ok=1    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    flatten 代替 with_items

    若希望重构旧的剧本里的 with_items 任务以使用 loop 关键字,可使用 flatten 过滤器。

    $ cat loop_demos.yaml
    ---
    - name: loop Play
      hosts: servera
      gather_facts: no
      tasks:
        - name: using with_list
          debug:
            msg: "{{ item }}"
            #with_items:
          loop:
            - [ 1,2,4,[3,4,5,[6]] ]
            - [ r,u ]
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    $ ansible-playbook loop_demos.yaml
    
    PLAY [loop Play] *********************************************************************************************
    TASK [using with_list] ***************************************************************************************
    ok: [servera] => (item=[1, 2, 4, [3, 4, 5, [6]]]) => {
        "msg": [
            1,
            2,
            4,
            [
                3,
                4,
                5,
                [
                    6
                ]
            ]
        ]
    }
    ok: [servera] => (item=['r', 'u']) => {
        "msg": [
            "r",
            "u"
        ]
    }
    
    PLAY RECAP ***************************************************************************************************
    servera                    : ok=1    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0
    $
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • flatten 过滤器将以递归方式搜索嵌入式列表,并从发现的值创建一个列表。

    • flatten 过滤器接受 levels 参数,用于指定搜索嵌入式列表的整数的级别数,levels = 1 与 with_items 隐式进行的相同的一级扁平化。

    $ cat loop_demos.yaml
    ---
    - name: loop Play
      hosts: servera
      gather_facts: no
      tasks:
        - name: using with_list
          debug:
            msg: "{{ item }}"
          loop: "{{ numList | flatten(levels=3) }}"
          vars:
                  numList:
                     - [ 1,2,4,[3,4,5,[6]] ]
                     - [ r,u ]
    
    $
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    执行测试

    $ ansible-playbook loop_demos.yaml
    
    PLAY [loop Play] *********************************************************************************************
    
    TASK [using with_list] ***************************************************************************************
    ok: [servera] => (item=1) => {
        "msg": 1
    }
    ok: [servera] => (item=2) => {
        "msg": 2
    }
    ok: [servera] => (item=4) => {
        "msg": 4
    }
    ok: [servera] => (item=3) => {
        "msg": 3
    }
    ok: [servera] => (item=4) => {
        "msg": 4
    }
    ok: [servera] => (item=5) => {
        "msg": 5
    }
    ok: [servera] => (item=6) => {
        "msg": 6
    }
    ok: [servera] => (item=r) => {
        "msg": "r"
    }
    ok: [servera] => (item=u) => {
        "msg": "u"
    }
    
    PLAY RECAP ***************************************************************************************************
    servera                    : ok=1    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0
    
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37

    迭代嵌套列表

    subelements 过滤器从 names 变量数据中创建一个新列表。列表中的每一项本身是一个两元素列表。

    $ cat  subelements.yaml
    ---
    - name: liruilong demo
      hosts: servera
      vars:
        names:
          - liruilong:
                - key: li
          - liruilong:
                - key: rui
          - liruilong:
                - key: long
      tasks:
        - name: loop demo
          debug: msg={{ item }}
          loop: "{{ names | subelements('liruilong') }}"
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    执行测试

    $ ansible-playbook subelements.yaml -b
    
    PLAY [liruilong demo] ****************************************************************************************
    
    TASK [Gathering Facts] ***************************************************************************************
    ok: [servera]
    
    TASK [loop demo] *********************************************************************************************
    ok: [servera] => (item=[{'liruilong': [{'key': 'li'}]}, {'key': 'li'}]) => {
        "msg": [
            {
                "liruilong": [
                    {
                        "key": "li"
                    }
                ]
            },
            {
                "key": "li"
            }
        ]
    }
    ok: [servera] => (item=[{'liruilong': [{'key': 'rui'}]}, {'key': 'rui'}]) => {
        "msg": [
            {
                "liruilong": [
                    {
                        "key": "rui"
                    }
                ]
            },
            {
                "key": "rui"
            }
        ]
    }
    ok: [servera] => (item=[{'liruilong': [{'key': 'long'}]}, {'key': 'long'}]) => {
        "msg": [
            {
                "liruilong": [
                    {
                        "key": "long"
                    }
                ]
            },
            {
                "key": "long"
            }
        ]
    }
    
    PLAY RECAP ***************************************************************************************************
    servera                    : ok=2    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 第一个元素包含对每个用户的引用。
    • 第二个元素包含该用户的 liruilong 字典中的单个条目的引用。

    迭代字典

    在 Ansible 2.5 之前,必须使用 with_dict 关键字来迭代字典中的键值对。

    item 变量具有两个属性:key 和 value。

    key 属性包含一个字典键的值,而 value 属性则包含与字典关联的数据

    $ cat dist_demo.yaml
    ---
    - name: 字典 demo
      hosts: servera
      vars:
        users:
          xiaoming:
             name: xiaoming
          xiaoli:
             name: xiaoli
      tasks:
        - name: dist demo
          debug: msg={{ item.value.name }}
          loop: "{{ users | dict2items }}"
        - name: dist demo1
          debug: msg={{ item.key }}
          with_dict : "{{ users  }}"
    $
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    这里使用 dict2item 可以实现相同的功能

    $ ansible-playbook dist_demo.yaml
    
    PLAY [字典 demo] *************************************************************************************************************
    
    TASK [Gathering Facts] *****************************************************************************************************
    ok: [servera]
    
    TASK [dist demo] ***********************************************************************************************************
    ok: [servera] => (item={'key': 'xiaoming', 'value': {'name': 'xiaoming'}}) => {
        "msg": "xiaoming"
    }
    ok: [servera] => (item={'key': 'xiaoli', 'value': {'name': 'xiaoli'}}) => {
        "msg": "xiaoli"
    }
    
    TASK [dist demo1] **********************************************************************************************************
    ok: [servera] => (item={'key': 'xiaoming', 'value': {'name': 'xiaoming'}}) => {
        "msg": "xiaoming"
    }
    ok: [servera] => (item={'key': 'xiaoli', 'value': {'name': 'xiaoli'}}) => {
        "msg": "xiaoli"
    }
    
    PLAY RECAP *****************************************************************************************************************
    servera                    : ok=3    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0
    
    $
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27

    迭代文件通配模式

    通过fileglob查找插件构建循环,以迭代与所提供的文件通配模式的文件列表:

    $ cat file_loop.yaml
    ---
    - name: demo
      hosts: serverc
      tasks:
              - name: file demo
                debug: msg={{lookup('fileglob','~/.bash*')}}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    执行测试

    $ ansible-playbook  file_loop.yaml
    
    PLAY [demo] ****************************************************************************************************************
    
    TASK [Gathering Facts] *****************************************************************************************************
    ok: [serverc]
    
    TASK [file demo] ***********************************************************************************************************
    ok: [serverc] => {
        "msg": "/home/student/.bash_logout,/home/student/.bash_profile,/home/student/.bashrc,/home/student/.bash_history"
    }
    
    PLAY RECAP *****************************************************************************************************************
    serverc                    : ok=2    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    可以使用 query 代替 lookup, 以列表的形式展示

    $ sed 's/lookup/query/' file_loop.yaml -i
    $ ansible-playbook  file_loop.yaml
    
    PLAY [demo] ****************************************************************************************************************
    
    TASK [Gathering Facts] *****************************************************************************************************
    ok: [serverc]
    
    TASK [file demo] ***********************************************************************************************************
    ok: [serverc] => {
        "msg": [
            "/home/student/.bash_logout",
            "/home/student/.bash_profile",
            "/home/student/.bashrc",
            "/home/student/.bash_history"
        ]
    }
    
    PLAY RECAP *****************************************************************************************************************
    serverc                    : ok=2    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0
    
    $
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    博文参考


    《Red Hat Ansible Engine 2.8 DO447》

  • 相关阅读:
    嵌入式虹膜人脸多模态识别模组Max1/Max1+让创造由点及面
    Keepalived双机热备——Haproxy搭建web群集
    GMOS6803为7频段高精度RTK系统应用桥梁监测
    Embedding
    【无标题】
    [附源码]计算机毕业设计SpringbootON-FIT
    中国人民大学与加拿大女王大学金融硕士——人生总要逼自己一把
    Java语言编写猜字游戏
    基于matlab的瑞利衰落信道建模和仿真
    【ansible问题处理】远程执行用户环境变量加载问题
  • 原文地址:https://blog.csdn.net/sanhewuyang/article/details/128148630