codecamp

Ansible 在 Playbooks 使用 loops

在 Shell Script 里,我们会使用 for 和 while 等回圈 (loop) 来简化重复的程式码,而在 Ansible 我们也可用 loop 来简化重复的任务 (Tasks)。以下冻仁将介绍常见的 loop 语法。

automate_with_ansible_practice-23.jpg

图片来源:http://screencastsolutions.ca/looping-for-continuous-play/

标准回圈 (Standard Loops)

首先让我们以简单的重复印出 3 笔讯息为例。

Shell Script

先复习一下 Shell Script 的写法。

  1. 建立 for loop 的 Script。

    #!/bin/bash
    $ vi bash_loop.sh
    for X in 0 1 2; do
     echo Loop $X
    done
    
    • 在第 3 行,我们用了 for,并代入了 0, 1, 2 三个值到 $X 变数。
    • 在第 4 行时,则用了 echo,印出讯息和 $X 变数。
  2. 执行 Script:可以看到底下跑了 3 次的loop。

    $ ./bash_loop.sh
    Loop 0
    Loop 1
    Loop 2
    

Ansible Playbooks

我们需通过 item 和 with_items 来使用 Ansible 的 loop,其 item 为预设名,一般情况下不可修改。

在 Ansible 2.1 新增了 Loop Control 的语法,可通过 loop_control 和 loop_var 来自定 item 的名字,这在多重 loop 等较复杂的环境下会有很大的帮助。1

  1. 建立 loop 的 playbook。

    $ vi playbook_loop.yml
    ---
    - name: a basic loop with playbook
     hosts: localhost
     tasks:
       - name: print loop message
         debug:
           msg: "Loop "
         with_items:
           - 0
           - 1
           - 2
    
    • 在第 7, 8 行里,我们用了 debug module 来印出讯息,并定义 item
    • 在第 9 ~ 12 行里,则用了 with_items 将 0, 1, 2 的值传入 item
  2. 执行 Playbook:可以看到 print loop message task 跑了 3 次的 loop。

    $ ansible-playbook playbook_loop.yml
    
    PLAY [a basic loop with playbook] *********************************************
    
    TASK [setup] *******************************************************************
    ok: [localhost]
    
    TASK [print loop message] ******************************************************
    ok: [localhost] => (item=0) => {
       "item": 0,
       "msg": "Loop 0"
    }
    ok: [localhost] => (item=1) => {
       "item": 1,
       "msg": "Loop 1"
    }
    ok: [localhost] => (item=2) => {
       "item": 2,
       "msg": "Loop 2"
    }
    
    PLAY RECAP *********************************************************************
    localhost                  : ok=2    changed=0    unreachable=0failed=0
    
  3. 冻仁常用此手法来安装多个套件,接著以建立 chusiang/ansible-jupyter Docker image 的 setup_jupyter.yml 为例。 2

    $ vi setup_jupyter_yml
    01 ---
    02 - hosts: localhost
    03 
    04   vars:
    05     # Same package on GNU/Linux.
    06     same_packages:
    07       - bash
    08       - bash-completion
    09       - ca-certificates
    10       - curl
    11       - git
    12       - openssl
    13
    14     # Alpine Linux.
    15     apk_packages:
    16       - openssh-client
    17       - vim
    18
    19     # Debian, Ubuntu.
    20     apt_packages: ""
    21     ...
    22
    23   tasks:
    24     # General Linux.
    25     - name: install same packages
    26       package: name= state=present
    27       with_items: ""
    28       when:
    29         - same_packages is defined
    30         - ansible_pkg_mgr != "portage"
    31
    32     # Alpine Linux.
    33     - name: install apk packages
    34       apk: name= state=present
    35       with_items: ""
    36       when:
    37         - apk_packages is defined
    38         - ansible_pkg_mgr == "apk"
    39
    40     # Debian, Ubuntu.
    41     - name: install apt packages
    42       apt: name= state=present
    43       with_items: ""
    44       when:
    45         - apt_packages is defined
    46         - ansible_pkg_mgr == "apt"
    47     ...
    
    • 在第 6, 15, 20 行里,分別宣告 same_packagesapk_packages 和 apt_packages 变数,并传入了几个套件名称。
    • 在第 26, 27, 34, 35, 42, 43 行里,定义了 item,并将 same_packages 变数传入。换句话说就是 install same packages task 会安装 same_packages 定义的所有套件。
    • 由于此例中 apk, apt 的套件名称皆相同,故在第 20 行用了 apt_packages: "" 的手法让 apt_packages = apk_packages

进阶回圈 (Advanced Loops)

如有数个变数需求,可用 item.firstitem.second 类似属性的方式定义 items。

  1. 建立拥有两个 item 属性的 loop 的 playbook。

    $ vi playbook_loop_adv1.yml
    ---
    - name: a advanced loop with playbook
     hosts: localhost
     tasks:
       - name: print loop message
         debug:
           msg: "Loop : "
         with_items:
           - { num: '0', str: 'automate' }
           - { num: '1', str: 'with' }
           - { num: '2', str: 'ansible' }
    
  2. 执行 Playbook:这次除了跑 3 次 loop 以外,还代入 num 和 str 属性的 items。

    $ ansible-playbook playbook_loop_adv1.yml
    
    PLAY [a advanced loop with playbook] *******************************************
    
    TASK [setup] *******************************************************************
    ok: [localhost]
    
    TASK [print loop message] ******************************************************
    ok: [localhost] => (item={u'num': u'0', u'str': u'automate'}) => {
       "item": {
           "num": "0",
           "str": "automate"
       },
       "msg": "Loop 0: automate"
    }
    ok: [localhost] => (item={u'num': u'1', u'str': u'with'}) => {
       "item": {
           "num": "1",
           "str": "with"
       },
       "msg": "Loop 1: with"
    }
    ok: [localhost] => (item={u'num': u'2', u'str': u'ansible'}) => {
       "item": {
           "num": "2",
           "str": "ansible"
       },
       "msg": "Loop 2: ansible"
    }
    
    PLAY RECAP *********************************************************************
    localhost                  : ok=2    changed=0    unreachable=0    failed=0
    
  3. 这部份在新增多个使用者、多个软连结 (soft link) 时都会用到。

    $ vi playbook_loop_adv2.yml
    ---
    - name: a advanced loop with playbook
     hosts: localhost
     tasks:
       - name: create multiple soft link
         file:
           src: "~/vcs/4.docs/automate-with-ansible/lab/ch18/"
           dest: "/tmp/"
           state: link
         with_items:
           - { src: 'playbook_loop.yml', dest: 'loop0.yml' }
           - { src: 'playbook_loop_adv1.yml', dest: 'loop1.yml' }
           - { src: 'playbook_loop_adv2.yml', dest: 'loop2.yml' }
    
    # vim: ft=ansible :
    
    • 第 8 行的 src 的绝对路径会因环境而有变动,还请特别留意一下。

后话

以个人经验而言,掌握这两个技巧就可以解决大多的回圈需求!若想深入了解这部份,请研读官方的 Loops | Ansible Documentation 文件 3

相关连结

1. 更多 Loop Control 的介绍请参考 http://docs.ansible.com/ansible/playbooks_loops.html#loop-control ↩
2. 在友人的提醒下补了行号以利阅读,若想复制该范例,请直接上 GitHub 取用。 ↩
3. 冻仁从 Ansible 1.9 开始踏入 Ansible 的世界,在 Ansible 2.0 之后新增的 loop 语法冻仁至今 (2016.12.18) 还未完全使用过。 ↩


Ansible 发送 notification 到 HipChat
Ansible 维护大型的 Playbooks
温馨提示
下载编程狮App,免费阅读超1000+编程语言教程
取消
确定
目录

关闭

MIP.setData({ 'pageTheme' : getCookie('pageTheme') || {'day':true, 'night':false}, 'pageFontSize' : getCookie('pageFontSize') || 20 }); MIP.watch('pageTheme', function(newValue){ setCookie('pageTheme', JSON.stringify(newValue)) }); MIP.watch('pageFontSize', function(newValue){ setCookie('pageFontSize', newValue) }); function setCookie(name, value){ var days = 1; var exp = new Date(); exp.setTime(exp.getTime() + days*24*60*60*1000); document.cookie = name + '=' + value + ';expires=' + exp.toUTCString(); } function getCookie(name){ var reg = new RegExp('(^| )' + name + '=([^;]*)(;|$)'); return document.cookie.match(reg) ? JSON.parse(document.cookie.match(reg)[2]) : null; }