自動化運維工具ansible-playbook(入門篇)

1、playbook簡介與文件格式

playbook字面意思,即劇本,現實中由演員按照劇本表演,在Ansible中,這次由計算機進行表演,由計算機安裝、部署應用,提供對外服務,以及組織計算機處理各種各樣的事情。

playbook文件由YMAL語言編寫。YMAL格式是類似於JSON的文件格式,便於人理解和閱讀,同時便於書寫。首先學習瞭解一下YMAL的格式,對後面書寫playbook很有幫助。以下爲playbook常用到的YMAL格式規則。

 文件的第一行應該以“---” (三個連字符)開始,表明YMAL文件的開始。
 在同一行中,#之後的內容表示註釋,類似於shell,python和ruby。
 YMAL中的列表元素以“-”開頭然後緊跟着一個空格,後面爲元素內容。
 同一個列表中的元素應該保持相同的縮進。否則會被當作錯誤處理。
 play中hosts,variables,roles,tasks等對象的表示方法都是鍵值中間以“:”分隔表示,“:”後面還要增加一個空格。



首先看下面這個例子:

- apple
- banana
- orange

等價於JSON的下面這個格式:

[
 “apple”,
 “banana”,
 “orange”
]

playbook文件是通過ansible-playbook命令進行解析的,ansbile-playbook命令會根據自上而下的順序依次執行playbook文件中的內容。

2、playbook的組成

playbook是由一個或多個“play”組成的列表。play的主要功能在於,將事先合併爲一組的主機組合成事先通過Ansible定義好的角色。將多個play組織在一個playbook中就可以讓它們聯同起來按事先編排好的機制完成一系列複雜的任務。

playbooks主要有以下四部分構成,分別如下。

 Target部分: 定義將要執行 playbook 的遠程主機組。
 Variable部分: 定義playbook運行時需要使用的變量。
 Task部分: 定義將要在遠程主機上執行的任務列表。
 Handler部分: 定義task 執行完成以後需要調用的任務。


下面介紹下構成playbook的四個組成部分。

(1)Hosts和Users

  • playbook中的每一個play的目的都是爲了讓某個或某些遠程主機以某個指定的用戶身份執行任務。
  • hosts:用於指定要執行任務的遠程主機,每個playbook都必須指定hosts,hosts也可以使用通配符格式。主機或主機組在inventory清單(hosts文件)中指定,可以使用系統默認的/etc/ansible/hosts,也可以自己編輯,在運行的時候加上-i選項,可指定自定義主機清單的位置。
  • remote_user:用於指定在遠程主機上執行任務的用戶。可以指定任意用戶,也可以使用sudo,但是用戶必須要有執行相應任務的權限。

(2)任務列表

play的主體部分是task list。

task list中的各任務按次序逐個在hosts中指定的所有遠程主機上執行,即在所有遠程主機上完成第一個任務後再開始第二個。在運行自上而下某playbook時,如果中途發生錯誤,則所有已執行任務都將回滾,因此在更正playbook後需要重新執行一次。

task的目的是使用指定的參數執行模塊,而在模塊參數中可以使用變量。模塊執行一個命令,即使執行一次或多次, 其結果是一樣的,這意味着playbook多次執行是安全的,因爲其結果均一致。tasks包含name和要執行的模塊,name是可選的,只是爲了便於用戶閱讀,建議加上去,模塊是必需的,同時也要給予模塊相應的參數。

定義tasks推薦使用module: options”的格式,例如:

service: name=httpd state=running

(3)handlers

用於當關注的資源發生變化時採取一定的操作。handlers是和“notify”配合使用的。
“notify”這個動作可用於在每個play的最後被觸發,這樣可以避免多次有改變發生時,每次都執行指定的操作,通過“notify”,僅在所有的變化發生完成後一次性地執行指定操作。
在notify中列出的操作稱爲handler,也就是說notify用來調用handler中定義的操作。
注意:在notify中定義的內容一定要和handlers中定義的“ - name”內容一樣,這樣才能達到觸發的效果,否則會不生效。


(4)tags
tags用於讓用戶選擇運行或略過playbook中的部分代碼。Ansible具有冪等性,因此會自動跳過沒有變化的部分;但是當一個playbook任務比較多時,一個一個的判斷每個部分是否發生了變化,也需要很長時間。因此,如果確定某些部分沒有發生變化,就可以通過tags跳過這些代碼片斷。

