ansible-playbook 運維

2020-08-10 22:30:55

爲什麼引入playbook?
一般運維人員完成一個任務, 比如安裝部署一個httpd服務會需要多個模組(一個模組也可以稱之爲task)提供功能來完成。而playbook就是組織多個task的容器,它的實質就是一個檔案,有着特定的組織格式,它採用的語法格式是YAML(Yet Another Markup Language)。YAML語法能夠簡單的表示雜湊表,字典等數據結構。簡單來說, playbook是由一個或多個模組組成的,使用多個不同的模組,完成一件事情。 

Ansible核心功能
-  pyYAML用於ansible編寫劇本所使用的語言格式(saltstack---python);
-  rsync-ini語法, sersync-xml語法, nsible-pyYAML語法;
-  paramiko遠端連線與數據傳輸;
-  Jinja2用於編寫ansible的模板資訊;

YAML三板斧
縮排: YAML使用一個固定的縮排風格表示層級結構,每個縮排由兩個空格組成, 不能使用tabs;
冒號: 以冒號結尾的除外,其他所有冒號後面所有必須有空格;
短橫線: 表示列表項,使用一個短橫槓加一個空格。多個項使用同樣的縮排級別作爲同一列表;

YAML基本語法
Ansible-playbook採用YAML語法編寫。連續的專案(即列表)用 -減號來表示,key/value(字典)用冒號:分隔。

列表:每一個列表成員前面都要有一個短橫線和一個空格

1

2

3

4

5

6

7

8

fruits:

    - Apple

    - Orange

    - Strawberry

    - Mango

 

或者:

fruits: ['Apple''Orange''Strawberry''Mango']

字典:每一個成員由鍵值對組成,注意冒號後面要有空格

1

2

3

4

5

6

martin:

    name: Martin D'vloper

    job: Developer

    skill: Elite

或者

martin: {name: Martin D'vloper, job: Developer, skill: Elite}

列表和字典可以混合使用

1

2

3

4

5

6

7

8

9

10

11

12

13

14

-  martin:

    name: Martin D'vloper

    job: Developer

    skills:

      - python

      - perl

      - pascal

-  tabitha:

    name: Tabitha Bitumen

    job: Developer

    skills:

      - lisp

      - fortran

      - erlang

範例如下:
[root@localhost ~]# cat httpd.yaml

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

---

- hosts: control-node    #將要執行任務的主機,已經在hosts檔案中定義好了,可是單個主機或主機組

  remote_user: root      #在目標主機上執行任務時的使用者身份

  vars:

    - pkg: httpd

  tasks:

    - name: "install httpd package."

      yum: name={{ pkg }}  state=installed

    - name: "copy httpd configure file to remote host."

      copy: src=/root/conf/httpd.conf dest=/etc/httpd/conf/httpd.conf

      notify: restart httpd     #當這個任務執行狀態發生改變時,觸發handlers執行.

    - name: "boot httpd service."

      service: name=httpd state=started

  handlers:                     #handlers與tasks是同一級別

    - name: restart httpd

      service: name=httpd state=restarted

playbook語法特性
1.  以 --- (三個減號)開始,必須頂行寫;
2. 次行開始寫Playbook的內容,但是一般要求寫明該playbook的功能;
3. 嚴格縮排,並且不能用Tab鍵縮排;
4. 縮排級別必須是一致的,同樣的縮排代表同樣的級別,程式判別設定的級別是通過縮排結合換行來實現的;
5. K/V的值可同行寫,也可換行寫。同行使用 :分隔,換行寫需要以 - 分隔;

playbook基礎元件
Hosts:執行執行任務(task)的目標主機
remote_user:在遠端主機上執行任務的使用者
tasks:任務列表
handlers:任務,與tasks不同的是隻有在接受到通知時纔會被觸發
templates:使用模板語言的文字檔案,使用jinja2語法。
variables:變數,變數替換{{ variable_name }}

整個playbook是以task爲中心,表明要執行的任務。hosts和remote_user表明在遠端主機以何種身份執行,其他元件讓其能夠更加靈活。下面 下麪介紹外掛:

1.  variable
變數定義在資產 (inventory) 中, 預設就是/etc/ansible/hosts檔案中

1

2

3

4

5

6

7

8

9

10

11

12

主機變數:

192.168.200.136 http_port=808 maxRequestsPerChild=808

192.168.200.137 http_port=8080 maxRequestsPerChild=909

 

主機組變數:

[websers]

192.168.200.136

192.168.200.137

 

[websers:vars] 

ntp_server=ntp.exampl.com

proxy=proxy.exampl.com

變數定義在playbook中

1

2

3

- hosts: webservers

  vars:

    http_port: 80

使用facts變數

1

2

3

4

facts變數是由setup模組獲取遠端主機的資訊。

 

用法:

# ansible 192.168.200.136 -m setup

在roles中定義變數, 這個後面會介紹到.

ansible-playbook 命令中傳入參數

1

2

使用 -e選項傳入參數

# ansible-playbook 192.168.200.136 -e "httpd_port=808" httpd04.yml

變數的參照

1

{{ var_name }}

2.  templates
它是一個模組功能,與copy不同的是他的文字檔案採用了jinga2語法,jinga2基本語法如下:

1

2

3

4

5

6

7

8

9

10

11

12

字面量:

  字串:使用單引號或雙引號

  數位:整型,浮點數

  列表:{item1,item2,...}

  字典:{key1:value1,key2:value2,...}

  布爾型:true/false

算術運算:

  +,-,*,/,//,%,**

比較運算:

  ==,!=,>,>=,<,<=

邏輯運算:

  and,or,not

注意:template只能在palybook中使用。

3.  tasks
執行的模組命令

1

2

3

4

5

6

7

8

9

10

11

12

13

格式:

  action:模組參數(此種方式只在較新的版本中出現)

  module:參數(已鍵值對的形式出現)

 

每一個task都有一個名稱,用於標記此任務。任務範例:

  name: install httpd

  yum: name=httpd state=present

 

注意:shell和command沒有參數,可在後面直接跟命令

  shell: ss -tnl | grep :80

 

1)某任務的執行狀態爲changed後,可通過相應的notify通知相應的handlers

2)任務可以通過tags打標籤,然後通過palybook命令-t選項呼叫.

playbook命令及呼叫方式

用法:
ansible-playbook  <filename.yml> ... [options]

<filename.yml>: yaml格式的playbook檔案路徑,必須指明
[options]: 選項

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99

100

101

102

103

104

105

106

107

108

109

110

111

112

113

114

115

116

117

118

119

120

121

122

123

124

125

126

127

128

129

130

131

132

133

134

135

136

137

Options:

--ask-vault-pass    

             #ask for vault password

             #加密playbook檔案時提示輸入密碼

  -C, --check         

             #don't make any changes; instead, try to predict some of the changes that may occur

             #模擬執行,不會真正在機器上執行(檢視執行會產生什麼變化)。即並不在遠端主機上執行,只是測試。

  -D, --diff          

             #when changing (small) files and templates, show the differences in those files; works great with --check

             #當更新的檔案數及內容較少時,該選項可顯示這些檔案不同的地方,該選項結合-C用會有較好的效果

  -e EXTRA_VARS, --extra-vars=EXTRA_VARS

             #set additional variables as key=value or YAML/JSON

             #在Playbook中引入外部參數變數

  --flush-cache       

             #clear the fact cache

             #清理fact快取,將fact清除到的遠端主機快取

  --force-handlers    

             #run handlers even if a task fails

             #強制執行handlers的任務,即使在任務失敗的情況下

  -f FORKS, --forks=FORKS

             #specify number of parallel processes to use(default=5)

             #並行任務數。FORKS被指定爲一個整數,預設是5

  -h, --help          

             #show this help message and exit

             #開啓幫助文件API

  -i INVENTORY, --inventory-file=INVENTORY

             #specify inventory host path (default=/etc/ansible/hosts) or comma separated host list.

             #指定要讀取的Inventory清單檔案

  -l SUBSET, --limit=SUBSET

             #further limit selected hosts to an additional pattern

             #限定執行的主機範圍

  --list-hosts        

             #outputs a list of matching hosts; does not execute anything else

             #列出執行匹配到的主機,但並不會執行任何動作。

  --list-tags         

             #list all available tags

             #列出所有可用的tags

  --list-tasks        

             #list all tasks that would be executed

             #列出所有即將被執行的任務

  -M MODULE_PATH, --module-path=MODULE_PATH

             #specify path(s) to module library (default=None)

             #要執行的模組的路徑

  --new-vault-password-file=NEW_VAULT_PASSWORD_FILE

             #new vault password file for rekey

             #

  --output=OUTPUT_FILE

             #output file name for encrypt or decrypt; use - for stdout

             #

  --skip-tags=SKIP_TAGS

             #only run plays and tasks whose tags do not match these values

             #跳過指定的tags任務

  --start-at-task=START_AT_TASK

             #start the playbook at the task matching this name

             #從第幾條任務(START_AT_TASK)開始執行

  --step              

             #one-step-at-a-time: confirm each task before running

             #逐步執行Playbook定義的任務,並經人工確認後繼續執行下一步任務

  --syntax-check      

             #perform a syntax check on the playbook, but do not execute it

             #檢查Playbook中的語法書寫,並不實際執行

  -t TAGS, --tags=TAGS

             #only run plays and tasks tagged with these values

             #指定執行該tags的任務

  --vault-password-file=VAULT_PASSWORD_FILE

             #vault password file

             #

  -v, --verbose       

             #verbose mode (-vvv for more, -vvvv to enable connection debugging)

             #執行詳細輸出

  --version           

             #show program's version number and exit

             #顯示版本

  

  ############Connection Options,即下面 下麪時連線許可權############

    control as whom and how to connect to hosts

  

    -k, --ask-pass    

             #ask for connection password

             #

    --private-key=PRIVATE_KEY_FILE, --key-file=PRIVATE_KEY_FILE

             #use this file to authenticate the connection

             #

    -u REMOTE_USER, --user=REMOTE_USER

             #connect as this user (default=None)

             #指定遠端主機以USERNAME執行命令

    -c CONNECTION, --connection=CONNECTION

             #connection type to use (default=smart)

             #指定連線方式,可用選項paramiko (SSH)、ssh、local,local方式常用於crontab和kickstarts

    -T TIMEOUT, --timeout=TIMEOUT

             #override the connection timeout in seconds(default=10)

             #SSH連線超時時間設定,預設10s

    --ssh-common-args=SSH_COMMON_ARGS

             #specify common arguments to pass to sftp/scp/ssh (e.g.ProxyCommand)

             #

    --sftp-extra-args=SFTP_EXTRA_ARGS

             #specify extra arguments to pass to sftp only (e.g. -f, -l)

             #

    --scp-extra-args=SCP_EXTRA_ARGS

             #specify extra arguments to pass to scp only (e.g. -l)

             #

    --ssh-extra-args=SSH_EXTRA_ARGS

             #specify extra arguments to pass to ssh only (e.g. -R)

             #

  

  ############Privilege Escalation Options, 即下面 下麪時許可權提升許可權############

    control how and which user you become as on target hosts

  

    -s, --sudo        

             #run operations with sudo (nopasswd) (deprecated, use become)

             #相當於Linux系統下的sudo命令

    -U SUDO_USER, --sudo-user=SUDO_USER

             #desired sudo user (default=root) (deprecated, use become)

             #使用sudo,相當於Linux下的sudo命令

    -S, --su          

             #run operations with su (deprecated, use become)

             #

    -R SU_USER, --su-user=SU_USER

             #run operations with su as this user (default=root)(deprecated, use become)

    -b, --become      

             #run operations with become (does not imply password prompting)

             #

    --become-method=BECOME_METHOD

             #privilege escalation method to use (default=sudo),valid choices: [ sudo | su | pbrun | pfexec | doas |dzdo | ksu | runas ]

             #

    --become-user=BECOME_USER

             #run operations as this user (default=root)

             #

    --ask-sudo-pass   

             #ask for sudo password (deprecated, use become)

             #傳遞sudo密碼到遠端主機,來保證sudo命令的正常執行

    --ask-su-pass     

             #ask for su password (deprecated, use become)

             #

    -K, --ask-become-pass

             #ask for privilege escalation password

             #

ansible-playbook需要注意的兩個命令
1)檢查語法,只檢查是否是yaml語法格式。並不做邏輯校驗。(記住這個要經常使用, 它是判斷語法是否正確!!!)
# ansible-playbook --syntax-check kevin.yml
2)模擬執行(不是真的執行)
# ansible-playbook -C kevin.yml

關閉Facts
如果不需要使用主機的任何fact數據,可以選擇關閉fact數據的獲取,這樣有利於增強Ansible面對大量系統的push模組。
在playbook中關閉Facts方法(gather_facts: no):

1

2

3

---

- hosts: webserver

  gather_facts: no

palybook書寫格式

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

---                                   # 也可以不使用這一行。可以省略。

- hosts: 172.16.60.211                #處理指定伺服器.   - (空格)hosts:(空格)172.16.20.211

  task:                               #劇本所要乾的事情; (空格)(空格)task:

  - name:                             #(兩個空格)-(空格)name。

    commandecho hello clsn linux    #(四個空格)command:(空格)

 

需要注意:

Task任務裡的name可以省略不寫,將-(空格)放到下一行模組牆面。例如:

---                                  

- hosts: 172.16.60.211              

  task:                            

  commandecho hello clsn linux

 

小範例:

[root@localhost ansible]# cat haha.yaml

---

- hosts: test_host

  remote_user: root

  gather_facts: no

  tasks:

     file: path=/opt/task1.txt state=touch

palybook格式範例

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

[root@ansible-server ~]# vim /etc/ansible/test.yaml

- hosts: 172.16.60.213

  tasks:

    - name: Install Rsync

      yum: name=rsync state=installed

 

playbook檢查方法

[root@ansible-server ~]# ansible-playbook --syntax-check /etc/ansible/test.yaml

 

playbook: /etc/ansible/test.yaml

[root@ansible-server ~]# ansible-playbook -C /etc/ansible/test.yaml

 

PLAY [172.16.60.213] *******************************************************************************************************************

 

TASK [Gathering Facts] *****************************************************************************************************************

ok: [172.16.60.213]

 

TASK [Install Rsync] *******************************************************************************************************************

