自動化運維工具ansible-playbook

一、playbook簡介與文件格式

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

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

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

首先看下面這個例子:shell

- apple
- banana
- orange

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

[
 「apple」,
 「banana」,
 「orange」
]

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

二、playbook的組成

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

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

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

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

(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跳過這些代碼片段。

三、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變量的值。

三、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文件中。

相關文章
相關標籤/搜索