一、簡介
YAML 語言(發音 /jml/ )的設計目标,就是友善人類讀寫。它實質上是一種通用的資料串行化格式。
它的基本文法規則如下。
- 大小寫敏感
- 使用縮進表示層級關系 # 結構
- 縮進時不允許使用Tab鍵,隻允許使用空格。
- 縮進的空格數目不重要,隻要相同層級的元素左側對齊即可
#
表示注釋,從這個字元一直到行尾,都會被解析器忽略。
YAML 支援的資料結構有三種。
- 對象:鍵值對的集合,又稱為映射(mapping)/ 哈希(hashes) / 字典(dictionary)
- 數組:一組按次序排列的值,又稱為序列(sequence) / 清單(list)
- 純量(scalars):單個的、不可再分的值
以下分别介紹這三種資料結構。
二、對象
對象的一組鍵值對,使用冒号結構表示。
animal: pets
Yaml 也允許另一種寫法,将所有鍵值對寫成一個行内對象。
hash: { name: Steve, foo: bar }
三、數組
一組連詞線開頭的行,構成一個數組。
- Cat - Dog - Goldfish
資料結構的子成員是一個數組,則可以在該項下面縮進一個空格。
- - Cat - Dog - Goldfish
數組也可以采用行内表示法。
animal: [Cat, Dog]
一、YAML
1、YAML介紹
YAML是一個可讀性高的用來表達資料序列的格式。
YAML參考了其他多種語言,包括:XML、C語言、Python、Perl以及電子郵件格式RFC2822等。Clark Evans在2001年在首次發表了這種語言,另外Ingy dt Net與Oren Ben-Kiki也是這語言的共同設計者。
YAML Ain't Markup Language,即YAML不是XML。不過,在開發的這種語言時,YAML的意思其實是:"Yet Another Markup Language"(仍是一種标記語言)。
其特性:
YAML的可讀性好
YAML和腳本語言的互動性好
YAML使用實作語言的資料類型
YAML有一個一緻的資訊模型
YAML易于實作
YAML可以基于流來處理
YAML表達能力強,擴充性好
更多的内容及規範參見http://www.yaml.org
2、YAML文法
YAML的文法和其他高階語言類似,并且可以簡單表達清單、散清單、标量等資料結構。
其結構(Structure)通過空格來展示,
序列(Sequence)(同一類資料)裡的項用"-"來代表,
Map裡的鍵值對用":"分隔。
下面是一個示例:
name: John Smith # 鍵值對
age: 41 # 鍵值對
gender: Male # 鍵值對
spouse: # 鍵值對,值又是一組鍵值對,使用空格來表示鍵的值有多項,空格不能随意縮減,否則會報錯
name: Jane Smith
age: 37
gender: Female
children:
- name: Jimmy Smith # 序列,表示同一類資料
age: 17
gender: Male
- name: Jenny Smith
age 13
gender: Female
YAML檔案擴充名通常為.yml和.yaml,如example.yaml。
3、在ansible中常用的資料類型
1)list
清單的所有元素均使用“-”打頭
例如:
# A list of tasty fruits
- Apple
- Orange
- Strawberry
- Mango
2)dictionary
字典通過key與valuef進行辨別
# An employee record
name: Example Developer
job: Developer
skill: Elite
也可以将key:value放置于{}中進行表示
# An employee record
{name: Example Developer, job: Developer, skill: Elite}
二、Ansible中使用的YAML基礎元素
1、變量
1)變量命名
變量名僅能由字母、數字和下劃線組成,且隻能以字母開頭。
2)facts
facts是由正在通信的遠端目标主機發回的資訊,這些資訊被儲存在ansible變量中,可以直接引用。要擷取指定的遠端主機所支援的所有facts
可使用如下指令進行:
# ansible hostname -m setup
3)register 注冊器 # 很少用
把任務的輸出定義為變量,然後用于其他任務
示例如下:
tasks:
- shell: /usr/bin/foo
register: foo_result
ignore_errors: True
4)通過指令行傳遞變量
在運作playbook的時候也可以傳遞一些變量供playbook使用
使用選項:-e EXTRA_VARS, --extra-vars=EXTRA_VARS
# ansible-playbook test.yml -e "hosts=www user=mageedu"
5)通過roles傳遞變量
當給一個主機應用角色的時候可以傳遞變量,然後在角色内使用這些變量
- hosts: webservers
roles:
- common
- { role: foo_app_instance, dir: '/web/htdocs/a.com', port: 8080 }
2、Inventory
ansible的主要功用在于批量主機操作,為了便捷地使用其中的部分主機,可以在inventory file中将其分組命名。預設的inventory file為/etc/ansible/hosts。
inventory file可以有多個,且也可以通過Dynamic Inventory來動态生成。
1)inventory檔案格式
inventory檔案遵循ini檔案風格,中括号中的字元為組名。可以将同一個主機同時歸并到多個不同的組中;此外,當如若目标主機使用了非預設的SSH端口,還可以在主機名稱之後使用冒号加端口号來标明。
ntp.magedu.com
[webservers]
www1.magedu.com:2222
www2.magedu.com
[dbservers]
db1.magedu.com
db2.magedu.com
db3.magedu.com
如果主機名稱遵循相似的命名模式,還可以使用清單的方式辨別各主機
[webservers]
www[01:50].example.com
[databases]
db-[a:f].example.com
2)主機變量
可以在inventory中定義主機時為其添加主機變量以便于在playbook中引用。
[webservers]
www1.magedu.com http_port=80 maxRequestsPerChild=808
www2.magedu.com http_port=8080 maxRequestsPerChild=909
3)組變量
組變量是指賦予給指定組内所有主機上的在playbook中可用的變量。
[webservers]
www1.magedu.com
www2.magedu.com
[webservers:vars]
ntp_server=ntp.magedu.com
nfs_server=nfs.magedu.com
4)組嵌套
inventory中,組還可以包含其它的組,并且也可以向組中的主機指定變量。不過,這些變量隻能在ansible-playbook中使用,而ansible指令不支援。
[apache]
httpd1.magedu.com
httpd2.magedu.com
[nginx]
ngx1.magedu.com
ngx2.magedu.com
[webservers:children] # 固定格式,表示webservers組包含以下組
apache
nginx
[webservers:vars]
ntp_server=ntp.magedu.com
3、inventory參數
ansible基于ssh連接配接inventory中指定的遠端主機時,還可以通過參數指定其互動方式
這些參數如下所示:
ansible_ssh_host認證
The name of the host to connect to, if different from the alias you wish to give to it.
ansible_ssh_port
The ssh port number, if not 22
ansible_ssh_user
The default ssh user name to use.
ansible_ssh_pass
The ssh password to use (this is insecure, we strongly recommend using --ask-pass or SSH keys)
ansible_sudo_pass
The sudo password to use (this is insecure, we strongly recommend using --ask-sudo-pass)
ansible_connection
Connection type of the host. Candidates are local, ssh or paramiko. The default is paramiko before Ansible 1.2, and 'smart' afterwards which detects whether usage of 'ssh' would be feasible based on whether ControlPersist is supported.
ansible_ssh_private_key_file
Private key file used by ssh. Useful if using multiple keys and you don't want to use SSH agent.
ansible_shell_type
The shell type of the target system. By default commands are formatted using 'sh'-style syntax by default. Setting this to 'csh' or 'fish' will cause commands executed on target systems to follow those shell's syntax instead.
ansible_python_interpreter
The target host python path. This is useful for systems with more
than one Python or not located at "/usr/bin/python" such as \*BSD, or where /usr/bin/python
is not a 2.X series Python. We do not use the "/usr/bin/env" mechanism as that requires the remote user's
path to be set right and also assumes the "python" executable is named python, where the executable might
be named something like "python26".
ansible\_\*\_interpreter
Works for anything such as ruby or perl and works just like ansible_python_interpreter.
This replaces shebang of modules which will run on that host.
是以這裡可以使用另一種主機認證方式:
[root@localhost ansible]# cat hosts
# This is the default ansible 'hosts' file.
#
# It should live in /etc/ansible/hosts
#
# - Comments begin with the '#' character
# - Blank lines are ignored
# - Groups of hosts are delimited by [header] elements
# - You can enter hostnames or ip addresses
# - A hostname/ip can be a member of multiple groups
10.0.250.203
10.0.250.202 ansible_ssh_user=root ansible_ssh_pass=HY111!(123456)
4、條件測試
如果需要根據變量、facts或此前任務的執行結果來做為某task執行與否的前提時要用到條件測試。
when語句
在task後添加when子句即可使用條件測試;when語句支援Jinja2表達式文法。
tasks:
- name: "shutdown Debian flavored systems"
command: /sbin/shutdown -h now
when: ansible_os_family == "Debian"
when語句中還可以使用Jinja2的大多“filter”,例如要忽略此前某語句的錯誤并基于其結果(failed或者sucess)運作後面指定的語句,可使用類似如下形式:
- 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
此外,when語句中可以使用facts或playbook中定義的變量。
5、疊代
當有需要重複性執行的任務時,可以使用疊代機制。
其使用格式為将需要疊代的内容定義為item變量引用,并通過with_items語句來指明疊代的元素清單即可。
- name: add several users
user: name={{ item }} state=present groups=wheel
# item是固定用法,會自動重複執行他的所有值
with_items:
- testuser1
- testuser2
上面語句的功能等同于下面的語句:
- name: add user testuser1
user: name=testuser1 state=present groups=wheel
- name: add user testuser2
user: name=testuser2 state=present groups=wheel
事實上,with_items中可以使用元素還可為hashes,清單值也可以是字典,但引用item.KEY
- name: add several users
user: name={{ item.name }} state=present groups={{ item.groups }}
with_items:
- { name: 'testuser1', groups: 'wheel' }
- { name: 'testuser2', groups: 'root' }
ansible的循環機制還有更多的進階功能,具體請參見官方文檔:http://docs.ansible.com/playbooks_loops.html
三、ansible playbooks
1、ansible playbookes簡介
playbook是由一個或多個“play”組成的清單。play的主要功能在于将事先歸并為一組的主機裝扮成事先通過ansible中的task定義好的角色。
從根本上來講,所謂task無非是調用ansible的一個module。将多個play組織在一個playbook中,即可以讓它們聯同起來按事先編排的機制同唱一台大戲。
下面是一個簡單示例:
- hosts: webnodes
vars: # 定義變量,在後面可以使用{{ 變量名 }} 格式來引用
- package: httpd
- service: httpd
remote_user: root # 定義執行的使用者
tasks: # 定義任務
- name: ensure apache is at the latest version # 自定義一個任務名稱
yum: name=`package` state=latest # 任務内容
- name: ensure apache is running
service: name=`service` state=started
handlers: # 處理器
- name: restart apache
service: name=httpd state=restarted
2、playbook基礎元件
可以對比上面的示例來了解
Inventory # 主機清單,應用在哪些主機上
Modules # 調用哪些子產品
Ad Hoc Commands # 運作的指令
Playbooks
Tasks # 任務,即調用子產品完成的某操作
Variables # 變量
Templates # 模版
Handlers # 處理器,由某事件觸發執行的操作
Tags # 标簽
Roles # 角色
1)Hosts和Users
playbook中的每一個play的目的都是為了讓某個或某些主機以某個指定的使用者身份執行任務。hosts用于指定要執行指定任務的主機,其可以是一個或多個由冒号分隔主機組;remote_user則用于指定遠端主機上的執行任務的使用者。如上面示例中的
-hosts: webnodes
remote_user: root
不過,remote_user也可用于各task中。也可以通過指定其通過sudo的方式在遠端主機上執行任務,其可用于play全局或某任務;此外,甚至可以在sudo時使用sudo_user指定sudo時切換的使用者。
- hosts: webnodes
remote_user: mageedu
tasks:
- name: test connection
ping:
remote_user: mageedu
sudo: yes
2)任務清單和action
play的主體部分是task list。task list中的各任務按次序逐個在hosts中指定的所有主機上執行,即在所有主機上完成第一個任務後再開始第二個。在運作自上而下某playbook時,如果中途發生錯誤,所有已執行任務都可能復原,是以,在更正playbook後重新執行一次即可。
task的目的是使用指定的參數執行子產品,而在子產品參數中可以使用變量。子產品執行是幂等的,這意味着多次執行是安全的,因為其結果均一緻。
每個task都應該有其name,用于playbook的執行結果輸出,建議其内容盡可能清晰地描述任務執行步驟。如果未提供name,則action的結果将用于輸出。
定義task的可以使用“action: module options”或“module: options”的格式,推薦使用後者以實作向後相容。如果action一行的内容過多,也中使用在行首使用幾個空白字元進行換行。
- name: make sure apache is running
service: name=httpd state=running
在衆多子產品中,隻有command和shell子產品僅需要給定一個清單而無需使用“key=value”格式
- name: disable selinux
command: /sbin/setenforce 0
如果指令或腳本的退出碼不為零(可能會阻止playbook繼續運作),可以使用如下方式替代:
- name: run this command and ignore the result
shell: /usr/bin/somecommand || /bin/true
或者使用ignore_errors來忽略錯誤資訊:
shell: /usr/bin/somecommand
ignore_errors: True
簡單例子:
[root@Node6 ~]# cat xxj.yml # 注意:yaml格式對空格很嚴格,不能多打和少打
- hosts: xj,192.168.10.4
remote_user: root
tasks:
- name: create xxj group
group: name=xxj system=yes gid=808
- name: create xxj user
user: name=xxj system=yes createhome=no uid=808 group=808
### 這就是1個play,即1場戲;也可以有多個play,就是上面的結構再定義另一個
[root@Node6 ~]# ls -l xxj.yml
-rw-r--r-- 1 root root 226 Apr 3 22:20 xxj.yml
[root@Node6 ~]# ansible-playbook xxj.yml # 使用ansible-playbook 指令執行劇本
PLAY [xj,192.168.10.4] *********************************************************
TASK [setup] *******************************************************************
# 1 play
ok: [192.168.10.1]
ok: [192.168.10.2]
ok: [192.168.10.4]
TASK [create xxj group] ********************************************************
# 2 play
changed: [192.168.10.4]
changed: [192.168.10.2]
changed: [192.168.10.1]
TASK [create xxj user] *********************************************************
# 3 play
changed: [192.168.10.2]
changed: [192.168.10.1]
changed: [192.168.10.4]
PLAY RECAP *********************************************************************
192.168.10.1 : ok=3 changed=2 unreachable=0 failed=0
192.168.10.2 : ok=3 changed=2 unreachable=0 failed=0
192.168.10.4 : ok=3 changed=2 unreachable=0 failed=0
[root@Node6 ~]# cat xxj.yml
- hosts: all
remote_user: root
tasks:
# - name: create xxj group
- group: name=xxj system=yes gid=808
# - name: create xxj user
- user: name=xxj system=yes createhome=no
[root@Node6 ~]# ansible-playbook xxj.yml
PLAY [all] *********************************************************************
TASK [setup] *******************************************************************
ok: [192.168.10.4]
ok: [192.168.10.1]
ok: [192.168.10.2]
TASK [group] *******************************************************************
ok: [192.168.10.4]
ok: [192.168.10.2]
ok: [192.168.10.1]
TASK [user] ********************************************************************
ok: [192.168.10.2]
ok: [192.168.10.4]
ok: [192.168.10.1]
PLAY RECAP *********************************************************************
192.168.10.1 : ok=3 changed=0 unreachable=0 failed=0
192.168.10.2 : ok=3 changed=0 unreachable=0 failed=0
192.168.10.4 : ok=3 changed=0 unreachable=0 failed=0
3)handler
用于當關注的資源發生變化時采取一定的操作
“notify”這個action可用于在每個play的最後被觸發,這樣可以避免多次有改變發生時每次都執行指定的操作,取而代之,僅在所有的變化發生完成後一次性地執行指定操作。
在notify中列出的操作稱為handler,也即notify中調用handler中定義的操作。
- name: template configuration file
template: src=template.j2 dest=/etc/foo.conf
notify:
- restart memcached # 通過任務名稱調用
- restart apache
handler是task清單,這些task與前述的task并沒有本質上的不同。
handlers:
- name: restart memcached
service: name=memcached state=restarted
- name: restart apache
service: name=apache state=restarted
即:在handlers中定義動作,在notify中調用
例子:
[root@Node6 ~]# cat httpd.yml
- hosts: all
remote_user: root
tasks:
- name: install httpd
yum: name=httpd
- name: configuration httpd
copy: src=/tmp/httpd.conf dest=/etc/httpd/conf/httpd.conf
- name: start httpd service
service: name=httpd enabled=true state=started
[root@Node6 ~]# ansible-playbook httpd.yml
PLAY [all] *********************************************************************
TASK [setup] *******************************************************************
ok: [192.168.10.2]
ok: [192.168.10.4]
ok: [192.168.10.1]
TASK [install httpd] ***********************************************************
ok: [192.168.10.2]
ok: [192.168.10.1]
ok: [192.168.10.4]
TASK [configuration httpd] *****************************************************
changed: [192.168.10.1]
changed: [192.168.10.4]
changed: [192.168.10.2]
TASK [start httpd service] *****************************************************
changed: [192.168.10.1]
changed: [192.168.10.2]
changed: [192.168.10.4]
PLAY RECAP *********************************************************************
192.168.10.1 : ok=4 changed=2 unreachable=0 failed=0
192.168.10.2 : ok=4 changed=2 unreachable=0 failed=0
192.168.10.4 : ok=4 changed=2 unreachable=0 failed=0
[root@Node6 ~]# ansible all -m shell -a "netstat -tan|grep 8080"
192.168.10.2 | SUCCESS | rc=0 >>
tcp 0 0 :::8080 :::* LISTEN
192.168.10.4 | SUCCESS | rc=0 >>
tcp 0 0 :::8080 :::* LISTEN
192.168.10.1 | SUCCESS | rc=0 >>
tcp 0 0 :::8080 :::* LISTEN
現在需要實作:修改httpd監聽的端口後,重新開機httpd服務
### 将/tmp/httpd.conf檔案中httpd監聽的端口改為8090
[root@Node6 ~]# cat httpd.yml
- hosts: all
remote_user: root
tasks:
- name: install httpd
yum: name=httpd
- name: configuration httpd
copy: src=/tmp/httpd.conf dest=/etc/httpd/conf/httpd.conf
notify:
- restart httpd
- name: start httpd service
service: name=httpd enabled=true state=started
handlers:
- name: restart httpd
service: name=httpd state=restarted
[root@Node6 ~]# ansible-playbook httpd.yml
PLAY [all] *********************************************************************
TASK [setup] *******************************************************************
ok: [192.168.10.2]
ok: [192.168.10.1]
ok: [192.168.10.4]
TASK [install httpd] ***********************************************************
ok: [192.168.10.2]
ok: [192.168.10.4]
ok: [192.168.10.1]
TASK [configuration httpd] *****************************************************
changed: [192.168.10.1]
changed: [192.168.10.4]
changed: [192.168.10.2]
TASK [start httpd service] *****************************************************
ok: [192.168.10.1]
ok: [192.168.10.2]
ok: [192.168.10.4]
RUNNING HANDLER [restart httpd] ************************************************
changed: [192.168.10.2]
changed: [192.168.10.4]
changed: [192.168.10.1]
PLAY RECAP *********************************************************************
192.168.10.1 : ok=5 changed=2 unreachable=0 failed=0
192.168.10.2 : ok=5 changed=2 unreachable=0 failed=0
192.168.10.4 : ok=5 changed=2 unreachable=0 failed=0
[root@Node6 ~]# ansible all -m shell -a "netstat -tan|grep 8080"
192.168.10.2 | FAILED | rc=1 >>
192.168.10.1 | FAILED | rc=1 >>
192.168.10.4 | FAILED | rc=1 >>
[root@Node6 ~]# ansible all -m shell -a "netstat -tan|grep 8090"
192.168.10.4 | SUCCESS | rc=0 >>
tcp 0 0 :::8090 :::* LISTEN
192.168.10.2 | SUCCESS | rc=0 >>
tcp 0 0 :::8090 :::* LISTEN
192.168.10.1 | SUCCESS | rc=0 >>
tcp 0 0 :::8090 :::* LISTEN
4)template 模版
定義一個模版在其中使用變量,為其賦不同的值,使得分發給各節點的檔案都不同
準備模版檔案:
[root@Node6 ~]# mv /tmp/httpd.conf /tmp/httpd.conf.j2
### 改名隻是為了辨別此檔案是模版檔案
[root@Node6 ~]# vim /tmp/httpd.conf.j2
[root@Node6 ~]# grep -i '^\(listen\|servername\)' /tmp/httpd.conf.j2
Listen `http_port` # Listen的值使用變量代替
ServerName `ansible_fqdn` # ServerName的值使用變量代替
這個變量來自facts、主機清單檔案、或playbook檔案(不友善為1個變量賦每個主機不同的值)中定義的
這裡我使用在主機清單中定義該變量:
[root@Node6 ~]# tail -7 /etc/ansible/hosts
#################################################
192.168.10.4
[xj]
192.168.10.1:22 http_port=801
192.168.10.2 http_port=802
準備playbook檔案:
[root@Node6 ~]# cat httpd.yml
- hosts: all
remote_user: root
tasks:
- name: install httpd
yum: name=httpd
- name: configuration httpd
# copy: src=/tmp/httpd.conf.j2 dest=/etc/httpd/conf/httpd.conf
### 注意這裡不能使用copy子產品,使用copy子產品 變量不會用值替換
template: src=/tmp/httpd.conf.j2 dest=/etc/httpd/conf/httpd.conf
notify:
- restart httpd
- name: start httpd service
service: name=httpd enabled=true state=started
handlers:
- name: restart httpd
service: name=httpd state=restarted
[root@Node6 ~]# ansible-playbooke httpd.yml
-bash: ansible-playbooke: command not found
[root@Node6 ~]# ansible-playbook httpd.yml
PLAY [all] *********************************************************************
TASK [setup] *******************************************************************
ok: [192.168.10.2]
ok: [192.168.10.4]
ok: [192.168.10.1]
TASK [install httpd] ***********************************************************
ok: [192.168.10.1]
ok: [192.168.10.4]
ok: [192.168.10.2]
TASK [configuration httpd] *****************************************************
fatal: [192.168.10.4]: FAILED! => {"changed": false, "failed": true, "msg": "AnsibleUndefinedVariable: 'http_port' is undefined"}
changed: [192.168.10.1]
changed: [192.168.10.2]
TASK [start httpd service] *****************************************************
ok: [192.168.10.1]
ok: [192.168.10.2]
RUNNING HANDLER [restart httpd] ************************************************
changed: [192.168.10.2]
changed: [192.168.10.1]
to retry, use: --limit @/root/httpd.retry
PLAY RECAP *********************************************************************
192.168.10.1 : ok=5 changed=2 unreachable=0 failed=0
192.168.10.2 : ok=5 changed=2 unreachable=0 failed=0
192.168.10.4 : ok=2 changed=0 unreachable=0 failed=1
### 10.4失敗是因為在主機清單檔案中沒有定義http_port變量,報錯,後續的配置也沒繼續完成
5)Tags 标簽
tags用于讓使用者選擇運作或跳過playbook中的部分代碼。ansible具有幂等性,是以會自動跳過沒有變化的部分,即便如此,有些代碼為測試其确實沒有發生變化的時間依然會非常地長。此時,如果确信其沒有變化,就可以通過tags跳過此些代碼片斷。
[root@Node6 ~]# cat httpd.yml
- hosts: xj
remote_user: root
tasks:
- name: install httpd
yum: name=httpd
- name: configuration httpd
# copy: src=/tmp/httpd.conf.j2 dest=/etc/httpd/conf/httpd.conf
template: src=/tmp/httpd.conf.j2 dest=/etc/httpd/conf/httpd.conf
tags: ### 添加一個 conf 标簽
- conf
notify:
- restart httpd
- name: start httpd service
service: name=httpd enabled=true state=started
handlers:
- name: restart httpd
service: name=httpd state=restarted
[root@Node6 ~]# ansible-playbook httpd.yml --list-tasks # 列出playbook中所有任務
playbook: httpd.yml
play #1 (xj): xj TAGS: []
tasks:
install httpd TAGS: []
configuration httpd TAGS: [conf]
start httpd service TAGS: []
[root@Node6 ~]# ansible-playbook httpd.yml --list-tags # 列出playbook中所有标簽
playbook: httpd.yml
play #1 (xj): xj TAGS: []
TASK TAGS: [conf]
[root@Node6 ~]# ansible-playbook httpd.yml --tags=conf # 隻運作conf标簽 任務
PLAY [xj] **********************************************************************
TASK [setup] *******************************************************************
ok: [192.168.10.2]
ok: [192.168.10.1]
TASK [configuration httpd] *****************************************************
changed: [192.168.10.2]
changed: [192.168.10.1]
RUNNING HANDLER [restart httpd] ************************************************
changed: [192.168.10.1]
changed: [192.168.10.2]
PLAY RECAP *********************************************************************
192.168.10.1 : ok=3 changed=2 unreachable=0 failed=0
192.168.10.2 : ok=3 changed=2 unreachable=0 failed=0
執行多個tags:
[root@localhost tmp]# ansible-playbook test3.yml --tags=test2,test3
### 執行标簽test2,test3
PLAY [all] ********************************************************************
GATHERING FACTS ***************************************************************
ok: [10.0.250.202]
ok: [10.0.250.203]
TASK: [test2] *****************************************************************
changed: [10.0.250.202]
changed: [10.0.250.203]
TASK: [test3] *****************************************************************
changed: [10.0.250.202]
changed: [10.0.250.203]
PLAY RECAP ********************************************************************
10.0.250.202 : ok=3 changed=2 unreachable=0 failed=0
10.0.250.203 : ok=3 changed=2 unreachable=0 failed=0
[root@localhost tmp]# ansible-playbook test3.yml --skip-tags=test2
### 跳過标簽test2
PLAY [all] ********************************************************************
GATHERING FACTS ***************************************************************
ok: [10.0.250.202]
ok: [10.0.250.203]
TASK: [test1] *****************************************************************
changed: [10.0.250.202]
changed: [10.0.250.203]
TASK: [test3] *****************************************************************
changed: [10.0.250.202]
changed: [10.0.250.203]
PLAY RECAP ********************************************************************
10.0.250.202 : ok=3 changed=2 unreachable=0 failed=0
10.0.250.203 : ok=3 changed=2 unreachable=0 failed=0
四、roles 角色
1、roles 概述
ansilbe自1.2版本引入的新特性,用于層次性、結構化地組織playbook。roles能夠根據層次型結構自動裝載變量檔案、tasks以及handlers等。要使用roles隻需要在playbook中使用include指令即可。
簡單來講,roles就是通過分别将變量、檔案、任務、子產品及處理器放置于單獨的目錄中,并可以便捷地include它們的一種機制。
角色一般用于基于主機建構服務的場景中,但也可以是用于建構守護程序等場景中。
一個roles的案例如下所示:
site.yml #主檔案
webservers.yml #副檔案
fooservers.yml #副檔案
roles/
common/ #角色1
files/ #角色下的檔案
templates/ #模版檔案
tasks/ #任務檔案
handlers/ #處理器檔案
vars/ #變量檔案
meta/ #中繼資料檔案
webservers/ #角色2
files/
templates/
tasks/
handlers/
vars/
meta/
而在playbook中,可以這樣使用roles:
---
roles: # 直接調用各roles
- common
- webservers
也可以向roles傳遞參數,例如:
- { role: foo_app_instance, dir: '/opt/a', port: 5000 }
- { role: foo_app_instance, dir: '/opt/b', port: 5001 }
甚至也可以條件式地使用roles,例如:
- { role: some_role, when: "ansible_os_family == 'RedHat'" }
2、建立role的步驟
1) 建立以roles命名的目錄; # 預設已存在
2) 在roles目錄中分别建立以各角色名稱命名的目錄,如webservers等;
3) 在每個角色命名的目錄中分别建立files、handlers、meta、tasks、templates和vars目錄;用不到的目錄可以建立為空目錄,也可以不建立;
4) 在playbook檔案中,調用各角色;
3、role内各目錄中可用的檔案
tasks目錄:
至少應該包含一個名為main.yml的檔案,其定義了此角色的任務清單;此檔案可以使用include包含其它的位于此目錄中的task檔案;
files目錄:
存放由copy或script等子產品調用的檔案;
templates目錄:
template子產品會自動在此目錄中尋找Jinja2模闆檔案;
handlers目錄:
此目錄中應當包含一個main.yml檔案,用于定義此角色用到的各handler;在handler中使用include包含的其它的handler檔案也應該位于此目錄中;
vars目錄:
應當包含一個main.yml檔案,用于定義此角色用到的變量;
meta目錄:
應當包含一個main.yml檔案,用于定義此角色的特殊設定及其依賴關系;ansible 1.3及其以後的版本才支援;
default目錄:
為目前角色設定預設變量時使用此目錄;應當包含一個main.yml檔案;
執行個體:
1、環境
192.168.10.1 安裝 httpd + ntpdate
192.168.10.2 安裝 httpd + ntpdate + mysql
2、步驟
1)建立各角色和其相關目錄
[root@localhost ~]# cd /etc/ansible/
[root@localhost ansible]# ls
ansible.cfg hosts roles
[root@localhost ansible]# cd roles/
[root@localhost roles]# mkdir -pv {httpd,mysql}/{tasks,files,templates,meta,handlers,vars}
mkdir: created directory `httpd'
mkdir: created directory `httpd/tasks'
mkdir: created directory `httpd/files'
mkdir: created directory `httpd/templates'
mkdir: created directory `httpd/meta'
mkdir: created directory `httpd/handlers'
mkdir: created directory `httpd/vars'
mkdir: created directory `mysql'
mkdir: created directory `mysql/tasks'
mkdir: created directory `mysql/files'
mkdir: created directory `mysql/templates'
mkdir: created directory `mysql/meta'
mkdir: created directory `mysql/handlers'
mkdir: created directory `mysql/vars'
2)準備各檔案
file目錄準備需要用到的檔案:
[root@localhost roles]# ls
httpd mysql
[root@localhost roles]# cd httpd/
[root@localhost httpd]# ls
files handlers meta tasks templates vars
[root@localhost httpd]# cp /etc/httpd/conf/httpd.conf.rpmsave files/httpd.conf
[root@localhost httpd]# ls files/
httpd.conf
tasks目錄定義任務檔案:
[root@localhost httpd]# cat tasks/main.yml
- name: install httpd package
yum: name=httpd
- name: install configuration file
copy: src=httpd.conf dest=/etc/httpd/conf/httpd.conf
tags:
- conf
notify:
- restart httpd
- name: start httpd
service: name=httpd state=started
handlers目錄檔案:
[root@localhost httpd]# cat handlers/main.yml
- name: restart httpd
service: name=httpd state=restarted
建立主yml檔案: # 要在roles目錄外
[root@localhost httpd]# cd ..
[root@localhost roles]# cd ..
[root@localhost ansible]# vi site.yml
[root@localhost ansible]# cat site.yml
- hosts: 10.0.250.203
remote_user: root
roles:
- httpd
[root@localhost ansible]# ansible-playbook site.yml
PLAY [10.0.250.203] ***********************************************************
GATHERING FACTS ***************************************************************
ok: [10.0.250.203]
TASK: [httpd | install httpd package] *****************************************
changed: [10.0.250.203]
TASK: [httpd | install configuration file] ************************************
changed: [10.0.250.203]
TASK: [httpd | start httpd] ***************************************************
changed: [10.0.250.203]
NOTIFIED: [httpd | restart httpd] *********************************************
changed: [10.0.250.203]
PLAY RECAP ********************************************************************
10.0.250.203 : ok=5 changed=4 unreachable=0 failed=0
第一個節點已經部署完畢,現在來部署第二個節點: # 應該直接在一個site.yml檔案中
[root@localhost ansible]# cd roles/
httpd/ mysql/
[root@localhost ansible]# cd roles/mysql/
[root@localhost mysql]# ls
files handlers meta tasks templates vars
[root@localhost mysql]# cp /etc/my.cnf files/
[root@localhost mysql]# vi tasks/main.yml
[root@localhost mysql]# cat tasks/main.yml
- name: install mysql-server package
yum: name=mysql-server
- name: install configuration file
copy: src=my.cnf dest=/etc/my.cnf
tags:
- mycof
notify:
- restart myslqd
- name: start mysqld service
service: name=mysqld state=started
[root@localhost mysql]# cat handlers/main.yml
- name: restart mysqld
service: name=mysqld state=restarted
[root@localhost mysql]# cd ../..
[root@localhost ansible]# ls
ansible.cfg hosts roles site.yml
[root@localhost ansible]# vi site.yml
[root@localhost ansible]# cat site.yml
- hosts: 10.0.250.203
remote_user: root
roles:
- httpd
- hosts: 10.0.250.202
remote_user: root
roles:
- httpd
- mysql
執行:
[root@localhost ansible]# ansible-playbook site.yml
PLAY [10.0.250.203] ***********************************************************
GATHERING FACTS ***************************************************************
ok: [10.0.250.203]
TASK: [httpd | install httpd package] *****************************************
ok: [10.0.250.203]
TASK: [httpd | install configuration file] ************************************
ok: [10.0.250.203]
TASK: [httpd | start httpd] ***************************************************
ok: [10.0.250.203]
PLAY [10.0.250.202] ***********************************************************
GATHERING FACTS ***************************************************************
ok: [10.0.250.202]
TASK: [httpd | install httpd package] *****************************************
changed: [10.0.250.202]
TASK: [httpd | install configuration file] ************************************
changed: [10.0.250.202]
TASK: [httpd | start httpd] ***************************************************
changed: [10.0.250.202]
TASK: [mysql | install mysql-server package] **********************************
changed: [10.0.250.202]
TASK: [mysql | start mysqld service] ******************************************
failed: [10.0.250.202] => {"failed": true}
msg: ERROR: 1004 Can't create file '/tmp/#sql2941_1_0.frm' (errno: 13)
160122 12:27:40 [ERROR] Aborting
160122 12:27:40 [Note] /usr/libexec/mysqld: Shutdown complete
FATAL: all hosts have already failed -- aborting
PLAY RECAP ********************************************************************
to retry, use: --limit @/root/site.retry
10.0.250.202 : ok=5 changed=4 unreachable=0 failed=1
10.0.250.203 : ok=4 changed=0 unreachable=0 failed=0
注意:這裡有一個報錯,是因為在/etc/ansible/roles/mysql/tasks/main.yml中有文法錯誤,上面貼出來的已經改過了。
[root@localhost ansible]# ansible-playbook site.yml
PLAY [10.0.250.203] ***********************************************************
GATHERING FACTS ***************************************************************
ok: [10.0.250.203]
TASK: [httpd | install httpd package] *****************************************
ok: [10.0.250.203]
TASK: [httpd | install configuration file] ************************************
ok: [10.0.250.203]
TASK: [httpd | start httpd] ***************************************************
ok: [10.0.250.203]
PLAY [10.0.250.202] ***********************************************************
GATHERING FACTS ***************************************************************
ok: [10.0.250.202]
TASK: [httpd | install httpd package] *****************************************
ok: [10.0.250.202]
TASK: [httpd | install configuration file] ************************************
ok: [10.0.250.202]
TASK: [httpd | start httpd] ***************************************************
ok: [10.0.250.202]
TASK: [mysql | install mysql-server package] **********************************
ok: [10.0.250.202]
TASK: [mysql | install configuration file] ************************************
ok: [10.0.250.202]
TASK: [mysql | start mysqld service] ******************************************
ok: [10.0.250.202]
PLAY RECAP ********************************************************************
10.0.250.202 : ok=7 changed=0 unreachable=0 failed=0
10.0.250.203 : ok=4 changed=0 unreachable=0 failed=0