ok: [172.16.60.213]

 

PLAY RECAP *****************************************************************************************************************************

172.16.60.213              : ok=2    changed=0    unreachable=0    failed=0 

 

上面兩個檢查命令, 第一個是進行playbook劇本設定資訊語法檢查; 第二個是模擬playbook劇本執行(彩排)

palybook劇本檔案範例

ansible-playbook編寫內容擴充套件劇本任務編寫多個任務

1

2

3

4

5

6

- hosts: all

  tasks:

    - name: restart-network

      cron: name='restart network' minute=00 hour=00 job='/usr/sbin/ntpdate time.nist.gov >/dev/null 2>&1'

    - name: sync time

      cron: name='sync time' minute=*/5 job="/usr/sbin/ntpdate pool.ntp.com >/dev/null 2>&1"

劇本編寫內容擴充套件:劇本任務編寫多個主機

1

2

3

4

5

6

7

8

9

10

11

- hosts: 172.16.60.7

  tasks:

    - name: restart-network

      cron: name='restart network' minute=00 hour=00 job='/usr/sbin/ntpdate time.nist.gov >/dev/null 2>&1'

    - name: sync time

      cron: name='sync time' minute=*/5 job="/usr/sbin/ntpdate pool.ntp.com >/dev/null 2>&1"

 

- hosts: 172.16.60.31

  tasks:

    - name: show ip addr to file

      shell: echo $(hostname -i) >> /tmp/ip.txt

playbook劇本編寫方式
-  多主機單任務編寫方式
-  多主機多工編寫方式
-  不同主機多工編寫方式

來看一個比較完整的ansible的yml檔案寫法:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

---

- host: webservers       ###要管理的遠端伺服器組名稱, 伺服器地址維護在/etc/ansible/hosts 裡, 也可以直接寫地址

  vars:

    port: 8081           ###定義了一個變數 埠號

  remote_user: root      ###遠程登錄後用什麼使用者執行

  

  pre_tasks:             ###執行正式 task 之前執行的任務

  - name: pre task       ###任務名稱

    shell: echo 'execute pre task'      ###執行一行 shell 命令, 支援 >> 等符號

  

  roles:            ###引入 roles, 可以理解爲參照了一個其他專案 ansible 包, 參照的 roles 可以是另一個完整的 ansible 指令碼

  - role: my_role   ###要參照的 role 名稱

    when: "ansible_os_family == 'RedHat'"   ###判斷條件, ansible_os_family 是一個內建變數, 可直接使用

  

  tasks:           ###按順序執行以下 task

  - include: my_tasks/some_task.yml       ###可以引入其他 yml 檔案

  - name: get hostname        ###這是一個 task, 名稱

    commandcat log.log      ###執行一行 command , 和 shell 類似, 但是不支援 >> 等操作符

    register: result          ###執行的結果, 設到 result 這個變數中, 後面可以使用

  - name: set hostname

    shell: cat {{result.stdout}} >> host.text

  - name: task1

    commandecho 'execute task1'

  - name: task2 start apache

    service:             ###啓動 httpd 伺服器, service 是一個 ansible 內建模組, 讀者可以自行檢視更多模組, 包括下載複製等等

      name: httpd

      state: started

      tags:

        - apache        ###這是一個標籤, 可以用 ansible-playbook main.yml --tags "apache" 指定只執行這個任務

  - name: copy and set value of index.html

    template:           ###這是一個複製方法, 也叫模組, 並且.j2檔案中可以使用{{}}來設定需要替換的變數

      src: templates/index.html.j2

      dest: /etc/httpd/index.html

    notify:             ###喚醒執行後面的 handlers 中名字叫 restart apache 的任務

    - restart apache

  

  post_tasks:           ###最後需要執行的任務

  - name: posy task

    shell: echo 'execute post task'

  

  handlers:

  - name: restart apache

    debug:              ###這是一個列印模組

      msg: start restart apche

palybook劇本中的方法

1.  handlers 任務觸發
在需要被監控的任務(tasks)中定義一個notify,只有當這個任務被執行時,纔會觸發notify對應的handlers去執行相應操作。例如組態檔被修改後,有可能需要重新啓動程式,此時我們可以設定一個handlers,類似觸發器。注意:handlers下的name名稱必須要和它對應的notify名稱相同!否則不會執行!!

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

[root@localhost ~]# cat httpd.yaml

---

- hosts: control-node

  remote_user: root

  vars:

    - pkg: httpd

  tasks:

    - name: "install httpd package."

      yum: name={{ pkg }}  state=installed

    - name: "copy httpd configure file to remote host."

      copy: src=/root/conf/httpd.conf dest=/etc/httpd/conf/httpd.conf

      notify: restart httpd

    - name: "boot httpd service."

      service: name=httpd state=started

  handlers:

    - name: restart httpd

      service: name=httpd state=restarted

########  在使用handlers的過程中,需要注意下面 下麪幾點  ########
1. handlers只有在其所在的任務被執行完時,它纔會被執行;如果一個任務中定義了notify呼叫Handlers,但由於條件判斷等原因,該任務未被執行,則Handlers同樣不會被執行。
2. handlers只會在Play的末尾執行一次;如果想在一個Playbook的中間執行handlers,則需要使用meta模組來實現,例如:-meta: flush_handlers。
3. 可以直接在Handlers中使用notify選項,實現Handlers呼叫Handlers。
4. 可以使用listen關鍵字,在一個tasks任務中一次性notify多個handler。即將多個handler分爲"一組",使用相同的"組名"即可,當notify對應的值爲"組名"時,"組"內的所有handler都會被notify。
5. 如果一個Play在執行到呼叫handlers的語句之前失敗了,那麼這個handlers將不會被執行。但是可以使用mega模組的--force-handlers選項來強制執行handlers,即使在handlers所在Play中途執行失敗也能執行。需要注意:--force-handlers參數主要針對即使playbook執行失敗,也要執行程式碼塊成功了的handlers(即執行成功的task任務), 如果程式碼塊本身執行失敗(即執行失敗的task任務),那麼它所對應的handlers應當不會被執行!

handlers可以理解成另一種tasks,handlers是另一種"任務列表",可以理解handlers和tasks是"平級關係",所以他們的縮排相同。handlers的任務會被tasks中的任務進行"呼叫",但是,被"呼叫"並不意味着一定會執行,只有當tasks中的任務"真正執行"以後,handlers中被呼叫的任務纔會執行,如果tasks中的任務並沒有做出任何實際的操作,那麼handlers中的任務即使被"呼叫",也並不會執行。handlers中可以有多個任務,被tasks中不同的任務notify。

場景1:headlers在所有tasks任務被執行完時才執行。

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

[root@localhost ansible]# cat haha.yaml

---

- hosts: test_host

  remote_user: root

  become: yes

  become_method: sudo

  tasks:

    - name: make file task1

      file: path=/opt/task1.txt state=touch

      notify: task1

    - name: make file task2

      file: path=/opt/task2.txt state=touch

      notify: task2

  handlers:

    - name: task1

      file: path=/opt/task1.txt mode=777 owner=root group=root

    - name: task2

      file: path=/opt/task2.txt src=/opt/task2.txt dest=/opt/heihei state=link force=yes

 

執行結果:

[root@localhost ansible]# ansible-playbook haha.yaml

 

PLAY [test_host] *********************************************************************************************************************************

 

TASK [Gathering Facts] ***************************************************************************************************************************

ok: [172.16.60.233]

ok: [172.16.60.234]

 

TASK [make file task1] ***************************************************************************************************************************

changed: [172.16.60.234]

changed: [172.16.60.233]

 

TASK [make file task2] ***************************************************************************************************************************

changed: [172.16.60.233]

changed: [172.16.60.234]

 

RUNNING HANDLER [task1] **************************************************************************************************************************

changed: [172.16.60.234]

changed: [172.16.60.233]

 

RUNNING HANDLER [task2] **************************************************************************************************************************

changed: [172.16.60.233]

changed: [172.16.60.234]

 

PLAY RECAP ***************************************************************************************************************************************

172.16.60.233              : ok=5    changed=4    unreachable=0    failed=0  

172.16.60.234              : ok=5    changed=4    unreachable=0    failed=0

從上面執行結果看出,Handlers執行的順序與Handlers在playbook中定義的順序是相同的,與"handler"被notify的順序無關。

場景2:使用meta模組,headlers會在它所對應的task任務執行完後立即被觸發並執行,即在playbook的中間環節執行。
預設情況下,所有的task執行完畢後,纔會執行各個handles,並不是執行完某個task後,立即執行相應的handler,如果想要在執行完某些task以後立即執行對應的handlre,那麼需要使用meta模組。

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

[root@localhost ansible]# cat haha.yaml

---

- hosts: test_host

  remote_user: root

  become: yes

  become_method: sudo

  tasks:

    - name: make file task1

      file: path=/opt/task1.txt state=touch

      notify: task1

    - meta: flush_handlers

    - name: make file task2

      file: path=/opt/task2.txt state=touch

      notify: task2

  handlers:

    - name: task1

      file: path=/opt/task1.txt mode=777 owner=root group=root

    - name: task2

      file: path=/opt/task2.txt src=/opt/task2.txt dest=/opt/heihei state=link force=yes

 

執行結果:

[root@localhost ansible]# ansible-playbook haha.yaml

 

PLAY [test_host] *********************************************************************************************************************************

 

TASK [Gathering Facts] ***************************************************************************************************************************

ok: [172.16.60.234]

ok: [172.16.60.233]

 

TASK [make file task1] ***************************************************************************************************************************

changed: [172.16.60.234]

changed: [172.16.60.233]

 

RUNNING HANDLER [task1] **************************************************************************************************************************

changed: [172.16.60.234]

changed: [172.16.60.233]

 

TASK [make file task2] ***************************************************************************************************************************

changed: [172.16.60.234]

changed: [172.16.60.233]

 

RUNNING HANDLER [task2] **************************************************************************************************************************

changed: [172.16.60.234]

changed: [172.16.60.233]

 

PLAY RECAP ***************************************************************************************************************************************

172.16.60.233              : ok=5    changed=4    unreachable=0    failed=0  

172.16.60.234              : ok=5    changed=4    unreachable=0    failed=0

上面使用了meta模組後,注意它的執行順序於場景1做下對比!

場景3:Handlers呼叫Handlers
若實現Handlers呼叫Handlers,則直接在Handlers中使用notify選項即可以。

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99

100

101

102

103

104

105

106

[root@localhost ansible]# cat haha.yaml

---

- hosts: test_host

  remote_user: root

  become: yes

  become_method: sudo

  tasks:

    - name: make file task1

      file: path=/opt/task1.txt state=touch

      notify: task1

    - name: make file task2

      file: path=/opt/task2.txt state=touch

        

  handlers:

    - name: task1

      file: path=/opt/task1.txt mode=777 owner=root group=root

      notify: task2

    - name: task2

      file: path=/opt/task2.txt src=/opt/task2.txt dest=/opt/heihei state=link force=yes

  

執行結果:

[root@localhost ansible]# ansible-playbook haha.yaml            

  

PLAY [test_host] *********************************************************************************************************************************

  

TASK [Gathering Facts] ***************************************************************************************************************************

ok: [172.16.60.234]

ok: [172.16.60.233]

  

TASK [make file task1] ***************************************************************************************************************************

changed: [172.16.60.233]

changed: [172.16.60.234]

  

TASK [make file task2] ***************************************************************************************************************************

changed: [172.16.60.234]

changed: [172.16.60.233]

  

RUNNING HANDLER [task1] **************************************************************************************************************************

changed: [172.16.60.234]

changed: [172.16.60.233]

  

RUNNING HANDLER [task2] **************************************************************************************************************************

changed: [172.16.60.234]

changed: [172.16.60.233]

  

PLAY RECAP ***************************************************************************************************************************************

172.16.60.233              : ok=5    changed=4    unreachable=0    failed=0 

172.16.60.234              : ok=5    changed=4    unreachable=0    failed=0

 

 

注意:上面執行的順序是:make file task1 > make file task2 > task1 > task2

 

====================================================================

也可以改成下面 下麪的方式:實現Handlers呼叫Handlers

 

[root@localhost ansible]# cat haha.yaml

---

- hosts: test_host

  remote_user: root

  become: yes

  become_method: sudo

  tasks:

    - name: make file task1

      file: path=/opt/task1.txt state=touch

      notify: task1

 

  handlers:

    - name: task1

      file: path=/opt/task1.txt mode=777 owner=root group=root

      notify: task2

    - name: task2

      file: path=/opt/task2.txt state=touch

      notify: task3

    - name: task3

      file: path=/opt/task2.txt src=/opt/task2.txt dest=/opt/heihei state=link force=yes

 

執行結果:

[root@localhost ansible]# ansible-playbook haha.yaml

 

PLAY [test_host] *********************************************************************************************************************************

 

TASK [Gathering Facts] ***************************************************************************************************************************

ok: [172.16.60.233]

ok: [172.16.60.234]

 

TASK [make file task1] ***************************************************************************************************************************

changed: [172.16.60.233]

changed: [172.16.60.234]

 

RUNNING HANDLER [task1] **************************************************************************************************************************

changed: [172.16.60.233]

changed: [172.16.60.234]

 

RUNNING HANDLER [task2] **************************************************************************************************************************

changed: [172.16.60.234]

changed: [172.16.60.233]

 

RUNNING HANDLER [task3] **************************************************************************************************************************

changed: [172.16.60.234]

changed: [172.16.60.233]

 

PLAY RECAP ***************************************************************************************************************************************

172.16.60.233              : ok=5    changed=4    unreachable=0    failed=0  

172.16.60.234              : ok=5    changed=4    unreachable=0    failed=0 

 

注意:上面的執行順序是:make file task1 > task1 > task2 > task3

場景4:使用listen關鍵字,在一個tasks任務中一次性notify多個handler
怎麼才能 纔能一次性notify多個handler呢?如果嘗試將多個handler使用相同的name呢?其實這樣並不可行!因爲當多個handler的name相同時,只有一個handler會被執行。要想實現一次notify多個handler,需要藉助一個關鍵字,它就是"listen",可以把listen理解成"組名",可以把多個handler分成"組",當需要一次性notify多個handler時,只要將多個handler分爲"一組",使用相同的"組名"即可,當notify對應的值爲"組名"時,"組"內的所有handler都會被notify。需要注意:listen的名稱要和notify名稱保持一致!

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

