上篇文章Ansible 入門指南 - 安裝及 Ad-Hoc 命令使用介紹的額是 Ad-Hoc 命令方式,本文將介紹 Playbook
方式。
Playbook
譯為「劇本」,覺得還挺恰當的。
playbook 執行語法
Playbook
譯為「劇本」,覺得還挺恰當的。那么 play
那我就譯為 「場景」吧。playbook 由一個或多個 ‘plays’ 組成.它的內容是一個以 ‘plays’ 為元素的列表
playbook 基礎
可以為 playbook 中的每一個 play,個別地選擇操作的目標機器是哪些,以哪個用戶身份去完成要執行的步驟(called tasks)
$ ansible-playbook -h
Usage: ansible-playbook [options] playbook.yml [playbook2 ...]
Runs Ansible playbooks, executing the defined tasks on the targeted hosts.
palybook 小栗子
在指定的目標主機上執行定義好的 tasks
。
playbook.yml
常包含下面幾個關鍵字:
hosts
:為主機的IP,或者主機組名,或者關鍵字allremote_user
: 以哪個用戶身份執行。vars
: 變量tasks
: playbook的核心,定義順序執行的動作action
。每個action
調用一個ansbile module
。action
語法:module: module_parameter=module_value
- 常用的
module
有yum
、copy
、template
等,module
在 ansible 的作用,相當於 bash 腳本中yum
,copy
這樣的命令。 - 每一個 task 必須有一個名稱
name
,這樣在運行 playbook 時,從其輸出的任務執行信息中可以很好的辨別出是屬於哪一個 task 的 handers
: 是 playbook 的 event ,默認不會執行,在 action 里觸發才會執行。多次觸發只執行一次。
一個簡單的示例:
deploy.yml
的功能為 web 主機部署 apache, 其中包含以下部署步驟:
- 安裝apache包;
- 拷貝配置文件httpd,並保證拷貝文件后,apache服務會被重啟;
- 拷貝默認的網頁文件index.html;
- 啟動apache服務;
---
- hosts: centos
vars:
httpd_port: 8080
max_clients: 200
remote_user: root
tasks:
- name: ensure apache is at the latest version
yum: name=httpd state=present
- name: Write the configuration file
template: src=templates/httpd.conf.j2 dest=/etc/httpd/conf/httpd.conf
notify:
- restart apache
ignore_errors: False
- name: Write the default index.html file
template: src=templates/index.html.j2 dest=/var/www/html/index.html
ignore_errors: False
- name: ensure apache is running
service: name=httpd state=started
handlers:
- name: restart apache
service: name=httpd state=restarted
運行ansible-playbook deploy.yml
即可執行。
tasks
taks
參數其實是有不同寫法的,當參數比較少時,可用key=value
的方式
tasks:
- name: make sure apache is running
service: name=httpd state=running
當tasks
參數比較多時,為了美觀和不易出錯,用 yml
的字典傳參比較好:
tasks:
- name: make sure apache is running
service:
name: httpd
state: running
task中每個action會調用一個module,在module中會去檢查當前系統狀態是否需要重新執行。
- 如果本次執行了,那么
action
會得到返回值changed
; - 如果不需要執行,那么
action
得到返回值ok
module 的執行狀態的具體判斷規則由各個 module 自己決定和實現的。例如,”copy” module的判斷方法是比較文件的checksum,代碼如下:
ansbile-playbook 常用命令
查看腳本影響到的 hosts
下面這條命令,指定 inventory 文件,列出 hosts 列表,並不會去執行定義的 tasks,觀察 host 是否配置正確很有用:
ansible-playbook -i inventory/slave_init.yml execute_slave_init.yml --list-hosts
查看輸出的細節
ansible-playbook playbook.yml --verbose
並行執行腳本
ansible-playbook playbook.yml -f 10
輸入密碼
playbook 中使用到了 become
,執行playbook
時可以加上--ask-become-pass
參數:
ansible-playbook deploy.yml --ask-become-pass
yml 語法簡介
- 大小寫敏感
- 使用縮進表示層級關系
- 縮進時不允許使用Tab鍵,只允許使用空格。
- 縮進的空格數目不重要,只要相同層級的元素左側對齊即可
#
表示注釋,從這個字符一直到行尾,都會被解析器忽略- YAML 還有一個小的怪癖. 所有的 YAML 文件(無論和 Ansible 有沒有關系)開始行都應該是
---
. 這是 YAML 格式的一部分, 表明一個文件的開始. - 列表中的所有成員都開始於相同的縮進級別, 並且使用一個 "- " 作為開頭(一個橫杠和一個空格)
- 一個字典是由一個簡單的
鍵: 值
的形式組成(這個冒號后面必須是一個空格) - Ansible 使用 “{{ var }}” 來引用變量,
foo: "{{ variable }}"
。
參考
Play vs Playbook
其實在一個Playbook文件中還可以有針對兩組server進行不同的操作,例如給web安裝http服務器,和給lb安裝mysql放在一個文件中:
---
#安裝apache的play
- hosts: web
remote_user: root
tasks:
- name: ensure apache is at the latest version
yum: pkg=httpd state=latest
# 安裝mysql server的play
- hosts: lb
remote_user: root
tasks:
- name: ensure mysqld is at the latest version
yum: pkg=mariadb state=latest
上面例子中針對每一組 server 的所有操作就組成一個 play,一般一個 playbook 中只包含一個 play,play 的內容稱為 tasks,即任務。
參考:
響應事件 Hanlder
handlers與tasks不同,tasks會默認的按定義順序執行每一個task,handlers則不會,它需要在tasks中被調用,才有可能被執行。
Handlers will only be fired for tasks which report a changed state.
只有當 task 執行狀態顯示是 changed 時,handler 動作才會被觸發
Tasks中的任務都是有狀態的,changed或者ok。 在Ansible中,只在task的執行狀態為 changed
的時候,才會執行該task調用的handler。
在所有的task列表執行之后執行,如果有多個task notify同一個handler,那么 handlers 也只執行一次。
什么情況下使用handlers呢?
如果你在tasks中修改了apache的配置文件。需要重起apache。此外還安裝了apache的插件。那么還需要重起apache。像這樣的應該場景中,重起apache就可以設計成一個handler.
當一個文件的內容被改動時,重啟兩個 services:
- name: template configuration file
template: src=template.j2 dest=/etc/foo.conf
notify:
- restart memcached
- restart apache
notify
下列出的即是 handlers.- Handlers 也是一些 task 的列表,通過名字(
name
)來引用。 - Handlers 是由通知者進行 notify, 如果沒有被 notify,handlers 不會執行。
- 不管有多少個通知者進行了 notify,等到 play 中的所有 task 執行完成之后,handlers 也只會被執行一次.
handlers:
- name: restart memcached
service: name=memcached state=restarted
- name: restart apache
service: name=apache state=restarted
此外,還有個注意點:
- handlers是按照在handlers中定義個順序執行的,而不是安裝notify的順序執行的。比如,
handlers
定義的順序是1>2>3
,notify 的順序是3>2>1
,實際執行順序:1>2>3
.
總結,Handlers 最佳的應用場景是用來重啟服務,或者觸發系統重啟操作.除此以外很少用到。
參考
變量
playbook 中常用的集中變量:
- 在 playbook 中,用戶自定義的變量
- 無需用戶定義,ansible 在執行 playbook 之前,去遠程主機上搜集的關於遠程主機的系統信息變量
- task 運行的結果「注冊」為一個變量來使用,這個變量叫做「注冊變量」
- 允許用戶在執行的時候傳入變量的值,這時候用到的是「額外變量」
playbook 中定義的變量
在 playbook 中,通過關鍵字 vars
自定義變量,用 {{}}
引用變量。
將變量放在單獨的文件中
通過 vars_files
關鍵字指定了變量文件:
---
- hosts: centos
vars:
httpd_port: 80
vars_files:
- ./vars_servers.yml
remote_user: root
tasks:
- debug:
msg: "http_port: {{httpd_port}}"
- debug:
msg: "x86 passwd: {{x86.password}}"
- debug:
msg: "arm passwd: {{arm.password}}"
# 也可以用 arm['password'] 表示
專門存放變量的文件:
# vars_servers.yml
x86:
password: 123
arm:
password: 456
遠程節點的系統變量(facts)
ansible 通過 module setup 收集主機的系統信息,這些收集到的系統信息叫做 facts
,這些facts
可以直接以變量的形式使用。
哪些 facts
變量可以引用的?通過如下命令行調用setup module
可以查看:
ansible all -m setup -u root
可以看到它輸出的變量信息有很多!
復雜的facts
變量的使用可以用如下兩種形式:
{{ ansible_ens3["ipv4"]["address"] }}
{{ ansible_ens3.ipv4.address }}
好用的一些 facts 變量
ansible_hostname
指定的 host 名稱ansible_default_ipv4.address
主機真實的 ipv4 地址,小網IP
ansible_os_family 查看系統類型的變量
---
- hosts: all
user: root
tasks:
- name: echo system
shell: echo {{ ansible_os_family }}
- name install ntp on Debian linux
apt: name=git state=installed
when: ansible_os_family == "Debian"
- name install ntp on redhat linux
yum: name=git state=present
when: ansible_os_family == "RedHat"
關閉 facts
在 playbook 中,如果不收集系統信息,那么上面的變量就不能再 playbook 中使用了,但是有時候關閉會加快執行的效率:
- hosts: all
gather_facts: no
注冊變量 register
將某個 task 執行的結果「注冊」為一個變量。后面的 action 就可以使用它
---
- hosts: centos
tasks:
- name: ls /tmp
shell: ls -l /tmp
register: result
ignore_errors: True
- name: echo result when rc==5
shell: echo "{{result}}"
when: result.rc == 5
- name: debug show stdout
debug:
msg: "{{result.stdout}}"
「注冊變量」經常和debug module
一起使用,這樣可以獲得 action 更多的輸出信息,幫助調試。
參考
- 注冊Ansible變量屬性 介紹到了一個
set_facts
的模塊 - 朱雙印-ansible 變量5
- Ansible系列(五):各種變量定義方式和變量引用
命令行傳遞變量 --extra-vars
---
- hosts: "{{hosts}}"
remote_user: "{{user}}""
tasks:
- debug: msg="{{hosts}}""
命令輸入變量:
ansible-playbook extra_learn.yml --extra-vars "{'hosts':'x86','user':‘’michael'}"
# or
ansible-playbook extra_learn.yml --extra-vars "hosts=x86 user=michael"
playbook 中的邏輯控制語句
when
:條件判斷,類似編程語言中的if
loop
:循環,類似編程語言中的while
block
:將幾個task
組成一塊代碼,便於針對一組操作進行異常處理等
條件語句 wehn
例如,在某個特定版本的系統上裝包,或者只在磁盤空間滿了的文件系統上執行清理操作。這些操作在Playbook中用when語句實現。
主機為Debian Linux立刻關機
tasks:
- name: "shutdown Debian flavored systems"
command: /sbin/shutdown -t now
when: ansible_os_family == "Debian"
根據action的執行結果,來決定接下來執行的action。
tasks:
- command: /bin/false
register: result
ignore_errors: True
- command: /bin/something
when: result|failed
- command: /bin/something_else
when: result|success
- command: /bin/still/something_else
when: result|skipped
遠程中的系統變量facts變量作為when的條件,用“|int”還可以轉換返回值的類型:
---
- hosts: web
tasks:
- debug: msg="only on Red Hat 7, derivatives, and later"
when: ansible_os_family == "RedHat" and ansible_lsb.major_release|int >= 6
循環語句 loop
標准循環 with_items
為了保持簡潔,重復的任務可以用以下簡寫的方式:
- name: add several users
user: name="{{ item }}" state=present groups=wheel
with_items:
- michael
- qq
如果你在變量文件中或者 ‘vars’ 區域定義了一組YAML列表,你也可以這樣做:
vars:
userlist: ["micahel", "qq"]
tasks:
-name: add several user
user:
name: "{{ item }}"
state: present
groups: wheel
with_items: "{{userlist}}"
使用 with_items
用於迭代的條目類型不僅僅支持簡單的字符串列表.如果你有一個哈希列表,那么你可以用以下方式來引用子項:
- name: add several users
user: name="{{ item.name }}" state=present groups="{{ item.groups }}"
with_items:
- { name: 'michael', groups: 'wheel' }
- { name: 'qq', groups: 'root' }
對哈希表使用循環 with_dict
這個例子不僅演示了 with_dict
用法,還使用循環安裝了 RPM 包
---
- hosts: centos
vars:
users:
michael:
name: michael xiang
phone: 123
qq:
name: qq huang
phone: 456
rpms:
- httpd
- lrzsz
- vim
- git
tasks:
- name: print phone records
debug: msg="User {{item.key }} is {{ item.value.name }} {{item.value.phone}}"
with_dict: "{{ users }}"
- name: install rpms
yum: name="{{item}}" state=installed
with_items: "{{rpms}}"
對文件列表使用循環 with_filegloab
with_fileglob
可以以非遞歸的方式來模式匹配單個目錄中的文件.如下面所示:
tasks:
# first ensure our target directory exists
- file: dest=/etc/fooapp state=directory
# copy each file over that matches the given pattern
- copy: src=\{\{ item \}\} dest=/etc/fooapp/ owner=root mode=600
with_fileglob:
- /playbooks/files/fooapp/*
參考
塊語句
多個action組裝成塊,可以根據不同條件執行一段語句 :
tasks:
- block:
- yum: name=\{\{ item \}\} state=installed
with_items:
- httpd
- memcached
- template: src=templates/src.j2 dest=/etc/foo.conf
- service: name=bar state=started enabled=True
when: ansible_distribution == 'CentOS'
become: true
become_user: root
ansible 示例
- ansible/ansible-examples Ansible官方提供了一些比較常用的、經過測試的Playbook例子
- galaxy-ansible Playbook分享平台