1. 程式人生 > >java分散式系統部署學習(九)ansible-playbook進階

java分散式系統部署學習(九)ansible-playbook進階

一、併發執行

ansible預設只會建立5個程序,所以一次任務只能同時控制5臺機器執行.那如果你有大量的機器需要控制,或者你希望減少程序數,那你可以採取非同步執行.ansible的模組可以把task放進後臺,然後輪詢它.這使得在一定程序數下能讓大量需要的機器同時運作起來.

使用async和poll這兩個關鍵字便可以並行執行一個任務. async這個關鍵字觸發ansible並行運作任務,而async的值是ansible等待執行這個任務的最大超時值,而poll就是ansible檢查這個任務是否完成的頻率時間.

如果你希望在整個叢集裡面平行的執行一下updatedb這個命令.使用下面的配置

- hosts: all
    tasks:
      - name: Install mlocate
        yum: name=mlocate state=installed
      - name: Run updatedb
        command: /usr/bin/updatedb
        async: 300
        poll: 10

你會發現當你使用上面的例子控制超過5臺機器的時候,command.在上面yum模組會先在5臺機器上跑,完成後再繼續下面的機器.而上面command模組的任務會一次性在所有機器上都執行了,然後監聽它的回撥結果

如果你的command是控制機器開啟一個程序放到後臺,那就不需要檢查這個任務是否完成了.你只需要繼續其他的動作,最後再使用wait_for這個模組去檢查之前的程序是否按預期中開啟了便可.只需要把poll這個值設定為0,便可以按上面的要求配置ansible不等待job的完成.

最後,或者你還有一種需求是有一個task它是需要執行很長的時間,那你需要設定一直等待這個job完成.這個時候你把async的值設成0便可.

總結來說,大概有以下的一些場景你是需要使用到ansible的polling特性的

  • 你有一個task需要執行很長的時間,這個task很可能會達到timeout.
  • 你有一個任務需要在大量的機器上面執行
  • 你有一個任務是不需要等待它完成的

當然也有一些場景是不適合使用polling特性的

  • 你的這個任務是需要執行完後才能繼續另外的任務的
  • 你的這個任務能很快的完成

Looping

在ansible你能夠通過不同的輸入去重複的執行同一個模組,舉個例子,你需要管理幾個具有相同許可權的檔案.你能夠用一個for迴圈迭代一個facts或者variables去減少你的重複勞動.

使用with_items這個關鍵字就可以完成迭代一個列表.列表裡面的每個變數都叫做item.有一些模組譬如yum,它就支援使用with_items去安裝一列表的包,而不需要寫好多個yum的task

下面來一個with_items的例子

tasks:
  - name: Secure config files
    file: path=/etc/{{ item }} mode=0600 owner=root group=root
    with_items:
     - my.cnf
     - shadow
     - fstab

除了使用items輪訓,ansible還有一種方式是lookup外掛.這些外掛可以讓ansible從外部取得資料,例如,你或許希望可以通過一種特定模式去上傳你的檔案.

在這個例子裡面,我們會上傳所有的public keys到一個目錄,然後聚合它們到一個authorized_keys檔案

tasks:     #1
    - name: Make key directory     #2
      file: path=/root/.sshkeys ensure=directory mode=0700
      owner=root group=root     #3
    - name: Upload public keys     #4
      copy: src={{ item }} dest=/root/.sshkeys mode=0600
      owner=root group=root     #5
      with_fileglob:     #6
       - keys/*.pub     #7
    - name: Assemble keys into authorized_keys file     #8
      assemble: src=/root/.sshkeys dest=/root/.ssh/authorized_keys
      mode=0600 owner=root group=root     #9

loop模組一般在下面的場景中使用

  • 類似的配置模組重複了多遍
  • fact是一個列表
  • 建立多個檔案,然後使用assemble聚合成一個大檔案
  • 使用with_fileglob匹配特定的檔案管理

條件語句

有一些模組,例如copy這個模組有一些機制能跳過本次模組的執行.其實我們也可以使用自己的條件語句去配置跳過模組,這樣方便你服務能夠選擇使用不同的包管理(apt,yum)和不同的檔案系統.並且你還可以使用set_fact這個模組做成更多的差異配置

你能夠使用when這個關鍵字去達到跳過本次模組執行的效果,when關鍵字後面跟著的是python的表示式,在表示式中你能夠使用任何的變數或者fact,當表示式的結果返回的是false,便會跳過本次的模組

下面一段配置就說明了如何在debian和redhat系統中選擇apt還是yum包管理,並且如果不是以上兩個系統,會用debug模組把系統打印出來

---
- name: Install VIM
  hosts: all
  tasks:
    - name: Install VIM via yum
      yum: name=vim-enhanced state=installed
      when: ansible_os_family == "RedHat"
    - name: Install VIM via apt
      apt: name=vim state=installed
      when: ansible_os_family == "Debian"
    - name: Unexpected OS family
      debug: msg="OS Family {{ ansible_os_family }} is not supported" fail=yes
      when: not ansible_os_family == "RedHat" or ansible_os_family == "Debian"

條件語句還有一種用法,它還可以讓你當達到一定的條件的時候暫停下來,等待你的輸入確認.一般情況下,當ansible遭遇到error時,它會直接結束執行.那其實你可以當遭遇到不是預期的情況的時候給使用pause模組,這樣可以讓使用者自己決定是否繼續執行任務

name: pause for unexpected conditions
pause: prompt="Unexpected OS"
when: ansible_os_family != "RedHat"

下面一些情景建議你使用條件語句做跳過動作

  • job裡面有不同作業系統的機器
  • 提示使用者,然後再執行操作請求
  • 提高效能,避免執行一個需要執行一段時間模組,而且你知道這個模組不會返回changed

task委託

預設ansible的所有task是在我們的配置的管理機器上面執行的,當在一個獨立的群集裡面配置,那是適用的.而有一些情況是,某些任務執行的狀態是需要傳遞給其他機器的,在同一個任務你需要在其他機器上執行,這時候你就許多要用task委託

使用delegate_to關鍵字便可以配置任務在其他機器上執行.其他模組還是在所有配置的管理機器上執行的,當到了這個關鍵字的任務就是使用委託的機器上執行.而facts還是適用於當前的host,下面我們演示一個例子,使用get_url模組去下載一個web叢集的配置

---
  - name: Fetch configuration from all webservers
    hosts: webservers
    tasks:
      - name: Get config
        get_url: dest=configs/{{ ansible_hostname }} force=yes url=http://{{ ansible_hostname }}/diagnostic/config
        delegate_to: localhost

如果需要委託loaclhost執行任務,這裡提供一個快捷的方式,只要使用local_action作為task的key便行.我們嘗試使用這種方式來配置上面的例子,會更加簡潔.

---    #1
 - name: Fetch configuration from all webservers     #2
     hosts: webservers     #3
     tasks:     #4
        - name: Get config
          local_action: get_url dest=configs/{{ ansible_hostname }}.cfg url=http://{{ ansible_hostname }}/diagnostic/config

委託不限於localhost,可以在你的inventory裡面的任何host.下列一些場景適用使用委託

  • 部署之前你希望從負載均衡裡面把host移除
  • 更改你的server時候更改dns的指向
  • 建立一個iSCSI卷儲存裝置
  • 使用一個外部伺服器去檢測一下服務

額外的變數

大家應該在之前的章節的例子裡面有看到group_names這個變數.這個是ansible提供的一個很神奇變數.直至寫本書的時候,有7個這樣的變數,我會在下面的章節介紹

a.hostvars變數

hostvars允許你在當前的任務中應用所有host的變數.當setup模組沒有執行的時候,只有這些變數將是可用.例如你配置 ${hostvars.hostname.fact}可以訪問其他複雜的變數.例如你可以配置${hostvars.ns1.ansible_ distribution}得到ns1這個server的linux髮型版本.

下面的例子設定了一個dns_master變數,這是ns1 server的ip.然後這個變數能夠在所有機器上呼叫

---
- name: Setup DNS Servers
   hosts: allnameservers
   tasks:
     - name: Install BIND
       yum: name=named state=installed
- name: Setup Slaves     #7
  hosts: slavenamesservers     #8
  tasks:     #9
    - name: Get the masters IP
      set_fact: dns_master="{{ hostvars.ns1.ansible_default_ipv4.address }}"
    - name: Configure BIND
      template: dest=/etc/named.conf src/templates/named.conf.j2

b.groups變數

groups變數是inventory裡面的group分組列表.這個是一個非常強大的工具,能夠讓你迭代你配置的所有的hosts.看下面的例子.

---
- name: Configure the database
  hosts: dbservers
  user: root
  tasks:
    - name: Install mysql
      yum: name={{ item }} state=installed
      with_items:
        - mysql-server
        - MySQL-python
    - name: Start mysql
      service: name=mysqld state=started enabled=true
    - name: Create a user for all app servers
      with_items: groups.appservers
      mysql_user: name=kate password=test host={{ hostvars[item].ansible_eth0.ipv4.address }} state=present

groups變數實際不是你的hosts變數的列表.它只是你hosts的name的列表.如果你需要呼叫host裡面的變數還需要配合hostvars使用

下面的例子配置建立known_hosts檔案

playbook配置

---
   hosts: all
   tasks:
   - name: Setup known hosts
     hosts: all
     tasks:
       - name: Create known_hosts
         template: src=templates/known_hosts.j2 dest=/etc/ssh/ssh_known_hosts owner=root group=root mode=0644

template模板

{% for host in groups['all'] %}
{{ hostvars[host]['ansible_hostname'] }}
{{ hostvars[host]['ansible_ssh_host_key_rsa_public'] }}
{% endfor %}

c.group_names變數

group_names是當前host所屬的組的列表.這可以用於在條件語句中呼叫成員的group關係,或者用於debugging.通常來說這變數大部分用於跳過一些task或者在模板中用於條件語句的變數.在下面的例子中,如果你有兩套sshd的配置檔案,一套用於安全性更加嚴謹的,一個安全性普通的.然後我們根據group名來配分host到哪個sshd配置下.

---
- name: Setup SSH
  hosts: sshservers
  tasks:
    - name: For secure machines
      set_fact: sshconfig=files/ssh/sshd_config_secure
      when: "'secure' in group_names"
    - name: For non-secure machines
      set_fact: sshconfig=files/ssh/sshd_config_default
      when: "'secure' not in group_names"
    - name: Copy over the config
      copy: src={{ sshconfig }} dest=/tmp/sshd_config

d.inventory_hostname變數

inventory_hostname是機器的hostname,當你沒有使用setup模組,或者由於各種原因導致setup的變數是錯誤的,你可以選擇使用這個變數.此變數可以幫助你初始化你的機器和改變hostname

e.inventory_hostname_short

inventory_hostname_short類似與上面的inventory_hostname變數,只是它是擷取第一個句點的前面的字元,例如hostname是host.example.com,就會只擷取到host

f.inventory_dir

此變數是inventory檔案的路徑,包括目錄與檔名

g.inventory_file

類似上面的變數,只是它只有檔名

使用變數來查詢檔案

所有的模組都可以把變數作為引數的一部分,通過使用”{{}}”符號擴起來.譬如變數test就是”{{ test }}”.這樣你就可以通過變數載入特定的檔案.例如,你希望根據不同的機器architecture選擇不同的NRPE(nagios的客戶端)配置檔案,那可以像這樣的配置

---
- name: Configure NRPE for the right architecture
 hosts: ansibletest
 user: root
 tasks:
   - name: Copy in the correct NRPE config file
     copy: src=files/nrpe.{{ ansible_architecture }}.conf dest=/etc/nagios/nrpe.cfg

在copy和tempalate模組裡面,你能夠使用ansible去查詢一組的檔案.然後預設使用第一個檔案.這能夠讓你達到效果是,當第一個檔案不存在時,會查詢第二個檔案,如此類推知道最後一個檔案還不存在就報fail.使用first_available_file這個關鍵字便可以到上述效果.

---
- name: Install an Apache config file
  hosts: ansibletest
  user: root
  tasks:
   - name: Get the best match for the machine
     copy: dest=/etc/apache.conf src={{ item }}
     first_available_file:
      - files/apache/{{ ansible_os_family }}-{{ ansible_architecture }}.cfg
      - files/apache/default-{{ ansible_architecture }}.cfg
      - files/apache/default.cfg

環境變數

unix命令經常需要依賴環境變數,例如C makefiles,installers,和aws cli工具.很幸運,ansible很容易實現,譬如你現在需要控制遠端的機器一個檔案到s3,那你許多要配置aws的access key.下面我們的例子演示,安裝pip,用pip安裝aws cli,並且通過cli上傳檔案到s3

---
 - name: Upload a remote file via S3
  hosts: ansibletest
  user: root
  tasks:
 - name: Setup EPEL
     command: rpm -ivh http://download.fedoraproject.org/pub/epel/6/i386/epel-release-6-8.noarch.rpm creates=/etc/yum.repos.d/epel.repo
 - name: Install pip
     yum: name=python-pip state=installed
 - name: Install the AWS tools
     pip: name=awscli state=present
 - name: Upload the file
     shell: aws s3 put-object --bucket=my-test-bucket --key={{ ansible_hostname }}/fstab --body=/etc/fstab --region=eu-west-1
  environment:
    AWS_ACCESS_KEY_ID: XXXXXXXXXXXXXXXXXXX
    AWS_SECRET_ACCESS_KEY: XXXXXXXXXXXXXXXXXXXXX

一些模組例如get_url,yum,和apt是需要使用環境變數配置proxy的.下面一些場景也是需要配置環境變數的

  • 執行application installers
  • 當執行shell的時候需要新增一些額外的的變數在path裡
  • 需要load的一些庫不在系統的library路徑中
  • 在執行模組時使用LD_PRELOAD hack

External data lookups

ansible在0.9版本開始引進了lookup外掛,這些外掛執行ansible在外圍獲取資料.ansible已經提供了幾個外掛,但它還是支援自己編寫外掛.這真的讓你使用ansible配置更加伸縮自如

lookup是在master機器執行的python程式.下面一個例子是使用lookup外掛獲取環境變數裡面的http_proxy,然後配置在遠端機器,確保遠端機器使用相同的proxy下載檔案

---     #1
- name: Downloads a file using the same proxy as the controlling machine
  hosts: all
  tasks:
    - name: Download file
      get_url: dest=/var/tmp/file.tar.gz url=http://server/file.tar.gz
      environment:
       http_proxy: "{{ lookup('env', 'http_proxy') }}"

使用with_*能夠使用lookup外掛迭代出特別的東西.您可以使用任何這樣的外掛,但最好是返回一個列表.下面的例子讓你自動註冊webapp,使用下面的例子會創建出虛擬機器並配置它

---
 - name: Registers the app server farm
  hosts: localhost
  connection: local
  vars:
    hostcount: 5
  tasks:
    - name: Register the webapp farm
      local_action: add_host name={{ item }} groupname=webapp
      with_sequence: start=1 end={{ hostcount }} format=webapp%02x

在下面的場景,lookup非常有用

  • 複製整個目錄的apache配置到conf.d
  • 使用環境變數調整playbook的執行
  • 從DNS TXT記錄中獲取配置
  • 獲取一個命令的輸出到一個變數中

儲存結果

幾乎所有的模組都是會outputs一些東西,甚至debug模組也會.大多數我們會使用的結果變數是changed.這個changed變數決定了是否要直接handlers和輸出的顏色是什麼.然而,結果變數還有其他的用途,譬如我需要儲存我的結果變數,然後咋我的playbook的其他地方給使用.在下面的例子我們建立了一個/tmp目錄,然後在後面我們建立一個/tmp/subtmp使用和前面目錄一樣的許可權

---
- name: Using register
  hosts: ansibletest
  user: root
  tasks:
   - name: Get /tmp info
     file: dest=/tmp state=directory
     register: tmp
   - name: Set mode on /var/tmp
     file: dest=/tmp/subtmp mode={{ tmp.mode }} state=directory

一些模組,例如上面的file模組,是能夠獲取到一些簡單的資訊.結合register這個功能,可以讓你在playbook裡面檢查你的環境和計算如何進行

register對於數多場景是很有用的

  • 在一臺遠端的伺服器獲取一個目錄下的一列表的檔案,然後下載這些檔案
  • 在handler執行之前,發現前面一個task發生了changed,然後執行一個指定的task
  • 獲取遠端伺服器的ssh key的內容,構建出known_hosts檔案

debugging playbook

有好幾種方法去debug我們的playbook.ansible有verbose模式和debug模式,也可以使用例如fetch和get_url模組來協助debug.當你想學習怎樣使用一些模組時,這些debugging技術能夠幫助你.

a.debug模組

debug模組使用很簡單.它具有兩個引數,msg和fail.msg就是打印出來的資訊,而當fail引數設定為yes時,會發送失敗通知給ansible,然後ansible會停止執行任務.

在下面的例子,配置了使用debug模組去顯示遠端機器所有的network interface.

---
- name: Demonstrate the debug module
  hosts: ansibletest
  user: root
  vars:
    hostcount: 5
  tasks:
    - name: Print interface
      debug: msg="{{ item }}"
      with_items: ansible_interfaces

執行上面的配置會出現這樣的輸出

PLAY [Demonstrate the debug module] *********************************
GATHERING FACTS *****************************************************
ok: [ansibletest]
TASK: [Print IP address] ********************************************
ok: [ansibletest] => (item=lo) => {"item": "lo", "msg": "lo"}
ok: [ansibletest] => (item=eth0) => {"item": "eth0", "msg": "eth0"}
PLAY RECAP **********************************************************
ansibletest                : ok=2    changed=0    unreachable=0 failed=0

如你說見,debug模組可以讓你很容易看到在playbook執行期間一些變數

b.verbose模式

另外的debug選擇是verbose模式.當執行verbose模式時,會打印出所有模組執行後的變數.這對於你要使用register功能時候很重要.只需要在執行playbook命令時加上引數–verbose便可以.ansible-playbook –verbose playbook.yml

c.check模式

除了verbose模式外,ansible還提供了check模式和diff模式.只需要執行playbook時新增引數–check和–diff.check模式執行時,ansible不會真正控制遠端機器發生變更.這能夠讓你獲得這次playbook任務中,將會發生changed事件的列表.

很重要的一點是check模式不是完美的.有一些模組是會跳過check模式的.尤其明顯的限制是在執行command和shell模組

在diff模式下,當檔案發現更變,會打印出變更檔案的變更部分.配合check模式使用效果更好

d.pause模組

另外一個debug技巧是使用pause模組,它可以讓你需要在某個地方需要檢查遠端機器的配置的時候暫停playbook的執行.這樣可以讓先觀察一下執行到這裡為止的效果,再判斷是否繼續執行下去.

總結

在這個章節我們更加深入探索了編寫playbook的一些細節.現在你應該可以使用一些ansible的特性.例如delegation,looping,conditionals,和fact,registration等等,讓你能夠更容易的編寫和維護你的playbook.我們也看到了如何獲取其他host的資訊,如何配置環境變數,如何從外圍獲取到資料.最後我們展示了一些debug技巧,讓playbook能按你的預期來執行.

下一章節,我們會學習如何在大規模環境中使用ansible,也會講到一些方法讓你在一些需要執行很久的任務中提高你的效能.我們也會介紹一些特性讓你的playbook如何更加可維護,更加解藕,讓它們按目的分配到不同的地方.

轉自:http://www.361way.com/playbook-advanced/4443.html