[root@localhost ansible]# cat haha.yaml

---

- hosts: test_host

  remote_user: root

  become: yes

  become_method: sudo

  tasks:

    - name: make file task1

      file: path=/opt/task1.txt state=touch

      notify: group1_handler

  handlers:

    - name: task1

      listen: group1_handler

      file: path=/opt/task1.txt mode=777 owner=root group=root

    - name: task2

      listen: group1_handler

      file: path=/opt/task1.txt src=/opt/task1.txt dest=/opt/heihei state=link force=yes

    - name: task3

      listen: group1_handler

      shell: echo "this is test,haha...." >> /opt/task1.txt

 

執行結果:

[root@localhost ansible]# ansible-playbook haha.yaml

 

PLAY [test_host] *********************************************************************************************************************************

 

TASK [Gathering Facts] ***************************************************************************************************************************

ok: [172.16.60.233]

ok: [172.16.60.234]

 

TASK [make file task1] ***************************************************************************************************************************

changed: [172.16.60.234]

changed: [172.16.60.233]

 

RUNNING HANDLER [task1] **************************************************************************************************************************

changed: [172.16.60.234]

changed: [172.16.60.233]

 

RUNNING HANDLER [task2] **************************************************************************************************************************

changed: [172.16.60.234]

changed: [172.16.60.233]

 

RUNNING HANDLER [task3] **************************************************************************************************************************

changed: [172.16.60.234]

changed: [172.16.60.233]

 

PLAY RECAP ***************************************************************************************************************************************

172.16.60.233              : ok=5    changed=4    unreachable=0    failed=0  

172.16.60.234              : ok=5    changed=4    unreachable=0    failed=0

場景5:使用--force-handlers選項來強制執行handlers
當playbook劇本執行失敗以後,handlers可能並沒有被觸發,也就不會執行了!如果想不管task任務是否成功執行,都強制執行handlers。在這個時候,可以在執行playbook的時候,新增--force-handlers來強制執行handlers!但是必須要注意的是:--force-handlers參數主要針對即使playbook執行失敗,也要執行程式碼塊成功了的handlers(即執行成功的task任務), 如果程式碼塊本身執行失敗(即執行失敗的task任務),那麼它所對應的handlers應當不會被執行!

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

[root@localhost ansible]# cat haha.yaml

---

- hosts: test_host

  remote_user: root

  become: yes

  become_method: sudo

  tasks:

    - name: make file task1

      file: path=/opt/task1.txt state=touch

      notify: task1

    - name: make file task2

      file: path=/opt/kevin/task2.txt state=touch

      notify: task2

  handlers:

    - name: task1

      file: path=/opt/task1.txt mode=777 owner=root group=root

    - name: task2

      shell: ln -s /opt/task1.txt /opt/task2.txt

 

執行結果:

[root@localhost ansible]# ansible-playbook haha.yaml

 

PLAY [test_host] *********************************************************************************************************************************

 

TASK [Gathering Facts] ***************************************************************************************************************************

ok: [172.16.60.234]

ok: [172.16.60.233]

 

TASK [make file task1] ***************************************************************************************************************************

changed: [172.16.60.233]

changed: [172.16.60.234]

 

TASK [make file task2] ***************************************************************************************************************************

fatal: [172.16.60.234]: FAILED! => {"changed"false"module_stderr""""module_stdout""Traceback (most recent call last):\r\n  File \"/tmp/ansible_iNMDpU/ansible_module_file.py\", line 474, in <module>\r\n    main()\r\n  File \"/tmp/ansible_iNMDpU/ansible_module_file.py\", line 448, in main\r\n    open(b_path, 'wb').close()\r\nIOError: [Errno 2] No such file or directory: '/opt/kevin/task2.txt'\r\n""msg""MODULE FAILURE""rc": 0}

fatal: [172.16.60.233]: FAILED! => {"changed"false"module_stderr""""module_stdout""Traceback (most recent call last):\r\n  File \"/tmp/ansible_OvTacW/ansible_module_file.py\", line 474, in <module>\r\n    main()\r\n  File \"/tmp/ansible_OvTacW/ansible_module_file.py\", line 448, in main\r\n    open(b_path, 'wb').close()\r\nIOError: [Errno 2] No such file or directory: '/opt/kevin/task2.txt'\r\n""msg""MODULE FAILURE""rc": 0}

 

RUNNING HANDLER [task1] **************************************************************************************************************************

        to retry, use: --limit @/etc/ansible/haha.retry

 

PLAY RECAP ***************************************************************************************************************************************

172.16.60.233              : ok=2    changed=1    unreachable=0    failed=1  

172.16.60.234              : ok=2    changed=1    unreachable=0    failed=1

 

如上執行結果,由於/opt/kevin目錄不存在,導致task的第二個任務執行失敗,這個時候handler根本沒有被觸發,也就不會執行。

即使第一個任務執行成功,但是它對應的第一個handler也不會被執行!!

 

###################################################################################

接下來使用--force-handlers選項來強制執行handlers(強制執行的是:成功執行的task對應的handler)

[root@localhost ansible]# ansible-playbook haha.yaml --force-handlers

 

PLAY [test_host] *********************************************************************************************************************************

 

TASK [Gathering Facts] ***************************************************************************************************************************

ok: [172.16.60.234]

ok: [172.16.60.233]

 

TASK [make file task1] ***************************************************************************************************************************

changed: [172.16.60.234]

changed: [172.16.60.233]

 

TASK [make file task2] ***************************************************************************************************************************

fatal: [172.16.60.233]: FAILED! => {"changed"false"module_stderr""""module_stdout""Traceback (most recent call last):\r\n  File \"/tmp/ansible_rEJQHm/ansible_module_file.py\", line 474, in <module>\r\n    main()\r\n  File \"/tmp/ansible_rEJQHm/ansible_module_file.py\", line 448, in main\r\n    open(b_path, 'wb').close()\r\nIOError: [Errno 2] No such file or directory: '/opt/kevin/task2.txt'\r\n""msg""MODULE FAILURE""rc": 0}

fatal: [172.16.60.234]: FAILED! => {"changed"false"module_stderr""""module_stdout""Traceback (most recent call last):\r\n  File \"/tmp/ansible_7CDxpp/ansible_module_file.py\", line 474, in <module>\r\n    main()\r\n  File \"/tmp/ansible_7CDxpp/ansible_module_file.py\", line 448, in main\r\n    open(b_path, 'wb').close()\r\nIOError: [Errno 2] No such file or directory: '/opt/kevin/task2.txt'\r\n""msg""MODULE FAILURE""rc": 0}

 

RUNNING HANDLER [task1] **************************************************************************************************************************

changed: [172.16.60.234]

changed: [172.16.60.233]

        to retry, use: --limit @/etc/ansible/haha.retry

 

PLAY RECAP ***************************************************************************************************************************************

172.16.60.233              : ok=3    changed=2    unreachable=0    failed=1  

172.16.60.234              : ok=3    changed=2    unreachable=0    failed=1

 

如上執行結果,即使playbook執行中有task任務執行失敗,但是執行成功的task任務所呼叫的handler依然會被強制觸發並執行!但是執行失敗的task任務所呼叫的handler依然不會被執行。

即handlers中的task1會被執行,task2不會被執行!

2.  tags任務標籤
tags用於讓使用者選擇執行playbook中的部分程式碼。ansible具有冪等性,因此會自動跳過沒有變化的部分,即便如此,有些程式碼爲測試其確實沒有發生變化的時間依然會非常地長。此時如果確信其沒有變化,就可以通過tags跳過此些程式碼片斷。tags可以看作是ansible的任務控制!

ansible的標籤(Tags)功能可以給角色(Roles)、檔案、單獨的任務,甚至整個Playbook打上標籤,然後利用這些標籤來指定要執行Playbook中的個別任務,或不執行指定的任務。如果有一個很大的playbook劇本,而只想執行playbook其中的某個或部分task任務,而不是執行playbook中所有的任務,這個時候tags是你的最佳選擇。

2.1  ansible支援"tags:"屬性,執行playbook時,可以通過兩種方式根據"tags"過濾任務:
1. 在命令列上,使用或選項"--tags或 --skip-tags",後面使用空格或"="都可以。
2. 在ansible設定設定中,使用和選項"TAGS_RUN或TAGS_SKIP";
3. 可以使用"--list-tags"檢視playbook中有哪些tags會被執行;

2.2  ansible系統中內建的特殊tags(目前有5個特殊的tags)
到ansible 2.5版本以後,目前系統內建的tags有以下幾個:
always: 除非--skip-tags指定這個標籤,否則該標記爲always的task一直都會執行。"--tags always"只執行標記了always的tasks;
never: 除非--tags指定了這個標籤,否則該標記爲never的task一直都不會執行。"--tags never"執行標記了always和never的tasks;
tagged: --tags tagged表示執行所有有tags標籤的tasks任務,但不包括tags標籤是never的tasks任務;--skip-tags tagged表示所有有tags標籤的tasks任務都跳過,即不會執行。
untagged: --tags untagged表示執行所有沒有tags標籤的tasks任務和tags標籤爲always的tasks任務;--skip-tags untagged效果相反!
all:--tags all表示執行所有的tags標籤爲非never的task,包括有tags標籤和無tags標籤的tasks。

執行ansible-playbook命令時,使用下面 下麪兩個參數的含義(自定義的tags可以是單個,也可以是多個,多個之間使用逗號隔開):
"--tags 自定義的tag" 表示執行tags爲指定的標籤名的tasks和tags爲always的tasks。如果執行命令ansible-playbook site.yml 時不指定tags,則會執行所有tags爲非never的tasks
"--skip-tags 自定義tag" 表示執行所有非指定tag和非never的tasks

2.3  tags標籤設定語法有下面 下麪三種:

1

2

3

4

5

6

7

8

9

語法一:

tags:

  - tag_test

  

語法二:

tags: tag_test

  

語法三:

tags: ['tag_test']

2.3  ansible的tags使用
1)最常見的使用形式。一個task任務新增一個tags標籤。

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

官方範例如下:

[root@localhost ansible]# vim example.yml

---

- hosts: all

  remote_user: root

  gather_facts: no

  tasks:

    - yum: name={{ item }} state=installed

      with_items:

         - httpd

         - memcached

      tags:

         - packages

    - template: src=templates/src.j2 dest=/etc/foo.conf

      tags:

         - configuration

此時如果希望只run其中的某個task,則run的時候指定tags即可。可以執行多個tags,中間使用逗號隔開;也可以執行單個tags。

1

2

3

4

5

6

7

[root@localhost ansible]# ansible-playbook example.yml --tags "configuration,packages"  

[root@localhost ansible]# ansible-playbook example.yml --tags configuration  

[root@localhost ansible]# ansible-playbook example.yml --tags packages

或者

[root@localhost ansible]# ansible-playbook example.yml --tags="configuration,packages"  

[root@localhost ansible]# ansible-playbook example.yml --tags=configuration  

[root@localhost ansible]# ansible-playbook example.yml --tags=packages

相反,也可以使用--skip-tags跳過某個task任務。

1

2

3

[root@localhost ansible]# ansible-playbook example.yml --skip-tags configuration

或者

[root@localhost ansible]# ansible-playbook example.yml --skip-tags=configuration

具體看下面 下麪範例(tags三種語法都用上):

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

[root@localhost ansible]# cat haha.yaml

---

- hosts: test_host

  remote_user: root

  gather_facts: no

  tasks:

    - name: task1

      file: path=/opt/task1.txt state=touch

      tags: make_task1

    - name: task2

      file: path=/opt/task2.txt state=touch

      tags:

         - make_task2

    - name: task3

      file: path=/opt/task2.txt src=/opt/task2.txt dest=/opt/heihei state=link force=yes

      tags: ['link_task3']

 

只執行make_task1標籤的task任務

[root@localhost ansible]# ansible-playbook haha.yaml --tags make_task1

 

PLAY [test_host] *********************************************************************************************************************************

 

TASK [task1] *************************************************************************************************************************************

changed: [172.16.60.234]

 

PLAY RECAP ***************************************************************************************************************************************

172.16.60.234              : ok=1    changed=1    unreachable=0    failed=0  

 

執行多個tags

[root@localhost ansible]# ansible-playbook haha.yaml --tags make_task1,make_task2

 

PLAY [test_host] *********************************************************************************************************************************

 

TASK [task1] *************************************************************************************************************************************

changed: [172.16.60.234]

 

TASK [task2] *************************************************************************************************************************************

changed: [172.16.60.234]

 

PLAY RECAP ***************************************************************************************************************************************

172.16.60.234              : ok=2    changed=2    unreachable=0    failed=0

 

跳過make_task2標籤的任務,其他任務正常執行

[root@localhost ansible]# ansible-playbook haha.yaml --skip-tags make_task2

 

PLAY [test_host] *********************************************************************************************************************************

 

TASK [task1] *************************************************************************************************************************************

changed: [172.16.60.234]

 

TASK [task3] *************************************************************************************************************************************

changed: [172.16.60.234]

 

PLAY RECAP ***************************************************************************************************************************************

172.16.60.234              : ok=2    changed=2    unreachable=0    failed=0

2)一個task任務新增多個tags標籤。
上面是一個task任務新增一個tags標籤,其實一個task任務可以新增多個標籤,而且不同的task任務可以使用相同的tags標籤。

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

一個任務新增多個tags標籤的語法仍然也有三種:

語法1:

tags:

  - tag1

  - tag2

 

語法2:

tags: tag1,tag2

 

語法3:

tags: ['tag1,tag2']

 

========================================================

具體範例如下:

[root@localhost ansible]# vim https.yml

---

- hosts: test_host

  remote_user: root

  tasks:

     - name: install httpd package

       tags: 

         - httpd

         - package

       yum:

         name=httpd

         state=latest

  

     - name: start up httpd service

       tags: httpd,service

       service:

         name: httpd

         state: started

 

 

上面例子中每個任務都有多個標籤,而且上例中兩個任務都有一個共同的標籤,就是httpd標籤。

所以當執行"ansible-playbook httpd.yml --tags=httpd"時,上面兩個task任務都會被執行。

 

由於上面例子中的所有任務都有共同的httpd標籤,所以像這種情況,可以把httpd標籤提取出來並寫在play劇本中,範例如下:

