【Ansible】 基於SSH的遠程管理工具

【Ansible】html

  參考文檔:【http://www.ansible.com.cn/docs/intro.html】python

  和ansible相似的工具還有saltstack,puppet,sshpass等,都用於遠程(批量)地管理服務器資源。各類工具實現原理不一樣,像ansible的話就是基於SSH開發的,這就表示其無需安裝客戶端,在一臺全新的服務器上線以後(只要其有sshd服務在運行)就能夠直接加入被管理的集羣了。linux

  ■  安裝驗證nginx

  安裝ansible的話我仍是用了yum install ansible,聽說用pip也能夠裝。對於其餘一些的系統,能夠選擇從源碼開始安裝。源碼安裝一年前本身那本筆記本上有,可參考。總的來講,ansible的安裝還要求許多系統的組件如pcre,zlib以及其開發包、還有就是不少python模塊如paramiko等。正則表達式

  安裝完成後先驗證一下。ansible --version能夠看到版本信息。shell

  ansible的主要配置文件在/etc/ansible中,修改其中的hosts文件,默認狀況下應該全是註釋,在合適的地方好比第12行左右寫入一行127.0.0.1。這個hosts文件主要用來配置全部被控端,如今驗證性地把本地先配置進去。安全

  ●  關於驗證用戶方式ruby

  由於ansible基於SSH,因此不可避免的一個問題就是經過ansible遠程鏈接主機時如何驗證用戶?兩種方式,一個是密碼,一個是公鑰認證。服務器

  若是選擇經過密碼來認證,能夠在/etc/ansible/hosts的相應主機配置項後面加上" ansible_ssh_user=xxx ansible_ssh_pass=yyy",此外經過密碼登陸時還要保證目標主機在當前使用ansible的用戶的known_hosts中,即要求其至少要登陸一次。添加known_hosts的方法能夠是手動ssh登陸一下,而後添加公鑰。在/etc/ansible/ansible.cfg中配置host_key_checking = False也能夠取消公鑰檢查步驟,可是這樣並不安全。數據結構

  其實,使用ansible的時候大概都是指望不要在配置文件中寫入密碼來經過ssh驗證了,免密ssh驗證的方法是ssh-keygen和ssh-copy-id兩個命令一塊兒用。默認配置時,ansible使用和當前用戶同名的用戶登陸目標主機,所以須要目標主機的此用戶的HOME/.ssh/authorized_key中有本機本用戶的公鑰。在此前提下,當咱們用root用戶執行,且/root/.ssh/authorized_key中有了root的公鑰信息,此時執行

  ansible all -m ping

  來測試ansible的安裝狀況,若是返回中沒有報錯,就表示OK了。

■  關於hosts文件

  /etc/ansible/hosts文件指出了ansible被控端的一些配置。除了上面的簡單配置,其實這個配置文件的形式能夠很是豐富。好比上面說了在一個IP地址後面加上ansible_ssh_user和ansible_ssh_pass這兩個額外的配置項。另外還能夠加上ansible_ssh_port(默認22),ansible_ssh_host(主機名,假如你想給目標IP的主機取一個別名,可是又不想下到系統層面的/etc/hosts中去改,就能夠在這裏取完別名後加上此參數指明IP便可),ansible_ssh_private_key_file(指定私鑰文件)還有ansible_connection,可選local,ssh或paramiko。一些其餘額外配置選項以下:

ansible_sudo_pass
      sudo 密碼(這種方式並不安全,咱們強烈建議使用 --ask-sudo-pass)

ansible_sudo_exe (new in version 1.8)
      sudo 命令路徑(適用於1.8及以上版本)

ansible_connection
      與主機的鏈接類型.好比:local, ssh 或者 paramiko. Ansible 1.2 之前默認使用 paramiko.1.2 之後默認使用 'smart','smart' 方式會根據是否支持 ControlPersist, 來判斷'ssh' 方式是否可行.

ansible_shell_type
      目標系統的shell類型.默認狀況下,命令的執行使用 'sh' 語法,可設置爲 'csh' 或 'fish'.

