ansible小結(一)ansible的安裝
常用的自動化運維工具:
CFengine 最早的自動化運維工具
Chef
Puppet --基於Ruby開發,采用C/S架構,擴展性強,基於SSL認證 (遠程命令執行比較短板)
SaltStack --基於Python開發,采用C/S架構,比Puppet更輕量級,配置YMAL,配置更為簡單
Ansible --基於Python開發,無客戶端,更輕量級,配置語言也是采用YMAL,無守護進程 (目前市場上運用較多的還是puppet和saltstack)
一、簡介
Ansible 是一個配置管理和應用部署工具,功能類似於目前業界的配置管理工具 Chef,Puppet,Saltstack。Ansible 是通過 Python 語言開發。Ansible 平台由 Michael DeHaan 創建,他同時也是知名軟件 Cobbler 與 Func 的作者。Ansible 的第一個版本發布於 2012 年 2 月。Ansible 默認通過 SSH 協議管理機器,所以 Ansible 不需要安裝客戶端程序在服務器上。您只需要將 Ansible 安裝在一台服務器,在 Ansible 安裝完后,您就可以去管理控制其它服務器。不需要為它配置數據庫,Ansible 不會以 daemons 方式來啟動或保持運行狀態。Ansible 可以實現以下目標:
- 自動化部署應用
- 自動化管理配置
- 自動化的持續交付
- 自動化的(AWS)雲服務管理。
根據 Ansible 官方提供的信息,當前使用 Ansible 的用戶有:evernote、rackspace、NASA、Atlassian、twitter 等。
注:以上簡介來自於ibm developerworks 中國的介紹。
二、Ansible的安裝
1、yum源安裝
以centos為例,默認在源里沒有ansible,不過在fedora epel源里有ansible,配置完epel 源后,可以直接通過yum 進行安裝。這里以centos6.8為例:
yum install http://mirrors.sohu.com/fedora-epel/6/x86_64/epel-release-6-8.noarch.rpm
yum install ansible
2、apt-get安裝
在ubuntu及其衍生版中,可以通過增加ppa源進行apt-get安裝,具體如下:
sudo apt-get install software-properties-common sudo apt-add-repository ppa:ansible/ansible sudo apt-get update sudo apt-get install ansible
3、源碼安裝
源碼安裝需要python2.6以上版本,其依賴模塊paramiko、PyYAML、Jinja2、httplib2、simplejson、pycrypto模塊,以上模塊可以通過pip或easy_install 進行安裝,不過本部分既然提到的是源碼安裝,主要針對的無法上外網的情況下,可以通過pypi 站點搜索以上包,下載后通過python setup.py install 進行安裝。
最后通過github或pypi上下載ansible源碼包,通過python setup.py install 安裝即可。由於安裝過程相對簡單,這里略過,主要介紹安裝后,可能遇到的問題。
python setup.py install libyaml is not found or a compiler error: forcing --without-libyaml (if libyaml is installed correctly, you may need to specify the option --include-dirs or uncomment and modify the parameter include_dirs in setup.cfg) running install_lib running install_egg_info Removing /usr/lib64/python2.6/site-packages/PyYAML-3.11-py2.6.egg-info Writing /usr/lib64/python2.6/site-packages/PyYAML-3.11-py2.6.egg-info
在centos6.8系統中,可以通過yum -y install libyaml 包解決,或者從ISO文件中提供該包,通過rpm -ivh進行安裝。
b、安裝完ansible是,報錯如下:
[root@361way.com ansible-1.9.1]# ansible -h Traceback (most recent call last): File "/usr/local/src/ansible-devel/bin/ansible", line 36, in <module> from ansible.runner import Runner File "/usr/local/src/ansible-devel/lib/ansible/runner/__init__.py", line 62, in <module> from Crypto.Random import atfork File "/usr/lib64/python2.6/site-packages/Crypto/Random/__init__.py", line 29, in <module> from Crypto.Random import _UserFriendlyRNG File "/usr/lib64/python2.6/site-packages/Crypto/Random/_UserFriendlyRNG.py", line 38, in <module> from Crypto.Random.Fortuna import FortunaAccumulator File "/usr/lib64/python2.6/site-packages/Crypto/Random/Fortuna/FortunaAccumulator.py", line 39, in <module> import FortunaGenerator File "/usr/lib64/python2.6/site-packages/Crypto/Random/Fortuna/FortunaGenerator.py", line 34, in <module> from Crypto.Util.number import ceil_shift, exact_log2, exact_div File "/usr/lib64/python2.6/site-packages/Crypto/Util/number.py", line 56, in <module> if _fastmath is not None and not _fastmath.HAVE_DECL_MPZ_POWM_SEC: AttributeError: 'module' object has no attribute 'HAVE_DECL_MPZ_POWM_SEC'
import paramiko包時,報錯如下:
>>> import paramiko Traceback (most recent call last): File "<stdin>", line 1, in <module> File "/usr/lib/python2.6/site-packages/paramiko/__init__.py", line 69, in <module> from transport import randpool, SecurityOptions, Transport File "/usr/lib/python2.6/site-packages/paramiko/transport.py", line 32, in <module> from paramiko import util File "/usr/lib/python2.6/site-packages/paramiko/util.py", line 32, in <module> from paramiko.common import * File "/usr/lib/python2.6/site-packages/paramiko/common.py", line 98, in <module> from rng import StrongLockingRandomPool File "/usr/lib/python2.6/site-packages/paramiko/rng.py", line 22, in <module> from Crypto.Util.randpool import RandomPool as _RandomPool File "/usr/lib64/python2.6/site-packages/Crypto/Util/randpool.py", line 30, in <module> import Crypto.Random File "/usr/lib64/python2.6/site-packages/Crypto/Random/__init__.py", line 29, in <module> from Crypto.Random import _UserFriendlyRNG File "/usr/lib64/python2.6/site-packages/Crypto/Random/_UserFriendlyRNG.py", line 38, in <module> 19. from Crypto.Random.Fortuna import FortunaAccumulator 20. File "/usr/lib64/python2.6/site-packages/Crypto/Random/Fortuna/FortunaAccumulator.py", line 39, in <module> 21. import FortunaGenerator 22. File "/usr/lib64/python2.6/site-packages/Crypto/Random/Fortuna/FortunaGenerator.py", line 34, in <module> 23. from Crypto.Util.number import ceil_shift, exact_log2, exact_div 24. File "/usr/lib64/python2.6/site-packages/Crypto/Util/number.py", line 56, in <module> 25. if _fastmath is not None and not _fastmath.HAVE_DECL_MPZ_POWM_SEC: 26.AttributeError: 'module' object has no attribute 'HAVE_DECL_MPZ_POWM_SEC'
經網上查找,確認為pycrypto包安裝時依賴的GMP版本不對的問題,具體可以通過以下步驟驗證:
[root@361way.com pycrypto-2.6.1]# python setup.py build running build running build_py running build_ext running build_configure warning: GMP or MPIR library not found; Not building Crypto.PublicKey._fastmath.
解決方法:
打開 /usr/lib64/python2.6/site-packages/Crypto/Util/number.py 文件,可以 看到 56 行上的注釋說明,要求 libgmp 為 v5 以上版本。而系統現有版本為 4.1.4,把以下兩行暫時注釋掉,Ansible 執行正常。
if _fastmath is not None and not _fastmath.HAVE_DECL_MPZ_POWM_SEC: _warn("Not using mpz_powm_sec. You should rebuild using libgmp >= 5 to avoid timing attack vulnerability.", PowmInsecureWarning)
不過,此方法只是臨時加以解決,更好的方式是去將 libgmp 升級到符合要求的版本
c、執行時報錯
[root@361way.com src]# ansible test -m raw -a 'uptime' 10.212.52.14 | FAILED => to use the 'ssh' connection type with passwords, you must install the sshpass program 10.212.52.16 | FAILED => to use the 'ssh' connection type with passwords, you must install the sshpass program
安裝sshpass程序。默認源里沒有,我這里選擇直接從sohu源里下載安裝。
三、Ansible的配置與驗證
這里以pypi上下載的源碼內有一個examles包,可以將使用該示例文件做為默認配置,具體如下:
[root@361way.com ansible-1.9.1]# mkdir -p /etc/ansible [root@361way.com ansible-1.9.1]# cp -rp examples/* /etc/ansible/ [root@361way.com ansible-1.9.1]# cd /etc/ansible/
使用默認示例配置文件后,編輯/etc/ansible/hosts文件,通過以下方式驗證ansible是否可用:
[root@361way.com ~]# cat /etc/ansible/hosts [test] 10.212.52.252 ansible_ssh_user=root ansible_ssh_pass=361way.com 10.212.52.14 ansible_ssh_user=root ansible_ssh_pass=abc123 10.212.52.16 ansible_ssh_user=root ansible_ssh_pass=91it.org
以上的配置中,我配置了一個test組,該組下有三台主機,三台都使用root驗證,三台的密碼分別是361way.com、abc123、91it.org 。
注:后面的用戶和密碼項是非必須的,在配置key認證的情況下,不使用密碼也可以直接操作 。未使用key的,也可以在ansible通過 -k參數在操作前詢問手動輸入密碼。
[root@361way.com ~]# ansible test -a 'uptime' 10.212.52.252 | success | rc=0 >> 18:01pm up 21 days 3:24, 3 users, load average: 0.39, 0.38, 0.35 10.212.52.16 | success | rc=0 >> 18:09pm up 329 days 1:01, 2 users, load average: 0.08, 0.03, 0.05 10.212.52.14 | success | rc=0 >> 18:08pm up 329 days 0:23, 2 users, load average: 0.06, 0.06, 0.05
執行以上指令后,有結果輸出,證明安裝成功。
ansible小結(二)ansible架構
一、Ansible基本架構
Ansible 是一個模型驅動的配置管理器,支持多節點發布、遠程任務執行。默認使用 SSH 進行遠程連接。無需在被管理節點上安裝附加軟件,可使用各種編程語言進行擴展。
上圖為ansible的基本架構,從上圖可以了解到其由以下部分組成:
- 核心:ansible
- 核心模塊(Core Modules):這些都是ansible自帶的模塊
- 擴展模塊(Custom Modules):如果核心模塊不足以完成某種功能,可以添加擴展模塊
- 插件(Plugins):完成模塊功能的補充
- 劇本(Playbooks):ansible的任務配置文件,將多個任務定義在劇本中,由ansible自動執行
- 連接插件(Connectior Plugins):ansible基於連接插件連接到各個主機上,雖然ansible是使用ssh連接到各個主機的,但是它還支持其他的連接方法,所以需要有連接插件
- 主機群(Host Inventory):定義ansible管理的主機
二、ansible工作原理
以上是從網上找到的兩張ansible工作原理圖,兩張圖基本都是在架構圖的基本上進行的拓展。從上面的圖上可以了解到:
1、管理端支持local 、ssh、zeromq 三種方式連接被管理端,默認使用基於ssh的連接---這部分對應基本架構圖中的連接模塊;
2、可以按應用類型等方式進行Host Inventory(主機群)分類,管理節點通過各類模塊實現相應的操作---單個模塊,單條命令的批量執行,我們可以稱之為ad-hoc;
3、管理節點可以通過playbooks 實現多個task的集合實現一類功能,如web服務的安裝部署、數據庫服務器的批量備份等。playbooks我們可以簡單的理解為,系統通過組合多條ad-hoc操作的配置文件 。
三、ansible的七個命令
安裝完ansible后,發現ansible一共為我們提供了七個指令:ansible、ansible-doc、ansible-galaxy、ansible-lint、ansible-playbook、ansible-pull、ansible-vault 。這里我們只查看usage部分,詳細部分可以通過 “指令 -h” 的方式獲取。
1、ansible
[root@localhost ~]# ansible -h Usage: ansible <host-pattern> [options]
ansible是指令核心部分,其主要用於執行ad-hoc命令,即單條命令。默認后面需要跟主機和選項部分,默認不指定模塊時,使用的是command模塊。如:
[root@localhost ~]# ansible 127.0.0.1 -a 'date' 127.0.0.1 | SUCCESS | rc=0 >> Sun May 28 11:00:40 CST 2017
默認使用的模塊是可以在ansible.cfg 中進行修改的。ansible命令下的參數部分解釋如下
參數: -a 'Arguments', --args='Arguments' 命令行參數 -m NAME, --module-name=NAME 執行模塊的名字,默認使用 command 模塊,所以如果是只執行單一命令可以不用 -m參數 -i PATH, --inventory=PATH 指定庫存主機文件的路徑,默認為/etc/ansible/hosts. -u Username, --user=Username 執行用戶,使用這個遠程用戶名而不是當前用戶 -U --sud-user=SUDO_User sudo到哪個用戶,默認為 root -k --ask-pass 登錄密碼,提示輸入SSH密碼而不是假設基於密鑰的驗證 -K --ask-sudo-pass 提示密碼使用sudo -s --sudo sudo運行 -S --su 用 su 命令 -l --list 顯示所支持的所有模塊 -s --snippet 指定模塊顯示劇本片段 -f --forks=NUM 並行任務數。NUM被指定為一個整數,默認是5。 #ansible testhosts -a "/sbin/reboot" -f 10 重啟testhosts組的所有機器,每次重啟10台 --private-key=PRIVATE_KEY_FILE 私鑰路徑,使用這個文件來驗證連接 -v --verbose 詳細信息 all 針對hosts 定義的所有主機執行 -M MODULE_PATH, --module-path=MODULE_PATH 要執行的模塊的路徑,默認為/usr/share/ansible/ --list-hosts 只打印有哪些主機會執行這個 playbook 文件,不是實際執行該 playbook 文件 -o --one-line 壓縮輸出,摘要輸出.嘗試一切都在一行上輸出。 -t Directory, --tree=Directory 將內容保存在該輸出目錄,結果保存在一個文件中在每台主機上。 -B 后台運行超時時間 -P 調查后台程序時間 -T Seconds, --timeout=Seconds 時間,單位秒s -P NUM, --poll=NUM 調查背景工作每隔數秒。需要- b -c Connection, --connection=Connection 連接類型使用。可能的選項是paramiko(SSH),SSH和地方。當地主要是用於crontab或啟動。 --tags=TAGS 只執行指定標簽的任務 例子:ansible-playbook test.yml --tags=copy 只執行標簽為copy的那個任務 --list-hosts 只打印有哪些主機會執行這個 playbook 文件,不是實際執行該 playbook 文件 --list-tasks 列出所有將被執行的任務 -C, --check 只是測試一下會改變什么內容,不會真正去執行;相反,試圖預測一些可能發生的變化 --syntax-check 執行語法檢查的劇本,但不執行它 -l SUBSET, --limit=SUBSET 進一步限制所選主機/組模式 --limit=192.168.0.15 只對這個ip執行 --skip-tags=SKIP_TAGS 只運行戲劇和任務不匹配這些值的標簽 --skip-tags=copy_start -e EXTRA_VARS, --extra-vars=EXTRA_VARS 額外的變量設置為鍵=值或YAML / JSON #cat update.yml --- - hosts: {{ hosts }} remote_user: {{ user }} .............. #ansible-playbook update.yml --extra-vars "hosts=vipers user=admin" 傳遞{{hosts}}、{{user}}變量,hosts可以是 ip或組名 -l,--limit 對指定的 主機/組 執行任務 --limit=192.168.0.10,192.168.0.11 或 -l 192.168.0.10,192.168.0.11 只對這個2個ip執行任務
2、ansible-doc
[root@localhost ~]# ansible-doc -h Usage: ansible-doc [options] [module...] Options: -a, --all Show documentation for all modules -h, --help show this help message and exit -l, --list List available modules -M MODULE_PATH, --module-path=MODULE_PATH specify path(s) to module library (default=None) -s, --snippet Show playbook snippet for specified module(s) -v, --verbose verbose mode (-vvv for more, -vvvv to enable connection debugging) --version show program's version number and exit
該指令用於查看模塊信息,常用參數有兩個-l 和 -s ,具體如下
//列出所有已安裝的模塊 # ansible-doc -l //查看具體某模塊的用法,這里如查看command模塊 # ansible-doc -s command
3、ansible-galaxy
ansible-galaxy 指令用於方便的從https://galaxy.ansible.com/ 站點下載第三方擴展模塊,我們可以形象的理解其類似於centos下的yum、python下的pip或easy_install 。如下示例:
[root@localhost ~]# ansible-galaxy install aeriscloud.docker - downloading role 'docker', owned by aeriscloud - downloading role from https://github.com/AerisCloud/ansible-docker/archive/v1.0.0.tar.gz - extracting aeriscloud.docker to /etc/ansible/roles/aeriscloud.docker - aeriscloud.docker was installed successfully
這個安裝了一個aeriscloud.docker組件,前面aeriscloud是galaxy上創建該模塊的用戶名,后面對應的是其模塊。在實際應用中也可以指定txt或yml 文件進行多個組件的下載安裝。這部分可以參看官方文檔。
4、ansible-lint
ansible-lint是對playbook的語法進行檢查的一個工具。用法是ansible-lint playbook.yml 。
5、ansible-playbook
該指令是使用最多的指令,其通過讀取playbook 文件后,執行相應的動作,這個后面會做為一個重點來講。
6、ansible-pull
該指令使用需要談到ansible的另一種模式---pull 模式,這和我們平常經常用的push模式剛好相反,其適用於以下場景:你有數量巨大的機器需要配置,即使使用非常高的線程還是要花費很多時間;你要在一個沒有網絡連接的機器上運行Anisble,比如在啟動之后安裝。這部分也會單獨做一節來講。
7、ansible-vault
ansible-vault主要應用於配置文件中含有敏感信息,又不希望他能被人看到,vault可以幫你加密/解密這個配置文件,屬高級用法。主要對於playbooks里比如涉及到配置密碼或其他變量時,可以通過該指令加密,這樣我們通過cat看到的會是一個密碼串類的文件,編輯的時候需要輸入事先設定的密碼才能打開。這種playbook文件在執行時,需要加上 –ask-vault-pass參數,同樣需要輸入密碼后才能正常執行。具體該部分可以參查官方博客。
ansible小結(三)Inventory與Patterns
Ansible的Inventory文件,可以理解為saltstack中的salt-key中的所有minion的列表以及用戶自定義的nodegroup的概念,默認情況下這個文件是/etc/ansible/hosts ,后面還會講到Dynamic Inventory,本節主要講靜態主機群部分。Patterns(模式)部分我們可以理解為正則表達式,通過Patterns我們可以匹配Inventory分組中的部分主機。
一、Hosts and Groups(主機與組)
對於/etc/ansible/hosts最簡單的定義格式像下面:
1、簡單的主機和組
mail.361way.com [webservers] web1.361way.com web2.361way.com [dbservers] db1.361way.com db2.361way.com
a、中括號中的名字代表組名,你可以根據你自己的需求將龐大的主機分成具有標識的組,如上面我分了兩個組webservers和dbservers組;
b、主機(hosts)部分可以使用域名、主機名、IP地址表示;當然使用前兩者時,也需要主機能反解析到相應的IP地址,一般此類配置中多使用IP地址;
2、端口與別名
如果某些主機的SSH運行在自定義的端口上,ansible使用Paramiko進行ssh連接時,不會使用你SSH配置文件中列出的端口,但是如果修改ansible使用openssh進行ssh連接時將會使用:
192.168.0.10:5309
假如你想要為某些靜態IP設置一些別名,類似於SaltStack中minion配置文件中id的參數配置。你可以這樣做:
jumper ansible_ssh_port = 5555 ansible_ssh_host = 192.168.1.50
3、指定主機范圍
[webservers] www[01:50].361way.com [databases] db-[a:f].91it.org
上面指定了從web1到web50,webservers組共計50台主機;databases組有db-a到db-f共6台主機
4、使用主機變量
以下是Hosts部分中經常用到的變量部分
ansible_ssh_host # 要連接的主機名 ansible_ssh_port # 端口號默認是22 ansible_ssh_user # ssh連接時默認使用的用戶名 ansible_ssh_pass # ssh連接時的密碼 ansible_sudo_pass # 使用sudo連接用戶是的密碼 ansible_ssh_private_key_file # 秘鑰文件如果不想使用ssh-agent管理時可以使用此選項 ansible_shell_type # shell的類型默認sh ansible_connection # SSH 連接的類型: local , ssh , paramiko在 ansible 1.2 之前默認是 paramiko ,后來智能選擇,優先使用基於 ControlPersist 的 ssh (支持的前提) ansible_python _ interpreter #用來指定 python 解釋器的路徑,同樣可以指定ruby 、perl 的路徑
示例
[test] 10.212.52.252 ansible_ssh_user=root ansible_ssh_pass='361way.com' 10.212.52.14 ansible_ssh_user=test1 ansible_ssh_pass='91it.org' 10.212.52.16 ansible_ssh_user=test2 ansible_ssh_port=7788 ansible_ssh_pass='123456'
上面的示例中指定了三台主機,三台主機的用密碼分別是361way.com、91it.org、123456,指定的ssh連接的用戶名分別為root、test1、test2,ssh 端口分別為22、22、7788 ,這樣在ansible命令執行的時候就不用再指令用戶和密碼等了,執行結果如下:
[root@361way.com ~]# ansible test -a 'uptime' 10.212.52.252 | success | rc=0 >> 01:34am up 23 days 10:57, 2 users, load average: 0.42, 0.39, 0.41 10.212.52.16 | success | rc=0 >> 01:41am up 331 days 8:33, 2 users, load average: 0.00, 0.01, 0.05 10.212.52.14 | success | rc=0 >> 01:40am up 331 days 7:55, 2 users, load average: 0.09, 0.03, 0.05
5、組內變量
變量也可以通過組名,應用到組內的所有成員:
[test] host1 host2 [test:vars] ntp_server=ntp.361way.com proxy=proxy.361way.com
上面test組中包含兩台主機,通過對test組指定vars變更,相應的host1和host2相當於相應的指定了ntp_server和proxy變量參數值 。
6、組的包含與組內變量
[hangzhou] host1 host2 [jiaxing] host2 host3 [zhejiang:children] hangzhou jiaxing [zhejiang:vars] some_server=foo.southeast.example.com halon_system_timeout=30 self_destruct_countdown=60 escape_pods=2 [china:children] zhejiang henan shandong hebei
如上面的示例中,我指定了杭州組我有host1、hosts2;嘉興組我有host3、host4主機;我又指定了一個組浙江組,同時包含杭州和嘉興;同時為該組內的所有主機指定了四個vars變量。后面我又設定了一個組中國組,包含浙江、河南、山東、河北
注:由於vars變量在ansible ad-hoc部分中基本用不到,主要用在ansible-playbook中,后面的章節部分也會提到。
以上部分基本上是完全按照官方Inventory 文檔部分進行了翻譯和微小的變化。英文感覺還可以的可以直接查看官方頁面。
二、Patterns(主機與組正則匹配部分)
把Patterns 直接理解為正則實際是不完全准確的,正常的理解為patterns意味着在ansible中管理哪些主機,也可以理解為,要與哪台主機進行通信。在探討這個問題之前我們先看下ansible的用法:
ansible <pattern_goes_here> -m <module_name> -a <arguments>
直接上一個示例:
ansible webservers -m service -a "name=httpd state=restarted"
這里是對webservers 組或主機重啟httpd服務 ,其中webservers 就是Pattern部分。而之所以上面我說Pattern(模式)可以理解為正則,主要針對下面經常用到的用法而言的。
1、表示所有的主機可以使用all 或 *
2、通配符與邏輯或
利用通配符還可以指定一組具有規則特征的主機或主機名,冒號表示or---邏輯或
one.361way.com one.361way:two.361way.com 192.168.1.50 192.168.1.*
當然,這里的*通配符也可以用在前面,如:
*.361way.com *.com
上面的用法,在多個組之間同樣適用 ,如:
webservers webservers:dbservers //表示兩個組中所有的主機
3、邏輯非與邏輯and
當然你可以做出非的表達式,例如,目標主機必須在組webservers但不在phoenix組中
webserver:!phoenix
你還可以做出交集的表達式,例如,目標主機必須即在組webservers中又在組staging中
webservers:&staging
一個更復雜的示例:
webserver:dbservers:&staging:!phoenix
上面這個復雜的表達式最后表示的目標主機必須滿足:在webservers或者dbservers組中,必須還存在於staging組中,但是不在phoenix組中。這些可以看作是SaltStack中Compound matchers
4、混合高級用法
*.361way.com:*.org
還可以在開頭的地方使用”~”,用來表示這是一個正則表達式:
~(web|db).*\.91it\.org
到這里估計你應該用能明白為什么前面我會提到Patterns 可以理解為正則的原因了。最后部分給兩個ansible-playbook中具體可能用的用法:
a、在ansible-palybook命令中,你也可以使用變量來組成這樣的表達式,但是你必須使用“-e”的選項來指定這個表達式(通常我們不這樣用):
ansible-palybook -e webservers:!{{excluded}}:&{{required}}
b、在ansible和ansible-playbook中,還可以通過一個參數”–limit”來明確指定排除某些主機或組:
ansible-playbook site.yml --limit datacenter2
ansible小結(四)ansible.cfg與默認配置
Ansible默認安裝好后有一個配置文件/etc/ansible/ansible.cfg,該配置文件中定義了ansible的主機的默認配置部分,如默認是否需要輸入密碼、是否開啟sudo認證、action_plugins插件的位置、hosts主機組的位置、是否開啟log功能、默認端口、key文件位置等等。
[defaults] # some basic default values... hostfile = /etc/ansible/hosts \\指定默認hosts配置的位置 # library_path = /usr/share/my_modules/ remote_tmp = $HOME/.ansible/tmp pattern = * forks = 5 poll_interval = 15 sudo_user = root \\遠程sudo用戶 #ask_sudo_pass = True \\每次執行ansible命令是否詢問ssh密碼 #ask_pass = True \\每次執行ansible命令時是否詢問sudo密碼 transport = smart remote_port = 22 module_lang = C gathering = implicit host_key_checking = False \\關閉第一次使用ansible連接客戶端是輸入命令提示 log_path = /var/log/ansible.log \\需要時可以自行添加。chown -R root:root ansible.log system_warnings = False \\關閉運行ansible時系統的提示信息,一般為提示升級 # set plugin path directories here, separate with colons action_plugins = /usr/share/ansible_plugins/action_plugins callback_plugins = /usr/share/ansible_plugins/callback_plugins connection_plugins = /usr/share/ansible_plugins/connection_plugins lookup_plugins = /usr/share/ansible_plugins/lookup_plugins vars_plugins = /usr/share/ansible_plugins/vars_plugins filter_plugins = /usr/share/ansible_plugins/filter_plugins fact_caching = memory [accelerate] accelerate_port = 5099 accelerate_timeout = 30 accelerate_connect_timeout = 5.0 # The daemon timeout is measured in minutes. This time is measured # from the last activity to the accelerate daemon. accelerate_daemon_timeout = 30
本篇就結合一個示例對其進行下了解。我在對之前未連接的主機進行連結時報錯如下:
[root@361way.com ~]# ansible test -a 'uptime' 10.212.52.14 | FAILED => Using a SSH password instead of a key is not possible because Host Key checking is enabled and sshpass does not support this. Please add this host's fingerprint to your known_hosts file to manage this host. 10.212.52.16 | FAILED => Using a SSH password instead of a key is not possible because Host Key checking is enabled and sshpass does not support this. Please add this host's fingerprint to your known_hosts file to manage this host.
從上面的輸出提示上基本可以了解到由於在本機的~/.ssh/known_hosts文件中並有fingerprint key串,ssh第一次連接的時候一般會提示輸入yes 進行確認為將key字符串加入到 ~/.ssh/known_hosts 文件中。
方法1:
了解到問題原因為,我們了解到進行ssh連接時,可以使用-o參數將StrictHostKeyChecking設置為no,使用ssh連接時避免首次連接時讓輸入yes/no部分的提示。通過查看ansible.cfg配置文件,發現如下行:
[ssh_connection] # ssh arguments to use # Leaving off ControlPersist will result in poor performance, so use # paramiko on older platforms rather than removing it #ssh_args = -o ControlMaster=auto -o ControlPersist=60s
所以這里我們可以啟用ssh_args 部分,使用下面的配置,避免上面出現的錯誤:
ssh_args = -o ControlMaster=auto -o ControlPersist=60s -o StrictHostKeyChecking=no
方法2:
在ansible.cfg配置文件中,也會找到如下部分:
# uncomment this to disable SSH key host checking host_key_checking = False
默認host_key_checking部分是注釋的,通過找開該行的注釋,同樣也可以實現跳過 ssh 首次連接提示驗證部分。由於配置文件中直接有該選項,所以推薦用方法2 。
其他部分
由於官方給的說明比較詳細,同時ansible.cfg 文件本身默認也有注釋提示部分,所以不做過多說明,這里再舉個例子,默認ansible 執行的時候,並不會輸出日志到文件,不過在ansible.cfg 配置文件中有如下行:
# logging is off by default unless this path is defined # if so defined, consider logrotate log_path = /var/log/ansible.log
同樣,默認log_path這行是注釋的,打開該行的注釋,所有的命令執行后,都會將日志輸出到/var/log/ansible.log 文件,便於了解在何時執行了何操作及其結果,如下:
[root@361way.com ansible]# cat /var/log/ansible.log 2015-05-04 01:57:19,758 p=4667 u=root | 2015-05-04 01:57:19,759 p=4667 u=root | /usr/bin/ansible test -a uptime 2015-05-04 01:57:19,759 p=4667 u=root | 2015-05-04 01:57:20,563 p=4667 u=root | 10.212.52.252 | success | rc=0 >> 01:57am up 23 days 11:20, 2 users, load average: 0.38, 0.38, 0.40 7.2015-05-04 01:57:20,831 p=4667 u=root | 10.212.52.14 | success | rc=0 >> 02:03am up 331 days 8:19, 2 users, load average: 0.08, 0.05, 0.05 2015-05-04 01:57:20,909 p=4667 u=root | 10.212.52.16 | success | rc=0 >> 02:05am up 331 days 8:56, 2 users, load average: 0.00, 0.01, 0.05
ansible小結(五)Dynamic Inventory
Ansible Inventory實際上是包含靜態Inventory和動態Inventory兩部分,靜態Inventory指的是在文件/etc/ansible/hosts中指定的主機和組,Dynamic Inventory指通過外部腳本獲取主機列表,並按照ansible 所要求的格式返回給ansilbe命令的。這部分一般會結合CMDB資管系統、zabbix 監控系統、crobble安裝系統、雲計算平台等獲取主機信息。由於主機資源一般會動態的進行增減,而這些系統一般會智能更新。我們可以通過這些工具提供的API 或者接入庫查詢等方式返回主機列表。
一、最簡單示例
由於Ansible在接受腳本動態獲取主機信息時支持的是json格式,這里我也不從其他系統中取了,向通過一段代碼打印一個段json格式的主機信息:
#!/usr/bin/env python # coding=utf-8 import json host1ip = ['10.212.52.252','10.212.52.14'] host2ip = ['10.212.52.16'] group = 'test1' group2 = 'test2' hostdata = {group:{"hosts":host1ip},group2:{"hosts":host2ip}} print json.dumps(hostdata,indent=4)
注:
1、主機部分必須是列表格式的;
2、hostdata行,其中的"hosts" 部分可以省略,但如果使用時,必須是"hosts" ,不能是其他如‘‘hostlist’’ 等字符串。省略后可以這樣寫:
hostdata = {group:host1ip,group2:host2ip}
直接執行該段代碼結果如下:
[root@361way.com ~]# python twogroup.py { "test1": { "hosts": [ "10.212.52.252", "10.212.52.14" ] }, "test2": { "hosts": [ "10.212.52.16" ] } }
上面定義了兩個主機組,test1組內包含主機10.212.52.252、10.212.52.14,test2組內包含主機10.212.52.16 。ansible可以通過如下方法調用:
[root@361way.com ~]# ansible -i twogroup.py test1 -m command -a 'uptime' -k SSH password: 10.212.52.252 | success | rc=0 >> 23:01pm up 24 days 8:24, 2 users, load average: 0.21, 0.35, 0.39 10.212.52.14 | success | rc=0 >> 23:08pm up 332 days 5:23, 2 users, load average: 0.00, 0.01, 0.05 [root@361way.com ~]# ansible -i twogroup.py test2 -m command -a 'uptime' -k SSH password: 10.212.52.16 | success | rc=0 >> 23:09pm up 332 days 6:00, 2 users, load average: 0.08, 0.06, 0.05
二、復雜示例
在靜態主機配置文件示例中,會有組變量(vars),組之間的包含,如下圖(點擊圖片看大圖):
如果以上部分想要,通過腳本獲取實現,實現后返回的json格式應該如下圖:
像上面這種復雜的返回格式,一般不會用在ad-hoc環境中,多數會用在ansible-playbook 中,應為playbook文件中有時假會涉及到vars 參數的傳參。
三、從第三方平台獲取主機示例
這個在本篇一開頭就提到了,我們從如cobbler、cmdb等平台上獲取的示例。由於ansible 的發起者(作者)同時又是cobbler軟件的創建者,所以官方文檔給了我們cobbler的示例,同時給出了一個從AWS 去上獲取主機信息的示例 。如下:
關於如何從aws上獲取主機信息並入庫,這個我之前也寫過相關的篇章,具體也可以參看我之前的博文----AWS主機資產管理 (該篇也是純python實現的)。
通過zabbix api 獲取主機信息,我也寫過一篇zabbix小結(八)Zabbix api ,想要通過zabbix平台上獲取主機列表信息也可以參考下。
四、其他
1、ansible -i 參數后調用的腳本並非一定是py文件,也可以是其他腳本輸出的結果,這里做了個簡單的測試:
[root@361way.com yaml]# ansible -i group.sh test1 -m command -a 'uptime' -k SSH password: 10.212.52.16 | success | rc=0 >> 00:18am up 332 days 7:10, 2 users, load average: 0.00, 0.01, 0.05 10.212.52.14 | success | rc=0 >> 00:17am up 332 days 6:32, 2 users, load average: 0.01, 0.03, 0.05 10.212.52.252 | success | rc=0 >> 00:11am up 24 days 9:33, 2 users, load average: 0.49, 0.42, 0.41 [root@localhost yaml]# cat group.sh #!/bin/bash groups=''' { "test1": { "hosts": [ "10.212.52.252", "10.212.52.14", "10.212.52.16" ] } } ''' echo $groups
2、-i 參數指定的腳本需要有可執行權限 ,不然會報錯,如下:
[root@361way.com yaml]# ansible -i hostjson.py AA -a 'uptime' ERROR: The file hostjson.py looks like it should be an executable inventory script, but is not marked executable. Perhaps you want to correct this with `chmod +x hostjson.py`?
ansible小結(六)Ad-hoc與commands模塊
Ad-Hoc 是指ansible下臨時執行的一條命令,並且不需要保存的命令,對於復雜的命令后面會說playbook。講到Ad-hoc 就要提到模塊,所有的命令執行都要依賴於事先寫好的模塊,默認安裝好的ansible 里面已經自帶了很多模塊,如:command、raw、shell、file、cron等,具體可以通過ansible-doc -l 進行查看 。
一、Ad-hoc
1、直接執行
這里還是先來一個上幾篇幅經常用到的一個例子:
root@361way ~]# ansible 10.212.52.252 -a 'uptime' -k SSH password: 10.212.52.252 | success | rc=0 >> 10:10am up 27 days 19:33, 2 users, load average: 0.39, 0.34, 0.33
一個ad-hoc命令的執行,需要按以下格式進行執行:
ansible 主機或組 -m 模塊名 -a '模塊參數' ansible參數
主機和組,是在/etc/ansible/hosts 里進行指定的部分,當然動態Inventory 使用的是腳本從外部應用里獲取的主機,這部分具體可以參考ansible小結(五)Dynamic Inventory ;
- 模塊名,可以通過ansible-doc -l 查看目前安裝的模塊,默認不指定時,使用的是command模塊,具體可以查看/etc/ansible/ansible.cfg 的“#module_name = command ” 部分,默認模塊可以在該配置文件中進行修改;
- 模塊參數,可以通過 “ansible-doc 模塊名” 查看具體的用法及后面的參數;
- ansible參數,可以通過ansible命令的幫忙信息里查看到,這里有很多參數可以供選擇,如是否需要輸入密碼、是否sudo等
2、后台執行
當命令執行時間比較長時,也可以放到后台執行,這里會用到-B、-P參數,如下:
ansible all -B 3600 -a "/usr/bin/long_running_operation --do-stuff" \\后台執行命令 3600s,-B 表示后台執行的時間 ansible all -m async_status -a "jid=123456789" \\檢查任務的狀態 ansible all -B 1800 -P 60 -a "/usr/bin/long_running_operation --do-stuff" \\后台執行命令最大時間是 1800s 即 30 分鍾,-P 每 60s 檢查下狀態默認 15s
示例如下:
[root@361way ~]# ansible 10.212.52.252 -B 3600 -P 0 -a 'watch ls' background launch... 10.212.52.252 | success >> { "ansible_job_id": "411650646689.13501", "results_file": "/root/.ansible_async/411650646689.13501", "started": 1 } [root@361way ~]# ansible 10.212.52.252 -m async_status -a 'jid=411650646689.13501' 10.212.52.252 | success >> { "ansible_job_id": "411650646689.13501", "changed": false, "finished": 0, "results_file": "/root/.ansible_async/411650646689.13501", "started": 1 }
不指定-P或-P參數為非0時,該任務就會按-P直接的參數一直刷新下去,直到超出-B參數指定的時間或命令執行完成:
[root@361way ~]# ansible 10.212.52.252 -B 3600 -a 'watch ls' background launch... 10.212.52.252 | success >> { "ansible_job_id": "397200656414.15008", "results_file": "/root/.ansible_async/397200656414.15008", "started": 1 } 10.212.52.252 | success >> { "ansible_job_id": "397200656414.15008", "changed": false, "finished": 0, "results_file": "/root/.ansible_async/397200656414.15008", "started": 1 } <job 397200656414.15008> polling on 10.212.52.252, 3585s remaining …………………………………………略
二、commands模塊
上面已經提到,ansbile自身已經自帶了很多模塊,可以通過ansible-doc -l 進行查看。這里就結合command、shell、raw、script模塊了解下其用法。
上面四個模塊都屬於commands 類。
- command模塊,該模塊通過-a跟上要執行的命令可以直接執行,不過命令里如果有帶有如下字符部分則執行不成功 ,command模塊不是調用的shell的指令,所以沒有bash的環境變量,也不能使用shell的一些操作方式,其他和shell沒有區別 “ so variables like $HOME and operations like "<", ">", "|", and "&" will not work (use the shell module if you need these features).”;
- shell 模塊,用法其本和command一樣,不過的是其是通過/bin/sh進行執行,所以shell 模塊可以執行任何命令,就像在本機執行一樣,“ It is almost exactly like the command module but runs the command through a shell (/bin/sh) on the remote node.”;
- raw模塊,用法和shell 模塊一樣 ,其也可以執行任意命令,就像在本機執行一樣,raw很多地方和shell類似,更多的地方建議使用shell和command模塊。但是如果是使用老版本python,需要用到raw,又或者是客戶端是路由器,因為沒有安裝python模塊,那就需要使用raw模塊了 “Executes a low-down and dirty SSH command, not going through the module subsystem. There is no change handler support for this module. This module does not require python on the remote system,This is useful and should only be done in two cases. The first case is installing python-simplejson on older (Python 2.4 and before) hosts that need it as a dependency to run modules, since nearly all core modules require it. Another is speaking to any devices such as routers that do not have any Python installed. In any other case, using the shell or command module is much more appropriate. Arguments given to raw are run directly through the configured remote shell. Standard output, error output and return code are returned when available. There is no change handler support for this module. This module does not require python on the remote system, much like the script module.”
- script模塊,其是將管理端的shell 在被管理主機上執行,其原理是先將shell 復制到遠程主機,再在遠程主機上執行,原理類似於raw模塊,“This module does not require python on the remote system, much like the raw module.” 。
注:raw模塊和comand、shell 模塊不同的是其沒有chdir、creates、removes參數,chdir參數的作用就是先切到chdir指定的目錄后,再執行后面的命令,這在后面很多模塊里都會有該參數 。
command模塊包含如下選項:
- creates:一個文件名,當該文件存在,則該命令不執行
- free_form:要執行的linux指令
- chdir:在執行指令之前,先切換到該指定的目錄
- removes:一個文件名,當該文件不存在,則該選項不執行
- executable:切換shell來執行指令,該執行路徑必須是一個絕對路徑
command模塊、raw模塊、shell模塊示例:
[root@361way ~]# ansible 10.212.52.252 -m command -a 'ps auxf|grep snmp' 10.212.52.252 | FAILED | rc=1 >> ERROR: Unsupported option (BSD syntax) ********* simple selection ********* ********* selection by list ********* -A all processes -C by command name -N negate selection -G by real group ID (supports names) -a all w/ tty except session leaders -U by real user ID (supports names) -d all except session leaders -g by session OR by effective group name -e all processes -p by process ID T all processes on this terminal -s processes in the sessions given a all w/ tty, including other users -t by tty g OBSOLETE -- DO NOT USE -u by effective user ID (supports names) r only running processes U processes for specified users x processes w/o controlling ttys t by tty *********** output format ********** *********** long options *********** -o,o user-defined -f full --Group --User --pid --cols --ppid -j,j job control s signal --group --user --sid --rows --info -O,O preloaded -o v virtual memory --cumulative --format --deselect -l,l long u user-oriented --sort --tty --forest --version -F extra full X registers --heading --no-heading --context ********* misc options ********* -V,V show version L list format codes f ASCII art forest -m,m,-L,-T,H threads S children in sum -y change -l format -M,Z security data c true command name -c scheduling class -w,w wide output n numeric WCHAN,UID -H process hierarchy [root@361way ~]# ansible 10.212.52.252 -m raw -a 'ps auxf|grep snmp' 10.212.52.252 | success | rc=0 >> root 5580 25.0 0.0 12876 1792 pts/2 Ss+ 12:36 0:00 \_ bash -c ps auxf|grep snmp root 5607 0.0 0.0 5720 832 pts/2 S+ 12:36 0:00 \_ grep snmp root 24364 0.0 0.0 70416 6696 ? SNl May15 0:22 /usr/sbin/snmpd -r -A -LF i /var/log/net-snmpd.log -p /var/run/snmpd.pid [root@361way ~]# ansible 10.212.52.252 -m shell -a 'ps auxf|grep snmp' 10.212.52.252 | success | rc=0 >> root 5803 0.0 0.0 11308 1308 pts/2 S+ 12:36 0:00 \_ /bin/sh -c ps auxf|grep snmp root 5805 0.0 0.0 4260 572 pts/2 S+ 12:36 0:00 \_ grep snmp root 24364 0.0 0.0 70416 6696 ? SNl May15 0:22 /usr/sbin/snmpd -r -A -LF i /var/log/net-snmpd.log -p /var/run/snmpd.pid 上面的執行結果可以看到,我這里加了管道,command模塊執行時出錯,而使用raw模塊和shell 模塊都正常。
使用chdir的示例:
[root@361way ~]# ansible 10.212.52.252 -m command -a 'chdir=/tmp/361way touch test.file' 10.212.52.252 | success | rc=0 >> [root@361way ~]# ansible 10.212.52.252 -m shell -a 'chdir=/tmp/361way touch test2.file' 10.212.52.252 | success | rc=0 >> [root@361way ~]# ansible 10.212.52.252 -m raw -a 'chdir=/tmp/361way touch test3.file' 10.212.52.252 | success | rc=0 >>
從上面執行結果來看,三個命令都執行成功了。不過通過在遠程主機上查看,前兩個文件被成功創建
linux-wdh1:/tmp/361way # ls /tmp/361way test.file test2.file
使用raw模塊的執行的結果文件也被正常創建了,不過不是在chdir 指定的目錄,而是在當前執行用戶的家目錄。
linux-wdh1:~ # ls ~/test3.file /root/test3.file
creates與removes示例:
這里我在測試主機上創建/tmp/361way/server.txt文件,執行結果如下:
[root@361way ~]# ansible 10.212.52.252 -a 'creates=/tmp/361way/server.txt uptime' 10.212.52.252 | success | rc=0 >> skipped, since /tmp/361way/server.txt exists [root@361way ~]# ansible 10.212.52.252 -a 'removes=/tmp/361way/server.txt uptime' 10.212.52.252 | success | rc=0 >> 15:11pm up 28 days 0:34, 2 users, load average: 0.75, 0.46, 0.39
script模塊示例:
[root@361way ~]# cat script.sh #!/bin/bash df -hl ifconfig ps auxf|grep snmp [root@361way ~]# ansible 10.212.52.252 -m script -a 'scrip.sh' 10.212.52.252 | FAILED => file or module does not exist: /root/scrip.sh [root@361way ~]# ansible 10.212.52.252 -m script -a 'script.sh' 10.212.52.252 | success >> { "changed": true, "rc": 0, "stderr": "OpenSSH_5.3p1, OpenSSL 1.0.1e-fips 11 Feb 2013\ndebug1: Reading configuration data /etc/ssh/ssh_config\r\ndebug1: Applying options for *\r\ndebug1: auto-mux: Trying existing master\r\nControl socket connect(/root/.ansible/cp/ansible-ssh-10.212.52.252-22-root): Connection refused\r\ndebug1: Connecting to 10.212.52.252 [10.212.52.252] port 22.\r\ndebug1: fd 3 clearing O_NONBLOCK\r\ndebug1: Connection established.\r\ndebug1: permanently_set_uid: 0/0\r\ndebug1: identity file /root/.ssh/identity type -1\r\ndebug1: identity file /root/.ssh/identity-cert type -1\r\ndebug1: identity file /root/.ssh/id_rsa type -1\r\ndebug1: identity file /root/.ssh/id_rsa-cert type -1\r\ndebug1: identity file /root/.ssh/id_dsa type -1\r\ndebug1: identity file /root/.ssh/id_dsa-cert type -1\r\ndebug1: identity file /root/.ssh/id_ecdsa type -1\r\ndebug1: identity file /root/.ssh/id_ecdsa-cert type -1\r\ndebug1: Remote protocol version 2.0, remote software version OpenSSH_6.2\r\ndebug1: match: OpenSSH_6.2 pat OpenSSH*\r\ndebug1: Enabling compatibility mode for protocol 2.0\r\ndebug1: Local version string SSH-2.0-OpenSSH_5.3\r\ndebug1: SSH2_MSG_KEXINIT sent\r\ndebug1: SSH2_MSG_KEXINIT received\r\ndebug1: kex: server->client aes128-ctr hmac-md5 zlib@openssh.com\r\ndebug1: kex: client->server aes128-ctr hmac-md5 zlib@openssh.com\r\ndebug1: SSH2_MSG_KEX_DH_GEX_REQUEST(1024<1024<8192) sent\r\ndebug1: expecting SSH2_MSG_KEX_DH_GEX_GROUP\r\ndebug1: SSH2_MSG_KEX_DH_GEX_INIT sent\r\ndebug1: expecting SSH2_MSG_KEX_DH_GEX_REPLY\r\ndebug1: Host '10.212.52.252' is known and matches the RSA host key.\r\ndebug1: Found key in /root/.ssh/known_hosts:1\r\ndebug1: ssh_rsa_verify: signature correct\r\ndebug1: SSH2_MSG_NEWKEYS sent\r\ndebug1: expecting SSH2_MSG_NEWKEYS\r\ndebug1: SSH2_MSG_NEWKEYS received\r\ndebug1: SSH2_MSG_SERVICE_REQUEST sent\r\ndebug1: SSH2_MSG_SERVICE_ACCEPT received\r\ndebug1: Authentications that can continue: publickey,password,keyboard-interactive\r\ndebug1: Next authentication method: keyboard-interactive\r\ndebug1: Enabling compression at level 6.\r\ndebug1: Authentication succeeded (keyboard-interactive).\r\ndebug1: setting up multiplex master socket\r\nControlSocket /root/.ansible/cp/ansible-ssh-10.212.52.252-22-root already exists, disabling multiplexing\r\ndebug1: channel 0: new [client-session]\r\ndebug1: Requesting no-more-sessions@openssh.com\r\ndebug1: Entering interactive session.\r\ndebug1: Sending environment.\r\ndebug1: Sending env LANG = en_US.UTF-8\r\ndebug1: Sending command: LANG=C LC_CTYPE=C /root/.ansible/tmp/ansible-tmp-1431924855.88-242473611260231/script.sh \r\ndebug1: client_input_channel_req: channel 0 rtype exit-status reply 0\r\ndebug1: client_input_channel_req: channel 0 rtype eow@openssh.com reply 0\r\ndebug1: channel 0: free: client-session, nchannels 1\r\ndebug1: fd 1 clearing O_NONBLOCK\r\ndebug1: fd 2 clearing O_NONBLOCK\r\nConnection to 10.212.52.252 closed.\r\nTransferred: sent 1928, received 3920 bytes, in 0.1 seconds\r\nBytes per second: sent 37017.0, received 75262.7\r\ndebug1: Exit status 0\r\ndebug1: compress outgoing: raw data 537, compressed 375, factor 0.70\r\ndebug1: compress incoming: raw data 1837, compressed 1019, factor 0.55\r\n", "stdout": "Filesystem Size Used Avail Use% Mounted on\r\n/dev/sda2 9.9G 872M 8.5G 10% /\r\nudev 3.9G 128K 3.9G 1% /dev\r\ntmpfs 3.9G 76K 3.9G 1% /dev/shm\r\n/dev/sda3 5.0G 219M 4.5G 5% /boot\r\n/dev/sda8 40G 15G 23G 40% /home\r\n/dev/sda9 9.9G 5.2G 4.3G 55% /opt\r\n/dev/sda6 5.0G 2.7G 2.1G 57% /tmp\r\n/dev/sda5 9.9G 3.4G 6.0G 36% /usr\r\n/dev/sda7 9.9G 823M 8.6G 9% /var\r\neth0 Link encap:Ethernet HWaddr 00:50:56:A8:65:7E \r\n inet addr:10.212.52.252 Bcast:10.212.52.255 Mask:255.255.255.0\r\n inet6 addr: fe80::250:56ff:fea8:657e/64 Scope:Link\r\n UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1\r\n RX packets:24112135 errors:0 dropped:792372 overruns:0 frame:0\r\n TX packets:10697339 errors:0 dropped:0 overruns:0 carrier:0\r\n collisions:0 txqueuelen:1000 \r\n RX bytes:17137233328 (16343.3 Mb) TX bytes:13390377826 (12770.0 Mb)\r\n\r\nlo Link encap:Local Loopback \r\n inet addr:127.0.0.1 Mask:255.0.0.0\r\n inet6 addr: ::1/128 Scope:Host\r\n UP LOOPBACK RUNNING MTU:16436 Metric:1\r\n RX packets:3407332 errors:0 dropped:0 overruns:0 frame:0\r\n TX packets:3407332 errors:0 dropped:0 overruns:0 carrier:0\r\n collisions:0 txqueuelen:0 \r\n RX bytes:262675450 (250.5 Mb) TX bytes:262675450 (250.5 Mb)\r\n\r\nroot 25332 0.0 0.0 4260 568 pts/2 S+ 12:54 0:00 \\_ grep snmp\r\nroot 24364 0.0 0.0 70416 6696 ? SNl May15 0:22 /usr/sbin/snmpd -r -A -LF i /var/log/net-snmpd.log -p /var/run/snmpd.pid\r\n" }
輸出結果很多,看起來也很亂,不過查下stdout部分,這個部分是實際上執行后的結果。這里可以配合管道一起使用,可以如下使用:
[root@361way ~]# ansible 10.212.52.252 -m script -a 'script.sh' |egrep '>>|stdout'
ansible小結(七)常用模塊
在上一篇中介紹了commands部分模塊,本篇承接上篇介紹下常用的模塊。根據官方的分類,將模塊按功能分類為:雲模塊、命令模塊、數據庫模塊、文件模塊、資產模塊、消息模塊、監控模塊、網絡模塊、通知模塊、包管理模塊、源碼控制模塊、系統模塊、單元模塊、web設施模塊、windows模塊 ,具體可以參看官方頁面。
一、ping模塊
測試主機是否是通的,用法很簡單,不涉及參數:
[root@361way ~]# ansible 10.212.52.252 -m ping 10.212.52.252 | success >> { "changed": false, "ping": "pong" }
二、setup模塊
setup模塊,主要用於獲取主機信息,在playbooks里經常會用到的一個參數gather_facts就與該模塊相關。setup模塊下經常使用的一個參數是filter參數,具體使用示例如下(由於輸出結果較多,這里只列命令不寫結果):
[root@361way ~]# ansible 10.212.52.252 -m setup -a 'filter=ansible_*_mb' //查看主機內存信息 [root@361way ~]# ansible 10.212.52.252 -m setup -a 'filter=ansible_eth[0-2]' //查看地接口為eth0-2的網卡信息 [root@361way ~]# ansible all -m setup --tree /tmp/facts //將所有主機的信息輸入到/tmp/facts目錄下,每台主機的信息輸入到主機名文件中(/etc/ansible/hosts里的主機名)
三、file模塊
file模塊主要用於遠程主機上的文件操作,file模塊包含如下選項:
- force:需要在兩種情況下強制創建軟鏈接,一種是源文件不存在但之后會建立的情況下;另一種是目標軟鏈接已存在,需要先取消之前的軟鏈,然后創建新的軟鏈,有兩個選項:yes|no
- group:定義文件/目錄的屬組
- mode:定義文件/目錄的權限
- owner:定義文件/目錄的屬主
- path:必選項,定義文件/目錄的路徑
- recurse:遞歸的設置文件的屬性,只對目錄有效
- src:要被鏈接的源文件的路徑,只應用於state=link的情況
- dest:被鏈接到的路徑,只應用於state=link的情況
- state: directory:如果目錄不存在,創建目錄 file:即使文件不存在,也不會被創建 link:創建軟鏈接 hard:創建硬鏈接 touch:如果文件不存在,則會創建一個新的文件,如果文件或目錄已存在,則更新其最后修改時間 absent:刪除目錄、文件或者取消鏈接文件
使用示例:
ansible test -m file -a "src=/etc/fstab dest=/tmp/fstab state=link" ansible test -m file -a "path=/tmp/fstab state=absent" ansible test -m file -a "path=/tmp/test state=touch"
四、copy模塊
復制文件到遠程主機,copy模塊包含如下選項:
- backup:在覆蓋之前將原文件備份,備份文件包含時間信息。有兩個選項:yes|no
- content:用於替代"src",可以直接設定指定文件的值
- dest:必選項。要將源文件復制到的遠程主機的絕對路徑,如果源文件是一個目錄,那么該路徑也必須是個目錄
- directory_mode:遞歸的設定目錄的權限,默認為系統默認權限
- force:如果目標主機包含該文件,但內容不同,如果設置為yes,則強制覆蓋,如果為no,則只有當目標主機的目標位置不存在該文件時,才復制。默認為yes
- others:所有的file模塊里的選項都可以在這里使用
- src:要復制到遠程主機的文件在本地的地址,可以是絕對路徑,也可以是相對路徑。如果路徑是一個目錄,它將遞歸復制。在這種情況下,如果路徑使用"/"來結尾,則只復制目錄里的內容,如果沒有使用"/"來結尾,則包含目錄在內的整個內容全部復制,類似於rsync。
- validate :The validation command to run before copying into place. The path to the file to validate is passed in via '%s' which must be present as in the visudo example below.
示例如下:
ansible test -m copy -a "src=/srv/myfiles/foo.conf dest=/etc/foo.conf owner=foo group=foo mode=0644" ansible test -m copy -a "src=/mine/ntp.conf dest=/etc/ntp.conf owner=root group=root mode=644 backup=yes" ansible test -m copy -a "src=/mine/sudoers dest=/etc/sudoers validate='visudo -cf %s'"
五、service模塊
用於管理服務 該模塊包含如下選項:
arguments:給命令行提供一些選項
enabled:是否開機啟動 yes|no
name:必選項,服務名稱
pattern:定義一個模式,如果通過status指令來查看服務的狀態時,沒有響應,就會通過ps指令在進程中根據該模式進行查找,如果匹配到,則認為該服務依然在運行
runlevel:運行級別
sleep:如果執行了restarted,在則stop和start之間沉睡幾秒鍾
state:對當前服務執行啟動,停止、重啟、重新加載等操作(started,stopped,restarted,reloaded)
使用示例:
# Example action to reload service httpd, in all cases - service: name=httpd state=reloaded # Example action to enable service httpd, and not touch the running state - service: name=httpd enabled=yes # Example action to start service foo, based on running process /usr/bin/foo - service: name=foo pattern=/usr/bin/foo state=started # Example action to restart network service for interface eth0 - service: name=network state=restarted args=eth0
六、cron模塊
用於管理計划任務 包含如下選項:
- backup:對遠程主機上的原任務計划內容修改之前做備份
- cron_file:如果指定該選項,則用該文件替換遠程主機上的cron.d目錄下的用戶的任務計划
- day:日(1-31,*,*/2,……)
- hour:小時(0-23,*,*/2,……)
- minute:分鍾(0-59,*,*/2,……)
- month:月(1-12,*,*/2,……)
- weekday:周(0-7,*,……)
- job:要執行的任務,依賴於state=present
- name:該任務的描述
- special_time:指定什么時候執行,參數:reboot,yearly,annually,monthly,weekly,daily,hourly
- state:確認該任務計划是創建還是刪除
- user:以哪個用戶的身份執行
示例:
ansible test -m cron -a 'name="a job for reboot" special_time=reboot job="/some/job.sh"' ansible test -m cron -a 'name="yum autoupdate" weekday="2" minute=0 hour=12 user="root ansible 10.212.52.252 -m cron -a 'backup="True" name="test" minute="0" hour="2" job="ls -alh > /dev/null"' ansilbe test -m cron -a 'cron_file=ansible_yum-autoupdate state=absent'
七、yum模塊
使用yum包管理器來管理軟件包,其選項有:
- config_file:yum的配置文件
- disable_gpg_check:關閉gpg_check
- disablerepo:不啟用某個源
- enablerepo:啟用某個源
- name:要進行操作的軟件包的名字,也可以傳遞一個url或者一個本地的rpm包的路徑
- state:狀態(present,absent,latest)
示例如下:
ansible test -m yum -a 'name=httpd state=latest' ansible test -m yum -a 'name="@Development tools" state=present' ansible test -m yum -a 'name=http://nginx.org/packages/centos/6/noarch/RPMS/nginx-release-centos-6-0.el6.ngx.noarch.rpm state=present'
八、user模塊與group模塊
user模塊是請求的是useradd, userdel, usermod三個指令,goup模塊請求的是groupadd, groupdel, groupmod 三個指令,具體參數這里不再細講,直接上示例。
1、user模塊示例:
- user: name=johnd comment="John Doe" uid=1040 group=admin - user: name=james shell=/bin/bash groups=admins,developers append=yes - user: name=johnd state=absent remove=yes - user: name=james18 shell=/bin/zsh groups=developers expires=1422403387 #生成密鑰時,只會生成公鑰文件和私鑰文件,和直接使用ssh-keygen指令效果相同,不會生成authorized_keys文件。 - user: name=test generate_ssh_key=yes ssh_key_bits=2048 ssh_key_file=.ssh/id_rsa
注:指定password參數時,不能使用后面這一串密碼會被直接傳送到被管理主機的/etc/shadow文件中,所以需要先將密碼字符串進行加密處理。然后將得到的字符串放到password中即可。
[root@361way ~]# openssl passwd -1 -salt $(< /dev/urandom tr -dc '[:alnum:]' | head -c 32) Password: $1$YngB4z8s$atSVltYKnDxJmWZ3s.4/80 或者 [root@361way ~]# echo "123456" | openssl passwd -1 -salt $(< /dev/urandom tr -dc '[:alnum:]' | head -c 32) -stdin $1$4P4PlFuE$ur9ObJiT5iHNrb9QnjaIB0 #經驗證下面生成的密碼串也可以正常使用,不過與/etc/shadow的格式不統一,不建議使用 [root@361way ~]# openssl passwd -salt -1 "123456" -1yEWqqJQLC66 #使用上面的密碼創建用戶 [root@361way ~]#ansible all -m user -a 'name=foo password="$1$4P4PlFuE$ur9ObJiT5iHNrb9QnjaIB0
不同的發行版默認使用的加密方式可能會有區別,具體可以查看/etc/login.defs文件確認,centos 6.5版本使用的是SHA512加密算法,生成密碼可以通過ansible官方給出的示例:
python -c "from passlib.hash import sha512_crypt; import getpass; print sha512_crypt
2、group示例
- group: name=somegroup state=present
九、synchronize模塊
使用rsync同步文件,其參數如下:
- archive: 歸檔,相當於同時開啟recursive(遞歸)、links、perms、times、owner、group、-D選項都為yes ,默認該項為開啟
- checksum: 跳過檢測sum值,默認關閉
- compress:是否開啟壓縮
- copy_links:復制鏈接文件,默認為no ,注意后面還有一個links參數
- delete: 刪除不存在的文件,默認no
- dest:目錄路徑
- dest_port:默認目錄主機上的端口 ,默認是22,走的ssh協議
- dirs:傳速目錄不進行遞歸,默認為no,即進行目錄遞歸
- rsync_opts:rsync參數部分
- set_remote_user:主要用於/etc/ansible/hosts中定義或默認使用的用戶與rsync使用的用戶不同的情況
- mode: push或pull 模塊,push模的話,一般用於從本機向遠程主機上傳文件,pull 模式用於從遠程主機上取文件
另外還有其他參數,這里不再一一說明。上幾個用法:
src=some/relative/path dest=/some/absolute/path rsync_path="sudo rsync" src=some/relative/path dest=/some/absolute/path archive=no links=yes src=some/relative/path dest=/some/absolute/path checksum=yes times=no src=/tmp/helloworld dest=/var/www/helloword rsync_opts=--no-motd,--exclude=.git mode=pull
十、mount模塊
- 配置掛載點 選項:
- dump fstype:必選項,掛載文件的類型
- name:必選項,掛載點
- opts:傳遞給mount命令的參數
- src:必選項,要掛載的文件
- state:必選項
present:只處理fstab中的配置
absent:刪除掛載點
mounted:自動創建掛載點並掛載之
umounted:卸載
示例:
name=/mnt/dvd src=/dev/sr0 fstype=iso9660 opts=ro state=present name=/srv/disk src='LABEL=SOME_LABEL' state=present name=/home src='UUID=b3e48f45-f933-4c8e-a700-22a159ec9077' opts=noatime state=present ansible test -a 'dd if=/dev/zero of=/disk.img bs=4k count=1024' ansible test -a 'losetup /dev/loop0 /disk.img' ansible test -m filesystem 'fstype=ext4 force=yes opts=-F dev=/dev/loop0' ansible test -m mount 'name=/mnt src=/dev/loop0 fstype=ext4 state=mounted opts=rw'
十一、get_url 模塊
該模塊主要用於從http、ftp、https服務器上下載文件(類似於wget),主要有如下選項:
sha256sum:下載完成后進行sha256 check;
timeout:下載超時時間,默認10s
url:下載的URL
url_password、url_username:主要用於需要用戶名密碼進行驗證的情況
use_proxy:是事使用代理,代理需事先在環境變更中定義
示例:
- name: download foo.conf get_url: url=http://example.com/path/file.conf dest=/etc/foo.conf mode=0440 - name: download file with sha256 check get_url: url=http://example.com/path/file.conf dest=/etc/foo.conf sha256sum=b5bb9d8014a0f9b1d61e21e796d78dccdf1352f23cd32812f4850b878ae4944c
官方提供的可能用到模塊有git、svn版本控制模塊,sysctl 、authorized_key_module系統模塊,apt、zypper、pip、gem包管理模塊,find、template文件模塊,mysql_db、redis數據庫模塊,url 網絡模塊等。具體可以參看官方手冊模塊部分。
ansible小結(八)ansible-playbook簡單使用
ansbile-playbook是一系統ansible命令的集合,其利用yaml 語言編寫,運行過程,ansbile-playbook命令根據自上而下的順序依次執行。同時,playbook開創了很多特性,它可以允許你傳輸某個命令的狀態到后面的指令,如你可以從一台機器的文件中抓取內容並附為變量,然后在另一台機器中使用,這使得你可以實現一些復雜的部署機制,這是ansible命令無法實現的。
playbook通過ansible-playbook命令使用,它的參數和ansible命令類似,如參數-k(–ask-pass) 和 -K (–ask-sudo) 來詢問ssh密碼和sudo密碼,-u指定用戶,這些指令也可以通過規定的單元寫在playbook 。ansible-playbook的簡單使用方法: ansible-playbook example-play.yml 。
一、一個簡單的示例
下面給出一個簡單的ansible-playbook示例,了解下其構成。
# cat user.yml - name: create user hosts: all user: root gather_facts: false vars: - user: "test" tasks: - name: create user user: name="{{ user }}"
上面的playbook 實現的功能是新增一個用戶:
name參數對該playbook實現的功能做一個概述,后面執行過程中,會打印 name變量的值 ;
hosts參數指定了對哪些主機進行參作;
user參數指定了使用什么用戶登錄遠程主機操作;
gather_facts參數指定了在以下任務部分執行前,是否先執行setup模塊獲取主機相關信息,這在后面的task會使用到setup獲取的信息時用到;
vars參數,指定了變量,這里指字一個user變量,其值為test ,需要注意的是,變量值一定要用引號引住;
task指定了一個任務,其下面的name參數同樣是對任務的描述,在執行過程中會打印出來。user提定了調用user模塊,name是user模塊里的一個參數,而增加的用戶名字調用了上面user變量的值。具體執行結果如下:
[root@361way playbooks]# ansible-playbook user.yml PLAY [create user] ************************************************************ TASK: [create user ] ********************************************** changed: [10.212.52.252] changed: [10.212.52.14] changed: [10.212.52.16] PLAY RECAP ******************************************************************** 10.212.52.14 : ok=1 changed=1 unreachable=0 failed=0 10.212.52.16 : ok=1 changed=1 unreachable=0 failed=0 10.212.52.252 : ok=1 changed=1 unreachable=0 failed=0
同樣,如果想實現把這個新增的用戶刪除,只需將該playbook文件的最后一行替換為如下行再執行相應的playbook即可:
user: name="{{ user }}" state=absent remove=yes
二、一鍵修補bash shellcode示例
再給出一個稍微復雜的示例,通過ansible-playbook實現對N台主機同時修補bash shellcode 漏洞。需要注意的是,可能現網主機分布着不同的系統版本。這里假設現網同時存在centos5和6版本,具體playbook內容如下:
# cat update_bash.yml - hosts: all remote_user: root gather_facts: True tasks: - name: update bash in redhat 6 version yum: name=http://mirrors.aliyun.com/centos/6.6/os/x86_64/Packages/bash-4.1.2-2el6.x86_64.rpm.rpm state=present when: ansible_os_family == "RedHat" and ansible_distribution_version|int >=6 - name: update bash in redhat 5 version yum: name=http://mirrors.hustunique.com/centos/5/updates/x86_64/RPMS/bash-3.2-33.el5.1.x86_64.rpm state=present when: ansible_os_family == "RedHat" and ansible_distribution_version|int <=5
上面使用了when語句,同時也開啟了gather_facts setup模塊,這里的ansible_os_family變量和ansible_distribution_version變量就是直接使用的setup模塊獲取的信息。
如果有大量主機,就在運行的時候加上-f然后選擇一個合適的並發主機數量即可,我這里使用了這個,很快的就升級完成bash了。
三、playbook的構成
playbook是由一個或多個“play”組成的列表。play的主要功能在於將事先歸並為一組的主機裝扮成事先通過ansible中的task定義好的角色。從根本上來講所謂task無非是調用ansible的一個module。將多個play組織在一個playbook中即可以讓它們聯同起來按事先編排的機制同唱一台大戲。其主要有以下四部分構成
playbooks組成:
- Target section: 定義將要執行 playbook 的遠程主機組
- Variable section: 定義 playbook 運行時需要使用的變量
- Task section: 定義將要在遠程主機上執行的任務列表
- Handler section: 定義 task 執行完成以后需要調用的任務
下面介紹下構成playbook 的四層結構。
1、Hosts和Users
playbook中的每一個play的目的都是為了讓某個或某些主機以某個指定的用戶身份執行任務。
hosts 用於指定要執行指定任務的主機其可以是一個或多個由冒號分隔主機組。
remote_user 則用於指定遠程主機上的執行任務的用戶。 不過remote_user也可用於各task中。也可以通過指定其通過sudo的方式在遠程主機上執行任務其可用於play全局或某任務。 此外甚至可以在sudo時使用sudo_user指定sudo時切換的用戶。
示例:
- hosts: webnodes tasks: - name: test ping connection: remote_user: test sudo: yes
2、任務列表和action
play的主體部分是task list。
task list中的各任務按次序逐個在hosts中指定的所有主機上執行即在所有主機上完成第一個任務后再開始第二個。在運行自下而下某playbook時如果中途發生錯誤所有已執行任務都將回滾因此在更正playbook后重新執行一次即可。
task的目的是使用指定的參數執行模塊而在模塊參數中可以使用變量。模塊執行是冪等的這意味着多次執行是安全的因為其結果均一致。每個task都應該有其name用於playbook的執行結果輸出建議其內容盡可能清晰地描述任務執行步驟。如果未提供name則action的結果將用於輸出。
定義task的可以使用“action: module options”或“module: options”的格式推薦使用后者以實現向后兼容。如果action一行的內容過多也中使用在行首使用幾個空白字符進行換行。
tasks: - name: make sure apache is running service: name=httpd state=running 在眾多模塊中只有command和shell模塊僅需要給定一個列表而無需使用“key=value”格式例如 tasks: - name: disable selinux command: /sbin/setenforce 0 如果命令或腳本的退出碼不為零可以使用如下方式替代 tasks: - name: run this command and ignore the result shell: /usr/bin/somecommand || /bin/true 或者使用ignore_errors來忽略錯誤信息 tasks: - name: run this command and ignore the result shell: /usr/bin/somecommand ignore_errors: True
3、handlers
用於當關注的資源發生變化時采取一定的操作。 “notify”這個action可用於在每個play的最后被觸發這樣可以避免多次有改變發生時每次都執行指定的操作取而代之僅在所有的變化發生完成后一次性地執行指定操作。 在notify中列出的操作稱為handler也即notify中調用 handler中定義的操作。
注意:在 notify 中定義內容一定要和tasks中定義的 - name 內容一樣,這樣才能達到觸發的效果,否則會不生效。
- name: template configuration file template: src=template.j2 dest=/etc/foo.conf notify: - restart memcached - restart apache handler是task列表這些task與前述的task並沒有本質上的不同。 handlers: - name: restart memcached service: name=memcached state=restarted - name: restart apache service: name=apache state=restarted
4、tags
tags用於讓用戶選擇運行或略過playbook中的部分代碼。ansible具有冪等性因此會自動跳過沒有變化的部分即便如此有些代碼為測試其確實沒有發生變化的時間依然會非常地長。 此時如果確信其沒有變化就可以通過tags跳過此些代碼片斷。
5、示例
下面再給出一個安裝httpd web服務的示例:
# cat /etc/ansible/playbook/install_web.yml - hosts: webservers remote_user: root gather_fasks: False vars: packages: httpd tasks: - name: Install httpd yum: name={{ packages }} state=present - name: Cofiguration httpd copy: src=/root/httpd.conf dest=/etc/httpd/conf/httpd.conf tags: httpd_conf notify: - restart httpd - name: Start httpd service: name=httpd state=started enabled=no tags: start - name:Add centos user user: name={{ item }} state=absent tags: adduser with_items: - centos - admin handlers: - name: restart httpd service: name=httpd state=restart
上面的代碼沒有考慮ubuntu平台,僅僅考慮centos/redhat平台。
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=
委托不限於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_e
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
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.
在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如何更加可維護,更加解藕,讓它們按目的分配到不同的地方.
ansible小結(十)ansible api
ansible api 與ansible-playbook api 本應該是后面放在 ansible條件與循環、ansible變量篇之后講的,不過使用過后實在按捺不住提前寫的沖動,這個插個隊先講講API 部分。
一、ansible api
ansible api 的使用非常強大,也非常簡單,只不過把模塊需要使用的參數寫到了腳本中,這里先來看下官方給的示例,不過同於官方的是,我這里增我將結果進行了json美化輸出。
[root@361way api]# cat test_api.py 2.#!/usr/bin/env python 3.# coding=utf-8 4.import ansible.runner 5.import json 6.runner = ansible.runner.Runner( 7. module_name='ping', 8. module_args='', 9. pattern='all', 10. forks=10 11. ) 12.datastructure = runner.run() 13.data = json.dumps(datastructure,indent=4) 14.print data
其輸出結果如下:
注:如果主機是不通或失敗的,結果將會輸出到dark部分里,一個含有失敗主機的結果類似如下:
{ 2. "dark" : { 3. "web1.example.com" : "failure message" 4. }, 5. "contacted" : { 6. "web2.example.com" : 1 7. } 8.}
再為看下第二個示例:
#!/usr/bin/python 2.import ansible.runner 3.import sys 4.# construct the ansible runner and execute on all hosts 5.results = ansible.runner.Runner( 6. pattern='*', forks=10, 7. module_name='command', module_args='/usr/bin/uptime', 8.).run() 9.if results is None: 10. print "No hosts found" 11. sys.exit(1) 12.print "UP ***********" 13.for (hostname, result) in results['contacted'].items(): 14. if not 'failed' in result: 15. print "%s >>> %s" % (hostname, result['stdout']) 16.print "FAILED *******" 17.for (hostname, result) in results['contacted'].items(): 18. if 'failed' in result: 19. print "%s >>> %s" % (hostname, result['msg']) 20.print "DOWN *********" 21.for (hostname, result) in results['dark'].items(): 22. print "%s >>> %s" % (hostname, result
上面的示例中對主機的輸出結果進行了判斷,並且結果的輸出進行了定制化,上面執行的結果你可以和ansible all -m command -a 'uptime' 的結果進行下比對,看下有什么不同。
上面的示例基本上都是參照官方頁面進行執行的,更多用法可以通過pydoc ansible或者通過python里的help(ansible)查看。另外在多主機執行時,可以使用async(異部)方式運行。
二、ansible_playbook api
ansible_playbook api 部分在官方文檔上並沒有提,不過通過查看ansible模塊的幫助信息可以看到其是支持的。在ansible google論壇里(需翻牆),有老外也給出里代碼,其實它和執行ansible的api方式一樣,只是多了個幾個參數:
import ansible.playbook 2.from ansible import callbacks 3.from ansible import utils 4.stats = callbacks.AggregateStats() 5.playbook_cb = callbacks.PlaybookCallbacks(verbose=utils.VERBOSITY) 6.runner_cb = callbacks.PlaybookRunnerCallbacks(stats, verbose=utils.VERBOSITY) 7.pb = ansible.playbook.PlayBook( 8. playbook="nseries.yml", 9. stats=stats, 10. callbacks=playbook_cb, 11. runner_callbacks=runner_cb, 12. check=True 13.) 14.for (play_ds, play_basedir) in zip(pb.playbook, pb.play_basedirs): 15. import ipdb 16. ipdb.set_trace() 17. # Can play around here to see what's going on. 18.pb.run()
大致看了下代碼,在用api的方式執行playbook的時候,playbook,stats,callbacks,runner_callbacks這幾個參數是必須的。不使用的時候會報錯。
arguments = [] 2.if playbook is None: 3. arguments.append('playbook') 4.if callbacks is None: 5. arguments.append('callbacks') 6.if runner_callbacks is None: 7. arguments.append('runner_callbacks') 8.if stats is None: 9. arguments.append('stats') 10.if arguments: 11. raise Exception('PlayBook missing required arguments: %s' % ', '.join(arguments))
playbook用來指定playbook的yaml文件
stats用來收集playbook執行期間的狀態信息,最后會進行匯總
callbacks用來輸出playbook執行的結果
runner_callbacks用來輸出playbook執行期間的結果。但是它返回的結果太簡單,我想讓它詳細點,如果用自定義callback的方法插入到mongo里面的話也行,或者是直接輸出,但是我想所有task都執行完后,把每個task的詳細信息輸出到終端上,最后發現結果輸出都是靠callbacks.py里的AggregateStats這個類,在每執行完一個task后,都會調用AggregateStats進行計算,匯總。
[root@361way api]# cat playbook_api.py 2.#!/usr/bin/env python 3.# coding=utf-8 4.import ansible.playbook 5.from ansible import callbacks 6.from ansible import utils 7.import json 8.stats = callbacks.AggregateStats() 9.playbook_cb = callbacks.PlaybookCallbacks(verbose=utils.VERBOSITY) 10.runner_cb = callbacks.PlaybookRunnerCallbacks(stats,verbose=utils.VERBOSITY) 11.res=ansible.playbook.PlayBook( 12. playbook='/etc/ansible/playbooks/user.yml', 13. stats=stats, 14. callbacks=playbook_cb, 15. runner_callbacks=runner_cb 16. ).run() 17.data = json.dumps(res,indent=4) 18.print data 19.# 執行結果如下: 20.[root@361way api]# python playbook_api.py 21.PLAY [create user] ************************************************************ 22.TASK: [create test "{{ user }}"] ********************************************** 23.changed: [10.212.52.16] 24.changed: [10.212.52.14] 25.{ 26. "10.212.52.16": { 27. "unreachable": 0, 28. "skipped": 0, 29. "ok": 1, 30. "changed": 1, 31. "failures": 0 32. }, 33. "10.212.52.14": { 34. "unreachable": 0, 35. "skipped": 0, 36. "ok": 1, 37. "changed": 1, 38. "failures": 0 39. } 40.} 41.[root@361way api]#
三、總結
從上面的例子來看,感覺作用似乎有點雞肋。多條ansible shell 指令的執行可以寫成playbook 來執行,ansbile-playbook 也可以通過include 調用子playbook ,似乎API 部分用處並不大 。咋一聽深感有理,不過細究一下,
1、當需要先對前一次作任務執行的結果進行處理,並將相應的結果對應的作為輸入再在一次任務傳入時,這里使用api 更方便;
2、需要對結果輸出進行整形時,也比較api 方便;
3、playbook 之間進行調用或都playbook比較復雜時,想要理清任務之間的關系勢必累顯麻煩,而通過api,從上一層任務到下一層任務之間的調用關系明子。而且playbook之間可以是平行的關系。方便小的功能模塊的復用。
4、方便二次開發及和其他程序之間的耦合調用----目前感覺這條是最實用的。
ansible小結(十一)ansible普通用戶su切換問題
在之前我曾總結了一點ansible系列博文,本篇還承接之前的篇幅吧。在現網應用中,安全加固后的主機是不允許直接以root用戶登陸的,而很多命令又需要root用戶來執行,在不改造現網的情況下。希望通過一個普通用戶先登陸,再su切到root執行。而且每台主機的普通用戶和root用戶的密碼又不同。希望在通過ansible執行的時候不需要交互輸入密碼,而是直接執行后輸出結果。
一、ansible hosts配置文件
在之前的系列文章中我們提到,可以把密碼寫到hosts配置文件,通過查詢官網的相關信息了解了,其除了ansible_ssh_user、ansible_ssh_pass變量外,還為su切換提供了ansible_su_pass變量,通過該變量我們可以把root密碼直接寫到配置文件中。具體如下:
[root@361way.com ~]# cat /etc/ansible/hosts 2.[test01] 3.10.212.52.14 ansible_ssh_user=test ansible_ssh_pass=111111 ansible_su_pass=*I2145 4.10.212.52.16 ansible_ssh_user=test ansible_ssh_pass=xyz123 ansible_su_pass=mn1Pokm 5.10.212.52.252 ansible_ssh_user=amos ansible_ssh_pass=asdf ansible_su_pass=xyzp
注:我測試使用的ansible版本是1.9版的,在新的2.0版本中,變量也做了變化ansible_become_pass替換了之前的ansible_sudo_pass or ansible_su_pass ,具體可以參看官方文檔。
二、ansible命令參數
在執行ansible -h查看時,會看到如下條目:
-S, --su run operations with su (deprecated, use become) 2. -R SU_USER, --su-user=SU_USER 3. run operations with su as this user (default=root) 4. (deprecated, use become)
三、su切換執行
所以結合上面兩塊,我們做下簡單的測試:
[root@361way.com ~]# ansible all -S -R root -m shell -a "uptime" 2.10.212.52.252 | success | rc=0 >> 3. 16:13pm up 34 days 5:40, 2 users, load average: 0.08, 0.21, 0.30 4.10.212.52.16 | success | rc=0 >> 5. 16:26pm up 538 days 23:17, 2 users, load average: 0.00, 0.01, 0.05 6.10.212.52.14 | success | rc=0 >> 7. 16:24pm up 538 days 22:39, 2 users, load average: 0.00, 0.01, 0.05
這里需要注意的是,普通用戶的家目錄是要存在,並切該普通用戶要有寫的權限的,不然會出現類似如下的報錯:
10.212.52.252 | FAILED => Authentication or permission failure. 2.In some cases, you may have been able to authenticate and did not have permissions on the remote directory. 3.Consider changing the remote temp path in ansible.cfg to a path rooted in "/tmp". 4.Failed command was: mkdir -p $HOME/.ansible/tmp/ansible-tmp-1449456070.96-212322517029279 && echo $HOME/.ansible/tmp/ansible-tmp-1449456070.96-212322517029279, 5.exited with result 1: mkdir: cannot create directory `/home/amos/.ansible':
當然,如果這個普通用戶沒有家目錄或者家目錄沒有寫權限在不修改遠端主機也有辦法可以搞定,修改ansible主機的ansible.cfg配置文件,如下:
[root@361way.com ~]# vim /etc/ansible/ansible.cfg 2.找到如下行: 3.remote_tmp = $HOME/.ansible/tmp 4.修改為 5.remote_tmp = /tmp/.ansible/tmp
tmp目錄一般都有寫的權限吧,改成臨時目錄為/tmp下即可。
再下為我們再看看遠程主機的message日志文件確認下是否真的是通過普通用戶切換的:
Dec 3 11:36:20 linux su: (to root) test on /dev/pts/1 //由普通用戶test切換為su切換為root的日志 2.Dec 3 11:36:20 linux ansible-command: Invoked with creates=None executable
功能實現了,最后要說的是,由於該配置文件中涉及到多台主機的用戶名密碼,所以該文件的安全工作一定要做好。
ansible小結(十 二)磁盤使用率篩選
年底了雖然有各種告警,不過還是需要做一下全網巡檢,這里以磁盤使用率為例。由於目前平台上使用的是商業版的自動化工具bmc,其在執行分發都方面非常不存,不過在取值返回上做的不好---當然也和其是商業化工具有關,很多東西不開源,用shell 的語法概念寫nsh時又執行不通過,NND總是要問原廠支撐 。剛好在測試環境上有之前安裝的ansible環境,也測試下用ansible 來實現該功能。
一、實現目標
將所有主機的磁盤大於75%的主機獲取到,並輸出為下面的格式:
主機IP 主機名 磁盤掛載點信息 磁盤使用率
假如 host1上有多個分區都大於75% ,則寫多條。當主機上沒有一台符合時,則什么都不輸出。
注:現網主機上有兩個 bond 網卡,其中一個配置的是10段的IP,另一個配置的是192段的IP。這里要獲取的是10段的IP。
二、 ansible + awk 簡單輸出
該問題我處理的結果是使用ansible api 寫的一個python腳本實現的。先看下我在一個技術群和一個大牛的討論的結果(點擊圖片看大圖)。
如上圖,大牛的做法用的是awk 的next方法進行的處理,awk 的next功能我也做過記錄,具體參看awk next多行合並 。
這里有幾個注意點需要說明下:
1、大牛的寫法中在篩選上寫的不嚴謹,$5前應該有int轉化,不過不能正確的取到值
[root@361way ~]# df -hP|awk 'NR>1 && $5 > 20' 2./dev/xvda1 20G 4.6G 15G 25% / 3./dev/xvdb 20G 645M 18G 4% /data1 4.[root@361way ~]# df -hP|awk 'NR>1 && int($5) > 20' 5./dev/xvda1 20G 4.6G 15G 25% /
有沒有看到,如果不做int格式轉換取的結果是所有分區的信息,而加上int后獲取的才是我們想要的結果
2、在ansible執行時$5前面要加上轉義
# ansible all -m shell -a "df -hP|awk 'NR>1 && int($5) > 50'" 2.10.212.52.252 | FAILED | rc=2 >> 3.awk: fatal: 0 is invalid as number of arguments for int 4.[root@361way ~]# df -hP|awk 'NR>1 && int(\$5) > 20' 5.awk: NR>1 && int(\$5) > 20 6.awk: ^ backslash not last character on line
如上面的結果,如果在ansible執行時不加轉義時會有報錯提示,如果加上轉義在主機端執行時會自動是將轉義符去掉的結果。而在主機端執行時,如果加上了轉義也會報錯。所以主機端一定不能加上轉義。
3、在實際應用時,輸出結果可能和大牛的輸出有差距
[root@localhost ~]# ansible all -m shell -a "df -hP|awk 'NR>1 && int(\$5) > 30'"|awk '/success/{ip=$1;next}{print ip,$0}' 2.10.212.52.252 /dev/sda9 9.9G 2.9G 6.5G 31% /opt 3.10.212.52.252 /dev/sda6 5.0G 1.9G 2.8G 41% /tmp 4.10.212.52.252 /dev/sda5 9.9G 3.9G 5.5G 42% /usr 5.10.212.52.252 6.10.212.52.14 /dev/cciss/c0d0p5 9.9G 3.2G 6.3G 34% /usr 7.10.212.52.14 8.10.212.52.16 /dev/cciss/c0d0p7 9.9G 4.0G 5.4G 43% /tmp 9.10.212.52.16 /dev/cciss/c0d0p5 9.9G 2.9G 6.5G 31% /usrdf: `/root/.gvfs': Permission denied 10.10.212.52.16
上面的結果是我在自己的測試環境上執行的結果。可以看到多出的空行也打印了主機的IP 。還會需要注意的,我這里ansible輸出的success是小寫的。
不知道以上的問題是不是使用的環境不同造成的。我運行的環境是ansible主機為redhat6,ansible版本為1.9,被取數據主機有redhat6和suse11 。不過這都是小問題,同樣可以通過處理獲取到正常的結果。
4、ansible api 執行
上面的大牛的結果,我使用ansible api 執行,如下,可以對比下:
[root@localhost ~]# cat /tmp/test.py 2.#!/usr/bin/env python 3.# coding=utf-8 4.# author : www.361way.com 5.# mail : itybku@139.com 6.import ansible.runner 7.#import json 8.runner = ansible.runner.Runner( 9. module_name='shell', 10. module_args="df -hP|awk 'NR>1 && int($5)>30'", 11. pattern='all', 12. forks=10 13. ) 14.results = runner.run() 15.#print results 16.for (hostname, result) in results['contacted'].items(): 17. if not 'failed' in result: 18. for line in result['stdout'].split('\n'): 19. print "%s %s" % (hostname, line) 20.# 執行結果如下 21.[root@localhost ~]# python /tmp/test.py 22.10.212.52.16 /dev/cciss/c0d0p7 9.9G 4.0G 5.4G 43% /tmp 23.10.212.52.16 /dev/cciss/c0d0p5 9.9G 2.9G 6.5G 31% /usr 24.10.212.52.252 /dev/sda9 9.9G 2.9G 6.5G 31% /opt 25.10.212.52.252 /dev/sda6 5.0G 1.9G 2.8G 41% /tmp 26.10.212.52.252 /dev/sda5 9.9G 3.9G 5.5G 42% /usr 27.10.212.52.14 /dev/cciss/c0d0p5 9.9G 3.2G 6.3G 34% /usr
三、還是ansible api 獲取磁盤信息
上面的方法中實際執行時,比我們預期需要的效果少了主機名一項。這里我換做執行腳本實現,實現效果如下:
#/bin/bash 2.# author : www.361way.com 3.IP=`ip add show|grep inet|grep 10|awk '{print $2}'` 4.df -hl|grep '^/'|sed 's/%//g'|awk '{if($5>30) print $0}'|while read line 5.do 6. echo $IP `hostname` $line 7.done 8.# 執行結果如下 9.# sh aa.sh 10.10.212.52.253/24 localhost /dev/sda3 9.5G 5.7G 3.4G 64 / 11.10.212.52.253/24 localhost /dev/sda2 39G 19G 18G 52 /home 12.10.212.52.253/24 localhost /dev/sda6 9.5G 7.1G 2.0G 78 /usr
使用ansible api 執行該腳本的結果如下:
# python dfscript.py 2.df: `/root/.gvfs': Permission denied 3.df: `/root/.gvfs': Permission denied 4.10.212.52.16/24 linux /dev/cciss/c0d0p7 9.9G 4.0G 5.4G 43 /tmp 5.10.212.52.16/24 linux /dev/cciss/c0d0p5 9.9G 2.9G 6.5G 31 /usr 6.10.212.52.252/24 zjhz-bmc-test /dev/sda9 9.9G 2.9G 6.5G 31 /opt 7.10.212.52.252/24 zjhz-bmc-test /dev/sda6 5.0G 1.9G 2.8G 41 /tmp 8.10.212.52.252/24 zjhz-bmc-test /dev/sda5 9.9G 3.9G 5.5G 42 /usr 9.10.212.52.14/24 linux /dev/cciss/c0d0p5 9.9G 3.2G 6.3G 34 /usr
dfscript.py腳本內容如下
# cat dfscript.py 2.#!/usr/bin/env python 3.# coding=utf-8 4.# author : www.361way.com 5.# mail : itybku@139.com 6.import ansible.runner 7.#import json 8.runner = ansible.runner.Runner( 9. module_name='script', 10. module_args="aa.sh", 11. pattern='all', 12. forks=10 13. ) 14.results = runner.run() 15.#print results 16.for (hostname, result) in results['contacted'].items(): 17. if not 'failed' in result: 18. for line in result['stdout'].split('\r\n'): 19. #print "%s %s" % (hostname, line) 20. print line
直接對該腳本執行后的結果進行grep 標准輸出時,會發現其行與行之間是以\r\n這樣的方式分行的。
所以在數據獲取方面,盡量以ansible api 的方式進行獲取,而api 的使用非常簡單,無法是幾個參數的替換后面再調用run方法,最終在對結果進行處理。而涉及多項信息獲取時,建議使用自定義模塊的方法,先將所需數據取回來,以json方式返回---ansible自定義模塊所要求的格式。返回后可以再以api 或其他方式處理返回的數據即可。