[root@localhost ansible]# vim https.yml

---

- hosts: test_host

  remote_user: root

  tags:httpd

  tasks:

     - name: install httpd package

       tags: 

          - package

       yum:

         name=httpd

         state=latest

  

     - name: start up httpd service

       tags: ['service']

       service:

         name: httpd

         state: started

需要注意:當tags寫在play劇本中而非寫在task任務中時,play中的所有task任務都會繼續當前paly中的tags,就像上例中,兩個任務都會繼承httpds的tag標籤,同時還擁有自己的tag標籤。

3)內建的特殊tags的用法
上面已經介紹了5個內建的特殊的tags,每個都有其自身的用意。如下以always關鍵字的tags爲例:如果把任務的tags值指定爲always時,那麼這個任務就總是被執行,除非使用"--skip-tags"選項明確指定不執行對應任務的tags標籤。

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

[root@localhost ansible]# cat haha.yaml

---

- hosts: test_host

  remote_user: root

  gather_facts: no

  tasks:

    - name: task1

      file: path=/opt/task1.txt state=touch

      tags: make_task1

    - name: task2

      file: path=/opt/task2.txt state=touch

      tags:

         - always

    - name: task3

      file: path=/opt/task2.txt src=/opt/task2.txt dest=/opt/heihei state=link force=yes

      tags: ['link_task3']

 

執行1:如下,雖然tags指定了執行標籤爲make_task1的任務,但是由於任務2的標籤有關鍵字always,所以任務2也會被執行,這就是always的作用!

[root@localhost ansible]# ansible-playbook haha.yaml --tags=make_task1

 

PLAY [test_host] *********************************************************************************************************************************

 

TASK [task1] *************************************************************************************************************************************

changed: [172.16.60.234]

 

TASK [task2] *************************************************************************************************************************************

changed: [172.16.60.234]

 

PLAY RECAP ***************************************************************************************************************************************

172.16.60.234              : ok=2    changed=2    unreachable=0    failed=0 

 

執行2: 只執行標籤爲always的任務

[root@localhost ansible]# ansible-playbook haha.yaml --tags always

或者

[root@localhost ansible]# ansible-playbook haha.yaml --tags=always

 

PLAY [test_host] *********************************************************************************************************************************

 

TASK [task2] *************************************************************************************************************************************

changed: [172.16.60.234]

 

PLAY RECAP ***************************************************************************************************************************************

172.16.60.234              : ok=1    changed=1    unreachable=0    failed=0

 

執行3: 跳過標籤爲always關鍵字的任務,這裏明確指出跳過執行always標籤。

[root@localhost ansible]# ansible-playbook haha.yaml --skip-tags always   

 

PLAY [test_host] *********************************************************************************************************************************

 

TASK [task1] *************************************************************************************************************************************

changed: [172.16.60.234]

 

TASK [task3] *************************************************************************************************************************************

ok: [172.16.60.234]

 

PLAY RECAP ***************************************************************************************************************************************

172.16.60.234              : ok=2    changed=1    unreachable=0    failed=0  

 

其他四個特殊的tags標籤在這裏就不做範例說明了。特殊tags標籤可以在ansible-playbook命令執行時直接使用。

4)tags標籤可以和role 結合使用

1

2

3

4

[root@localhost ansible]# cat test.yml

---

roles:

  - { role: webserver, port: 5000, tags: [ 'web''foo' ] }

5)tags和include結合使用。

1

2

3

[root@localhost ansible]# cat test.yml

---

- include: kevin.yml tags=web,foo

如上,對一個include任務打了兩個tags標籤,直接執行"ansible_playbook test.yml" 或 "ansible_playbook test.yml --tags=web" 或 "ansible_playbook test.yml --tags=foo" 命令則會將kevin.yml檔案中所有task任務都執行。

再來看看一個include結合tags的範例:通過指定標籤(tags),來說明是安裝tomcat7還是tomcat8

tomcat.yml檔案

1

2

3

4

5

---

- include: install_tomcat7.yml

  tags: tomcat7

- include: install_tomcat8.yml

  tags: tomcat8

install_tomcat7.yml檔案

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

---

- name: "複製檔案到遠端主機"

  copy:

    src={{ item.src }}

    dest={{ item.dest }}

  with_items:

    - src: jdk-7u79-linux-x64.rpm

      dest: /usr/local/src/

    - src: java17.sh

      dest: /etc/profile.d/

- name: "安裝jdk"

  yum:

    name: /usr/local/src/jdk-7u79-linux-x64.rpm

    state: present

- name: "重新載入環境變數"

  shell: "source /etc/profile.d/java17.sh"

- name: "複製tomcat檔案到遠端伺服器並解壓"

  unarchive:

    src=apache-tomcat-7.0.64.zip

    dest=/data/

    copy=yes

    owner=staplesapp

    group=admin

- name: "對解壓後的檔案重新命名"

  shell: mv /data/apache-tomcat-7.0.64 /data/tomcat7

- name: "對tomcat進行相關設定"

  shell: find /data/tomcat7/bin -name "*.sh" xargs chmod +x

- name: "啓動tomcat"

  shell: 'nohup /data/tomcat7/bin/startup.sh &'

install_tomcat8.yml檔案

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

---

- name: "複製檔案到遠端主機"

  copy:

    src={{ item.src }}

    dest={{ item.dest }}

  with_items:

    - src: jdk-8u111-linux-x64.rpm

      dest: /usr/local/src/

    - src: java18.sh

      dest: /etc/profile.d/

- name: "安裝jdk"

  yum:

    name: /usr/local/src/jdk-8u111-linux-x64.rpm

    state: present

- name: "設定java環境變數"

  shell: "source /etc/profile.d/java18.sh"

- name: "安裝tomcat"

  unarchive:

      src=apache-tomcat-8.0.30.tar.gz

      dest=/data/

      copy=yes

      owner=staplesapp

      group=admin

- name: "對解壓後的檔案重新命名"

  shell: mv /data/apache-tomcat-8.0.30 /data/tomcat8

- name: "啓動tomcat"

  shell: 'nohup /data/tomcat8/bin/startup.sh &'

下面 下麪開始執行命令:

1

2

3

4

5

安裝tomcat7:

[root@localhost ansible]# ansible-playbook tomcat.yml --tags tomcat7

 

安裝tomcat8:

[root@localhost ansible]# ansible-playbook tomcat.yml --tags tomcat8

這裏需要特別注意:
在之前ansible版本中使用include 整合多個roles至統一入口結合tags標籤來管理roles劇本,但在ansible2.8版本之後將會刪除include語法,更改爲import_playbook。如果還使用include語法也可以,只不過ansible-playbook執行結果中會有告警資訊:"DEPRECATION WARNING]:'include' for playbook includes. You should use 'import_playbook' instead. This feature will be removed in version 2.8. Deprecation warnings can be disabled by setting deprecation_warnings=False in ansible.cfg."。所以,最好將上面tomcat.yml檔案中的include語法改成import_playbook,如下:

1

2

3

4

5

6

[root@localhost ansible]# cat tomcat.yml

---

- import_playbook: install_tomcat7.yml

  tags: tomcat7

- import_playbook: install_tomcat8.yml

  tags: tomcat8

3.  include用法
如果想在playbook中重複使用任務列表,則可以使用include檔案來執行此操作。 使用include的任務列表是定義系統將要實現的角色的好方法。主要清楚:ansible2.8版本之後include語法變成了import_playbook。如果還是使用include,則不會影響執行結果,只不過是有告警資訊。ansible也可以將變數傳遞給include。範例如下:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

範例1: 通過Include,可以在playbook中參照另一個playbook或者tasks

==============================================================

[root@localhost ansible]# cat install_MysqlAndPhp.yml

- yum:

    name: mysql

    state: present

- yum:

    name: php-fpm

    state: present

 

[root@localhost ansible]# cat lamp.yml

---

- hosts: test_host

  remote_user: root

  gather_facts: no

  tasks:

  - include: install_MysqlAndPhp.yml

  - yum:

      name: httpd

      state: present

  

[root@localhost ansible]# cat lnmp.yml

---

- hosts: test_host

  remote_user: root

  gather_facts: no

  tasks:

    - include: install_MysqlAndPhp.yml

    - yum:

        name: nginx

        state: present

 

 

範例2: 可以在handler中參照include

==============================================================

[root@localhost ansible]# cat test_include.yml

---

- hosts: test_host

  remote_user: root

  gather_facts: no

  tasks:

    file:

        path: /opt/ttt

        state: touch

      notify: test include handlers

  

  handlers:

    - name: test include handlers

      include: include_handler.yml

  

[root@localhost ansible]# cat include_handler.yml

- debug:

    msg: "task1 of handlers"

- debug:

    msg: "task2 of handlers"

- debug:

    msg: "task3 of handlers"

 

範例3: when在include中使用

==============================================================

[root@localhost ansible]# cat /etc/ansible/hosts

[db]

192.168.24.10

[app]

192.168.24.11

 

[root@localhost ansible]# cat install_client.yml

---

- hosts: '` hosts `'

  user: ansible

  sudoyes

  sudo_user:root

  roles:

    - install_client

 

[root@localhost ansible]# cat roles/install_client/tasks/main.yml 

---

- include: db.yml

  when: "hosts == 'db'"

- include: app.yml

  when: "hosts == 'app'"

 

[root@localhost ansible]# cat roles/install_client/tasks/db.yml

---

  - name: Touchdb file

    shell: touch /tmp/db.txt

 

[root@localhost ansible]# cat roles/install_client/tasks/app.yml

---

  - name: Touchdb file

    shell: touch /tmp/db.txt

 

執行命令:

[root@localhost ansible]# ansible-playbook -i hosts install_client.yml --extra-vars "hosts=db"

[root@localhost ansible]# ansible-playbook -i hosts install_client.yml --extra-vars "hosts=app"

 

範例4: 可以在include中使用tags標籤,這個在上面已經介紹過了

==============================================================

4.  role用法
角色(roles)是ansible自1.2版本開始引入的新特性,用於層次性,結構化地組織playbook。roles能夠根據層次型結構自動裝載變數檔案、tasks以及handlers等。要使用roles只需要在playbook中使用include指令即可。簡單的說,roles就是通過分別將變數、檔案、任務、模組及處理器放置於單獨的目錄中、並可以便捷地include他們的一種機制 機製。角色一般用於基於主機構建服務的場景中、但也可以是用於構建守護行程等場景中。role主要作用是重用playbook,例如無論安裝什麼軟體都會安裝時間同步服務,那麼每個playbook都要編寫ntp task,可以將ntp task寫好,等到用的時候再呼叫就行了。ansible中將其組織成role,它有着固定的組織格式,以便playbook呼叫。

4.1  role層級目錄結構
role以特定的層級目錄結構進行組織的tasks、variables、handlers、templates、files等;相當於函數的呼叫把各個功能切割成片段來執行。

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99

100

101

102

103

104

105

106

107

108

109

110

111

112

113

114

115

116

117

118

119

120

121

122

123

124

125

126

127

128

129

130

131

132

133

134

135

136

137

138

139

140

141

142

roles/

    role_name/:定義的role的名字

    file/:     用於存放copy或script等模組呼叫的函數

    tasks/:     用於定義各種task,此目錄一定要有main.yml;其他檔案需要main.yml包含呼叫

    handlers/: 用於定義各種handlers,此目錄一定要有main.yml;其他檔案需要main.yml包含呼叫

    vars/:      用於定義variables,此目錄一定要有main.yml;其他檔案需要main.yml包含呼叫

    templates/:儲存由template模組呼叫的模板文字;

    meta/:     定義當前角色的特殊設定及其依賴關係,此目錄中至少應該有一個名爲main.yml的檔案;其它的檔案需要由main.yml進行"包含"呼叫;

    default/:  此目錄中至少應該有一個名爲main.yml的檔案,用於設定預設變數;

 

[root@localhost ansible]# ll roles/

total 40

drwkebor-kebor-kebo 8 root root 4096 Jul 29 22:13 web_Deploy

drwkebor-kebor-kebo 8 root root 4096 May  7  2019 web_Deploy_af

 

[root@localhost ansible]# ll roles/web_Deploy                  

total 25

-rw-r--r-- 1 root root   45 May  7  2019 web_Deploy.yml

drwkebor-kebor-kebo 2 root root 4096 Jul 10 19:09 defaults

drwkebor-kebor-kebo 2 root root 4096 May  7  2019 handlers

drwkebor-kebor-kebo 2 root root 4096 May  7  2019 meta

drwkebor-kebor-kebo 2 root root 4096 Dec 26 19:42 tasks

drwkebor-kebor-kebo 2 root root 4096 May  7  2019 templates

drwkebor-kebor-kebo 2 root root 4096 May  7  2019 vars

 

[root@localhost ansible]# ll roles/web_Deploy/tasks/

total 35

-rwkebor-kebor-kebo 1 root root 1542 Jun 24  2019 Auth.yml

-rwkebor-kebor-kebo 1 root root 1482 Oct 11 16:13 StartService.yml

-rwkebor-kebor-kebo 1 root root  963 Jun 18  2019 main.yml

-rwkebor-kebor-kebo 1 root root 1415 May  7  2019 StopService.yml

 

[root@localhost ansible]# cat roles/web_Deploy/tasks/main.yml

---

- include_tasks: Auth.yml

  tags: userauth

   

- include_tasks: StopService.yml

  tags: stopservice

- include_tasks: StartService.yml

  tags: startservice

 

[root@localhost ansible]# cat roles/web_Deploy/web_Deploy.yml

---

- hosts: all

  roles:

    - web_Deploy

 

===================================================================================

再如下一個專案的role目錄結構:

site.yml

webservers.yml

fooservers.yml

roles/

   common/

     files/

     templates/

     tasks/

     handlers/

     vars/

     defaults/

     meta/

   webservers/

     files/

     templates/

     tasks/

     handlers/

     vars/

     defaults/

     meta/

 

 

再看下目錄解釋:

yml檔案:用於定義此角色用到的各handler:在handler中使用include包含的其他的handler檔案也應該位於此目錄中;

files目錄:存放由copy或script等模組呼叫的檔案;

templates目錄:templates模組會自動在此目錄中尋找Jinja2模板檔案;

tasks目錄:至少應該包含一個名爲main.yml的檔案,其定義了此角色的任務列表;此檔案可以使用include包含其他的位於此目錄中的task檔案;

handlers目錄:此目錄中應當包含一個main;

vars目錄:應當包含一個main.yml檔案,用於定義此角色用到的變數;

meta目錄:應當包含一個main.yml檔案,用於定義此角色的特殊設定及其依賴關係;ansible 1.3及其以後的版本才支援

default目錄:爲當前角色設定預設變數時使用此目錄;應當包含一個main.yml檔案;

 

 

那麼一個playbook就可以這樣寫:

---

 - hosts: webservers

  roles:

     - common

     - webservers

 

這個playbook爲一個角色"kebo"指定瞭如下的行爲:

如果 roles/kebo/tasks/main.yml 存在, 其中列出的tasks將被新增到play中

如果roles/kebo/handlers/main.yml 存在, 其中列出的handlers將被新增到play中

如果roles/kebo/vars/main.yml 存在, 其中列出的variables將被新增到play中

如果roles/kebo/meta/main.yml 存在, 其中列出的 "角色依賴"將被新增到roles列表中 (1.3 andlater)

所有 copy tasks 可以參照 roles/kebo/files/ 中的檔案,不需要指明檔案的路徑。

所有 scripttasks 可以參照 roles/kebo/files/ 中的指令碼,不需要指明檔案的路徑。

所有 template tasks 可以參照roles/kebo/templates/ 中的檔案,不需要指明檔案的路徑。

所有 include tasks 可以參照roles/kebo/tasks/ 中的檔案,不需要指明檔案的路徑。

 

如果roles目錄下有檔案不存在,這些檔案將被忽略。比如 roles目錄下面 下麪缺少了"vars/"目錄,這也沒關係。

 

需要注意:

仍然可以在playbook中鬆散地列出tasks,vars_files 以及 handlers,這種方式仍然可用,但是roles是一種很好的具有組織性的功能特性,強烈建議使用它。

如果在playbook中同時使用roles和tasks,vars_files 或者 handlers,roles 將優先執行。

 

而且也可以使用參數化的roles,這種方式通過新增變數來實現,比如:

--

- hosts: webservers

  roles:

    - common

    - { role: foo_app_instance, dir'/opt/a',  port: 5000 }

    - { role: foo_app_instance, dir'/opt/b',  port: 5001 }

 

當一些事情不需要頻繁去做時,也可以爲 roles 設定觸發條件,像這樣:

---

- hosts: webservers

  roles:

    - { role: some_role, when: "ansible_os_family == 'RedHat'" }

它的工作方式是:將條件子句應用到 role 中的每一個 task 上。

 

也可以給role分配指定的標籤,比如:

---

- hosts: webservers

  roles:

    - { role: foo, tags: ["bar""baz"] }

 

如果play仍然包含有 "tasks" section,這些 tasks 將在所有 roles 應用完成之後才被執行。也可定義一些tasks,讓它們在roles之前以及之後執行,可以這樣做:

---

- hosts: webservers

  pre_tasks:

    - shell: echo 'hello'

  roles:

    - { role: some_role }

  tasks:

    - shell: echo 'still busy'

  post_tasks:

    - shell: echo 'goodbye'

 

注意:

pre_tasks: 執行正式 task 之前執行的任務

post_tasks:最後需要執行的任務

4.2  在playbook中呼叫role

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

role存放的路徑在組態檔/etc/ansible/ansible.cfg中定義。如下,發現ansible的roles目錄定義到/root/app/script/ansible/roles路徑下了!!

[root@localhost ansible]# cat /etc/ansible/ansible.cfg |grep roles_path

roles_path    = /etc/ansible/roles:/root/app/script/ansible/roles

 

在playbook中呼叫role的方式有三種,如下:

第一種:

- hosts: HOSTS

  remote_user: root

  roles:

    - ROLE_NAME1

    - ROLE_NAME2

 

第二種:除了字典第一個元素指明呼叫的role,後面是傳遞給role的變數

- hosts: HOSTS

  remote_user: root

  roles:

  - { role: ROLE_NAME1, VARIABLE1: VALUE1, ... }

 

第三種:when指明role呼叫的條件

- hosts: HOSTS

  remote_user: root

  roles:

  - { role: ROLE_NAME1, when: CONDITIONS }

4.3  呼叫role範例

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99

100

101

102

103

104

105

106

107

108

109

110

111

112

113

0) 先來看看role的路徑定義

[root@localhost ansible]# cat /etc/ansible/ansible.cfg|grep roles_path

roles_path    = /etc/ansible/roles:/etc/ansible/roles

 

1)目錄結構

[root@localhost ansible]# tree /etc/ansible/roles

/etc/ansible/roles

└── httpd                   #palybook呼叫時role的名稱

    ├── defaults

    ├── files

    └── handlers

    │   └── main.yml       #所有的目錄檔案,並不一定要有,用時才建立

    └── mata

    └── tasks

    │   └── main.yml

    └── tamplates

        └── httpd.conf.c6.j2  #centos6,centos7的組態檔

        └── httpd.conf.c7.j2

 

2)tasks檔案

[root@localhost ansible]# cat /etc/ansible/roles/httpd/tasks/main.yml

- name: install httpd package

  yum: name=httpd state=present

- name: install configure file

  template: src=httpd.conf.c{{ ansible_distribution_major_version }}.j2 dest=/etc/httpd/conf/httpd.conf

  tags: instconf

  notify: restart httpd service

- name: start httpd service

  service: name=httpd state=started enabled=true

 

3) handlers檔案

[root@localhost ansible]# cat /etc/ansible/roles/httpd/handlers/main.yml

- name: restart httpd service

  service: name=httpd state=restarted

 

4) 模板檔案

[root@localhost ansible]# grep ^Listen /etc/ansible/roles/httpd/templates/httpd.conf.c6.j2

Lister {{ httpd_port }}

 

5) 變數

[root@localhost ansible]# cat /etc/ansible/roles/httpd/vars/main.yml

httpd_port: 8088

 

6) playbook檔案

[root@localhost ansible]# cat /etc/ansible/httpd_conf.yml

---

- hosts: webservers

  remote_user: root

  roles:

  - { role: httpd }

 

7) 執行playbook檔案,並檢視httpd埠

[root@localhost ansible]# ansible-playbook -i /etc/ansible/hosts /etc/ansible/httpd_conf.yml

[root@localhost ansible]# ansible -i /etc/ansible/hosts webservers -m shell -a "ss -tnlp|grep :80"

 

========================================================================================================

再來看一例:

1.group: 建立使用者組nginx

2.user: 建立使用者nginx

3.yum: 安裝nginx

4.template: 組態檔更新nginx.conf

5.service: 啓動nginx

 

[root@localhost ~]# cat /etc/ansible/ansible.cfg|grep roles_path

roles_path    = /etc/ansible/roles:/root/ansible/roles

 

[root@localhost ~]# cd /root/ansible/roles/nginx

[root@localhost nginx]# mkdir tasks templates

[root@localhost nginx]# cd tasks

 

[root@localhost tasks]# vim group.yml

- name: create group nginx

    group: name=nginx gid=80

 

[root@localhost tasks]# vim user.yml

-name: create user nginx

    user: name=nginx uid=80 group=nginx system=yes shell=/sbi/nologin

 

[root@localhost tasks]# vim install.yml

- name: install package

    yum: name=nginx

    

[root@localhost tasks]# vim start.yml

- name: start service

    service: name=nginx state=started enabled=yes

     

[root@localhost tasks]# vim restart.yml

- name: restart service

    service: name=nginx state=restarted

     

[root@localhost tasks]# vim templ.yml

- name: copy conf

    template: src=nginx.conf.j2 dest=/etc/nginx/conf/nginx.conf

     

[root@localhost tasks]# vim main.yml

- include: group.yml

- include: user.yml

- include: install.yml

- include: templ.yml

- include: start.yml

       

[root@localhost tasks]# cd ../templates  && ls

nginx.conf.j2

 

[root@localhost tasks]# cd /root/ansible

[root@localhost ansible]# vim nginx_role.yml

- hosts: websrvs

  remote_user: root

  roles:

    - role: nginx

 

執行命令:

[root@localhost ansible]# ansible-playbook nginx_role.yml

5. loop列表回圈用法
在ansible 2.5版本之前,大多數人習慣使用"with_X"風格的關鍵字操作回圈,從ansible 2.6版本開始,官方開始推薦使用"loop"關鍵字代替"with_X"風格關鍵字。下面 下麪通過一些小範例來說明使用loop關鍵字進行的列表回圈操作。[loop、with_items、with_list 三者等同,效果是一樣的!]。ansible的回圈使用,可以參考下面 下麪"回圈變數"以及參考這裏

playbook中的變數設定

########   變數的優先順序   ########
1.  extra vars變數(在命令列中使用 -e);優先順序最高
2.  在inventory中定義的連線變數(比如ansible_ssh_user);優先順序第二
3.  大多數的其他變數(命令列轉換,play中的變數,include的變數,role的變數等);優先順序第三
4.  在inventory定義的其他變數;優先順序第四
5.  有系統發現的facts;優先順序第五
6.  "role預設變數",這個是最預設的值,很容易喪失優先權。優先順序最小。

另外:在inventory清單列表裏定義的變數:單個主機定義的變數優先順序高於主機組定義的變數
經過實驗,ansible使用inventory定義變數的優先順序順序從高到低爲:
1. host_vars下定義變數
2. inventory中單個主機定義變數
3. group_vars下定義變數
4. inventory中組定義變數

playbook中定義變數,如下:

1

2

3

- hosts: webservers

  vars:

    http_port: 80

YAML陷阱
YAML語法要求如果值以{{ foo }}開頭的話,那麼就需要將整行用雙引號包起來,這是爲了確認你不是想宣告一個YAML字典。
如下面 下麪設定是不行的!!!

1

2

3

4

---

- hosts: app_servers

  vars:

    app_path: {{ base_path }}/data/web

應該改成下面 下麪這樣:

1

2

3

4

---

- hosts: app_servers

  vars:

    app_path: "{{ base_path }}/data/web"

########   Ansbile-playbook變數設定方法   ########

1.  在inventory主機清單檔案中定義變數
可以直接定義在主機清單檔案/etc/ansible/hosts中,表明該變數只對對應的主機或者組有效,對其餘的主機和組無效。

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

有針對單個主機定義變數和組定義變數兩種方式。

1)向不同的單個主機傳遞不同的變數;

IP/HOSTNAME  var1=value1 var2=value2

 

2)向組中的主機傳遞相同的變數;

[groupname:vars]

var1=value1

var2=value2

 

但是注意:

組定義變數的作用範圍是組下的所有主機。

當兩種定義方式同時存在時,ansible會優先採用單個主機定義的變數值!

 

[root@ss-server ansible]# pwd

/etc/ansible

[root@ss-server ansible]# cat hosts|egrep -v "^#|^$"

[kevin]

172.16.60.20 key=20180101

172.16.60.22 ansible_ssh_port=22288 key="niubility"

  

[root@ss-server ansible]# cat ansi.yml

---

- hosts: kevin

  gather_facts: False

  tasks:

    - name: haha

      debug: msg="the {{ inventory_hostname }} value is {{ key }}"

  

執行結果(注意inventory_hostname代表inventory列表列表裏被控節點的主機名):

[root@ss-server ansible]# ansible-playbook ansi.yml

  

PLAY [kevin] *************************************************************************************************************************************

  

TASK [haha] **************************************************************************************************************************************

ok: [172.16.60.20] => {

    "msg""the 172.16.60.20 value is 20180101"

}

ok: [172.16.60.22] => {

    "msg""the 172.16.60.22 value is niubility"

}

  

PLAY RECAP ***************************************************************************************************************************************

172.16.60.20             : ok=1    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0 

172.16.60.22             : ok=1    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0 

 

再看下面 下麪一範例

[root@ss-server ansible]# tail -n 10 /etc/ansible/hosts

[webserver]

172.16.60.51 dir=/root/node2

172.16.60.80 dir=/root/node1

 

[node1]

172.16.60.80

 

[webserver:vars]

file=hostname.txt

 

[root@ss-server ansible]# cat playbook.yml

---

- hosts: webserver

  remote_user: root

  tasks:

    - name: Create New Folder

      file: name={{ dir }} state=directory

    - name: Create New Folder

      shell: echo `hostname` > {{dir}}/{{ file }}

 

執行結果:

[root@ss-server ansible]# ansible-playbook playbook.yml   

 

PLAY [webserver] ************************************************************************

 

TASK [Gathering Facts] ***************************************************************

ok: [172.16.60.80]

ok: [172.16.60.51]

 

TASK [Create New Folder] *************************************************************

changed: [172.16.60.51]

changed: [172.16.60.80]

 

TASK [Create New Folder] *************************************************************

changed: [172.16.60.51]

changed: [172.16.60.80]

 

PLAY RECAP ***************************************************************************

172.16.60.51              : ok=3    changed=2    unreachable=0    failed=0  

172.16.60.80              : ok=3    changed=2    unreachable=0    failed=0

此外:ansible還內建了一些固定的主機變數名,在inventory中定義其值, 在另一篇文章中介紹過。

2.  通過host_vars和group_vars目錄來定義變數
/etc/ansible/目錄是linux系統上ansible預設的組態檔目錄(Mac系統上的話,其預設設定目錄是在/usr/local/etc/ansible/),在該目錄下建立host_vars和group_vars兩個目錄用來存放定義變數的檔案。

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

1)針對單個主機的變數 

[root@ss-server ansible]# pwd

/etc/ansible

[root@ss-server ansible]# cat host_vars/172.16.60.20

---

user: root

pass: root@123

 

2)針對test組的變數

[root@ss-server ansible]# cat group_vars/kevin

---

user: work

pass: work@123

 

############ 在inventory清單列表檔案裡,單個主機定義的變數優先順序高於主機組定義的變數 ############

############ 經過實驗,ansible使用變數的優先順序順序從高到低爲 #############

1. host_vars下定義變數

2. inventory中單個主機定義變數

3. group_vars下定義變數

4. inventory中組定義變數

 

範例如下:

[root@ss-server ~]# cat /root/ansible/hosts

[kevin]

172.16.60.20 ansible_ssh_port=22222

172.16.60.21 ansible_ssh_port=22288

 

