Skip to main content

第 6 章:使用 loops 重複任務

在 Ansible 我們也可用 loop 來簡化重複的任務 (Tasks)

標準迴圈 (Standard Loops)

Shell Script

先複習一下 Shell Script 的寫法。

建立 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 變數。
執行 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 等較複雜的環境下會有很大的幫助。
建立 loop 的 playbook
$ vi playbook_loop.yml
---
- name: a basic loop with playbook
hosts: localhost
tasks:
- name: print loop message
debug:
msg: "Loop {{ item }}"
with_items:
- 0
- 1
- 2

  • 註:raw 和 endraw 是為了相容 GitBook 所增加的語法,您可能會在某平台上看到它,請忽略之。
  • 在第 7, 8 行裡,我們用了 debug module 來印出訊息,並定義 item
  • 在第 9 ~ 12 行裡,則用了 with_items 將 0, 1, 2 的值傳入 item
  • 執行 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
  • 凍仁常用此手法來安裝多個套件,接著以建立 [chusiang/ansible-jupyter](https://hub.docker.com/r/chusiang/ansible-jupyter/) Docker image 的 setup_jupyter.yml 為例
vi setup_jupyter_yml
- hosts: localhost

vars:
# Same package on GNU/Linux.
same_packages:
- bash
- bash-completion
- ca-certificates
- curl
- git
- openssl

# Alpine Linux.
apk_packages:
- openssh-client
- vim

# Debian, Ubuntu.
apt_packages: "{{ apk_packages }}"
...

tasks:
# General Linux.
- name: install same packages
package: name={{ item }} state=present
with_items: "{{ same_packages }}"
when:
- same_packages is defined
- ansible_pkg_mgr != "portage"

# Alpine Linux.
- name: install apk packages
apk: name={{ item }} state=present
with_items: "{{ apk_packages }}"
when:
- apk_packages is defined
- ansible_pkg_mgr == "apk"

# Debian, Ubuntu.
- name: install apt packages
apt: name={{ item }} state=present
with_items: "{{ apt_packages }}"
when:
- apt_packages is defined
- ansible_pkg_mgr == "apt"
  • 在第 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

  • 建立擁有兩個 item 屬性的 loop 的 playbook
vi playbook_loop_adv1.yml
- name: a advanced loop with playbook
hosts: localhost
tasks:
- name: print loop message
debug:
msg: "Loop {{ item.num }}: {{ item.str }}"
with_items:
- { num: '0', str: 'automate' }
- { num: '1', str: 'with' }
- { num: '2', str: 'ansible' }
  • 執行 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

  • 這部份在新增多個使用者、多個軟連結 (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/{{ item.src }}"
dest: "/tmp/{{ item.dest }}"
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 的絕對路徑會因環境而有變動,還請特別留意一下