如何優(yōu)化 Ansible Playbook 執(zhí)行速度

寫(xiě)在前面
今天和小伙伴們分享一些 Ansible中 Playbook 執(zhí)行速度優(yōu)化的筆記
博文通過(guò)7種不同的優(yōu)化方式,合理利用可配置項(xiàng),從而提高 Playbook 的執(zhí)行速度
個(gè)人感覺(jué)如果受控機(jī)數(shù)量很少,其實(shí)沒(méi)必要速度調(diào)優(yōu)
所謂的執(zhí)行速度調(diào)優(yōu)大多是犧牲一定的功能,或則增加對(duì)資源的占用
如果搭建Pass環(huán)境機(jī)器初始化或者大型的分布式系統(tǒng)集群運(yùn)維,涉及機(jī)器多,優(yōu)化還是很有必要的
食用方式
了解Ansible基礎(chǔ)知識(shí)
了解Ansible劇本編寫(xiě)
理解不足小伙伴幫忙指正
「如果我不曾見(jiàn)到太陽(yáng),我本可以忍受黑暗?!桌颉さ医鹕?br>
優(yōu)化 Playbook 執(zhí)行
主要通過(guò)以下方式來(lái)優(yōu)化

優(yōu)化基礎(chǔ)架構(gòu)
禁用facts收集
增加任務(wù)并行
程序包管理器模塊不使用循環(huán)
高效拷貝文件
使用模板代替多l(xiāng)ineinfile操作
優(yōu)化SSH連接
啟用pipelining
下面我們一起來(lái)看一下如何優(yōu)化

優(yōu)化基礎(chǔ)架構(gòu)
運(yùn)行最新版本的 Ansible 可幫助提高使用 Ansible 核心模塊的 Playbook 的性能。同時(shí)盡可能讓控制節(jié)點(diǎn)靠近受管節(jié)點(diǎn)。Ansible嚴(yán)重依賴網(wǎng)絡(luò)通信和數(shù)據(jù)傳輸。

禁用facts收集
通過(guò)將gater_facts指令設(shè)置為Fasle來(lái)跳過(guò)收集,這樣做的前提是劇本不依賴采集主機(jī)信息生成的變量信息,如涉及到裝包或者其他不使用收集的系統(tǒng)變量,魔法變量的劇本,那么跳過(guò)收集可以節(jié)省很多時(shí)間,尤其是受控機(jī)的量級(jí)達(dá)到一定程度。

實(shí)際看一下,如果劇本中沒(méi)有顯示設(shè)置不采集主機(jī)信息,并且沒(méi)有在配置中顯示配置策略,那么劇本默認(rèn)收集主機(jī)信息

---
- name: do not become
  hosts: all
  tasks:
    - name: sleep 2
      shell: sleep 2
上面的劇本默認(rèn)收集主機(jī)信息,執(zhí)行中我們可以找日志里看到TASK [Gathering Facts]

$time ansible-playbook  fact.yaml
PLAY [do not become] ***********************************************************************************************
TASK [Gathering Facts] *********************************************************************************************
ok: [servera]
ok: [serverb]
ok: [serverc]
.............
real    0m10.204s
user    0m1.874s
sys     0m1.610s
劇本中設(shè)置
可以看到執(zhí)行時(shí)間耗時(shí)10.204s,在劇本中配置gather_facts:False禁用觀察一下

---
- name: do not become
  hosts: all
  gather_facts: false
  tasks:
    - name: sleep 2
      shell: sleep 2
可以發(fā)現(xiàn)執(zhí)行耗時(shí)6.928s執(zhí)行速度縮短了4秒

$vim +3 fact.yaml
$time ansible-playbook  fact.yaml

PLAY [do not become] ***********************************************************************************************
TASK [sleep 2] *****************************************************************************************************
changed: [serverd]
changed: [serverc]
changed: [serverb]
.......................
real    0m6.928s
user    0m1.329s
sys     0m0.581s
配置文件中設(shè)置
當(dāng)然,主機(jī)收集禁用作為變量,也可以在配置文件中去賦值,這里賦值是全局的。我們可以在Ansible默認(rèn)配置文件/etc/ansible/ansible.cfg中看到

$cat /etc/ansible/ansible.cfg | grep -i  gather
# plays will gather facts by default, which contain information about
# smart - gather by default, but don't regather if already gathered
# implicit - gather by default, turn off with gather_facts: False
# explicit - do not gather by default, must say gather_facts: True
#gathering = implicit
......
通過(guò) gathering=explicit 配置禁用全局的主機(jī)收集

$cat ansible.cfg
[defaults]
inventory=inventory
remote_user=devops
roles_path=roles
gathering=explicit

[privilege_escalation]
become=True
become_method=sudo
become_user=root
become_ask_pass=False

刪除劇本的禁用配置,時(shí)間和剛才差不多

$sed '4d' fact.yaml -i
$time ansible-playbook  fact.yaml
PLAY [do not become] ***********************************************************************************************
TASK [sleep 2] *****************************************************************************************************
changed: [servere]
changed: [serverd]
.......
real    0m7.323s
user    0m0.939s
sys     0m1.124s
$
增加并行
所謂增加并行,即一次要把命令分發(fā)給幾個(gè)受管機(jī)執(zhí)行,這個(gè)配置由參數(shù)forks控制, 說(shuō)的準(zhǔn)確的些,即Ansible可以有多少個(gè)連接同時(shí)處于活動(dòng)狀態(tài)。在默認(rèn)情況下,它設(shè)置為 5。

$ansible-config  dump | grep -i fork
DEFAULT_FORKS(default) = 5
可以在 Ansible 配置文件中指定,或者通過(guò) -f 選項(xiàng)傳遞給ansible-playbook命令:

配置文件中設(shè)置
$cat ansible.cfg
[defautts]
inventory=inventory
remote_user=devops
forks=10
命令行中的設(shè)置
ansible-playbook fact.yaml -f 10

$time ansible-playbook  fact.yaml  -f 10
PLAY [do not become] ***********************************************************************************************
TASK [sleep 2] *****************************************************************************************************
changed: [serverf]
changed: [servere]
changed: [serverd]
changed: [serverb]
.....
real    0m4.163s
user    0m1.013s
sys     0m0.471s
$
可以發(fā)現(xiàn),在禁用主機(jī)收集gather_facts=False的基礎(chǔ)上,設(shè)置多并行處理forks=10,時(shí)間由原來(lái)的10秒到現(xiàn)在的4秒。

使用軟件包管理器模塊避免循環(huán):
某些模塊接受要處理的項(xiàng)的列表,不要使用循環(huán)。此時(shí)模塊將調(diào)用一次而不是多次。比如使用yum模塊來(lái)裝包

- name: Install the packages on the web servers
  hosts: all

  tasks:
    - name: Ensure the packages are installed
      yum:
        name:
          - httpd
          - mod_ssl
          - httpd-tools
          - mariadb-server
          - mariadb
          - php
          - php-mysqlnd
        state: absent
等效于

 yum -y install httpd mod_ssl httpd-tools mariadb-server mariadb php php-mysqlnd
使用循環(huán)的方式,可以發(fā)現(xiàn)使用的循環(huán)的方式是通過(guò)多個(gè)子bash的方式來(lái)執(zhí)行,所以每次執(zhí)行都要重新申請(qǐng)資源為一個(gè)bash進(jìn)程來(lái)處理,而上面的方式始終只有個(gè)一個(gè)bash進(jìn)程






- name: Install the packages on the web servers
  hosts: all

  tasks:
    - name: Ensure the packages are installed
      yum:
        name: "{{ item }}"
        state: present
      loop:
          - httpd
          - mod_ssl
          - httpd-tools
          - mariadb-server
          - mariadb
          - php
          - php-mysqlnd
等效于

$yum install httpd
$yum install mod_sst
$yum install httpd-tools
$yum install mariadb-server
$yum install mariadb
$yum install php
$yum install php-mysqlnd
注意:并非所有模塊都接受 name 參數(shù)的列表,

高效復(fù)制文件到受管主機(jī):
在將大量文件復(fù)制到受管主機(jī)時(shí),使用 synchronize 模塊更為高效,這是因?yàn)?synchronize 模塊使用可rsync來(lái)同步文件,類似VDO卷一樣,會(huì)通過(guò)哈希值比較文件,如果文件存在,則不復(fù)制,速度非??欤源蠖鄶?shù)情況下此模塊后臺(tái)使用 rsync 速度比copy 模塊快,copy模塊本質(zhì)上是scp,所以他不會(huì)對(duì)文件是否存在進(jìn)行校驗(yàn)。

申請(qǐng)一個(gè)1G的文件測(cè)試下

$dd if=/dev/zero of=bigfile1 bs=1M count=1024
1024+0 records in
1024+0 records out
1073741824 bytes (1.1 GB, 1.0 GiB) copied, 0.431348 s, 2.5 GB/s
文件不存在的情況,通過(guò)synchronize復(fù)制文件

---
- name: Deploy the w eb content on the web servers
  hosts: all
  become: True
  gather_facts: False
  tasks:
    - name: copy demo
      synchronize:
        src: bigfile1
        dest: /tmp/
執(zhí)行耗時(shí)為26.146s

$time ansible-playbook  copy_task.yaml
PLAY [Deploy the w eb content on the web servers] ******************************************************************
TASK [copy demo] ***************************************************************************************************
changed: [servere]
changed: [serverf]
changed: [serverb]
changed: [servera]
changed: [serverd]
changed: [serverc]
PLAY RECAP *********************************************************************************************************
servera                    : ok=1    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0
serverb                    : ok=1    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0
serverc                    : ok=1    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0
serverd                    : ok=1    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0
servere                    : ok=1    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0
serverf                    : ok=1    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0

real    0m26.146s
user    0m50.033s
sys     0m2.382s
現(xiàn)在我么使用 copy 模塊來(lái)試下

---
- name: Deploy the w eb content on the web servers
  hosts: all
  become: True
  gather_facts: False
  tasks:
    - name: copy demo
      #synchronize:
      copy:
        src: bigfile1
        dest: /tmp/
$time ansible-playbook  copy_task.yaml
PLAY [Deploy the w eb content on the web servers] ******************************************************************
TASK [copy demo] ***************************************************************************************************
ok: [serverc]
ok: [serverd]
ok: [serverf]
ok: [servera]
ok: [serverb]
ok: [servere]

PLAY RECAP *********************************************************************************************************
servera                    : ok=1    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0
serverb                    : ok=1    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0
serverc                    : ok=1    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0
serverd                    : ok=1    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0
servere                    : ok=1    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0
serverf                    : ok=1    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0

real    0m14.868s
user    0m12.273s
sys     0m5.125s
copy 模塊耗時(shí)14.868s,因?yàn)樗挥脤?duì)文件進(jìn)行校驗(yàn)所以要少于synchronize模塊,我們?cè)诖问褂胹ynchronize

$cat copy_task.yaml
---
- name: Deploy the w eb content on the web servers
  hosts: all
  become: True
  gather_facts: False
  tasks:
    - name: copy demo
      synchronize:
      #copy:
        src: bigfile1
        dest: /tmp/
$time ansible-playbook  copy_task.yaml
PLAY [Deploy the w eb content on the web servers] ******************************************************************
TASK [copy demo] ***************************************************************************************************
ok: [servere]
ok: [serverb]
ok: [serverd]
ok: [servera]
ok: [serverf]
ok: [serverc]

PLAY RECAP *********************************************************************************************************
servera                    : ok=1    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0
serverb                    : ok=1    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0
serverc                    : ok=1    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0
serverd                    : ok=1    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0
servere                    : ok=1    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0
serverf                    : ok=1    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0

real    0m2.022s
user    0m1.757s
sys     0m0.568s
發(fā)現(xiàn)只使用了2秒,所以要分情況使用,如果是確定是新文件,那么使用copy模塊,如果不確定,使用synchronize模塊

使用模板:
lineinfile 模塊在文件中插入或刪除行,與循環(huán)搭配時(shí)不是很高效:請(qǐng)改用template模塊,這不多講,lineinfile 模塊用于少量的配置文件修改,比如關(guān)閉交換分區(qū),SELinux等。如果是Nginx等配置文件,使用模板文件會(huì)更高效

優(yōu)化 SSH 連接:
Ansible 建立 SSH 連接是一個(gè)速度較慢的過(guò)程,為緩解這類問(wèn)題,Ansible 依賴于SSH提供的兩個(gè)功能:

ControlMaster:允許多個(gè)同時(shí)與遠(yuǎn)程主機(jī)連接的 SSH 會(huì)話使用單一網(wǎng)絡(luò)連接。第一個(gè) SSH 會(huì)話建立連接,與同一主機(jī)連接的其他會(huì)話則重復(fù)利用該連接,從而繞過(guò)較慢的初始過(guò)程。SSH 在最后一個(gè)會(huì)話關(guān)閉后,立即銷毀共享的連接。
ControlPersist:使連接在后臺(tái)保持打開(kāi),而不是在上?次會(huì)話后銷毀連接。此指令允許稍后的 SSH 會(huì)話重用該連接。ControlPersist 指示 SSH 應(yīng)使空閑連接保持打開(kāi)的時(shí)間長(zhǎng)度,每個(gè)新會(huì)話將重置此空閑計(jì)時(shí)器。
所以這里和數(shù)據(jù)庫(kù)連接池有些類似,減少頻繁的資源申請(qǐng)關(guān)閉,Ansible 通過(guò)Ansible配置?件的[ssh_connection]部分下的ssh_args指令啟用ControlMaster 和ControlPersist功能,并且默認(rèn)是開(kāi)啟狀態(tài)的。

ssh_args 的默認(rèn)值:

$ansible-config  dump | grep -i master
ANSIBLE_SSH_ARGS(default) = -C -o ControlMaster=auto -o ControlPersist=60s
顯示設(shè)置

[ssh_connection]
ssh_args=-o ControlMaster=auto -o ControlPersist=60s
如果forks 值或 ControlPersist設(shè)置比較大,控制節(jié)點(diǎn)可能會(huì)使用更多的并發(fā)連接。確保控制節(jié)點(diǎn)配置有足夠的文件句柄,可用于支持許多活動(dòng)的網(wǎng)絡(luò)連接。






啟用 Pipelining:
為了在遠(yuǎn)程節(jié)點(diǎn)上運(yùn)行任務(wù),Ansible 會(huì)執(zhí)行多個(gè) SSH 操作,將模塊及其所有數(shù)據(jù)復(fù)制到遠(yuǎn)程節(jié)點(diǎn)并執(zhí)行該模塊。若要提高 playbook 的性能,可以激活Pipelining功能,Ansible 將建立較少的 SSH 連接。若要啟用 Pipelining ,將 Ansible 配置文件中的[ssh_connection] 部分:

[ssh_connection]
pipelining =True
此功能默認(rèn)不啟用,因?yàn)樾枰檬芄苤鳈C(jī)中的 requiretty sudo 選項(xiàng)。requiretty表示即使沒(méi)有交互式shell /會(huì)話也可以使用sudo。

禁用需要找受管機(jī)做如下配置

[root@servera student]# cat /etc/sudoers | grep -C 4 visiblepw

#
# Refuse to run if unable to disable echo on the tty.
#
Defaults   !visiblepw
Defaults   !requiretty
#
# Preserving HOME has security implications since many programs
# use it when searching for configuration files. Note that HOME
[root@servera student]#
實(shí)戰(zhàn)
來(lái)看一個(gè)demo,下面是一個(gè)編寫(xiě)好的劇本,我們進(jìn)行優(yōu)化后,利用callback插件來(lái)對(duì)比執(zhí)行時(shí)間,查看調(diào)優(yōu)效果。

$cat deploy_webservers.yml
---
- name: Deploy the web servers
  hosts: web_servers
  become: True

  tasks:
    - name: Ensure required packages are installed
      yum:
        name: "{{ item }}"
        state: present
      loop:
        - httpd
        - mod_ssl
        - httpd-tools
        - mariadb-server
        - mariadb
        - php
        - php-mysqlnd

    - name: Ensure the services are enabled
      service:
        name: "{{ item }}"
        state: started
        enabled: True
      loop:
        - httpd
        - mariadb

    - name: Ensure the web content is installed
      copy:
        src: web_content/
        dest: /var/www/html
添加插件

[defaults]
inventory=inventory.yml
remote_user=devops
callback_whitelist=timer,profile_tasks
執(zhí)行劇本,可以看到耗時(shí)57s

$ansible-playbook  deploy_webservers.yml

PLAY [Deploy the web servers] ****************************************************************************

TASK [Gathering Facts] ***********************************************************************************
Tuesday 16 August 2022  00:08:41 +0800 (0:00:00.033)       0:00:00.033 ********
ok: [serverb.lab.example.com]
ok: [serverc.lab.example.com]

TASK [Ensure required packages are installed] ************************************************************
Tuesday 16 August 2022  00:08:42 +0800 (0:00:01.165)       0:00:01.198 ********
changed: [serverc.lab.example.com] => (item=httpd)
changed: [serverb.lab.example.com] => (item=httpd)
changed: [serverc.lab.example.com] => (item=mod_ssl)
changed: [serverb.lab.example.com] => (item=mod_ssl)
ok: [serverc.lab.example.com] => (item=httpd-tools)
ok: [serverb.lab.example.com] => (item=httpd-tools)
changed: [serverc.lab.example.com] => (item=mariadb-server)
changed: [serverb.lab.example.com] => (item=mariadb-server)
ok: [serverc.lab.example.com] => (item=mariadb)
ok: [serverb.lab.example.com] => (item=mariadb)
changed: [serverc.lab.example.com] => (item=php)
changed: [serverb.lab.example.com] => (item=php)
changed: [serverc.lab.example.com] => (item=php-mysqlnd)
changed: [serverb.lab.example.com] => (item=php-mysqlnd)

TASK [Ensure the services are enabled] *******************************************************************
Tuesday 16 August 2022  00:09:00 +0800 (0:00:18.639)       0:00:19.838 ********
changed: [serverc.lab.example.com] => (item=httpd)
changed: [serverb.lab.example.com] => (item=httpd)
changed: [serverc.lab.example.com] => (item=mariadb)
changed: [serverb.lab.example.com] => (item=mariadb)

TASK [Ensure the web content is installed] ***************************************************************
Tuesday 16 August 2022  00:09:03 +0800 (0:00:02.720)       0:00:22.558 ********
changed: [serverc.lab.example.com]
changed: [serverb.lab.example.com]

PLAY RECAP ***********************************************************************************************
serverb.lab.example.com    : ok=4    changed=3    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0
serverc.lab.example.com    : ok=4    changed=3    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0

Tuesday 16 August 2022  00:09:38 +0800 (0:00:34.566)       0:00:57.124 ********
===============================================================================
Ensure the web content is installed -------------------------------------------------------------- 34.57s
Ensure required packages are installed ----------------------------------------------------------- 18.64s
Ensure the services are enabled ------------------------------------------------------------------- 2.72s
Gathering Facts ----------------------------------------------------------------------------------- 1.17s
Playbook run took 0 days, 0 hours, 0 minutes, 57 seconds
$
我們優(yōu)化之后執(zhí)行,耗時(shí)33s,之前修改了yum模塊的裝包方式,同時(shí)調(diào)整了文件復(fù)制的方式,當(dāng)然,因?yàn)槭窍萩opy和所以synchronize 比較快。

$ansible-playbook deploy_webservers.yml

PLAY [Deploy the web servers] ****************************************************************************

TASK [Ensure required packages are installed] ************************************************************
Tuesday 16 August 2022  00:16:20 +0800 (0:00:00.035)       0:00:00.035 ********
changed: [serverb.lab.example.com]
changed: [serverc.lab.example.com]

TASK [Ensure the services are enabled] *******************************************************************
Tuesday 16 August 2022  00:16:30 +0800 (0:00:10.005)       0:00:10.041 ********
changed: [serverc.lab.example.com] => (item=httpd)
changed: [serverb.lab.example.com] => (item=httpd)
changed: [serverb.lab.example.com] => (item=mariadb)
changed: [serverc.lab.example.com] => (item=mariadb)

TASK [Ensure the web content is installed] ***************************************************************
Tuesday 16 August 2022  00:16:33 +0800 (0:00:03.142)       0:00:13.184 ********
changed: [serverc.lab.example.com]
changed: [serverb.lab.example.com]

PLAY RECAP ***********************************************************************************************
serverb.lab.example.com    : ok=3    changed=3    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0
serverc.lab.example.com    : ok=3    changed=3    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0

Tuesday 16 August 2022  00:16:54 +0800 (0:00:20.718)       0:00:33.902 ********
===============================================================================
Ensure the web content is installed -------------------------------------------------------------- 20.72s
Ensure required packages are installed ----------------------------------------------------------- 10.01s
Ensure the services are enabled ------------------------------------------------------------------- 3.14s
Playbook run took 0 days, 0 hours, 0 minutes, 33 seconds
$
嗯,關(guān)于Ansible 中Playbook 執(zhí)行速度優(yōu)化就和小伙伴們分享到這里,生活加油 ^_^

博文參考
《Red Hat Ansible Engine 2.8 DO447》

作者:山河已無(wú)恙


歡迎關(guān)注微信公眾號(hào) :山河已無(wú)恙