ansible_python_interpreter
      目標主機的 python 路徑.適用於的狀況: 系統中有多個 Python, 或者命令路徑不是"/usr/bin/python",好比  \*BSD, 或者 /usr/bin/python
      不是 2.X 版本的 Python.咱們不使用 "/usr/bin/env" 機制,由於這要求遠程用戶的路徑設置正確,且要求 "python" 可執行程序名不可爲 python之外的名字(實際有可能名爲python26).

      與 ansible_python_interpreter 的工做方式相同,可設定如 ruby 或 perl 的路徑....

 

  hosts文件的基本格式是ini格式的,ini中的每一個section是一個服務器邏輯分組,每一個option是一個服務器,而後option後面能夠添加上面說的額外配置。如:

[localloop]
127.0.0.1 ansible_ssh_user=frank ansible_connection=paramiko

 

  section無形中也對服務器作了分組,咱們就能夠基於分組 對一部分主機進行操做了。一臺主機也可分屬於幾個不一樣的組。

  hosts文件還支持多匹配的配置,好比下面這個配置:

[localloops]
127.0.0.[1:5] ansible_connection=paramiko ansible_ssh_user=root

 

 指的是127.0.0.1到127.0.0.5全部IP都放在localloops組中。若是在這個配置下運行ansible all -m ping的話就會收到5條迴應(雖然127.0.0.x都是指本地機)。

  ●  變量

  對於批量管理系統,一個很重要的要素就是能夠設置根據主機不一樣而不一樣的變量。在ansible中,變量被使用在playbook中以發起對主機不一樣而個性化的配置。變量的設置能夠在hosts中進行。變量分紅好幾個層級的變量,最小的是主機變量,跟在主機後面直接設置:

127.0.0.1 http_port=80 maxRequestPerChild=808

 

  這兩個變量的做用域只有這個主機一臺。因爲對變量名沒有限制,因此前面說到的那些ansible的配置若是打錯字了也不會報錯,ansible會認爲這是你指定的一個主機變量

  另外還能夠設置組變量。設置組變量須要新開一個section,名爲[組名:vars],如:

[localloop:vars]
http_port=8080

 

  另外,組之間能夠經過[組名:children]的方式來指出組之間的隸屬關係。好比

[localloop1]
127.0.0.[1:5]

[localloop2]
127.0.0.[6:10]

[localloops:children]
localloop1
localloop2

 

  如此以後,在父組中定義的變量將也會能夠被子組使用

  也能夠把變量的定義在hosts文件外完成,這就是分文件定義主機和組變量。在/etc/ansible/下建立兩個目錄: host_vars和group_vars,這兩個組中再分別建立YAML格式的,對應主機or組名的文件。例如能夠在/etc/ansible/group_vars中建立localloop文件,在/etc/ansible/host_vars中建立testhost,而後修改hosts配置以下:

-----/etc/ansible/hosts-----
[localloop]
testhost ansible_ssh_host=127.0.0.1


-----/etc/ansible/group_vars/localloop-----
key1: value1
key2: vaule2


-----/etc/ansible/host_vars/testhost-----
key3: value3

   至於變量的用處,主要是在playbook中使用,這個後面細說。

 

■  命令行調用API

  配置完hosts以後,在命令行中咱們能夠調用一些命令來使用ansible如上面的ansible all -m ping。總的來講,命令行的ansible工具大致格式是這樣的:

  ansible <pattern> -m <module> -a <arguments>

  pattern是一個標識,規定出了一部分主機。好比能夠寫all或者*,就是表明全部主機;寫組名,表明該組內的主機;寫主機名,就表明這一個主機。若是主機名和組名衝突則以先規定的那個爲準,並且命令行中會給出警告信息,提示有重名發生。另外也能夠直接寫域名或者IP(只要在hosts中有配置),甚至能夠寫192.168.1.*這種格式的來匹配一批主機。

  pattern中能夠藉助符號來規劃簡單的組分邏輯,如

  a:b  a組和b組的並集

  a:!b  屬於a但不屬於b的主機們

  a:&b  a組和b組的交集

  甚至能夠有a[0]這種下標的形式來取得a組中指定第幾個的主機,a[0-1]則是下標羣,最萬能的正則表達式則須要在pattern最前面加上~,如~(www|db)\.example\.com。須要注意一旦用上正則,那麼點都要反轉義了。

  ●  命令行使用時一些其餘參數

  使用命令行時若是想動態的指定遠程執行命令的用戶,能夠加入-u參數;

  -f命令控制命令執行的進程數。好比操控5個主機進行一個比較耗時的任務,那麼-f 5說不定只要一個單位的時間就能搞定,而-f 1的話必然須要五個單位的時間才能搞定。完成任務後返回是及時的,有點像多線程模型,但這裏是多進程的。另外ansible必定會按照-f指出的進程數fork出那麼多個子進程,並無論是否真有那麼多主機須要處理。這個參數的值默認是5,在ansible.cfg中有配置。

  --sudo可讓命令在遠端經過sudo執行,固然沒有在遠端主機上配置運行sudo的話會要求輸密碼,在ansible中默認不提供這種進一步的交互因此直接報錯。若是想要手動輸入密碼來繼續sudo任務,能夠在運行命令時加上--ask-sudo-password參數。可是考慮到自動化的目的,相信這個參數是不太經常使用的。

  -m參數是指出了使用的ansible模塊。ansible模塊這東西有的說說,這類先不細講。目前用過的模塊有ping, command, shell等。

  -a參數就是argument,指出某個模塊要求的參數,一般-a後參數的值有必定格式要求。

 

  須要注意的是,命令行執行命令適合於一些簡單的批量管理任務,面對更加複雜的配置,安裝等任務應該使用ansible-playbook,二者之間的關係就像是linux命令行和shell腳本。至於playbook詳細後面說。

 

■  關於配置

  上面簡單提到過了/etc/ansible下的ansible.cfg配置文件是用來配置ansible主控端的一些參數,而/etc/ansible/hosts則是用來配置一些被控端主機信息的。下面詳細來看下這些配置文件。

  首先是ansible.cfg,這是一個INI格式,#開頭行註釋的配置文件。在目前的ansible中,運行ansible時會依次加載 環境變量ANSIBLE_CONFIG,當前目錄的ansible.cfg,~/.ansible.cfg,/etc/ansible/ansible.cfg,針對同一個配置項以最早加載到的爲準。

  這些配置文件中,大多數配置都集中在名爲defaults的section中,其中可使用的option有

  action_plugins  action代碼被執行時運行的一些插件(ansible支持開發者本身寫插件使用,很是靈活),相似的還有callback_plugins,connection_plugins,filter_plugins,lookup_plugins,vars_plugins

  ansible_managed

  ask_pass  默認是False,當設置爲True時,ansible執行會要求輸入遠端的密碼,即便配置了免密登陸。因此通常維持False

  forks  進程數,默認爲5

  gathering  用來指出是否收集遠程主機的facts(一些被控端的變量),默認的值是implicit,每次都會收集,explicit表示不收集,smart表示掃描主機,對已經收集過的不收集

  host_key_checking  以前說過了,是否檢驗被控端主機的公鑰,默認是True

  inventory  被控端配置文件(它的官方名稱是inventory),默認是/etc/ansible/hosts

  log_path  日誌文件,默認/var/log/ansible.log

  module_name  在不指出-m的參數時默認使用的模塊,默認值是command,建議能夠改爲shell

  nocolor  默認爲0,設置爲1時ansible不會爲不一樣狀態(成功or失敗or警告)的結果染不一樣顏色

  nocow  運行playbook的時候默認輸出會有一頭字符畫的牛,設置爲1能夠關掉牛的顯示(哈哈哈哈哈哈哈)

  pattern  當沒有給出pattern時的默認pattern,默認值是*即全部主機

  poll_interval  ansible中有異步任務,這個配置設置一個秒數,當執行開始後每隔這麼多秒查詢一次任務狀態

  remote_tmp  一些ansible任務涉及到文件的傳輸,而傳輸的第一目的地就是這個配置項

  remote_user  遠端用什麼用戶執行任務,默認是和主控端當前用戶名稱一致的用戶

  timeout  SSH鏈接超時時間

  

  除了defaults這個section以外,還有一些section能夠配置好比Paramiko Specific Settings,OpenSSH Specific Settings等等。因爲這些都是比較高端的玩法,就很少介紹了。

 

【Ansible-playbook】

  playbook的基本語法是YAML格式的。這個格式長成這樣:

---
- hosts: local
 name: this is a test for playbook remote_user: root tasks: - name: test to ping ping:

-------------------運行後返回是-------------------
PLAY [this is a test for playbook] ************************************************************************************************************************

TASK [Gathering Facts] **************************************************************************************************************
ok: [testhost2]
ok: [testhost1]
ok: [testhost3]

TASK [test to ping] *****************************************************************************************************************
ok: [testhost3]
ok: [testhost2]
ok: [testhost1]

PLAY RECAP **************************************************************************************************************************
testhost1                  : ok=2    changed=0    unreachable=0    failed=0   
testhost2                  : ok=2    changed=0    unreachable=0    failed=0   
testhost3                  : ok=2    changed=0    unreachable=0    failed=0 

 

  解釋一下,playbook有一個根節點,下面的第一級子節點被稱爲一個個play。這裏只有一個play。此play名爲this is a test for playbook,其含義就是對主機組local,以root的身份進行名爲test to ping的任務,具體內容是利用ping模塊,參數爲空進行操做。返回內容中能夠看到有gathering facts板塊,這個參看前面配置文件中說到的gathering項。關於facts的具體內容相信後面也會再講。另外須要注意的一點就是返回的內容並非在各個遠端執行的stdout,而是一個狀態碼。好比我把模塊換成了shell: echo hello,返回內容不是hello而也是ok。因此在使用command或者shell模塊的時候一般會採用 xxxx || /bin/true的方式。

  運行方式是ansible-playbook test.yaml,ansible-playbook這個命令也能夠加不少和ansible同樣的參數,好比-f 10就是開10條進程來進行操做。上面說經過ansible獲取到的返回信息是有限的,若是想要獲取到更多的信息,能夠加上參數--verbose。這樣返回的信息中就會包含一些其餘的好比stdout,stderr等輸出的信息了。若是在正式運行playbook以前想查看本次playbook會對哪些主機進行操做的話能夠加上參數--list-hosts來提早檢查一下。

  其中比較中心的是tasks這個列表,tasks中包含了不少task。這些task會針對hosts字段定義的主機組中相應主機,從上到下依次進行task操做。每一個task的本質是執行一個模塊(module),除了少數如ping同樣的模塊是不用參數,大多數模塊須要提供參數。參數中可使用在配置文件、命令行參數等多個地方定義的變量。在playbook中調用變量的格式採用Jinja2的語法,好比:

---
- hosts: local
  tasks:
    - name: make dir {{ name }}
      command: mkdir {{ name }}

  name變量能夠定義是組變量,也能夠是主機變量。能夠察覺到當其是主機變量且各個主機配的name值不一樣的時候,返回內容會返回什麼這個問題。通過實驗,返回的是第一個主機的變量值。

  利用playbook執行模塊只作必要的改動,若是ansible檢測到模塊動做的目的在執行前就已經達到,那麼就會放棄本次操做。能夠說即便重複執行playbook也是安全的。而像對於shell,command這樣的命令執行模塊而言是個例外,重複執行playbook等於重複執行命令。爲了不這樣的狀況發生,能夠爲參數中添加一個flag=creates來保證不重複操做。

  這種特性用術語來講就是每個task都具備「冪等性」。

  ■  一個task的其餘參數

  上面的例子中說到了一個task中最主要的兩個字段,name(指出task名)以及<模塊名>(指出task的具體操做)。除了這兩個以外,task還能夠寫一些其餘的字段參數。

  好比notify,notify下面跟着的每一條記錄被稱爲handler。前面說過ansible在執行模塊動做時會檢測動做的目的是否已經達到,若是達到那麼能夠在task結束的時候作一些鉤子操做。設置這個鉤子操做的就是handlers了。須要注意的是,一個handler自己也是一個task,只不過它經過notify的形式在主task中被調用。因爲handler自己就是task,因此說handler自己也具備冪等性,即不論主task經過notify調用了多少次同一個handler,在全部task結束以後這個handler都只被執行最多一次。handler的經常使用場景是在配置等變動後經過handler來重啓服務等。下面是一個帶有handler的task的例子:

tasks:
  - name: change configuration
    template: template/nginx.tpl.conf /etc/nginx/nginx.conf
    notify:
      - restart nginx

