在 Shell Script 裡,我們會使用 for 和 while 等迴圈 (loop) 來簡化重複的程式碼,而在 Ansible 我們也可用 loop 來簡化重複的任務 (Tasks) 。以下凍仁將介紹常見的 loop 語法。
▲ 圖片來源:http://screencastsolutions.ca/looping-for-continuous-play/ 。
首先讓我們以簡單的重複印出 3 筆訊息為例。
先複習一下 Shell Script 的寫法。
建立 for loop 的 Script。
#!/bin/bash
$ vi bash_loop.sh
for X in 0 1 2; do
echo Loop $X
done
for
,並代入了 0, 1, 2 三個值到 $X
變數。echo
,印出訊息和 $X
變數。執行 Script:可以看到底下跑了 3 次的 loop。
$ ./bash_loop.sh
Loop 0
Loop 1
Loop 2
我們需透過 item
和 with_items
來使用 Ansible 的 loop,其 item
為預設名,一般情況下不可修改。
在 Ansible 2.1 新增了 Loop Control 的語法,可透過
loop_control
和loop_var
來自訂 item 的名字,這在多重 loop 等較複雜的環境下會有很大的幫助。[^1]
建立 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
debug
module 來印出訊息,並定義 item
。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
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: "{{ apk_packages }}"
21 ...
22
23 tasks:
24 # General Linux.
25 - name: install same packages
26 package: name={{ item }} state=present
27 with_items: "{{ same_packages }}"
28 when:
29 - same_packages is defined
30 - ansible_pkg_mgr != "portage"
31
32 # Alpine Linux.
33 - name: install apk packages
34 apk: name={{ item }} state=present
35 with_items: "{{ apk_packages }}"
36 when:
37 - apk_packages is defined
38 - ansible_pkg_mgr == "apk"
39
40 # Debian, Ubuntu.
41 - name: install apt packages
42 apt: name={{ item }} state=present
43 with_items: "{{ apt_packages }}"
44 when:
45 - apt_packages is defined
46 - ansible_pkg_mgr == "apt"
47 ...
same_packages
, apk_packages
和 apt_packages
變數,並傳入了幾個套件名稱。item
,並將 same_packages
變數傳入。換句話說就是 install same packages
task 會安裝 same_packages
定義的所有套件。apt_packages: "{{ apk_packages }}"
的手法讓 apt_packages = apk_packages
。如有數個變數需求,可用 item.first
, item.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 :
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) 還未完全使用過。
Shell Script
本來想拿 Chef (另一套組態管理工具) 來比較的,但時間有限就算了。XDD
哈哈!! Shell Script 都還給老師,竟然都忘光光了,是該開始學習了
會基本的 Shell Script 就夠用了,如果要比較複雜的 script 凍仁會建議改用 Playbook 或 Python。