3、Playbook執行結果解析

使用ansible-playbook運行playbook文件,輸出的內容爲JSON格式。並且由不同顏色組成,便於識別。一般而言,輸出內容中,每個顏色表示的含義如下。
 綠色代表執行成功,但系統保持原樣。
 黃色代表系統狀態發生改變,也就是執行的操作生效。
 紅色代表執行失敗,會顯示錯誤信息。


下面是一個簡單的playbook文件:

- name: create user
  hosts: 172.16.213.231
  user: root
  gather_facts: false
  vars:
    user1: testuser
  tasks:
   - name: start createuser
     user: name="{{user1}}"

上面的playbook 實現的功能是新增一個用戶,每個參數含義如下。
 name參數對該playbook實現的功能做一個概述,後面執行過程中,會輸出name的值。
 hosts參數指定了對哪些主機進行操作。
 user參數指定了使用什麼用戶登錄到遠程主機進行操作。
 gather_facts參數指定了在執行task任務前,是否先執行setup模塊獲取主機相關信息,此參數默認值爲true,表示開啓,如果我們在task中要使用facts信息時,就需要開啓此功能。否則可以設置爲false。設置爲false可以加快playbook的執行速度。
 vars參數,指定了變量,這裏指字一個user1變量,其值爲testuser,需要注意的是,變量值一定要用引號括起來。
 tasks指定了一個任務,其下面的name參數同樣是對任務的描述,在執行過程中會打印出來。user是一個模塊,user後面的name是user模塊裏的一個參數,而增加的用戶名調用了上面user1變量的值。





3、playbook中tasks語法使用

在playbook中,task部分是整個任務的核心,我們前面介紹的ansible的常用模塊,例如commands模塊、shell模塊、file模塊、cron模塊、user模塊等,在playbook中仍然可用,每個模塊所使用的參數以及含義跟命令行模式下也完全一樣,只不過寫法不同而已,下面通過幾個例子來看看playbook中常見功能模塊的寫法。

(1)、playbook示例

下面是一個playbook示例,test.yml文件內容如下:

- hosts: hadoophosts
  remote_user: root
  tasks:
   - name: create hadoop user
     user: name=hadoop state=present
   - name: create hadoop directory and chmod/chown
     file: path=/opt/hadoop state=directory mode=0755 owner=hadoop group=hadoop
   - name: synchronize hadoop program
     synchronize: src=/data/hadoop/ dest=/opt/hadoop
   - name: Setting environment variables
     shell: echo "export JAVA_HOME=/usr/jdk" >> /etc/profile

這個playbook文件中,使用了user、file、synchronize和shell模塊,文件開始定義了一個主機組hadoophosts,然後設置root用戶在遠程主機上執行操作,接着,就是task任務的開始,“- name”是描述性信息,用來標識任務執行內容和進度,第一個task用來創建一個hadoop用戶,使用了user模塊,注意,上面的user表示ansible的user模塊,而user後面的name、state是user模塊的參數,這些參數含義上面已經做過介紹,這裏就不在重複了

下面還有file模塊、synchronize模塊以及shell模塊,它們的寫法跟user模塊類似,也不再過多介紹。

從此文件可以看出,通過playbook模式編寫的文件更加簡潔、易懂,只要設置好了任務的運行策略、順序,每次需要用到這個操作的話,直接執行就可以了。執行的方式如下:

[root@server239 ansible]# ansible-playbook  test.yml
除了前面已經介紹過的ansible模塊,還有一些模塊在playbook中也經常用到,下面再介紹一些常用的playbook模塊。

(2)、unarchive模塊

unarchive模塊用來實現解壓縮,也就是將壓縮文件解壓分發到遠程不同節點上。只需記住如下幾個參數即可:

 src: 源文件路徑,這個源文件在管理機上。
 dest: 指定遠程主機的文件路徑。
 mode:設置遠程主機上文件權限。

看下面這個例子:

- hosts: 172.16.213.231
  remote_user: root
  gather_facts: false
  tasks:
   - name: unarchive spark files
     unarchive: src=/src/spark.tar.gz dest=/opt