[root@ss-server ~]# cat /root/ansible/group_vars/kevin

key=20191010

 

[root@ss-server ~]# cat /root/ansible/bo.yml

---

- hosts: kevin

  remote_user: root

  tasks:

    - debug: msg='The key is {{key}} '

 

[root@ss-server ~]# ansible-playbook /root/ansible/bo.yml

  

PLAY [kevin] *************************************************************************************************************************************

  

TASK [haha] **************************************************************************************************************************************

ok: [172.16.60.20] => {

    "msg""The key is 20191010"

}

ok: [172.16.60.22] => {

    "msg""The key is 20191010"

}

  

PLAY RECAP ***************************************************************************************************************************************

172.16.60.20             : ok=1    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0 

172.16.60.22             : ok=1    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0

3.  通過var_files定義變數

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

[root@ss-server ansible]# cat laoji.yml

---

key: jiayou

 

[root@ss-server ansible]# cat bo.yml

---

- hosts: kevin

  gather_facts: False

  vars_files:

      - laoji.yml

  tasks:

    - name: display

      debug: msg="the {{ inventory_hostname }} valus is {{ key }}"

  

執行結果:

[root@ss-server ansible]# ansible-playbook bo.yml

PALY [kevin] ****************************************************

  

TASK [display] ****************************************************

ok: [172.16.60.20] => {

    "changed"false

    "msg":"the 172.16.60.20 value is jiayou"

}

  

PLAY RECAP ****************************************************

172.16.60.20         : ok=1  changed=0  unreachable=0  failed=0

4.  通過vars_prompt互動式傳入變數
在playbook中定義vars_prompt的變數名和互動式提示資訊,就可以實現在執行playbook時,通過互動的傳入變數值。
private欄位:用來定義互動時是否回顯輸入的值,預設private爲yes;
default欄位:用來定義變數的預設值。

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99

100

101

102

103

104

105

106

107

108

109

110

111

112

113

114

115

116

117

118

[root@ss-server ansible]#  cat prom.yml

---

- hosts: kevin

  remote_user: root

  vars_prompt:

      - name: "var1"

        prompt: "please input you name"

        private: no

      - name: "var2"

        prompt: "please input you age"

        private: yes

        default: 'kevin var'

  tasks:

      - name: display var1

        debug: msg="your name of var1 is {{ var1 }}"

      - name: display var2

        debug: msg="you age of var2 is {{ var2 }}"

  

執行結果:

[root@ss-server ansible]# ansible-playbook prom.yml

your name of var1: wangzhaojun       #把輸入的內容傳遞給變數var1。輸入的值顯示出來!!

you age of var2 [kevin var]:         #把輸入的內容傳遞給預設變數kevin var。但是輸入的值不顯示出來!! 比如這裏輸入的30

  

PALY [kevin] ****************************************************

  

TASK [Gathering Facts] ****************************************************

OK: [172.16.60.20]

  

TASK [display var1] ****************************************************

ok: [172.16.60.20] =>{

    "msg""youn name of var1 is wangzhaojun"

}

  

TASK [display var2] ****************************************************

ok: [172.16.60.20] =>{

    "msg""youn name of var2 is 30"

}

  

PLAY RECAP ****************************************************

172.16.60.20         : ok=3  changed=0  unreachable=0  failed=0

  

接下來再來看一個"ansible 中prompt 互動變數的使用"的範例

[root@ss-server ansible]# cat haha.yml

---

- hosts: kevin

  remote_user: root

  vars_prompt:

     - name: "your_name"

       prompt: "what is your name"

       private: no

     - name: "your_age"

       prompt: "how old are you"

       private: no

     - name: "solution"

       prompt: "Choose the solution you want \n

       A: solutionA\n

       B: solutionB\n

       C: solutionC\n"

       private: no

       default: A

     - name: "user_name"

       prompt: "Enter user name"

       private: no

     - name: "user_password"

       prompt: "Enter user password"

       private: no

       encrypt: "sha512_crypt"

       confirm: yes

  tasks:

     - name: "output vars"

       debug: msg="your name is {{ your_name }},you are {{ your_age }} years old"

     - name: "output solution"

       debug: msg="the final solution is {{solution}}"

     - name: "create_user"

       user:

         name: "{{user_name}}"

         password: "{{user_password}}"

     - name: "debug_create user"

       debug: msg="create user {{user_name}} in"

  

執行結果爲:

[root@ss-server ansible]# ansible-playbook haha.yml

what is your name: wangshibo

how old are you: 29

Choose the solution you want

 A: solutionA

 B: solutionB

 C: solutionC

 [A]: C

Enter user name: bobo

Enter user password: bobo123

confirm Enter user password: bobo123

  

PLAY [kevin] *************************************************************************************************************************************

  

TASK [Gathering Facts] ***************************************************************************************************************************

ok: [172.16.60.20]

  

TASK [output vars] *******************************************************************************************************************************

ok: [172.16.60.20] => {

    "msg""your name is wangshibo,you are 29 years old"

}

  

TASK [output solution] ***************************************************************************************************************************

ok: [172.16.60.20] => {

    "msg""the final solution is C"

}

  

TASK [create_user] *******************************************************************************************************************************

changed: [172.16.60.20]

  

TASK [debug_create user] *************************************************************************************************************************

ok: [172.16.60.20] => {

    "msg""create user bobo in"

}

  

PLAY RECAP ***************************************************************************************************************************************

172.16.60.20                  : ok=5    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0

5.  通過ansible-playbook命令列定義變數!即參數傳入變數
除了"vars_prompt"和"vars_files",也可以通過Ansible命令列發送變數。如果想要編寫一個通用的發佈playbook時則特別有用!你可以傳遞應用的版本以便部署。例如下面 下麪命令(注意: --extra-vars 相等於 -e)

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

[root@ss-server ~]# ansible-playbook release.yml --extra-vars "version=1.78.34 other_variable=fos"

  

其他場景中,通過參數傳入變數也是很有用的。比如爲playbook設定主機羣組或使用者。如下:

[root@ss-server ~]# vim exap.yml

---

- hosts: '{{hosts}}'

  remote_user: '{{user}}'

  tasks:

    - name: "一個測試"

      debug: msg="your hosts is {{hosts}}, user is {{user}}"

  

執行的時候,通過參數傳入變數(-e)。變數{{hosts}}可以是主機羣組名稱,也可以是主機ip。

[root@ss-server ansible]# ansible-playbook exap.yml -e "hosts=kevin user=root"  

[WARNING]: Found variable using reserved name: hosts

  

  

PLAY [kevin] *************************************************************************************************************************************

  

TASK [Gathering Facts] ***************************************************************************************************************************

ok: [172.16.60.20]

  

TASK [一個測試] **************************************************************************************************************************************

ok: [172.16.60.20] => {

    "msg""your hosts is kevin, user is root"

}

  

PLAY RECAP ***************************************************************************************************************************************

172.16.60.20                  : ok=2    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0

  

在命令列裏面傳值的方法:

[root@ss-server ansible]# ansible-playbook amp.yml --extra-vars "hosts=webserver user=root"

  

還可以使用json格式傳遞參數:

[root@ss-server ansible]# ansible-playbook amp.yml --extra-vars "{'hosts':'webserver', 'user':'root'}"

  

還可以將參數放在檔案裏面進行傳遞(注意命令列裡要是用"@檔名"):

[root@ss-server ansible]# ansible-playbook amp.yml --extra-vars "@anhui.yml"

[root@ss-server ansible]# cat anhui.yml

---

hosts: webserver

user: root

 

例如下面 下麪這個新增使用者的plakbook劇本設定,用傳遞了json檔案:

[root@ss-server ansible]# cat useradd.yml

---

- hosts: "{{ host }}"

    gather_facts: no

    remote_user: root

    vars:

      UserName: "{{ user }}"

      UserPassword: "{{ pass }}"

    tasks:

      - name: create new user {{ UserName }}

        user: name={{ UserName }} shell=/bin/bash  password={{ UserPassword |password_hash('sha512') }} update_password=always append=yes

      - name: Config /etc/sudoers

        lineinfile: dest=/etc/sudoers state=present  line='{{ item }}' validate='visudo -cf %s'

        with_items:

          "{{ user }} ALL=(ALL) NOPASSWD:ALL,!/bin/rm,!/bin/su,!/usr/bin/passwd,!/usr/sbin/visudo,!/sbin/shutdown,!/sbin/reboot,!/sbin/halt"

          "Defaults: {{ user }}  !requiretty"

 

[root@ss-server ansible]# ansible-playbook useradd.yml -e "host=172.16.60.20 user=kevin_bo pass=kevin@bo123"

 

在上例中,變數pass是密碼,如果變數pass裡有特殊的字元,或者變數pass是一串陣列的話,它將被跳脫。若不想被跳脫,可以使用如下方法:

[root@ss-server ansible]# cat user.json

host: webserver                     #ansible裏面定義的主機組或者直接設定主機ip,如172.16.60.20

user: kevin_bo                      #新增的使用者名稱

pass: 'Fxx6unM$R%I$Jna&'            #新增的使用者的密碼,可以用百度上隨機密碼生成器生成

 

指定user,json檔案執行劇本 (使用JSON格式的檔案即可,-e 傳遞變數,優先順序最高)

[root@ss-server ansible]# ansible-playbook useradd.yml -e "@user.json"       

 

刪除web組中所有使用者

[root@ss-server ansible]# ansible webserver -m user -a 'name=zhangsan state=absent remove=yes'

6.  在playbook劇本中定義變數

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

在playbook中定義變數需要用到Ansible的vars模組,可以將所有需要用到的變數統一在vars模組下定義,定義格式需要遵循YAML語言格式:

vars:

  - var1: value1

  - var2: value2

  - var3: value3

  - ....: .....

 

範例如下:

[root@ss-server ansible]# cat playbook.yml

---

- hosts: kevin

  remote_user: root

  vars:

    - dir1: /root/Ansible

    - dir2: /root/Ansible/test1

    - dir3: /root/Ansible/test2

  tasks:

    - name: Create New Folder

      file: name={{ dir1 }} state=directory

    - name: Create New Folder

      file: name={{ dir2 }} state=directory

    - name: Create New Folder

      file: name={{ dir3 }} state=directory

 

[root@ss-server ansible]# ansible-playbook playbook.yml   

 

PLAY [kevin] *************************************************************************

 

TASK [Gathering Facts] ***************************************************************

ok: [172.16.60.20]

 

TASK [Create New Folder] *************************************************************

changed: [172.16.60.20]

 

TASK [Create New Folder] *************************************************************

changed: [172.16.60.20]

 

TASK [Create New Folder] *************************************************************

changed: [172.16.60.20]

 

PLAY RECAP ***************************************************************************

[172.16.60.20]              : ok=4    changed=3    unreachable=0    failed=0

7.  通過roles角色定義變數

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

在Ansible的roles中定義變數,需要將變數及值的鍵值對形式寫到roles的vars目錄下的main.yml檔案中,同樣適用YAML語言格式,格式如下:

var1: value1

var2: value2

var3: value3

 

但是請注意:

通過Roles定義的變數只適用於當前roles。如下是roles檔案組織結構:

[root@ss-server roles]# tree test/                 

test/

├── files

├── handlers

├── playbook.retry

├── playbook.yml

├── tasks

│   └── main.yml

├── templates

└── vars

    └── main.yml

5 directories, 4 files

 

[root@ss-server roles]# cat test/tasks/main.yml         #任務檔案

- name: Get IP Address

  shell: echo `{{ cmd }}` >> {{ dir }}/{{ file }}

 

[root@ss-server roles]# cat test/vars/main.yml          #定義變數cmd

cmd: hostname -I

 

[root@ss-server roles]# cat test/playbook.yml

---

- hosts: webserver

  remote_user: root

  roles:

    test

 

hosts清單列表裏定義的變數,適用於所有roles。

[root@ss-server roles]# cat /etc/ansible/hosts

[webserver]

172.16.60.51 dir=/root/node2

172.16.60.80 dir=/root/node1

 

[node1]

172.16.60.80

 

[webserver:vars]

file=hostname.txt

 

playbook呼叫Roles,執行結果爲:

[root@Centos7T test]#ansible-playbook playbook.yml   

 

PLAY [websvr] ************************************************************************

 

TASK [Gathering Facts] ***************************************************************

ok: [172.16.60.80]

ok: [172.16.60.51]

 

TASK [test : Get IP Address] *********************************************************

changed: [172.16.60.51]

changed: [172.16.60.80]

 

PLAY RECAP ***************************************************************************

172.16.60.51              : ok=2    changed=1    unreachable=0    failed=0  

172.16.60.80              : ok=2    changed=1    unreachable=0    failed=0

8.  使用Facts獲取的資訊
還有其它地方可以獲取變數, 這些變數是自動發現的,而不是使用者自己設定的。Facts通過存取遠端系統獲取相應的資訊,一個很好的例子就是遠端主機的IP地址或者操作系統是什麼。

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

使用以下命令可以檢視哪些資訊是可用的(kevin是上面在/etc/ansible/hosts列表檔案中設定的主機羣組):

[root@ss-server ansible]# ansible kevin -m setup

  

[root@ss-server ansible]# ansible kevin -m setup|grep "ansible_python_version"

        "ansible_python_version""2.7.5",

可以在playbook中這樣參照上面被控制主機的python版本: {{ ansible_python_version }}

  

[root@ss-server ansible]# ansible kevin -m setup|grep "ansible_nodename"

        "ansible_nodename""ss-server",

可以在playbook中這樣參照上面被控制主機的主機名: {{ ansible_nodename }}

  

[root@ss-server ansible]# ansible kevin -m setup|grep "ansible_hostname"

        "ansible_hostname""ss-server",

被控制主機的主機名變數還可以是: {{ ansible_hostname }}

 

存取複雜變數數據

有些提供的facts, 比如網路資訊等, 是一個巢狀的數據結構。存取它們使用簡單的 {{ foo }} 語法並不夠用,

但是也仍然很容易.如下所示:

{{ ansible_eth0["ipv4"]["address"] }}

 

或者這樣寫:

{{ ansible_eth0.ipv4.address }}

  

############ 如果關閉Facts,可以大大提高ansible的執行速度 ###########

關閉方法如下:

[root@ss-server ansible]# cat anhui.yml

