簡介
當管理集群達到一定規模時,ansible達到性能瓶頸是難以避免的,此時我們可以通過一定手段提高ansible的執行效率和性能。
筆者雖未管理過超大規模服務器,但也通過查找資料和咨詢大神了解了一些。現總結一些調優方法,供大家參考。
Pipelinling
我們知道ansible執行一個模塊要ssh到目的主機多次,開啟「pipelining」特性實際上是通過減少ssh連接次數,從而縮短ansible執行時間。在部署大規模服務器或引用模塊非常多時,開啟「pipelining」特性會給ansible帶來顯著的性能提升。
開啟方法也很簡單,將ansible.cfg的pipelining參數設置為True即可,該參數默認值是False。
既然「pipelining」特性默認是關閉的,肯定有它的理由:關閉該特性可以與sudo的requiretty兼容(即/etc/sudoers配置文件的「Defaults requiretty」配置項)。大部分linux操作系統是默認開啟requiretty功能的,所以pipelining也是默認False的。
也就是說,如果我們要開啟pipelining特性,要么playbook不使用sudo越權功能,要么取消sudo的「requiretty」特性。
該特性可以通過命令行添加 -vvvv 后,根據執行結果對比出區別,因篇幅原因這里不再展示。
適用場景
- 管理大規模集群
- 部署代碼內容很多,調用了大量的ansible模塊
Control_path
control_path通過設置ControlPath sockets的文件路徑與文件命名避免因sockets文件過長(超過108個字符串)導致ansible報錯的問題。
設置方法為更改ansible.cfg里的control_path參數,ansible2.7版本默認值為「配置項control_path_dir的值」+「根據hostname生成的哈希值」+「ssh端口號」+「用戶名」
在ansible舊版本中,默認值是包含主機名的,這在一些特殊情況下(例如EC2主機),會因主機名過長導致ControlPath sockets文件過長,從而導致ansible執行報錯。但在新版本中默認值的主機名部分被替換為主機名的哈希值,這很大程度上避免了該問題的發生。
我們也可以設置其他的參數,例如:
control_path = %(directory)s/%%h-%%r
其中$directiry是control_path_dir的值,后面的參數可以靈活定制,可用參數如下:
%L 本地主機名的第一個組件 %l 本地主機名(包括域名) %h 遠程主機名(命令行輸入) %n 遠程原始主機名 %p 遠程主機端口 %r 遠程登錄用戶名 %u 本地 ssh 正在使用的用戶名 %i 本地 ssh 正在使用 uid %C 值為 %l%h%p%r 的 hash
適用場景
當ansible報錯並且使用 -vvvv 查看發現有類似「too long for Unix domain socket」的錯誤信息,我們應該想到這個調優方式。
Gather subset
Disable gather facts
在介紹Gather subset之前,我們先簡單說下gather_facts功能,gather_facts用於控制一個play是否收集目的主機的facts信息(參考《ansible基礎-變量》),默認值為true/True/yes,寫法如下:
- hosts: nodes gather_facts: True tasks:
在playbook執行過程中,ansible收集facts變量是很耗時的一個步驟,如果我們確定play中沒有用到fact變量信息,可以直接將其關閉,即將gather_facts設置為false/False/no。
Gather subset
但是在實際使用中不收集facts變量的情況很少。在gather_facts關閉的情況下,我們可以給play單獨添加一個setup模塊,並通過gather_subset參數嚴格控制facts的收集種類,這樣既拿到了我們需要的fact變量又提高了ansible的執行效率,gather_subset參數的默認值為all。
playbook中使用方法示例:
- name: Collect only facts returned by facter setup: gather_subset: - '!all'
- '!any'
- facter
命令行使用方法示例:
# Collect only facts returned by facter. ansible all -m setup -a 'gather_subset=!all,!any,facter'
可用參數有all, min, hardware, network, virtual, ohai, facter,可以使用列表的格式指定多個參數,使用「!」指定不收集的facts類型。
比較常用的幾個范例:
- 僅指定facter,說明收集puppet facter和min子集信息(默認包含min子集)。
- 指定「!all」,說明只收集min子集信息
- 指定「!all,!min」,說明不收集任何信息
- 指定「!all,!any」+其他的fact子集,說明只收集該子集信息
Fact gather cache
關於facts變量還有一個優化手段,即facts緩存。
fact緩存是指將收集到的facts信息緩存到本地json文件或者redis數據庫內,以便下次執行直接讀取,從而提高執行效率。
關於facts緩存,我們在《ansible基礎-變量》6.1.2 facts緩存有詳細介紹,在這里就不再重復介紹了。
Strategy
strategy的作用范圍是一個play,通過設置不同參數,控制一個play內所有任務的執行策略。
設置方法為更改ansible.cfg里的strategy參數,默認值為linear,可選參數為free;另外一種方式是在playbook內定義該策略,格式為:
- hosts: all strategy: free tasks: ...
參數含義:
- linear策略即線性執行策略,線性執行策略指主機組內所有主機完成一個任務后才繼續下一個任務的執行,在執行一個任務時,如果某個主機先執行完則會等待其他主機執行結束。說直白點就是第一個任務在指定的主機都執行完,再進行第二個任務的執行,第二個任務在指定的主機都執行完后,再進行第三個任務的執行…… 以此類推。
- free策略即自由策略,即在一個play執行完之前,每個主機都各顧各的盡可能快的完成play里的所有任務,而不會因為其他主機沒執行完任務而等待,不受線性執行策略那樣的約束。所以這種策略的執行結果給人感覺是無序的甚至是雜亂無章的,而且每次執行結果的task顯示順序很可能不一樣。
舉個🌰,展示下兩種策略的執行效果:
playbook要實現的是三台主機debug出test_1,test_2,test_3三個字符串。當使用linear策略時,執行效果如下:

➜ lab-ansible ansible-playbook playbooks/test_trategy.yaml PLAY [nodes] *********************************************************** TASK [Gathering Facts] *********************************************************** ok: [node3] ok: [node2] ok: [node1] TASK [debug] *********************************************************** ok: [node1] => { "msg": "test_1" } ok: [node3] => { "msg": "test_1" } ok: [node2] => { "msg": "test_1" } TASK [debug] *********************************************************** ok: [node1] => { "msg": "test_2" } ok: [node3] => { "msg": "test_2" } ok: [node2] => { "msg": "test_2" } TASK [debug] *********************************************************** ok: [node1] => { "msg": "test_3" } ok: [node3] => { "msg": "test_3" } ok: [node2] => { "msg": "test_3" } PLAY RECAP *********************************************************** node1 : ok=4 changed=0 unreachable=0 failed=0 node2 : ok=4 changed=0 unreachable=0 failed=0 node3 : ok=4 changed=0 unreachable=0 failed=0
當使用free策略時,執行效果如下:

PLAY [nodes] *********************************************************** TASK [Gathering Facts] *********************************************************** ok: [node3] ok: [node2] TASK [debug] *********************************************************** ok: [node3] => { "msg": "test_1" } ok: [node2] => { "msg": "test_1" } TASK [Gathering Facts] *********************************************************** ok: [node1] TASK [debug] *********************************************************** ok: [node3] => { "msg": "test_2" } ok: [node2] => { "msg": "test_2" } TASK [debug] *********************************************************** ok: [node1] => { "msg": "test_1" } TASK [debug] *********************************************************** ok: [node3] => { "msg": "test_3" } ok: [node2] => { "msg": "test_3" } TASK [debug] *********************************************************** ok: [node1] => { "msg": "test_2" } TASK [debug] *********************************************************** ok: [node1] => { "msg": "test_3" } PLAY RECAP *********************************************************** node1 : ok=4 changed=0 unreachable=0 failed=0 node2 : ok=4 changed=0 unreachable=0 failed=0 node3 : ok=4 changed=0 unreachable=0 failed=0
從上面兩個個執行結果很明顯的能看出區別,linear策略是遵循第一個任務、第二個任務、第三個任務……這樣順序執行下去的,而free策略則是無序的,甚至Gathering Facts任務也可能在debug任務之后執行。
Forks
forks用來設置同一時刻與目的主機連接數,也可以理解為主機並行數,默認值比較保守為5。在生產中,多數情況下我們會更改這個參數。如果控制節點的CPU和網絡性能夠用,設置幾十上百個也是可以的。
在ansible.cfg設置forks的全局默認值:
# ansible.cfg [defaults] forks = 15
命令行設置forks的數量,即在執行playbook時,通過「--forks」或「-f」指定:
lab-ansible ansible-playbook playbooks/test_forks.yaml --fork 10
Serial
serial用於控制一個play內的主機並行數,這個並行數不能超過forks,超過后則serial不會生效。
定義方法如下:
--
- hosts: nodes serial: 2 tasks:
本質上,serial作用范圍是一個play,受限於forks,但比forks控制的更加細節。假如我們的forks設置為100,但是想讓某個play里的所有任務並行數為50的執行,此時我們應該想到serial這個調優方法。
Async and poll
同步阻塞模式和異步模式
- 同步阻塞模式指在playbook執行時,控制端和被控制端會一直保持連接狀態,逐個任務的執行,直到該playbook執行完畢,這種模式稱為同步阻塞模式,也是absible的默認執行模式。
- 異步模式指ansible將一次性運行所有的任務,並將所有的任務丟到后台執行,每個任務有一個job_id,ansible會根據這個job_id每隔一段時間輪訓檢測該任務的執行情況,直到檢測到任務執行結束。這種模式稱為異步模式。
Async and pool
前面章節我們所說的Strategy、Forks、Serial都是ansible同步阻塞模式下的優化方法,其中,strategy是通過控制任務執行策略進行優化,forks和serial是通過控制並行數進行優化。
針對某些特殊任務,尤其是可能被鎖住或超時的任務,我們可以采用ansible異步模式來提高執行效率。
async和poll分別用來指定異步模式下任務的最大運行時間和檢測間隔時間,poll的缺省值為10。
示例如下:
---
- hosts: all remote_user: root tasks: - name: simulate long running op (15 sec), wait for up to 45 sec, poll every 5 sec command: /bin/sleep 15 async: 45 poll: 5
該示例中sleep命令采用異步的方式執行,ansible會等待該任務最長45秒,每隔5秒鍾檢測一次任務的執行結果。
特殊情況下,我們可以將poll的值設置為0,這代表ansible將任務放到后台后,不會再管這個任務的執行狀態,任其自生自滅。
這里舉一個利用異步重啟服務器的例子,因篇幅原因,僅給大家展示部署代碼,就不貼執行結果了,如果您感興趣,可以親自實踐下:

--- - hosts: node1 gather_facts: no tasks: - shell: cmd: grub2-set-default 0 notify: - reboot - wait for reboot - wait for ssh start handlers: - name: reboot shell: cmd: shutdown -r now "Reboot triggered by ansible" async: 1 poll: 0 ignore_errors: True - name: wait for reboot wait_for_connection: timeout: 300 - name: wait for ssh start wait_for: host: node1 state: started delay: 10 port: 22 timeout: 30
參考鏈接
- https://docs.ansible.com/ansible/2.4/playbooks_acceleration.html
- https://docs.ansible.com/ansible/2.4/intro_configuration.html#pipelining
- https://docs.ansible.com/ansible/2.4/intro_configuration.html#control-path
- https://docs.ansible.com/ansible/latest/modules/setup_module.html
- https://docs.ansible.com/ansible/2.5/user_guide/playbooks_strategies.html
- https://www.ansible.com/blog/ansible-performance-tuning
- https://www.cnblogs.com/f-ck-need-u/p/7580170.html
- https://docs.ansible.com/ansible/2.5/user_guide/playbooks_async.html
歡迎大家關注我的公眾號: