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

举报
山河已无恙 发表于 2022/09/30 23:20:40 2022/09/30
【摘要】 写在前面今天和小伙伴分享一些ansible剧本中数据循环的方式相关笔记博文内容比较简单主要介绍的常见的循环对比使用过滤器和查找插件在复杂数据结构上实施迭代循环食用方式:了解Ansible基础语法理解不足小伙伴帮忙指正 傍晚时分,你坐在屋檐下,看着天慢慢地黑下去,心里寂寞而凄凉,感到自己的生命被剥夺了。当时我是个年轻人,但我害怕这样生活下去,衰老下去。在我看来,这是比死亡更可怕的事。----...

写在前面


  • 今天和小伙伴分享一些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) }}"
$

运行剧本是一样的效果,这里第三种方式通过,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

$

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

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

迭代场景示例

在列表的列表上迭代

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

---
- 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) }}"

上面迭代的数据为一个嵌套的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

这个时候,我们可以使用 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 ]
$ 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

有时候希望在剧本里获取一些序列,可以通过 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
$ 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

若希望重构旧的剧本里的 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 ]
$ 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
$
  • 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 ]

$
$ 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


迭代嵌套列表

来自变量文件、Ansible 事实和外部服务的数据通常是简单的数据结构的组合,如列表和字典:

$ 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') }}"

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

  • 第一个元素包含对每个用户的引用。
  • 第二个元素包含该用户的 liruilong 字典中的单个条目的引用。
$ 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

迭代字典

users 变量定义如下:
在 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  }}"
$
$ 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

$

迭代文件统配模式

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

$ 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

$ cat file_loop.yaml
---
- name: demo
  hosts: serverc
  tasks:
          - name: file demo
            debug: msg={{lookup('fileglob','~/.bash*')}}


$ 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

$
【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息, 否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。