---

- hosts: kevin

  gather_facts: no

9.  register註冊變數
變數的另一個主要用途是在執行命令時,把命令結果儲存到一個變數中,不同模組的執行結果是不同的。執行playbook時使用-v選項可以看到可能的結果值,ansible執行任務的結果值可以儲存在變數中,以便稍後使用它。register方式主要用於在task之間傳遞變數。

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

範例如下:

[root@ss-server ansible]# cat /etc/ansible/hosts

[kevin]

172.16.60.237

172.16.60.238

  

[root@ss-server ansible]# cat /root/register.yml

---

- hosts: kevin

  remote_user: root

  tasks:

      - name: register bo_test

        shell: hostname -I

        register: info

      - name: display info

        debug: msg="this host ip is {{ info }}"

  

[root@ss-server ansible]# ansible-playbook /root/register.yml

  

PLAY [kevin] *************************************************************************************************************************************

  

TASK [Gathering Facts] ***************************************************************************************************************************

ok: [172.16.60.238]

ok: [172.16.60.237]

  

TASK [register bo_test] **************************************************************************************************************************

changed: [172.16.60.238]

changed: [172.16.60.237]

  

TASK [display info] ******************************************************************************************************************************

ok: [172.16.60.237] => {

    "msg""this host ip is {'stderr_lines': [], u'changed': True, u'end': u'2019-12-15 22:07:18.431549', 'failed': False, u'stdout': u'172.16.60.237 ', u'cmd': u'hostname -I', u'rc': 0, u'start': u'2019-12-15 22:07:18.408235', u'stderr': u'', u'delta': u'0:00:00.023314', 'stdout_lines': [u'172.16.60.237 ']}"

}

ok: [172.16.60.238] => {

    "msg""this host ip is {'stderr_lines': [], u'changed': True, u'end': u'2019-12-15 22:07:18.430108', 'failed': False, u'stdout': u'172.16.60.238 ', u'cmd': u'hostname -I', u'rc': 0, u'start': u'2019-12-15 22:07:18.407184', u'stderr': u'', u'delta': u'0:00:00.022924', 'stdout_lines': [u'172.16.60.238 ']}"

}

  

PLAY RECAP ***************************************************************************************************************************************

172.16.60.237              : ok=3    changed=1    unreachable=0    failed=0 

172.16.60.238              : ok=3    changed=1    unreachable=0    failed=0

  

這裏注意下:

register定義的info變數在第二個task中用來檢視前一個task中執行的hostname命令的結果。

可以看到playbook執行後的結果中,info返回的是一段python字典數據,如果只想看到stdout部分的資訊的話,可以通過info[‘stdout’]來參照。

  

[root@ss-server ansible]# cat /root/register.yml

---

- hosts: kevin

  remote_user: root

  tasks:

      - name: register bo_test

        shell: hostname -I

        register: info

      - name: display info

        debug: msg="this host ip is {{ info['stdout'] }}"

  

[root@ss-server ansible]# ansible-playbook /root/register.yml

  

PLAY [kevin] *************************************************************************************************************************************

  

TASK [Gathering Facts] ***************************************************************************************************************************

ok: [172.16.60.238]

ok: [172.16.60.237]

  

TASK [register bo_test] **************************************************************************************************************************

changed: [172.16.60.237]

changed: [172.16.60.238]

  

TASK [display info] ******************************************************************************************************************************

ok: [172.16.60.237] => {

    "msg""this host ip is 172.16.60.237 "

}

ok: [172.16.60.238] => {

    "msg""this host ip is 172.16.60.238 "

}

  

PLAY RECAP ***************************************************************************************************************************************

172.16.60.237              : ok=3    changed=1    unreachable=0    failed=0 

172.16.60.238              : ok=3    changed=1    unreachable=0    failed=0

10.  hostvars 變數
該變數用於參照其他主機上收集的facts中的數據,或者參照其他主機的主機變數、主機組變數。即從一臺遠端主機獲取另一臺遠端主機的變數。

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99

100

101

102

103

104

105

106

107

108

109

110

111

112

113

114

115

116

117

118

119

120

121

122

123

124

125

126

127

128

129

130

131

132

133

134

135

136

137

138

139

140

141

142

143

144

145

146

147

148

149

如下範例:

[root@ss-server ansible]# cat /etc/ansible/hosts

[kevin]

172.16.60.237 addr=beijing

172.16.60.238 user=shibo age=39

 

[root@ss-server ansible]# cat test.yml

---

- hosts: kevin

  remote_user: root

  gather_facts: False

  tasks:

    - name: this is test1

      debug: msg="She is come from {{ hostvars['172.16.60.237']['addr'] }}"

    - name: this is test2

      debug: msg="I am {{ hostvars['172.16.60.238']['user'] }}, and age is {{ hostvars['172.16.60.238']['age'] }}"

 

或者將test.yml檔案設定如下,即由 "[變數]"" 改爲 ".變數"

兩種設定的執行結果都是一樣的!

[root@ss-server ansible]# cat test.yml

---

- hosts: kevin

  remote_user: root

  gather_facts: False

  tasks:

    - name: this is test1

      debug: msg="She is come from {{ hostvars['172.16.60.237'].addr }}"

    - name: this is test2

      debug: msg="I am {{ hostvars['172.16.60.238'].user }}, and age is {{ hostvars['172.16.60.238'].age }}"

 

執行結果爲:

[root@ss-server ansible]# ansible-playbook test.yml            

 

PLAY [kevin] *************************************************************************************************************************************

 

TASK [this is test1] *****************************************************************************************************************************

ok: [172.16.60.237] => {

    "msg""She is come from beijing"

}

ok: [172.16.60.238] => {

    "msg""She is come from beijing"

}

 

TASK [this is test2] *****************************************************************************************************************************

ok: [172.16.60.237] => {

    "msg""I am shibo, and age is 39"

}

ok: [172.16.60.238] => {

    "msg""I am shibo, and age is 39"

}

 

PLAY RECAP ***************************************************************************************************************************************

172.16.60.237              : ok=2    changed=0    unreachable=0    failed=0  

172.16.60.238              : ok=2    changed=0    unreachable=0    failed=0 

 

 

############################# 這裏需要注意下 ###########################

除了上面的設定,還可以如下設定,但是請注意:

1)debug後面必須使用"var"欄位,即hostvar變數傳遞給var欄位的變數。

2)var=hostvars[....],等於兩邊不能有空格!

 

[root@ss-server ansible]# cat test.yml

---

- hosts: kevin

  remote_user: root

  gather_facts: False

  tasks:

    - name: this is test1

      debug: var=hostvars['172.16.60.237']['addr']

    - name: this is test2

      debug: var=hostvars['172.16.60.238']['user']

 

或者將test.yml檔案設定如下,這兩個設定方法的執行結果是一樣的!

[root@ss-server ansible]# cat test.yml

---

- hosts: kevin

  remote_user: root

  gather_facts: False

  tasks:

    - name: this is test1

      debug: var=hostvars['172.16.60.237'].addr

    - name: this is test2

      debug: var=hostvars['172.16.60.238'].user

 

執行結果爲:

[root@ss-server ansible]# ansible-playbook test.yml

 

PLAY [kevin] *************************************************************************************************************************************

 

TASK [this is test1] *****************************************************************************************************************************

ok: [172.16.60.237] => {

    "hostvars['172.16.60.237']['addr']""beijing"

}

ok: [172.16.60.238] => {

    "hostvars['172.16.60.237']['addr']""beijing"

}

 

TASK [this is test2] *****************************************************************************************************************************

ok: [172.16.60.237] => {

    "hostvars['172.16.60.238']['user']""shibo"

}

ok: [172.16.60.238] => {

    "hostvars['172.16.60.238']['user']""shibo"

}

 

PLAY RECAP ***************************************************************************************************************************************

172.16.60.237              : ok=2    changed=0    unreachable=0    failed=0  

172.16.60.238              : ok=2    changed=0    unreachable=0    failed=0

 

注意:如果同一個name裡設定了多個debug,則預設執行最後一個debug內容! (多個task的話,也是執行最後一個task)

[root@ss-server ansible]# cat test.yml

---

- hosts: kevin

  remote_user: root

  gather_facts: False

  tasks:

    - name: this is test1

      debug: var=hostvars['172.16.60.237']['addr']

    - name: this is test2

      debug: var=hostvars['172.16.60.238']['user']

      debug: var=hostvars['172.16.60.238']['age']

 

執行結果:

[root@ss-server ansible]# ansible-playbook test.yml

 [WARNING]: While constructing a mapping from /etc/ansible/test.yml, line 8, column 7, found a duplicate dict key (debug). Using last defined

value only.

 

 

PLAY [kevin] *************************************************************************************************************************************

 

TASK [this is test1] *****************************************************************************************************************************

ok: [172.16.60.237] => {

    "hostvars['172.16.60.237']['addr']""beijing"

}

ok: [172.16.60.238] => {

    "hostvars['172.16.60.237']['addr']""beijing"

}

 

TASK [this is test2] *****************************************************************************************************************************

ok: [172.16.60.237] => {

    "hostvars['172.16.60.238']['age']""39"

}

ok: [172.16.60.238] => {

    "hostvars['172.16.60.238']['age']""39"

}

 

PLAY RECAP ***************************************************************************************************************************************

172.16.60.237              : ok=2    changed=0    unreachable=0    failed=0  

172.16.60.238              : ok=2    changed=0    unreachable=0    failed=0

11. 列表變數、回圈變數、字典變數

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99

100

101

102

103

104

105

106

107

108

109

110

111

112

113

114

115

116

117

118

119

120

121

122

123

124

125

126

127

128

129

130

131

132

133

134

135

136

137

138

139

140

141

142

143

144

145

146

147

148

149

150

151

152

153

154

155

156

157

158

159

160

161

162

163

164

165

166

167

168

169

170

171

172

173

174

175

176

177

178

179

180

181

182

183

184

185

186

187

188

189

190

191

192

193

194

195

196

197

198

199

200

201

202

203

204

205

206

207

208

209

210

211

212

213

214

215

216

217

218

219

220

221

222

223

224

225

226

227

228

229

230

231

232

233

234

235

236

237

238

239

240

241

242

243

244

245

246

247

248

249

250

251

252

253

254

255

256

257

258

259

260

261

262

263

264

265

266

1)ansible的變數不僅可以是單個的值,也可以爲列表,即ansible傳列表作爲變數。如下範例:

#################################################################################################################

[root@ss-server ansible]# vim test.yml

---

- hosts: test_host

  gather_facts: no

  vars:

    - list: [1,2,3]

  tasks:

    - name: echo

      debug: msg="{{ list }}"

 

執行結果:

[root@ss-server ansible]# ansible-palybook test.yml

TASK [echo] ********************************************************************

ok: [172.16.60.220] => {

    "msg": [

        1,

        2,

        3

    ]

}

 

再來看一例:

linux建立使用者,需要獲取的變數有使用者名稱,使用者密碼,使用者組,有時候需要建立多個使用者,那麼傳遞給ansible的使用者肯定是列表,但每一組又有使用者名稱、密碼、組這些變數值。

做法如下:

[root@ss-server ~]# cat /etc/ansible/test.yml

---

- hosts: test_host

  gather_facts: no

  tasks:

   - name: create or update account

     user: name={{ item.user }} password={{ item.password }} groups={{ item.group }} system=no

     with_items:

       -  '{{ user_list }}'

 

執行結果:

[root@ss-server ansible]# ansible-playbook /etc/ansible/test.yml -e '{"user_list":[{"user":"user1","password":"*******","group":"user1"}]}'

 

#################################################################################################################

2)回圈列表

結合回圈,這個特性就變得很有用;以參數傳遞列表給playbook,不用在playbook中固定回圈的次數與內容。如下範例:

[root@ss-server ansible]# vim test.yml

---

- hosts: test_host

  gather_facts: no

  vars:

    - list: [1,2,3]

  tasks:

    - name: this is loop

      debug: msg="{{ item }}"

      with_items: '{{list}}'

 

執行結果:

[root@ss-server ansible]# ansible-palybook test.yml

TASK [this is loop] ********************************************************************

ok: [172.16.60.220] => (item=1) => {

    "item": 1,

    "msg": 1

}

ok: [localhost] => (item=2) => {

    "item": 2,

    "msg": 2

}

ok: [localhost] => (item=3) => {

    "item": 3,

    "msg": 3

}

 

接着看下面 下麪一例:

loop 關鍵字表示回圈,去讀回圈體裡的變數固定使用{{item}},item是個字典物件item.key=value。

需要注意:下面 下麪test.yml檔案中的"loop"關鍵字 改爲 "with_items"關鍵字,效果是一樣的!

ansible的回圈用法:在ansible 2.5版本之前,大多數人習慣使用"with_X"風格的關鍵字操作回圈,

從ansible 2.6版本開始,官方開始推薦使用"loop"關鍵字代替"with_X"風格關鍵字。

[root@ss-server ~]# cat /etc/ansible/test.yml

---

- name: this is test

  hosts: test_host

  connection: local

  gather_facts: no

 

  tasks:

    - name: debug loop

      debug:

        msg: "{{item.A1}}"

      loop:                      #這裏將"loop"關鍵字 改爲 "with_items"關鍵字,效果是一樣的!

        - A: a

          A1: a1

          A2: a2

        - B: b

          A1: b1

          A2: b2

        - C: c

          A1: c1

          A2: c2

        - D: d

          A1: d1

          A2: d2

 

執行結果:以上loop下的四個變數分別稱爲一塊,即一個item,符號"-"爲回圈體塊的標誌,{{item.A1}}的值,即分別爲a1,b1,c1,d1

[root@ss-server ~]# ansible-playbook /etc/ansible/test.yml

 

PLAY [this is test] *********************************************************************************************************************************

 

TASK [debug loop] ********************************************************************************************************************************

ok: [172.16.60.20] => (item={u'A': u'a', u'A1': u'a1', u'A2': u'a2'}) => {

    "msg""a1"

}

ok: [172.16.60.20] => (item={u'A1': u'b1', u'B': u'b', u'A2': u'b2'}) => {

    "msg""b1"

}

ok: [172.16.60.20] => (item={u'A1': u'c1', u'C': u'c', u'A2': u'c2'}) => {

    "msg""c1"

}

