1. 程式人生 > 其它 >Ansible入門教程

Ansible入門教程

簡介

Ansible是一款簡單的運維自動化工具,只需要使用ssh協議連線就可以來進行系統管理,自動化執行命令,部署等任務。

Ansible的特點

1、ansible不需要單獨安裝客戶端,也不需要啟動任何服務
2、ansible是Python中的一套完整的自動化執行任務模組
3、ansible playbook 採用yaml配置,對於自動化任務執行過一目瞭然

Ansible組成結構

Ansible的命令工具,核心執行工具;一次性或臨時執行的操作都是通過該命令執行。

  • Ansible Playbook
    任務劇本(又稱任務集),編排定義Ansible任務集的配置檔案,由Ansible順序依次執行,yaml
    格式。
  • Inventory
    Ansible管理主機的清單,預設是/etc/ansible/hosts檔案。
  • Modules
    Ansible執行命令的功能模組,Ansible2.3版本為止,共有1039個模組。還可以自定義模組。
  • Plugins
    外掛,模組功能的補充,常有連線型別外掛,迴圈外掛,變數外掛,過濾外掛,外掛功能用的較少。
  • API
    提供給第三方程式呼叫的應用程式程式設計介面,提供一個功能強大,操作性強的Web管理介面和REST API介面---- AWX平臺。

安裝

1)配置epel

[root@ansible ~]# wget -O /etc/yum.repos.d/epel.repo http://mirrors.aliyun.com/repo/epel-7.repo
[root@ansible ~]# yum clean all
[root@ansible ~]# yum makecache

2)安裝ansible

sudo yum install epel-release
sudo yum install ansible
ansible --version

Ansible Inventory檔案

Inventory檔案通常用於定義要管理的主機的認證資訊,例如ssh登入使用者名稱、密碼以及key相關資訊。可以同時操作一個組的多臺主機,組與主機組之間的關係都是通過inventory檔案配置。配置檔案路徑為:/etc/ansible/hosts

配置連線

基於密碼連線

> vim /etc/ansible/hosts

# 一、 主機+埠+密碼
[webserver]  #定義分組 webserver,可以定義多個分組
192.168.1.31 ansible_ssh_port=22 ansible_ssh_user=root ansible_ssh_pass="123456"
192.168.1.32 ansible_ssh_port=22 ansible_ssh_user=root ansible_ssh_pass="123456"
192.168.1.33 ansible_ssh_port=22 ansible_ssh_user=root ansible_ssh_pass="123456"

# 二 、主機+埠+密碼
[webserver]
192.168.1.3[1:3] ansible_ssh_user=root ansible_ssh_pass="123456"

基於祕鑰連線

基於祕鑰連線需要先建立公鑰和私鑰,併發送給被管理機器;

1)生成公私鑰

ssh-keygen

# ssh-copy-id 將本機的公鑰複製到遠端機器的authorized_keys檔案中,ssh-copy-id也能讓遠端機器的home, ~./ssh , 和 ~/.ssh/authorized_keys的權利
for i in {1,2,3,6}; do ssh-copy-id -i 192.168.1.3$i ; done

2)配置連線

vim /etc/ansible/hosts

# 一、主機+埠+金鑰
[webserver]
192.168.1.31:22
192.168.1.32
192.168.1.33
192.168.1.36

# 二、別名主機+埠+金鑰
[webserver]
node1 ansible_ssh_host=192.168.1.31 ansible_ssh_port=22
node2 ansible_ssh_host=192.168.1.32 ansible_ssh_port=22
node3 ansible_ssh_host=192.168.1.33 ansible_ssh_port=22

主機組的使用

主機組變數名+主機+密碼

[apache]
192.168.1.36
192.168.1.33
[apache.vars]
ansible_ssh_pass='123456'

主機組變數名+主機+金鑰

[nginx]
192.168.1.3[1:2]

定義多個組

把一個組當另外一個組的組員

[webserver:children]  #webserver組包括兩個子組:apache nginx
apache
nginx

完整檔案

shell > vim /etc/ansible/hosts

www.abc.com     # 定義域名
192.168.1.100   # 定義 IP
192.168.1.150:37268   # 指定埠號

[WebServer]           # 定義分組
192.168.1.10
192.168.1.20
192.168.1.30

[DBServer]            # 定義多個分組
192.168.1.50
192.168.1.60

Monitor ansible_ssh_port=12378 ansible_ssh_host=192.168.1.200   # 定義別名

# ansible_ssh_host 連線目標主機的地址
# ansible_ssh_port 連線目標主機的埠,預設 22 時無需指定
# ansible_ssh_user 連線目標主機預設使用者
# ansible_ssh_pass 連線目標主機預設使用者密碼
# ansible_ssh_connection 目標主機連線型別,可以是 local 、ssh 或 paramiko
# ansible_ssh_private_key_file 連線目標主機的 ssh 私鑰
# ansible_*_interpreter 指定採用非 Python 的其他指令碼語言,如 Ruby 、Perl 或其他類似 ansible_python_interpreter 直譯器

[webservers]         # 主機名支援正則描述
www[01:50].example.com

[dbservers]
db-[a:f].example.com

Inventory內建引數

其他配置檔案

/etc/ansible/ansible.cfg #主配置檔案,配置ansible工作特性
/etc/ansible/hosts #主機清單 (將要連線操控的主機IP地址寫在此配置檔案的最下方)
/etc/ansible/roles/ #存放角色的目錄

程式

/usr/bin/ansible     #主程式,臨時命令執行工具
/usr/bin/ansible-doc #檢視配置文件,模組功能檢視工具
/usr/bin/ansible-galaxy #下載/上傳優秀程式碼或Roles模組的官網平臺
/usr/bin/ansible-playbook #定製自動化任務,編排劇本工具/
/usr/bin/ansible-pull #遠端執行命令的工具
/usr/bin/ansible-vault #檔案加密工具
/usr/bin/ansible-console #基於Console介面與使用者互動的執行工具

ansible命令格式

ansible <需要執行命令的目標主機或群組> <ansible命令選項,指定模組> <需要使用的模組,shell命令模組> <ansible命令選項,指定模組中要用到的引數> <模組中的引數(shell命令)>

引數

引數可以有多個,如果不加-m,執行的是command模式,也就是shell命令;-a必須新增

ansible all    #對所有主機執行
ansible group --list    #輸出組中的主機
ansible 'a:!n' -m ping -o       #匹配a組中有,但是n組中沒有的所有主機
ansible 'apache:&nginx' -m ping -o     #匹配a組和n組中都有的機器(並集)
ansible-doc -l    # 列出 Ansible 支援的模組
ansible-doc ping  #檢視指定模組(ping)幫助用法
ansible-doc –s ping #檢視指定模組(ping)幫助用法

測試ansible

shell > ansible Client -m ping     # 操作 Client 組 ( all 為操作 hosts 檔案中所有主機 ),-m 指定執行 ping 模組,下面是返回結果
192.168.12.129 | SUCCESS => {
"changed": false, 
"ping": "pong"
}

# -i          指定 hosts 檔案位置
# -u username 指定 SSH 連線的使用者名稱
# -k          指定遠端使用者密碼
# -f          指定併發數
# -s          如需要 root 許可權執行時使用 ( 連線使用者不是 root 時 )
# -K          -s 時,-K 輸入 root 密碼

ansible常用模組

shell > ansible-doc -l    # 列出 Ansible 支援的模組

shell > ansible-doc ping  # 檢視該模組幫助資訊

遠端命令模組(command / script / shell)

command

command 作為 Ansible 的預設模組,可以執行遠端許可權範圍所有的 shell 命令,不支援管道符。

shell > ansible Client -m command -a "free -m"               # 檢視 Client 分組主機記憶體使用情況

script

script 的功能是在遠端主機執行主控端儲存的 shell 指令碼檔案,相當於 scp + shell 組合。

shell > ansible Client -m script -a "/home/test.sh 12 34"    # 遠端執行本地指令碼

shell

shell模組基本和command相同,但是shell支援管道符

shell > ansible Client -m shell -a "/home/test.sh"           # 執行遠端指令碼

copy模組

實現主控端向目標主機拷貝檔案,類似於 scp 功能

shell > ansible Client -m copy -a "src=/home/test.sh dest=/tmp/ owner=root group=root mode=0755"   # 向 Client 組中主機拷貝 test.sh 到 /tmp 下,屬主、組為 root ,許可權為 0755

stat模組

獲取遠端檔案狀態資訊,atime/ctime/mtime/md5/uid/gid 等資訊

shell > ansible Client -m stat -a "path=/etc/syctl.conf"

get_url

實現在遠端主機下載指定 URL 到本地,支援 sha256sum 檔案校驗

shell > ansible Client -m get_url -a "url=http://www.baidu.com dest=/tmp/index.html mode=0440 force=yes"

yum

軟體包管理

shell > ansible Client -m yum -a "name=curl state=latest"

corn

遠端主機 crontab 配置

shell > ansible Client -m cron -a "name='check dirs' hour='5,2' job='ls -alh > /dev/null'"

效果:
* 5,2 * * * ls -alh > /dev/null

mount

遠端主機分割槽掛載

shell > ansible Client -m mount -a "name=/mnt/data src=/dev/sd0 fstype=ext4 opts=ro state=present"

service

遠端主機系統服務管理

shell > ansible Client -m service -a "name=nginx state=stoped"
shell > ansible Client -m service -a "name=nginx state=restarted"
shell > ansible Client -m service -a "name=nginx state=reloaded"

user

遠端主機使用者管理

shell > ansible Client -m user -a "name=wang comment='user wang'"

shell > ansible Client -m user -a "name=wang state=absent remove=yes"    # 新增刪除使用者

PlayBook執行過程

使用ansible的-vvv-vvvv分析執行過程。以下是一個啟動遠端192.168.100.61上httpd任務的執行過程分析。其中將不必要的資訊都是用"....."替換了。

# 讀取配置檔案,然後開始執行對應的處理程式。
Using /etc/ansible/ansible.cfg as config file
META: ran handlers

# 第一個任務預設都是收集遠端主機資訊的任務。
# 第一個收集任務,載入setup模組
Using module file /usr/lib/python2.7/site-packages/ansible/modules/system/setup.py

# 建立連線,獲取被控節點當前使用者的家目錄,用於存放稍後的臨時任務檔案,此處返回值為/root。
# 在-vvv的結果中,第一行屬於描述性資訊,第二行為程式碼執行段,第三行類似此處的<host_node>(,,,,,)為上一段程式碼的返回結果。後同
<192.168.100.61> ESTABLISH SSH CONNECTION FOR USER: None
<192.168.100.61> SSH: EXEC ssh -C ..........................
<192.168.100.61> (0, '/root\n', '')

# 再次建立連線,在遠端建立臨時任務檔案的目錄,臨時目錄由配置檔案中的remote_tmp指令控制
<192.168.100.61> ESTABLISH SSH CONNECTION FOR USER: None
<192.168.100.61> SSH: EXEC ssh -C .......................... '/bin/sh -c '"'"'( umask 77 && mkdir -p "` echo /root/.ansible/tmp/ansible-tmp-1495977564.58-40718671162202 `" && echo ansible-tmp-1495977564.58-40718671162202="` echo /root/.ansible/tmp/ansible-tmp-1495977564.58-40718671162202 `" ) && sleep 0'"'"''
<192.168.100.61> (0, 'ansible-tmp-1495977564.58-40718671162202=/root/.ansible/tmp/ansible-tmp-1495977564.58-40718671162202\n', '')

# 將要執行的任務放到臨時檔案中,並使用sftp將任務檔案傳輸到被控節點上
<192.168.100.61> PUT /tmp/tmpY5vJGX TO /root/.ansible/tmp/ansible-tmp-1495977564.58-40718671162202/setup.py
<192.168.100.61> SSH: EXEC sftp -b - -C ............. '[192.168.100.61]'
<192.168.100.61> (0, 'sftp> put /tmp/tmpY5vJGX /root/.ansible/tmp/ansible-tmp-1495977564.58-40718671162202/setup.py\n', '')

# 建立連線,設定遠端任務檔案其所有者有可執行許可權
<192.168.100.61> ESTABLISH SSH CONNECTION FOR USER: None
<192.168.100.61> SSH: EXEC ssh -C ......................... '/bin/sh -c '"'"'chmod u+x /root/.ansible/tmp/ansible-tmp-1495977564.58-40718671162202/ /root/.ansible/tmp/ansible-tmp-1495977564.58-40718671162202/setup.py && sleep 0'"'"''
<192.168.100.61> (0, '', '')

# 建立連線,執行任務,執行完成後立即刪除任務檔案,並返回收集到的資訊給ansible。到此為止,setup收集任務結束,關閉共享連線
<192.168.100.61> ESTABLISH SSH CONNECTION FOR USER: None
<192.168.100.61> SSH: EXEC ssh -C ............ '/bin/sh -c '"'"'/usr/bin/python /root/.ansible/tmp/ansible-tmp-1495977564.58-40718671162202/setup.py; rm -rf "/root/.ansible/tmp/ansible-tmp-1495977564.58-40718671162202/" > /dev/null 2>&1 && sleep 0'"'"''
<192.168.100.61> (0, '\r\n{"invocation": {"...........', 'Shared connection to 192.168.100.61 closed.\r\n')


# 進入下一個任務,此處為服務管理任務,所以載入service模組
Using module file /usr/lib/python2.7/site-packages/ansible/modules/system/service.py

# 建立連線,獲取被控節點當前使用者的家目錄,用於存放稍後的臨時任務檔案,此處返回值為/root
<192.168.100.61> ESTABLISH SSH CONNECTION FOR USER: None
<192.168.100.61> SSH: EXEC ssh -C .........................
<192.168.100.61> (0, '/root\n', '')

# 建立連線,將要執行的任務放入到臨時檔案中,並傳輸到遠端目錄
<192.168.100.61> ESTABLISH SSH CONNECTION FOR USER: None
<192.168.100.61> SSH: EXEC ssh -C .............. '/bin/sh -c '"'"'( umask 77 && mkdir -p "` echo /root/.ansible/tmp/ansible-tmp-1495977564.97-137863382080241 `" && echo ansible-tmp-1495977564.97-137863382080241="` echo /root/.ansible/tmp/ansible-tmp-1495977564.97-137863382080241 `" ) && sleep 0'"'"''
<192.168.100.61> (0, 'ansible-tmp-1495977564.97-137863382080241=/root/.ansible/tmp/ansible-tmp-1495977564.97-137863382080241\n', '')

<192.168.100.61> PUT /tmp/tmpn5uZhP TO /root/.ansible/tmp/ansible-tmp-1495977564.97-137863382080241/service.py
<192.168.100.61> SSH: EXEC sftp -b - -C .............. '[192.168.100.61]'
<192.168.100.61> (0, 'sftp> put /tmp/tmpn5uZhP /root/.ansible/tmp/ansible-tmp-1495977564.97-137863382080241/service.py\n', '')

# 建立連線,設定遠端任務檔案其所有者有可執行許可權
<192.168.100.61> ESTABLISH SSH CONNECTION FOR USER: None
<192.168.100.61> SSH: EXEC ssh -C ........................ '/bin/sh -c '"'"'chmod u+x /root/.ansible/tmp/ansible-tmp-1495977564.97-137863382080241/ /root/.ansible/tmp/ansible-tmp-1495977564.97-137863382080241/service.py && sleep 0'"'"''
<192.168.100.61> (0, '', '')

# 建立連線,執行任務,執行完成後立即刪除任務檔案,並將執行的結果返回到ansible端。到此為止,service模組任務執行結束,關閉共享連線
<192.168.100.61> ESTABLISH SSH CONNECTION FOR USER: None
<192.168.100.61> SSH: EXEC ssh -C .............. '/bin/sh -c '"'"'/usr/bin/python /root/.ansible/tmp/ansible-tmp-1495977564.97-137863382080241/service.py; rm -rf "/root/.ansible/tmp/ansible-tmp-1495977564.97-137863382080241/" > /dev/null 2>&1 && sleep 0'"'"''

將上面的進行總結,執行過程將是這樣的:

  • 讀取配置檔案
  • 載入inventory檔案。包括主機變數和主機組變數
  • 執行第一個任務:收集遠端被控節點的資訊
    • 建立連線,獲取家目錄資訊
    • 將要執行的收集任務放到臨時檔案中
    • 將臨時任務檔案傳輸到被控節點的臨時目錄中
    • ssh連線到遠端執行收集任務
    • 刪除任務檔案
    • 將收集資訊返回給ansible端
    • 關閉連線
  • 執行第二個任務,此為真正的主任務
    • 建立連線,獲取家目錄資訊
    • 將要執行的任務放到臨時檔案中
    • 將臨時任務檔案傳輸到被控節點的臨時目錄中
    • ssh連線到遠端執行任務
    • 刪除任務檔案
    • 將執行結果返回給ansible端,ansible輸出到螢幕或指定檔案中
    • 關閉連線
  • 執行第三個任務
  • 執行第四個任務

如果是多個被控節點,那麼將同時在多個節點上並行執行每一個任務,例如同時執行資訊收集任務。不同節點之間的任務沒有先後關係,主要依賴於效能。每一個任務執行完畢都會立即將結果返回給ansible端,所以可以通過ansible端結果的輸出順序和速度判斷執行完畢的先後順序。

如果節點數太多,ansible無法一次在所有遠端節點上執行任務,那麼將先在一部分節點上執行一個任務(每一批節點的數量取決於fork程序數量),直到這一批所有節點上該任務完全執行完畢才會接入下一個批節點(數量取決於fork程序數量),直到所有節點將該任務都執行完畢,然後重新回到第一批節點開始執行第二個任務。依次類推,直到所有節點執行完所有任務,ansible端才會釋放shell。這是預設的同步模式,也就是說在未執行完畢的時候,ansible是佔用當前shell的,任務執行完畢後,釋放shell了才可以輸入其他命令做其他動作。

如果是非同步模式,假如fork控制的併發程序數為5,遠端控制節點為24個,則ansible一開始會將5個節點的任務扔在後臺,並每隔一段時間去檢查這些節點的任務完成情況,當某節點完成不會立即返回,而是繼續等待直到5個程序都空閒了,才會將這5個節點的結果返回給ansible端,ansible會繼續將下一批5個節點的任務扔在後臺並每隔一段時間進行檢查,依次類推,直到完成所有任務。

在非同步模式下,如果設定的檢查時間間隔為0,在將每一批節點的任務丟到後臺後都會立即返回ansible,並立即將下一批節點的任務丟到後臺,直到所有任務都丟到後臺完成後,會返回ansible端,ansible會立即釋放佔用的shell。也就是說,此時ansible是不會管各個節點的任務執行情況的,不管執行成功還是失敗。

因此,在輪訓檢查時間內,ansible仍然正在執行(儘管某批任務已經被放到後臺執行了),當前shell程序仍被佔用處於睡眠狀態,只有指定的檢查時間間隔為0,才會儘快將所有任務放到後臺並釋放shell。

需要注意3點:

  1. 按批(例如每次5臺全部完成一個任務才進入下一批的5臺)完成任務的模式在ansible 2.0版本之後可以通過修改ansible的執行策略來改變(見後文),改變後會變成"前赴後繼"的執行模式:當一個節點執行完一個任務會立即接入另一個節點,不再像預設情況一樣等待這一批中的其他節點完成該任務。
  2. 上面執行過程是預設的執行過程,如果開啟了pipelining加速ansible執行效率,會省去sftp到遠端的過程。
  3. 資訊收集任務是預設會執行的,但是可以設定禁用它。

Ansible併發和非同步

上面已經對ansible的執行過程進行了很詳細的分析,也解釋了同步和非同步的模式是如何處理任務的。所以此處簡單舉幾個例子。

ansible預設只會建立5個程序併發執行任務,所以一次任務只能同時控制5臺機器執行。如果有大量的機器需要控制,例如20臺,ansible執行一個任務時會先在其中5臺上執行,執行成功後再執行下一批5臺,直到全部機器執行完畢。使用 -f 選項可以指定程序數,指定的程序數量多一些,不僅會實現全併發,對非同步的輪訓poll也會有正面影響。

ansible預設是同步阻塞模式,它會等待所有的機器都執行完畢才會在前臺返回。可以採取非同步執行模式。

非同步模式下,ansible會將節點的任務丟在後臺,每臺被控制的機器都有一個job_id,ansible會根據這個job_id去輪訓該機器上任務的執行情況,例如某機器上此任務中的某一個階段是否完成,是否進入下一個階段等。即使任務早就結束了,但只有輪訓檢查到任務結束後才認為該job結束。可以指定任務檢查的時間間隔,預設是10秒。除非指定任務檢查的間隔為0,否則會等待所有任務都完成後,ansible端才會釋放佔用的shell。

如果指定時間間隔為0,則ansible會立即返回(至少得連線上目標主機,任務釋出成功之後立即返回),並不會去檢查它的任務進度。

ansible centos -B200 -P 0 -m yum -a "name=dos2unix" -o -f 6 
192.168.100.61 | SUCCESS => {"ansible_job_id": "986026954359.9166", "changed": true, "finished": 0, "results_file": "/root/.ansible_async/986026954359.9166", "started": 1}
192.168.100.59 | SUCCESS => {"ansible_job_id": "824724696770.9431", "changed": true, "finished": 0, "results_file": "/root/.ansible_async/824724696770.9431", "started": 1}
192.168.100.60 | SUCCESS => {"ansible_job_id": "276581152579.10006", "changed": true, "finished": 0, "results_file": "/root/.ansible_async/276581152579.10006", "started": 1}
192.168.100.64 | SUCCESS => {"ansible_job_id": "237326453903.72268", "changed": true, "finished": 0, "results_file": "/root/.ansible_async/237326453903.72268", "started": 1}
192.168.100.63 | SUCCESS => {"ansible_job_id": "276700021098.73070", "changed": true, "finished": 0, "results_file": "/root/.ansible_async/276700021098.73070", "started": 1}
192.168.100.65 | SUCCESS => {"ansible_job_id": "877427488272.72032", "changed": true, "finished": 0, "results_file": "/root/.ansible_async/877427488272.72032", "started": 1}

關於同步、非同步以及非同步時的並行數、輪訓間隔對ansible的影響,通過以下示例說明:

當有6個節點時,僅就釋放shell的速度而言,以下幾種寫法:

# 同步模式,大約10秒返回。
ansible centos -m command -a "sleep 5" -o 
# 非同步模式,分兩批執行。大約10秒返回。
ansible centos -B200 -P 1 -m command -a "sleep 5" -o -f 5
# 非同步模式,和上一條命令時間差不多,但每次檢查時間長一秒,所以可能會稍有延遲。大約11-12秒返回。
ansible centos -B200 -P 2 -m command -a "sleep 5" -o -f 5
# 非同步模式,一批就執行完,大約5-6秒返回。
ansible centos -B200 -P 1 -m command -a "sleep 5" -o -f 6
# 非同步模式,一批就完成,大約5-6秒完成。
ansible centos -B200 -P 2 -m command -a "sleep 5" -o -f 6 
# 非同步模式,分兩批,且檢查時間過長。即使只睡眠5秒,但仍需要10秒才能判斷該批執行結束。所以大約20秒返回。
ansible centos -B200 -P 10 -m command -a "sleep 5" -o -f 5 
# 非同步模式,一批執行完成,但檢查時間超過睡眠時間,因此大約10秒返回。
ansible centos -B200 -P 10 -m command -a "sleep 5" -o -f 6

在非同步執行任務時,需要注意那些有依賴性的任務。對於那些對資源要求佔有排它鎖的任務,如yum,不應該將Poll的間隔設定為0。如果設定為0,很可能會導致資源阻塞。

總結來說,大概有以下一些場景需要使用到ansible的非同步特性:

  • 某個task需要執行很長的時間,這個task很可能會達到ssh連線的timeout
  • 沒有任務是需要等待它才能完成的,即沒有任務依賴此任務是否完成的狀態
  • 需要儘快返回當前shell

當然也有一些場景不適合使用非同步特性:

  • 這個任務是需要執行完後才能繼續另外的任務的
  • 申請排它鎖的任務

當然,對於有任務依賴性的任務,也還是可以使用非同步模式的,只要檢查它所依賴的主任務狀態已完成就可以。例如,要配置nginx,要求先安裝好nginx,在配置nginx之前先檢查yum安裝的狀態。

- name: 'YUM - fire and forget task'
  yum: name=nginx state=installed
  async: 1000
  poll: 0
  register: yum_sleeper

- name: 'YUM - check on fire and forget task'
  async_status: jid={{ yum_sleeper.ansible_job_id }}
  register: job_result
  until: job_result.finished
  retries: 30

ansible的 -t 選項妙用

ansible的"-t"或"--tree"選項是將ansible的執行結果按主機名儲存在指定目錄下的檔案中。

有些時候,ansible執行起來的速度會非常慢,這種慢體現在即使執行的是一個立即返回的簡單命令(如ping模組),也會耗時很久,且不是因為ssh連線慢導致的。如果使用-t選項,將第一次執行得到的結果按inventory中定義的主機名儲存在檔案中,下次執行到同一臺主機時速度將會變快很多,即使之後不再加上-t選項,也可以在一定時間內保持迅速執行。即使執行速度正常(如執行一個Ping命令0.7秒左右),使用-t選項也可以在此基礎上變得更快。

除了使用-t選項,使用重定向將結果重定向到某個檔案中也是一樣的效果。至於為何會如此,我也不知道,是在無意中測試出來的。有必要指出:我在CentOS 6.6上遇到過這樣的問題,但並不是總會如此,且在CentOS 7上正常。因此,如果你也出現了這樣的問題,可以參考這種偏方。

以CentOS 6.6安裝的ansible 2.3為例,正常執行ansible會非常慢,使用-t可以解決這個問題。如下。

沒有使用-t時:移除dos2unix包所需時間為13秒多。

time ansible centos -B200 -P 0 -m yum -a "name=dos2unix state=removed" -o -f 6
192.168.100.60 | SUCCESS => {"ansible_job_id": "987125400759.10653", "changed": true, "finished": 0, "results_file": "/root/.ansible_async/987125400759.10653", "started": 1}
192.168.100.63 | SUCCESS => {"ansible_job_id": "735153954362.74074", "changed": true, "finished": 0, "results_file": "/root/.ansible_async/735153954362.74074", "started": 1}
192.168.100.61 | SUCCESS => {"ansible_job_id": "192721090554.9813", "changed": true, "finished": 0, "results_file": "/root/.ansible_async/192721090554.9813", "started": 1}
192.168.100.64 | SUCCESS => {"ansible_job_id": "494724112239.73269", "changed": true, "finished": 0, "results_file": "/root/.ansible_async/494724112239.73269", "started": 1}
192.168.100.59 | SUCCESS => {"ansible_job_id": "2259915341.10078", "changed": true, "finished": 0, "results_file": "/root/.ansible_async/2259915341.10078", "started": 1}
192.168.100.65 | SUCCESS => {"ansible_job_id": "755223232484.73025", "changed": true, "finished": 0, "results_file": "/root/.ansible_async/755223232484.73025", "started": 1}

real    0m13.746s
user    0m1.288s
sys     0m1.417s

使用-t選項後:安裝dos2unix只需1.9秒左右。

time ansible centos -B200 -P 0 -m yum -a "name=dos2unix state=installed" -o -f 6 -t /tmp/a 

real    0m1.933s
user    0m0.398s
sys     0m0.900s

之後即使不再使用-t選項,對同樣的主機進行操作,速度也會變得非常快。

time ansible centos -B200 -P 0 -m yum -a "name=dos2unix state=removed" -o -f 6

real    0m1.730s
user    0m0.892s
sys     0m0.572s

至於儲存的內容為何?實際上僅僅只是儲存了普通的輸出內容而已。

ll /tmp/a/
total 24
-rw-r--r-- 1 root root 145 May 28 15:54 192.168.100.59
-rw-r--r-- 1 root root 145 May 28 15:54 192.168.100.60
-rw-r--r-- 1 root root 143 May 28 15:54 192.168.100.61
-rw-r--r-- 1 root root 143 May 28 15:54 192.168.100.63
-rw-r--r-- 1 root root 145 May 28 15:54 192.168.100.64
-rw-r--r-- 1 root root 145 May 28 15:54 192.168.100.65

cat /tmp/a/192.168.100.59
{"ansible_job_id": "659824383578.10145", "changed": true, "finished": 0, "results_file": "/root/.ansible_async/659824383578.10145", "started": 1}

優化ansible速度

最初,ansible的執行效率和saltstack(基於zeromq訊息佇列的方式)相比要慢的多的多,特別是被控節點量很大的時候。但是ansible發展到現在,它的效率得到了極大的改善。在被控節點不太多的時候,預設的設定已經夠快,即使被控節點數量巨大的時候,也可以通過一些優化,極大的提高其執行效率。

前面"-t"選項也算是一種提速方式,但算是"bug"式的問題,所以沒有通用性。

設定ansible開啟ssh長連線

ansible天然支援openssh,預設連線方式下,它對ssh的依賴性非常強。所以優化ssh連線,在一定程度上也在優化ansible。其中一點是開啟ssh的長連線,即長時間保持連線狀態。

要開啟ssh長連線,要求ansible端的openssh版本高於或等於5.6。使用ssh -V可以檢視版本號。然後設定ansible使用ssh連線被控端的連線引數,此處修改/etc/ansible/ansible.cfg,在此檔案中啟動下面的連線選項,其中ControlPersist=5d是控制ssh連線會話保持時長為5天。

ssh_args = -C -o ControlMaster=auto -o ControlPersist=5d

除此之外直接設定/etc/ssh/ssh_config(不是sshd_config,因為ssh命令是客戶端命令)中對應的長連線項也是可以的。 開啟長連線後,在會話過期前會一直建立連線,在netstat的結果中會看到ssh連線是一直established狀態,且會在當前使用者家目錄的".ansible/cp"目錄下生成一些socket檔案,每個會話一個檔案。 例如:執行一次ad-hoc操作。

ansible centos -m ping

檢視netstat,發現ssh程序的會話一直是established狀態。

shell> netstat -tnalp

Active Internet connections (servers and established)
Proto Recv-Q Send-Q Local Address           Foreign Address      State       PID/Program name    
tcp        0      0 0.0.0.0:22              0.0.0.0:*            LISTEN      1143/sshd           
tcp        0      0 127.0.0.1:25            0.0.0.0:*            LISTEN      2265/master         
tcp        0      0 192.168.100.62:58474    192.168.100.59:22    ESTABLISHED 31947/ssh: /root/.a 
tcp        0     96 192.168.100.62:22       192.168.100.1:8189   ESTABLISHED 29869/sshd: root@pt 
tcp        0      0 192.168.100.62:37718    192.168.100.64:22    ESTABLISHED 31961/ssh: /root/.a 
tcp        0      0 192.168.100.62:38894    192.168.100.60:22    ESTABLISHED 31952/ssh: /root/.a 
tcp        0      0 192.168.100.62:48659    192.168.100.61:22    ESTABLISHED 31949/ssh: /root/.a 
tcp        0      0 192.168.100.62:33546    192.168.100.65:22    ESTABLISHED 31992/ssh: /root/.a 
tcp        0      0 192.168.100.62:54824    192.168.100.63:22    ESTABLISHED 31958/ssh: /root/.a 
tcp6       0      0 :::22                   :::*                 LISTEN      1143/sshd           
tcp6       0      0 ::1:25                  :::*                 LISTEN      2265/master

且家目錄下"~/.ansible/cp/"下會生成對應的socket檔案。

ls -l ~/.ansible/cp/
total 0
srw------- 1 root root 0 Jun  3 18:26 5c4a6dce87
srw------- 1 root root 0 Jun  3 18:26 bca3850113
srw------- 1 root root 0 Jun  3 18:26 c89359d711
srw------- 1 root root 0 Jun  3 18:26 cd829456ec
srw------- 1 root root 0 Jun  3 18:26 edb7051c84
srw------- 1 root root 0 Jun  3 18:26 fe17ac7eed

開啟pipelining

pipeline也是openssh的一個特性。在ansible執行每個任務的流程中,有一個過程是將臨時任務檔案put到一個ansible端的一個臨時檔案中,然後sftp傳輸到遠端,然後通過ssh連線過去遠端執行這個任務。如果開啟了pipelining,一個任務的所有動作都在一個ssh會話中完成,也會省去sftp到遠端的過程,它會直接將要執行的任務在ssh會話中進行。

開啟pipelining的方式是配置檔案(如ansible.cfg)中設定pipelining=true,預設是false

hell> grep '^pipelining' /etc/ansible/ansible.cfg
pipelining = True

但是要注意,如果在ansible中使用sudo命令的話(ssh user@host sudo cmd),需要在被控節點的/etc/sudoers中禁用"requiretty"。

之所以要設定/etc/sudoers中的requiretty,是因為ssh遠端執行命令時,它的環境是非登入式非互動式shell,預設不會分配tty,沒有tty,ssh的sudo就無法關閉密碼回顯(使用"-tt"選項強制SSH分配tty)。所以出於安全考慮,/etc/sudoers中預設是開啟requiretty的,它要求只有擁有tty的使用者才能使用sudo,也就是說ssh連線過去不允許執行sudo。可以通過visudo編輯配置檔案,註釋該選項來禁用它。

grep requiretty /etc/sudoers
# Defaults    requiretty

修改設定/etc/sudoers是在被控節點上進行的(或者ansible連線過去修改),其實在ansible端也可以解決sudo的問題,只需在ansible的ssh引數上加上"-tt"選項即可。

ssh_args = -C -o ControlMaster=auto -o ControlPersist=5d -tt

以下是開啟pipelining前ansible執行過程,其中將很多不必要的資訊使用......來替代了。

######## 開啟pipelining前,執行ping模組的過程 ########
Using /etc/ansible/ansible.cfg as config file
Loading callback plugin minimal of type stdout, v2.0 from /usr/lib/python2.7/site-packages/ansible/plugins/callback/__init__.pyc
META: ran handlers
Using module file /usr/lib/python2.7/site-packages/ansible/modules/system/ping.py

# 首先建立一次ssh連線,獲取遠端當前使用者家目錄,用於存放稍後的臨時任務檔案
<192.168.100.65> ESTABLISH SSH CONNECTION FOR USER: None
<192.168.100.65> SSH: EXEC ssh -vvv -C ................ 192.168.100.65 '/bin/sh -c '"'"'echo ~ && sleep 0'"'"''
<192.168.100.65> (0, '/root\n', ................)

# 再次建立ssh連線,建立臨時檔案目錄
<192.168.100.65> ESTABLISH SSH CONNECTION FOR USER: None
<192.168.100.65> SSH: EXEC ssh -vvv -C ..................... 192.168.100.65 '/bin/sh -c '"'"'( umask 77 && mkdir -p "......." ) && sleep 0'"'"''
<192.168.100.65> (0, 'ansible-tmp-1496489511.13-10633592020239=/root/.ansible/tmp/ansible-tmp-1496489511.13-10633592020239\n', '.............')

# 將任務放入到本地臨時檔案中,然後使用sftp傳輸到遠端
<192.168.100.65> PUT /tmp/tmp2_VKGo TO /root/.ansible/tmp/ansible-tmp-1496489511.13-10633592020239/ping.py
<192.168.100.65> SSH: EXEC sftp -b - -vvv -C ................. '[192.168.100.65]'
<192.168.100.65> (0, 'sftp> put /tmp/tmp2_VKGo /root/.ansible/tmp/ansible-tmp-1496489511.13-10633592020239/ping.py\n', '.....................')

# 又一次建立ssh連線,對任務檔案進行授權
<192.168.100.65> ESTABLISH SSH CONNECTION FOR USER: None
<192.168.100.65> SSH: EXEC ssh -vvv -C ............. 192.168.100.65 '/bin/sh -c '"'"'chmod u+x .......... /ping.py && sleep 0'"'"''
<192.168.100.65> (0, '', '........................')

# 最後執行任務,完成任務後刪除任務檔案,並返回ansible端資訊,注意ssh -tt選項,它強制為ssh會話分配tty,這樣可以執行sudo命令
<192.168.100.65> ESTABLISH SSH CONNECTION FOR USER: None
<192.168.100.65> SSH: EXEC ssh -vvv -C .............. -tt 192.168.100.65 '/bin/sh -c '"'"'/usr/bin/python .........../ping.py; rm -rf ".........." > /dev/null 2>&1 && sleep 0'"'"''
<192.168.100.65> (0, '\r\n{"invocation": {"...............')
以下是開啟pipelining後,ansible執行過程。
########### 開啟pipelining後,執行ping模組的過程 ########
Using /etc/ansible/ansible.cfg as config file
Loading callback plugin minimal of type stdout, v2.0 from /usr/lib/python2.7/site-packages/ansible/plugins/callback/__init__.pyc
META: ran handlers
Using module file /usr/lib/python2.7/site-packages/ansible/modules/system/ping.py

# 只建立一次ssh連線,所有動作都在這一個ssh連線中完成,由於沒有使用-tt選項,所以需要在被控主機上禁用requiretty選項
<192.168.100.65> ESTABLISH SSH CONNECTION FOR USER: None
<192.168.100.65> SSH: EXEC ssh -vvv -C ............ 192.168.100.65 '/bin/sh -c '"'"'/usr/bin/python && sleep 0'"'"''
<192.168.100.65> (0, '\n{".............')

從上面的過程對比中可以看到,開啟pipelining後,每次執行任務時都大量減少了ssh連線次數(只需要一次ssh連線),且省去了sftp傳輸任務檔案的過程,因此在管理大量節點時能極大提升執行效率。

修改ansible執行策略

預設ansible在遠端執行任務是按批並行執行的,一批控制多少臺主機由命令列的"-f"或"--forks"選項控制。例如,預設的並行程序數是5,如果有20臺被控主機,那麼只有在每5臺全部執行完一個任務才繼續下一批的5臺執行該任務,即使中間某臺機器效能較好,完成速度較快,它也會空閒地等待在那,直到所有20臺主機都執行完該任務才會以同樣的方式繼續下一個任務。如下所示:

h1 h2 h3 h4 h5(T1)-->h6 h7 h8 h9 h10(T1)...-->h16 h17 h18 h19 h20(T1)-->h1 h2 h3 h4 h5(T2)-->.....

在ansible 2.0中,添加了一個策略控制選項strategy,預設值為"linear",即上面按批並行處理的方式。還可以設定strategy的值為"free"。 在free模式下,ansible會盡可能快的切入到下一個主機。同樣是上面的例子,首先每5臺並行執行一個任務,當其中某一臺機器由於效能較好提前完成了該任務,它不會等待其他4臺完成,而是會跳出該任務讓ansible切入到下一臺機器來執行該任務。也就是說,這種模式下,一臺主機完成一個任務後,另一臺主機會立即執行任務,它是"前赴後繼"的方式。如下所示:

h1 h2 h3 h4 h5(T1)-->h1 h2 h3 h4 h6(T1)-->h1 h3 h4 h6 h7(T1)-->......-->h17 h18 h19 h20(T1) h1(T2)-->h18 h19 h20(T1) h1 h2(T2)-->...

設定的方式如下:

- hosts: all
  strategy: free
  tasks:
  ...

設定facts快取

ansible或ansible-playbook預設總是先收集facts資訊。在被控主機較少的情況下,收集資訊還可以容忍,如果被控主機數量非常大,收集facts資訊會消耗掉非常多時間。

可以設定"gather_facts: no"來禁止ansible收集facts資訊,但是有時候又需要使用facts中的內容,這時候可以設定facts的快取。例如,在空閒的時候收集facts,快取下來,在需要的時候直接讀取快取進行引用。

ansible的配置檔案中可以修改'gathering'的值為'smart'、'implicit'或者'explicit'。smart表示預設收集facts,但facts已有的情況下不會收集,即使用快取facts;implicit表示預設收集facts,要禁止收集,必須使用gather_facts: False;explicit則表示預設不收集,要顯式收集,必須使用gather_facts: Ture

在使用facts快取時(即設定為smart),ansible支援兩種facts快取:redis和jsonfile。

例如,以下是/etc/ansible/ansible.cfg中jsonfile格式的快取配置方法。

[defaults]
gathering = smart
fact_caching_timeout = 86400
fact_caching = jsonfile
fact_caching_connection = /path/to/cachedir

這裡設定的快取過期時間為86400秒,即快取一天。快取的json檔案放在/path/to/cachedir目錄下,各主機的快取檔案以主機名命名。快取檔案是一個json檔案,要檢視快取檔案,如/path/to/cachedir/192.168.100.59中的內容,使用如下語句即可。

cat /path/to/cachedir/192.168.100.59 | python -m json.tool

參考

https://www.linuxidc.com/Linux/2019-06/159057.htm Ansible快速入門教程

https://www.cnblogs.com/syy1757528181/p/13069043.html ansible常用模組彙總

ansible-playbook 變數定義與引用

https://juejin.cn/post/6844903631066513421 ansible自動化運維詳細教程及playbook詳解

Ansible系列(七):執行過程分析、非同步模式和速度優化