這個操作是將管理機上的/src/spark.tar.gz文件傳輸到遠程主機上後進行解壓縮,並將解壓縮後的文件放到遠程主機的/opt目錄下。注意,這個例子中我們設置了gather_facts選項爲false,這是因爲下面的操作中,沒有用到facts信息。

(3)、lineinfile、replace模塊

在自動化運維中,對文件進行內容替換是一個非常常見的場景,比如修改、刪除、添加操作系統的某些參數等,Ansible中雖然提供了shell模塊結合sed命令來達到替換的效果,但經常會遇到需要轉義的問題,並且考慮到可讀性性和可維護性等多方面因素,使用Ansible自帶的替換模塊是一個不錯的選擇。Ansible常用的替換模塊爲replace和lineinfile。

replace模塊可以根據指定的正則表達式替換遠程主機下某個文件中的內容,常用的參數有如下幾個:

 path:要操作的遠程主機上文件的路徑。
 regexp:正則表達式,指定替換規則。
 replace:指定最終要替換的字符串。
 backup:是否在修改文件之前對文件進行備份,yes是進行備份。


看下面這個例子:

- hosts: 172.16.213.231
  remote_user: root
  tasks:
   - name: modify selinux
     replace: path=/etc/selinux/config regexp="enforcing" replace=disabled backup=yes

這個操作是對遠程主機上/etc/selinux/config文件中的enforcing字符串進行替換,替換爲disabled,替換前進行備份。其實就是關閉遠程主機上selinux服務。

最後,再介紹一下lineinfile,此模塊也可以實現replace的功能,但lineinfile功能更加強大,支持的參數也比較多,常用參數含義如下:

 path:操作的遠程主機上的文件路徑
 regexp:正則表達式,要替換的內容規則
 line:指定替換後的文本內容
 state:當設置爲absent代表刪除匹配的行
 insertafter:insertafter參數可以將文本插入到“指定的行”之後
 insertbefore:insertbefore參數可以將文本插入到“指定的行”之前
 backup:進行替換操作前是否進行備份





下面來看一個基於lineinfile的playbook任務:

- hosts: 172.16.213.231
  remote_user: root
  tasks:
   - lineinfile: dest=/etc/profile insertafter='ulimit(.*)' line="ulimit -c unlimited"
   - lineinfile: dest=/etc/profile line="export JAVA_HOME=/usr/jdk"
   - lineinfile: dest=/etc/selinux/config regexp='SELINUX=(.*)' line='SELINUX=disabled'
   - lineinfile: dest=/etc/resolv.conf regexp='search(.*)' state=absent

這個playbook任務中,調用了四次lineinfile替換操作,第一次是在/etc/profile文件中找到以ulimit開頭的行,並在後面添加一行內容"ulimit -c unlimited",第二次是在/etc/profile文件的最後添加一個JAVA_HOME路徑,第三次是修改/etc/selinux/config文件中以“SELINUX=”開頭的行,將其替換爲“SELINUX=disabled”,其實就是關閉selinux,最後一個操作是在/etc/resolv.conf文件找查找以search開頭的行,然後將其刪除掉。

(4)、register、set_fact、debug模塊

ansible中定義變量的方式有很多種,可以將模塊的執行結果註冊爲變量,也可以在roles中的文件內定義變量,還可以使用內置變量等,而register、set_fact都可用來註冊一個變量。

使用register選項,可以將當前task的輸出結果賦值給一個變量,看下面這個例子:

- hosts: 172.16.213.231
  remote_user: root
  tasks:
   - name: ps command
     shell: hostname
     register: host_result
   - debug: var=host_result

此例子是將在遠程主機上執行的shell命令“hostname”的輸出結果賦值給變量host_result,然後再將變量引用並使用debug模塊輸出。輸出結果是json格式的。注意,此例子最後還使用了debug模塊,此模塊用於在調試中輸出信息.

下面是上面playbook的debug輸出結果:

TASK [debug] **********************************************************
ok: [172.16.213.231] => {
    "host_result": {
        "changed": true,
        "cmd": "hostname",
        "delta": "0:00:00.007228",
        "end": "2020-04-01 04:42:34.254587",
        "failed": false,
        "rc": 0,
        "start": "2020-04-01 04:42:34.247359",
        "stderr": "",
        "stderr_lines": [],
        "stdout": "server231.localdomain",
        "stdout_lines": [
            "server231.localdomain"
        ]
    }
}

可以看出,此輸出是一段json格式的數據,最頂端的key爲host_result,大括號內還有多個二級key,我們想要的結果是輸出遠程主機的主機名即可,不需要這些額外的二級key信息,如何實現這個要求呢,如果想要輸出json數據的某二級key項,可以使用"key.dict"或"key['dict']"的方式引用即可。從上面輸出可以看到,我們需要的二級key是stdout項,所以要僅僅輸出此項內容,可以將變量引用改爲host_result.stdout即可,也就是將上面的playbook任務改成如下內容:

- hosts: 172.16.213.231
  remote_user: root
  tasks:
   - name: hostname command
     shell: hostname
     register: host_result
   - debug: var=host_result.stdout
   - debug: 'msg="output: {{host_result.stdout}}"'

在這個playbook中,我們又增加了一個debug參數,debug模塊常用的參數有兩個,分別是msg和var,它們都可以引用變量輸出信息,但有一點小區別,msg可以輸出自定義信息,並且變量需要雙大括號包含起來,而var參數只能輸出變量,並且不需要雙大括號。

修改後的playbook執行debug輸出結果如下:

TASK [debug] ********************************************************
ok: [172.16.213.231] => {
    "host_result.stdout": "server231.localdomain"
}

TASK [debug] ********************************************************
ok: [172.16.213.231] => {
    "msg": "output: server231.localdomain"
}

從輸出可知,這個纔是我們想要的結果。

set_fact和register的功能很類似,它也可以將task輸出賦值給變量。set_fact更像shell中變量的賦值方式,可以將某個變量的值賦值給另一個變量,也可以將字符串賦值給變量。看下面這個例子:


- hosts: 172.16.213.231
  remote_user: root
  tasks:
   - name: hostname command
     shell: hostname
     register: host_result
   - set_fact: var1="{{host_result.stdout}}"
   - set_fact: var2="This is a string"
   - debug: msg="{{var1}}  {{var2}}"

這個例子是將hostname的輸出結果賦值給host_result變量,然後通過set_fact將host_result變量賦值給var1變量,接着又將一個字符串賦值給var2變量,最後,通過debug模塊輸出這些變量信息。注意這些模塊的使用方式和書寫格式。

這個playbook的輸出結果爲:

TASK [debug] **********************************************************
ok: [172.16.213.231] => {
    "msg": "server231.localdomain  This is a string"
}

(5)、delegate_to、connection、和local_action模塊

ansible默認只會對遠程主機執行操作,但有時候如果需要在管理機本機上執行一些操作,該如何實現呢,這個實現的方法有很多,可以通過delegate_to(任務委派)來實現,也可以通過connection:local方法,還可以通過local_action關鍵字來實現。

下面來看一個例子,說明它們的用法。

- hosts: 172.16.213.231
  remote_user: root
  gather_facts: true
  tasks:
   - name: connection
     shell: echo "connection . {{inventory_hostname}} $(hostname) ." >> /tmp/local.log
     connection: local
   - name: delegate_to
     shell: echo "delegate_to . {{inventory_hostname}} $(hostname) ." >> /tmp/local.log
     delegate_to: localhost
   - name: local_action
     local_action: shell echo "local_action. {{inventory_hostname}} $(hostname)" >> /tmp/local.log

這個例子中,依次使用了connection、delegate_to和local_action三種方式,還使用了一個變量{{inventory_hostname}},這是ansible的一個內置變量,它用來獲取遠程主機的主機名,說到主機名,其實就是用到了facts信息,所以,需要設置gather_facts選項爲true,另外,$(hostname)是shell裏面的變量,也是用來獲取主機名,此例子實現的功能是將遠程主機的主機名依次輸出到管理機的/tmp/local.log文件中。


文章內容不夠清晰,還有視頻輔助學習哦,訪問:

https://edu.51cto.com/course/23377.html ,Ansible與Ansible-playbook應用實戰視頻課程,幫助一次性掌握Ansible自動化運維工具的使用。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章