ok: [172.16.60.20] => (item={u'A1': u'd1', u'A2': u'd2', u'D': u'd'}) => {

    "msg""d1"

}

 

PLAY RECAP ***************************************************************************************************************************************

172.16.60.20                  : ok=1    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0

 

再來看一例:

[root@ss-server ~]# cat /etc/ansible/test.yml

---

- name: this is test

  hosts: test_host

  connection: local

  gather_facts: no

  vars:

    my_list:

      - a

      - b

      - c

      - 1

 

  tasks:

    - name: debug loop output

      debug:

        msg: "The {{index}} one is {{item}}"

      loop: "{{my_list}}"               # 或者使用with_items: "{{my_list}}"  或者 with_list: "{{my_list}}"

      loop_control:

        index_var: index

 

執行結果:

[root@ss-server ~]# ansible-playbook /etc/ansible/test.yml

 

PLAY [this is test] ******************************************************************************************************************************

 

TASK [debug loop output] *************************************************************************************************************************

ok: [172.16.60.20] => (item=a) => {

    "msg""The 0 one is a"

}

ok: [172.16.60.20] => (item=b) => {

    "msg""The 1 one is b"

}

ok: [172.16.60.20] => (item=c) => {

    "msg""The 2 one is c"

}

ok: [172.16.60.20] => (item=1) => {

    "msg""The 3 one is 1"

}

 

PLAY RECAP ***************************************************************************************************************************************

172.16.60.20                  : ok=1    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0

 

#################################################################################################################

3)字典變數

變數也可以爲字典。如下範例, 用到了ansible的回圈,回圈關鍵字是"with_X", 改爲loop關鍵字,效果是一樣的!

注意:在變數中使用回圈時,vars下必須要有"-",符號"-"爲回圈體塊的標誌!!

[root@ss-server ansible]# vim test.yml

---

- hosts: test_host

  gather_facts: no

  vars:

    - lists:

        list1: [1,2,3]

        list2: [4,5]

  tasks:

    - name: loop

      debug: msg="{{ item }}"

      with_items: '{{lists["list1"]}}'        #替換爲 loop: '{{lists["list1"]}}', 或者 with_list: '{{lists["list1"]}}' 效果是一樣的

 

執行結果:

[root@ss-server ansible]# ansible-palybook test.yml

TASK [loop] ********************************************************************

ok: [172.16.60.220] => (item=1) => {

    "item": 1,

    "msg": 1

}

ok: [localhost] => (item=2) => {

    "item": 2,

    "msg": 2

}

ok: [localhost] => (item=3) => {

    "item": 3,

    "msg": 3

}

 

接着看下面 下麪一例:

[root@ss-server ~]# cat /etc/ansible/test.yml

---

- hosts: test_host

  gather_facts: no

  vars:

    users:

        name: [xiaoming]

        address: [anhui]

        age: [28]

  tasks:

    - name: this is loop

      debug: msg="{{ item }}"

      loop: '{{users["name"]}}'       

[root@ss-server ~]# ansible-playbook /etc/ansible/test.yml

 

PLAY [test_host] *************************************************************************************************************************************

 

TASK [this is loop] **************************************************************************************************************************************

ok: [172.16.60.20] => (item=xiaoming) => {

    "msg""xiaoming"

}

 

PLAY RECAP ***************************************************************************************************************************************

172.16.60.20                  : ok=1    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0

 

上面的範例,如果要想列印多個字典變數,修改如下:

[root@ss-server ~]# cat /etc/ansible/test.yml

---

- hosts: test_host

  gather_facts: no

  vars:

    users:

        name: [xiaoming]

        address: [anhui]

        age: [28]

  tasks:

    - name: this is loop

      debug: msg="{{ item }}"

      loop:                         #這裏用loop,with_items,with_list都可以

         '{{users["name"]}}'   

         '{{users["address"]}}'

         '{{users["age"]}}'

 

執行結果:

[root@ss-server ~]# ansible-playbook /etc/ansible/test.yml

 

PLAY [test_host] *********************************************************************************************************************************

 

TASK [this is loop] ******************************************************************************************************************************

ok: [172.16.60.20] => (item=[u'xiaoming']) => {

    "msg": [

        "xiaoming"

    ]

}

ok: [172.16.60.20] => (item=[u'anhui']) => {

    "msg": [

        "anhui"

    ]

}

ok: [172.16.60.20] => (item=[28]) => {

    "msg": [

        28

    ]

}

 

PLAY RECAP ***************************************************************************************************************************************

172.16.60.20                  : ok=1    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0

######   可以在一個yaml檔案裡放置多個hosts,將多個tasks任務一起執行   ########

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99

100

101

102

103

104

105

106

107

108

109

110

111

112

[root@ss-server ansible]# cat /etc/ansible/hosts

172.16.60.237

[kevin]

172.16.60.238

[grace]

172.16.60.236

 

[root@ss-server ansible]# cat bo.yml           

---

- hosts: kevin

  remote_user: root

  gather_facts: False

  tasks:

    - name: this is test1

      shell: hostname -I

 

- hosts: 172.16.60.237

  remote_user: root

  tasks:

    - name: this is test2

      debug: msg="this server is {{ info }} "

 

- hosts: 172.16.60.238

  remote_user: root

  tasks:

    - name: this is test3

      debug: msg="this key is {{ haha }} "

 

執行結果:

[root@ss-server ansible]# ansible-playbook bo.yml -e "{'info':'my server','haha':'123123213123'}" 

 

PLAY [kevin] *************************************************************************************************************************************

 

TASK [this is test1] *****************************************************************************************************************************

changed: [172.16.60.238]

 

PLAY [172.16.60.237] *****************************************************************************************************************************

 

TASK [Gathering Facts] ***************************************************************************************************************************

ok: [172.16.60.237]

 

TASK [this is test2] *****************************************************************************************************************************

ok: [172.16.60.237] => {

    "msg""this server is my server "

}

 

PLAY [172.16.60.238] *****************************************************************************************************************************

 

TASK [Gathering Facts] ***************************************************************************************************************************

ok: [172.16.60.238]

 

TASK [this is test3] *****************************************************************************************************************************

ok: [172.16.60.238] => {

    "msg""this key is 123123213123 "

}

 

PLAY RECAP ***************************************************************************************************************************************

172.16.60.237              : ok=2    changed=0    unreachable=0    failed=0  

172.16.60.238              : ok=3    changed=1    unreachable=0    failed=0

 

 

############# 再來看下面 下麪一個測試環境用過的部署指令碼設定 #########################

[root@localhost ansible]# cat /opt/ansible_cfg/deploy.yml

---

- hosts: webservers

  tasks:

    - name: Installed Httpd Server

      yum: name=httpd state=present

 

    - name: Start Httpd Server

      systemd: name=httpd state=started enabled=yes

 

    - name: Start Firewalld Server

      systemd: name=firewalld state=started enabled=yes

 

    - name: Configure Firewalld Server

      firewalld: service=http immediate=yes permanent=yes state=enabled

 

- hosts: web01

  tasks:

    - name: Configure web01 Website

      copy: content='This is Web01' dest=/var/www/html/index.html

 

- hosts: web02

  tasks:

    - name: Cofnigure webi-2 weisite

      copy: content='This is Web02' dest=/var/www/html/index.html

 

- hosts: nfs01

  tasks:

    - name: Install NFS-utils Server

      yum: name=nfs-utils state=present

 

    - name: Configure Nfs-utils Server

      copy: src=./exports.j2 dest=/etc/exports owner=root group=root mode=0644

 

    - name: Create NFS Group

      group: name=www gid=666

 

    - name: Create NFS User

      user: name=www uid=666 group=www create_home=no shell=/sbin/nologin

 

    - name: Create Data Directory

      file: path=/data state=directory owner=www group=www mode=0755 recurse=yes

 

    - name: Start NFS Server

      systemd: name=nfs state=started enabled=yes

 

- hosts: nfs01

  tasks:

    - name: Mount NFS Server

      mount: path=/opt src=172.16.60.23:/data fstype=nfs opts=defaults state=mounted

######   Ansible-playbook如何正確獲取ip   ######

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

[root@ansible ~]# vim /etc/ansible/hosts_all.yaml

---

- hosts: all

  tasks:      

    - name: 將原有的hosts檔案備份        

      shell: mv /etc/hosts /etc/hosts_bak      

    - name: 將ansible端的hosts複製到各自機器上        

      copy: src=/root/hosts dest=/etc/ owner=root group=root mode=0644      

    - name: 在新的hosts檔案後面追加各自機器內網ip和hostname        

      lineinfile: dest=/etc/hosts line="`ansible_all_ipv4_addresses`  `ansible_hostname`"

  

但是執行完ansible-playbook之後,ansible客戶機器上的/etc/hosts檔案裡ip和主機名對應關係如下(cat /etc/hosts):

  

127.0.0.1   localhost localhost.localdomain localhost4 localhost4.localdomain4

::1         localhost localhost.localdomain localhost6 localhost6.localdomain6

[u'172.16.60.210'] redis-fun01.kevin.cn

  

實際想要的結果是(cat /etc/hosts):

127.0.0.1   localhost localhost.localdomain localhost4 localhost4.localdomain4

::1         localhost localhost.localdomain localhost6 localhost6.localdomain6

172.16.60.210 redis-fun01.kevin.cn

  

解決辦法:

調整ansible伺服器端的hosts_all.yaml檔案中獲取ip的變數

變數用 IP: "{{ ansible_eth0['ipv4']['address'] }}",而不是`ansible_all_ipv4_addresses`

  

修改後的yaml檔案設定如下:

[root@ansible ~]# vim /etc/ansible/hosts_all.yaml

---

- hosts: all

  vars:      

    IP: "{{ ansible_eth0['ipv4']['address'] }}"

  tasks:      

    - name: 將原有的hosts檔案備份        

      shell: mv /etc/hosts /etc/hosts_bak      

    - name: 將ansible端的hosts複製到各自機器上        

      copy: src=/root/hosts dest=/etc/ owner=root group=root mode=0644      

    - name: 在新的hosts檔案後面追加各自機器內網ip和hostname        

      lineinfile: dest=/etc/hosts line="`IP`  `ansible_hostname`"

########  獲取ansible清單列表裏對應組的ip、指定機器執行操作 [ --list-hosts、--limit ]  ########

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

注意下面 下麪兩個參數的使用:

1)獲取hosts清單檔案裡指定組內的ip,使用"--list-hosts"

2)指定hosts清單檔案裡的ip執行操作,使用"--limit"

  

如下是ansible的一個清單檔案:

[root@localhost ~]# cat /opt/kevin-bo.cfg

[kevin-bo_F]

172.16.60.65

  

[kevin-bo_A]

172.16.60.140

172.16.60.141

  

[kevin-bo:children]

kevin-bo_D

kevin-bo_F

kevin-bo_A

  

[kevin-bo:vars]

deploy_path=/opt/web/kevin-bo/

start_time_out=90

stop_time_out=60

module=kevin-bo

  

[kevin-bo_D]

172.16.60.195

  

現在需要獲取"kevin-bo_A" 和 "kevin-bo_D" 組下的ip,--list-hosts後面加不加引號都可以。

[root@localhost ~]# ansible -i /opt/kevin-bo.cfg --list-hosts kevin-bo_A

  hosts (2):

    172.16.60.140

    172.16.60.141

[root@localhost ~]# ansible -i /opt/kevin-bo.cfg --list-hosts "kevin-bo_A"|grep -v "hosts"

    172.16.60.140

    172.16.60.141

[root@localhost ~]# ansible -i /opt/kevin-bo.cfg --list-hosts kevin-bo_D|grep -v "hosts"

    172.16.60.195

 

獲取多個機器組內的ip,中間使用逗號隔開

[root@localhost ~]# ansible -i /opt/kevin-bo.cfg --list-hosts "kevin-bo_A,kevin-bo_D"

  hosts (3):

    172.16.60.140

    172.16.60.141

    172.16.60.195

 

[root@localhost ~]# ansible -i /opt/kevin-bo.cfg --list-hosts "kevin-bo_A,kevin-bo_D"|grep -v "hosts"

    172.16.60.140

    172.16.60.141

    172.16.60.195

  

在執行ansible-playbook時,可以指定單個ip或group,也可以指定多個ip或group,也可以一起指定ip和group,多箇中間使用逗號隔開!

--limit後面加不加引號都可以。

# ansible-playbook -i 清單檔案 yml檔案 --limit ip

# ansible-playbook -i 清單檔案 yml檔案 --limit ip1,iP2,ipn

# ansible-playbook -i 清單檔案 yml檔案 --limit group

# ansible-playbook -i 清單檔案 yml檔案 --limit group1,group2,groupn

# ansible-playbook -i 清單檔案 yml檔案 --limit ip1,ip2,group1,groupn

還可以將制定的ip或group放在一個檔案裡,如ip.txt,然後--limit後面跟"@ip.txt"

# ansible-playbook -i 列表檔案 yml檔案 --limit @ip.txt  

# cat ip.txt

172.16.60.140

172.16.60.141

kevin-bo_F

  

如上,需要對清單檔案"kevin-bo_A"組下的機器進行發佈操作,由於發佈過程中涉及服務啓停,爲了保證發佈中整體服務不中斷,需要一臺一臺的執行,不能所有機器一起執行。如下:

[root@localhost ~]# vim deploy.sh

#!/bin/bash

#設定變數,傳參等

BRANCH=$1

MODULE_NAME=$2

PRODUCT_PATH=$3

USER=$4

APP_NANE=${Deploy_App}

.....

  

for Next_Deploy_IP in $(ansible -i /opt/kevin-bo.cfg --list-hosts kevin-bo_A|grep -v "hosts")

do

   ansible-playbook -i /opt/kevin-bo.cfg /root/ansible/web_deploy.yml --limit "${Next_Deploy_IP}" -e "user=${USER} app_name=${APP_NANE} package_name=... ..."

   if [ $? -eq 0 ];then

      echo "[`date +%Y%m%d-%H%M%S`]++++++++++++++++++++++++${Next_Deploy_IP}部署成功!++++++++++++++++++++++++++++++"

      sleep 10

   else

      echo "[`date +%Y%m%d-%H%M%S`]++++++++++++++++++++++++${Next_Deploy_IP}部署失敗!++++++++++++++++++++++++++++++"

      exit 1

   fi

done