handlers:
  - name: change configuration
    shell: /usr/sbin/nginx -s reload || /bin/true

 

  上面的例子中task中有個template字段參數,template字段有點像saltstack裏面的file.manage模塊中的template字段,其將指定的一個本地的模板文件中的變量具體化,而後將其傳送到目標主機上的指定位置。變量能夠來自於以前所說的多種多樣的變量定義手段。

 

   ■  include(import)和roles

  include(在較新的版本中已經提示include關鍵字再也不鼓勵使用,應該使用更加確切的import_tasks和import_playbook)能夠在一個playbook中引用進一些其餘yaml文件的信息。這種引用能夠在多個層面進行,好比在play這一層面進行引用那就是將其餘文件中定義的play一併加入到當前文件中來執行。

  若是引用是發生在task層面,那麼就是在一個play中的tasks下面聲明引用,相對應的,被引用文件內容也是一個tasks集合。

  此外引用還能夠插入到handlers中去,可是因爲handler就是一個task,考慮這一點的話其實handlers中進行include的本質和在tasks下面進行是相似的。下面給出例子

---
- name: this is a test for playbook
  hosts: local
  tasks:
    - name: test connection
      ping:

    - import_tasks: mytask.yml

- import_playbook: playbook/myplaybook.yml

 

   在tasks中引入了mytask.yml文件中定義的task,在整個playbook中引入了myplaybook.yml中定義的那些play。

  另外還須要指出,當在task層面進行引入的時候,除了引入一個具體的yml文件,還能夠引入一個帶有模板變量的文件,而且能夠在引入以後定義變量的具體值(固然用其餘手段定義的變量也均可以使用)。此時關鍵字不能寫import_tasks而應該寫include_tasks。這種引入模板的操做只有在task層面才能進行。

  

  在瞭解了playbook的引入機制以後,接下來的重點就是要說明一下roles這個ansible裏面的重要概念了。一個role把一部分的task,handler等ansible要素邏輯上抽象成一個集合,咱們能夠基於這個集合進行ansible操做。而在playbook中,咱們只要指出哪些機子是什麼role(role和主機間多對多關係),那個角色該執行的全部操做都會被執行上去。

  那麼如何定義一個role? 首先是在ansible.cfg中有一個配置項roles_path,這是一個用冒號隔開的多個路徑。每一個路徑都被認爲是roles路徑。若這些路徑下有名爲testrole的目錄,則表示有名爲testrole的一個role。假設咱們在某一個roles目錄下的testrole目錄結構以下:

testrole
├── defaults
├── files
├── handlers
├── meta
├── tasks
├── templates
└── vars

 

  在playbook中指定role的方式以下:

---
- hosts: local
  roles:
    - testrole

 

  指定以後,此play還能夠補充一些其餘內容,而且針對local這個主機組承認了如下行爲:

  若testrole/tasks/main.yml存在,則其中定義的tasks將被添加到play中執行

  (main.yml中應該直接從task開始寫,連tasks都是不用寫的,也就是說相似- name:xxx\n  shell: xxx。下面也都是相似,直接從被引用那個層級開始寫)

  若testrole/handlers/main.yml存在,則其中定義的handlers將被添加到play中(被添加到的意思是在這個play的context中能夠直接寫其name來使用,下同)

  若testrole/vars/main.yml存在,則其中定義的變量將被添加到play中

  若testrole/meta/main.yml存在,則其中定義的角色依賴將被添加到roles列表中

  若testrole/files存在,其中的文件和腳本能夠分別被指定模塊爲copy和script類的task直接引用而不用加上路徑

  若testrole/templates存在,其中的模板文件能夠被template類task直接引用而不用加上路徑

  若testrole/tasks存在,其中的文件能夠被直接import_tasks等類型task直接引入,而不用加上路徑

  

  執行roles相關操做的優先級是高於通常定義的tasks的,也就是說tasks會在roles所有完成以後再被執行。關於給roles具體化一些變量,以及role之間的依賴等內容先不說了。

 

■  關於變量

  ansible的變量使用jinja2的語法制成,支持了{{ var }}之類的變量語法以及變量相關的一些過濾器方法,同時也支持{% if %}{% for %}等jinja2語法規定的邏輯結構,通常而言這些邏輯結構更多的用在template模塊的模板中,而不是playbook自己中,playbook自己更多的是用了簡單的變量形式。小提醒一下,若變量在一個值的開頭,要用引號引發,要否則解析yaml的時候會覺得咱們在試圖聲明一個字典。

  上面所說的全部變量,幾乎都是咱們自定義的變量。其實ansible爲咱們準備了不少內置的變量,一部分跟主機相關的被稱爲facts。

  facts的值根據主機不一樣而不一樣,ansible會爲咱們自動收集這些數據。一臺主機具體有哪些facts而且值是多少能夠ansible <host> -m setup來查看。數據量挺大的須要注意。能夠注意到,在默認配置gathering爲implicit的時候是不收集facts的,因此就沒法將facts的變量名應用在ansible中。若是設置成了其餘兩個,那麼在ansible執行以前必定會去gather一下facts。有時確認不須要用到facts,還想要加快運行速度的時候能夠在相關play中加上gather_facts: no做爲參數。

  facts是一個JSON結構的數據,其根節點的KEY名是ansible_facts,實際使用時不用寫出這個根節點。好比{"ansible_facts": {"ansible_all_ipv4_addresses": ["xxx","yyy"]}}這個不徹底的facts結構,若是在模板中想要獲取yyy的值,那麼能夠直接{{ ansible_all_ipv4_addresses[1] }}便可。

  另外一部分則是和ansible系統自己相關的變量,這些變量一般是一些預設的變量名好比groups, hostvars, group_names。這些變量名咱們不能覆蓋,具體哪些內容能夠試驗一下看看。只須要記得,這些變量都是和ansible自己相關,不隨某一次playbook的運行而變化。

  除了在playbook運行以前定義變量,tasks被執行前gather一下facts以外,在tasks運行過程當中也可使用變量。這種操做被稱爲註冊變量。註冊變量是指將某一個task的返回結果保存下來,而且供給給後續其餘task使用。通常而言,這種機制常和條件判斷一塊兒使用。

  脫離開playbook自己,從ansible的命令行來看(不管是ansible仍是ansible-playbook),均可以在後面加上參數--extra-vars來指定額外的變量。後面跟的值的格式能夠是--extra-vars "var1=value1 var2=value2"或者JSON格式的 --extra-vars {"var1":"value1", "var2": "value2"}

  如今來總結下能夠定義變量的地方:

  首先在inventory(也就是默認的/etc/ansible/hosts文件)中,能夠定義變量,能夠給單個主機定義(直接跟在一個主機行後面),也能夠給一整個組定義(開一個新的section)

  其次,在/etc/ansible/group_vars和/etc/ansible/host_vars下也能夠定義變量。方法是建立和組/主機同名的文件,文件格式是yaml,裏面定義變量。

  再次,roles的vars目錄下的main.yml中也能夠定義變量。

  此外,ansible有內置的變量,分紅facts(內置主機變量,隨着主機變化而變化)和其餘一些和ansible相關,諸如groups,group_names的變量。

  在playbook的運行過程當中,還能夠把某一個task的返回做爲變量的值保存下來。

  命令行加上--extra-vars也能夠指定額外變量

 

  當這些地方聲明的變量之間命名衝突了怎麼辦,優先級是這樣的,命令行的--extra-vars最爲優先,而後是inventory文件中定義的變量(包括/etc/ansible/host|group_vars),而後是play中定義的,roles中定義的變量等,而後是facts等系統內置變量。

 

■  條件判斷和When語句

  經過when語句進行條件判斷,並結合註冊變量,能夠在play的執行過程當中加入邏輯順序。好比下面這段:

tasks:
  - name: first step
    command: /bin/false
    register: result
    ignore_errors: True
 - name: do when failed
    command: /bin/dosomething
    when: result|failed
 - name: do when success
    command: /bin/dosomethingelse
    when: result|success
 - name: do when skipped
    command: /bin/other_something
    when: result|skipped

 

  注意到,when語句的值中可使用jinja2過濾器,將變量作一些處理後返回一個布爾值的判斷結果給when語句。當符合when語句規定的條件時,天然when語句所在的task就會被執行了。

  ignore_errors參數指出,若是當前task失敗了是否忽略錯誤繼續往下執行其餘task。

  更加靈活的when語句的使用方法還有諸如在其餘地方定義一個變量而後給when用。好比定義了var: True的話,在when: var的時候就能夠執行task,還有when: var is not defined這種用法來判斷一個變量是否被定義。簡單的判斷相等,不等,大於小於也能夠用when: xxx == "yyy",when: var|int <= 10之類的,就再也不一一列舉了。

 

  import的時候以及play在關聯role的時候,都可以使用when條件語句來進行邏輯判斷後再執行操做。

  ●  關於註冊變量

  以前簡單提到了下注冊變量的內容,註冊變量實質是將某一個task的返回的對象(完整對象能夠經過--verbose參數來查看,是一個JSON格式的東西)保存下來。因此有時候若是想要獲取到返回結果輸出到stdout的值,能夠result.stdout來調用。下面就是一個分析stdout輸出的例子:

- name: test
  shell: cat /tmp/testfile
  register: result
- name: test2
  shell: echo "find word hello in testfile"
  when: result.stdout.find('hello') != -1  # find方法是來源於python的,python中對字符串的處理方法這裏基本均可以用上

  其餘諸如stderr,stdout_lines均可以調用,詳細能夠用上--verbose看返回的結果究竟是怎樣一個JSON。

 

■  循環結構

  這裏要說的循環不是基於JINJA2模板語法的循環,而是自己ansible提供的對於playbook邏輯的循環。試想這樣一些場景,好比你要在新服務器上一次性新增多個用戶,或者要經過yum一次性安裝好多包。固然你能夠把每個用戶/每個包寫成一個task,但這樣會很麻煩。經過循環結構,只要控制每次的用戶名/包名不一樣,其餘操做都是相同的,就能夠更加輕鬆方便了:

- name: add many users
  user: name={{item.name}} state=present groups={{item.group}}
  with_items:
    - {"name": "Frank","group": "FrankG"}
    - {"name": "Takanashi", "group": "TakanashiG"}

 

  with_items,其中item是前面循環每一個單元的代號,是固定的,事實上下面全部循環的方法的循環體內單元的代號都是item。像這個例子給出的是對一個字典的列表的循環,而通常狀況下也能夠單純的對一個列表循環。

  若是指出是with_nested,那麼在下面能夠指出多個列表,進行笛卡爾積式的做業。好比:

- name: test
  shell: echo "{{item[0]}}: {{item[1]}}"
  with_nested:
    - ['a','b']
    - ['1','2','3']

 

  這個task最終的輸出是在每臺機子上分別都輸出了a:1,a:2,a:3,b:1,b:2,b:3。(實驗了下,with_nested出來的result不是一個和其餘簡單輸出同樣的帶有stdout什麼的結構,而是有一個字段名爲results的列表)

  除了with_items和with_nestsed,ansible中還有許多這種內置定義了的循環關鍵字:

  with_fileglob  以非遍歷的方式循環一個指定目錄下的文件名,一般能夠和template,copy等模塊相結合

  with_subelements  對於一些層級比較複雜的數據結構來講,能夠直接循環遍歷其指定名字的一個子字段

  with_sequence  相似於range函數,能夠指定start,end,stride,format等,經過它來生成一個有數字規律的字符串列表供playbook使用如with_sequences: start=0 end=10 stride=2 format=var%d

  with_random_choice  下可跟若干個字符串,隨機選中一個

 

■  其餘一些playbook的特性

  ●  加速模式

  對於較老版本的ansible用戶,以及還在使用比較老的系統版本主機的用戶,ansible提供了所謂的加速模式能夠在操做上加快不少。使用方法就是在play中指出accelerate: true。

 

  ●  異步和輪詢

  在默認狀況下,ansible會在操做過程當中一直保持SSH鏈接,當操做任務耗時較長,而且操做主機較多時維護這樣的鏈接顯然有些成本太高。因此ansible也提供了異步的模式。在異步模式下,操做請求發出以後每隔一個輪詢時間去查詢一次,當查詢到操做結果則返回,而且設置有一個超時時間。

  使用方法是在task中指出ansyc: nn,nn是超時時間的指定。同時也可經過poll: n來指出輪詢間隔時間。若不設置輪詢時間默認是10秒。

  ●  檢查

  對於某個playbook會影響哪些主機,以前說過只要加上--list-hosts參數便可。

  那麼對於其到底作哪些變化,能夠用--check參數來檢查。同時還能夠加上--diff參數,這樣還能夠看到本次playbook對目標主機上的文本會作出什麼變化。在--check模式下的全部變動都不是真實的變動,而只是作一個預覽,比較方便。

  ●  錯誤處理

  前面說過了,當設置一個task的ignore_error爲true或者yes的時候,這個task即便出錯也會跳過它繼續執行下面的幾個task。與之相對的,也能夠手動拋出錯誤。方法以下:

  fail: msg="the task is failed"

  failed_when: "'ERROR' in res.stderr"  這個是有條件的發起失敗。固然,也能夠經過fail+when兩個子句來實現。

 

 

更多的一些關於模塊的內容我打算另開一篇寫寫

相關文章
相關標籤/搜索