在項(xiàng)目中有很多地方用到ansible。最初使用ansible只是為了方便代碼部署和模板配置,畢竟手動(dòng)去30+臺(tái)機(jī)器手動(dòng)部署代碼是件很痛苦的事情。由于ansible基于ssh實(shí)現(xiàn)其功能,配置使用都比較簡單,后來在線上的系統(tǒng)環(huán)境搭建,數(shù)據(jù)庫權(quán)限統(tǒng)一配置中ansible都發(fā)揮了重要作用,極大的提高了生產(chǎn)力。這里整理了一份詳盡的使用說明,有興趣的可以瞅瞅。文章內(nèi)容主要翻譯整理自ansible官方網(wǎng)站推薦的
Ansible-Up and Running一書。
1 為什么選擇Ansible
來源:ansible一詞源于科幻小說,是一種超光速通信設(shè)備。
Ansible is the simplest way to automate apps and IT infrastructure。
750+模塊,24000+ github stars。

配置管理、應(yīng)用部署等。配置管理工具有Chef, Puppet, Salt等,應(yīng)用部署(將代碼編譯或打包然后傳輸?shù)椒?wù)器部署并啟動(dòng)服務(wù))工具有Capistrano,F(xiàn)abric等,ansible集兩者于一身,操作很簡單但是功能強(qiáng)大。此外,還可以對(duì)多個(gè)服務(wù)器進(jìn)行服務(wù)編排,支持openstack,amazon ec2, docker等。
ansible使用了一個(gè)DSL(domain-specific language)描述服務(wù)器狀態(tài)。執(zhí)行的文件稱為playbook,文件格式為yaml。ansible簡約而不簡單。比起puppet的繁瑣的配置和復(fù)雜語法( Puppet基礎(chǔ)篇4-安裝、配置并使用Puppet | Puppet運(yùn)維自動(dòng)化經(jīng)驗(yàn)分享 ),簡直是一股清流。 圖2描述了ansible執(zhí)行過程,執(zhí)行了兩個(gè)task和一個(gè)handler,先是使用了一個(gè)apt模塊在web1,web2,web3上面執(zhí)行了安裝nginx的任務(wù),再是用template模塊拷貝了配置文件。另外,執(zhí)行了一個(gè)notify nginx的handler重啟了nginx。

執(zhí)行流程:
1. 創(chuàng)建一個(gè)python腳本用于安裝nginx包。
2. 拷貝python腳本到web1,web2,web3。
3. 分別在web1,web2,web3上執(zhí)行該腳本。
4. 等待腳本在所有服務(wù)器上執(zhí)行完畢。
5. 接著執(zhí)行下一個(gè)task。
注意的幾點(diǎn):
- 1.在各個(gè)服務(wù)器執(zhí)行腳本的過程是并行的,有個(gè)forks參數(shù)可以指定,默認(rèn)是5,即一次可以在5個(gè)服務(wù)器上并行執(zhí)行腳本。
- 2.要在所有的服務(wù)器都執(zhí)行完第一個(gè)task后才會(huì)接著執(zhí)行第二個(gè)task。(新版本新增了異步參數(shù),一個(gè)服務(wù)器在執(zhí)行完了它的任務(wù)后可以不等其他服務(wù)器執(zhí)行完直接執(zhí)行下一個(gè)task)。
- 3.ansible執(zhí)行任務(wù)順序與playbook中的順序一致。
優(yōu)勢:
- 語法易讀。yaml->json好比markdown->html。ansible的playbook可以被稱之為可以執(zhí)行的README。
- 遠(yuǎn)程主機(jī)不需要安裝任何東西。(這有點(diǎn)夸大了,python2.5+或python2.4+simplejson模塊和ssh是必須的,當(dāng)然這些現(xiàn)在是Linux服務(wù)器標(biāo)配)
- push-based。如chef和puppet是pull-based,先將文件修改推送到中心服務(wù)器,其他服務(wù)器的agent定期拉取新的配置管理腳本并在本機(jī)執(zhí)行。而在ansible是push-based的,先在中心服務(wù)器修改playbook,執(zhí)行該playbook,ansible會(huì)連接到各個(gè)服務(wù)器并執(zhí)行模塊改變服務(wù)器狀態(tài)。push-based的好處就是只在需要的時(shí)候才操控去改變目標(biāo)服務(wù)器狀態(tài)。如果你更傾向于pull-based模式,可以用ansible-pull。
- ansible可以很方便的scaled down,單機(jī)跟多機(jī)沒有什么區(qū)別。 Simple things should be simple, complex things should be possible 。
- 很輕量級(jí)的抽象。不像puppet之類的工具,有很高的抽象,比如有package這個(gè)概念,用于不用區(qū)分服務(wù)器版本來安裝模塊。但是在ansible中,提供的是apt和yum模塊,由你自己采用,不要再額外學(xué)一些抽象的語法,簡化你的學(xué)習(xí)成本。也有人覺得這是ansible的缺點(diǎn),優(yōu)缺點(diǎn)與否,各有評(píng)判。
2 安裝配置
2.1 安裝
pip install ansible
依賴環(huán)境:python
2.2 配置
配置ansible.cfg文件,ansible配置文件尋找路徑:
1. File specified by the ANSIBLE_CONFIG environment variable
2. ./ansible.cfg (ansible.cfg in the current directory)
3. ~/.ansible.cfg (.ansible.cfg in your home directory)
4. /etc/ansible/ansible.cfg
ansible.cfg配置文件實(shí)例
[defaults]
hostfile=/etc/ansible/hosts
private_key_file = /Users/ssj/.ssh/id_rsa_ansible
remote_user = ssj
remote_port = 22
host_key_checking = False
注意,如果是在服務(wù)器上,不要放置private key,可以通過ssh forward。
2.3 測試
簡單執(zhí)行命令測試是否成功 ( -vvvv可以看到更多細(xì)節(jié)信息),”changed”:false表示執(zhí)行ping模塊沒有改變服務(wù)器狀態(tài),”ping”:pong表示模塊執(zhí)行后輸出結(jié)果為pong。你也可以將ping模塊改成command,加上參數(shù)執(zhí)行指定命令。比如 ansible testserver -m command -a uptime ,當(dāng)然,command是默認(rèn)模塊,因此還可以簡化為 ansible testserver -a uptime 。
#hosts
[testserver]
127.0.0.1
#run command,-i hosts可以省去。
ssj@ssj-mbp ~/ansible $ ansible testserver -i hosts -m ping
127.0.0.1 | SUCCESS => {
"changed": false,
"ping": "pong"
}
3 實(shí)體關(guān)系圖

- playbook包含很多個(gè)play
- play中包含name,tasks,hosts,vars,handles屬性。
- tasks中包含各個(gè)真正執(zhí)行的module,如apt,copy,file, git, svn,service,command,notify,mysql等。具體的模塊參數(shù)和使用文檔在這里
4 一個(gè)例子
---
- name: Configure webserver with nginx and tls
hosts: webservers
sudo: True
vars:
key_file: /etc/nginx/ssl/nginx.key
cert_file: /etc/nginx/ssl/nginx.crt
conf_file: /etc/nginx/sites-available/default
server_name: localhost
tasks:
- name: Install nginx
apt: name=nginx update_cache=yes cache_valid_time=3600
- name: create directories for TLS certificates
file: path=/etc/nginx/ssl state=directory
- name: copy TLS key
copy: src=files/nginx.key dest={{ key_file }} owner=root mode=0600
notify: restart nginx
- name: copy TLS certificate
copy: src=files/nginx.crt dest={{ cert_file }}
notify: restart nginx
- name: copy nginx config file
template: src=templates/nginx.conf.j2 dest={{ conf_file }}
notify: restart nginx
- name: enable configuration
file: dest=/etc/nginx/sites-enabled/default src={{ conf_file }} state=link
notify: restart nginx
- name: copy index.html
template: src=templates/index.html.j2 dest=/usr/share/nginx/html/index.html mode=0644
handlers:
- name: restart nginx
service: name=nginx state=restarted
- 可以看到用到了apt,file,copy,template,notify,service等模塊。
- 注意幾個(gè)語法點(diǎn):
YAML truthy
true, True, TRUE, yes, Yes, YES, on, On, ON, y, Y
YAML falsey
false, False, FALSE, no, No, NO, off, Off, OFF, n, N
- true和yes,on或者1都是一樣的意思,一般在模塊參數(shù)里面用yes和no,true和false在playbook中其他地方。
- 另外,比如下面的模塊參數(shù)分行寫,可以在第一行寫 > , 后面幾行跟參數(shù)來實(shí)現(xiàn)。
- 注意notify是嚴(yán)格按照它在play中定義的順序執(zhí)行的,而不是notify調(diào)用的順序執(zhí)行的。比如下面的playbook,盡管先notify的是
handler test2,實(shí)際執(zhí)行時(shí)時(shí)按照play中handlers定義的順序,也就是先執(zhí)行handler test1。
#!/usr/bin/env ansible-playbook
- name: test handlers
hosts: webservers
tasks:
- name: assure file exist
file: >
path=/tmp/test.conf
state=touch owner=ssj mode=0644
- name: task1
command: date
notify: handler test2
- name: task2
command: echo 'task2'
notify: handler test1
handlers:
- name: handler test1
command: echo 'handler test1'
- name: handler test2
command: echo 'handler test2'
5 更多細(xì)節(jié)
5.1 inventory格式和配置
inventory格式
[webserver]
127.0.0.1
dbserver1 ansible_ssh_host=127.0.0.1 ansible_ssh_port=22 color=red
dbserver2 ansible_ssh_host=127.0.0.1 ansible_ssh_port=2200 color=green
www.example.com
[dbserver] #group
dbserver1
dbserver2
[forum:children] #groups of groups
webserver
dbserver
可以用分組的方式,可以直接用域名(www.example.com),也可以用別名(如testserver2)+變量指定ssh的ip地址和端口,比如ansible_ssh_host和color變量。命令 ansible testserver2 -a date ,通常我們要控制多臺(tái)服務(wù)器,于是可以分組服務(wù)器,要在所有服務(wù)器執(zhí)行可以用all。 ansible all -a date。
inventory除了可以指定主機(jī)的變量如上面的color之外,還可以將變量分組,也可以對(duì)主機(jī)變量單獨(dú)存儲(chǔ)到一個(gè)文件中,格式如下,注意如果host_vars中和group_vars中有相同變量,則以host_vars中的為準(zhǔn)。host_vars變量只能本主機(jī)使用,group_vars是本group都可以使用。
############################
group_vars/dbserver
-----------------------
db:
user: bbsdbuser
password: bbsdbpasswd
port: 3306
name: bbs
replica:
host: slavedb
port: 3307
host_vars/dbserver1
---------------------
db:
user:server1dbuser
password: server1password
master: true
############################
ssj@ssj-mbp ~/ansible $ ansible dbserver1 -i hosts -a 'echo {{db.user}}' #host_vars優(yōu)先級(jí)高
dbserver1 | SUCCESS | rc=0 >>
server1dbuser
----------------------------------------------------------
ssj@ssj-mbp ~/ansible $ ansible dbserver2 -i hosts -a 'echo {{db.master}}' #dbserver2所在的組變量文件沒有db.master變量,報(bào)錯(cuò)。
dbserver2 | FAILED | rc=0 >>
the field 'args' has an invalid value, which appears to include a variable that is undefined. The error was: 'dict object' has no attribute 'master'
甚至支持:
[web]
web[1:20].example.com
web-[a-t].example.com
inventory文件還支持動(dòng)態(tài)的,通過 -i inventory 可以指定目錄或者文件,這樣目錄下面可以放一個(gè)python腳本,用來動(dòng)態(tài)獲取主機(jī)列表。python腳本要可執(zhí)行,同時(shí)實(shí)現(xiàn)下面兩個(gè)命令:
? --host=<hostname> for showing host details
? --list for listing groups
最后,還可以通過add_hosts模塊在運(yùn)行時(shí)增加host配置,使用group_by模塊在運(yùn)行時(shí)創(chuàng)建group。比如通過 ansible_distribution來根據(jù)操作系統(tǒng)創(chuàng)建不同的組,再分別安裝軟件。
- name: group hosts by distribution
hosts: myhosts
gather_facts: True
tasks:
- name: create groups based on distro
group_by: key={{ ansible_distribution }}
- name: do something to Ubuntu hosts
hosts: Ubuntu
tasks:
- name: install htop
apt: name=htop
# ...
- name: do something else to CentOS hosts
hosts: CentOS
tasks:
- name: install htop
yum: name=htop
# ...
inventory默認(rèn)配置

幾個(gè)參數(shù)解釋下:
- ansible_connection: ssh連接方式,默認(rèn)是smart,也就是看本地機(jī)器是否安裝了ssh客戶端且支持ControlPersist特性。如果支持,則使用本地的ssh客戶端,如果不支持,則使用一個(gè)基于python的ssh客戶端庫paramiko。
- ansible_shell_type: ansible認(rèn)為的遠(yuǎn)程服務(wù)器執(zhí)行script的shell,默認(rèn)認(rèn)為是/bin/sh,當(dāng)然也可以設(shè)置為csh,fish等,如果服務(wù)器支持的話。
- ansible_python_interpreter: 服務(wù)器python解釋器的路徑。如果你的服務(wù)器python解釋器不在這個(gè)目錄,這要修改該配置。
- ansible_*_interpreter: 如果用的是一個(gè)自定義的模塊,不是python的,比如ruby,則設(shè)置該值指定解釋器路徑(比如/usr/bin/ruby)。
5.2 變量和Facts
變量
變量可以在play中通過vars來指定,也可以通過var_file指定一個(gè)文件,文件中存儲(chǔ)變量。如之前的nginx的playbook可以改成這樣:
vars_files:
- nginx.yml
##nginx.yml文件內(nèi)容
key_file: /etc/nginx/ssl/nginx.key
cert_file: /etc/nginx/ssl/nginx.crt
conf_file: /etc/nginx/sites-available/default
server_name: localhost
可以在play中使用debug模塊打印變量的值,注意debug支持的參數(shù)有var,msg等,var中的變量不要使用 {{}}包裹。
#!/usr/bin/env ansible-playbook
- name: test name
hosts: webserver
vars:
myvar: testmyvar
tasks:
- debug: var=myvar
- name: capture output of id command
command: id -un
register: login
- debug: var=login
使用register來注冊一個(gè)變量后面使用,register注冊的變量在這個(gè)playbook的其他play中也是可以使用的,不局限于這一個(gè)play。比如command模塊的輸出如下所示,可以通過login.stdout得到用戶名。注意不同模塊的輸出可能是不一樣的,同一個(gè)模塊在不同情況下也不一樣,比如apt模塊安裝nginx,如果機(jī)器已經(jīng)安裝了nginx,則輸出里面change為false,而且不會(huì)有stdout,stderr和stdout_lines這些key。如果模塊執(zhí)行出錯(cuò),則其他的host默認(rèn)不會(huì)再執(zhí)行,可以設(shè)置 ignore_erros:True 忽略模塊的錯(cuò)誤。
其他指定變量的方式如 host_vars目錄,group_vars目錄等。
{
"changed": true,
"cmd": [
"id",
"-un"
],
"delta": "0:00:00.007369",
"end": "2016-11-17 15:09:49.061725",
"rc": 0,
"start": "2016-11-17 15:09:49.054356",
"stderr": "",
"stdout": "ssj",
"stdout_lines": [
"ssj"
],
"warnings": []
}
Facts
如果在playbook中配置了 gather_facts:True,則會(huì)看到真正的任務(wù)開始前,會(huì)先執(zhí)行一個(gè)[setup]的模塊,用于收集服務(wù)器信息,包括cpu架構(gòu),操作系統(tǒng)類型,ip地址等信息。這些信息存儲(chǔ)在特定的變量中,我們稱之為facts。如果你的playbook中不需要這些信息,也可以設(shè)置gather_facts:False來加快playbook執(zhí)行速度,收集服務(wù)器信息需要花費(fèi)不少時(shí)間的。
通過命令 ansible webserver -m setup 可以看到ansible的gathter_facts的輸出內(nèi)容,軟硬件信息都有。因?yàn)樾畔⑻?,還可以通過在setup模塊加上參數(shù)filter來篩選你需要的內(nèi)容,如果只需要網(wǎng)絡(luò)信息,可以這樣: ansible webserver -m setup -a 'filter=ansible_eth*’ ,其中ansible_facts這個(gè)key是固定的。
127.0.0.1 | SUCCESS => {
"ansible_facts": {
"ansible_eth0": {
"active": true,
"device": "eth0",
"ipv4": {
"address": "xxx.xxx.xxx.xxx",
"broadcast": "xxx.xxx.xxx.xxx",
"netmask": "xxx.xxx.xxx.xxx.xxx",
"network": "xxx.xxx.xxx.xxx"
},
"macaddress": "xx.xx.xx.xx.xx.xx",
"module": "bnx2",
"mtu": 1500,
"pciid": "0000:10:00.0",
"promisc": false,
"type": "ether"
},
},
"changed": false
}
可以在定義本地facts,在 /etc/ansible/facts.d/ 目錄新建example.fact文件,內(nèi)容如下:
[book]
title=Ansible: Up and Running
author=Lorin Hochstein
publisher=O'Reilly Media
然后在運(yùn)行playbook的時(shí)候就可以通過 ansible_local讀取這些變量了。
另外,還可以通過 set_fact 模塊設(shè)置變量,比如之前得到了一個(gè)命令的輸出,register到一個(gè)變量,然后把我們需要的變量提取出來用set_fact存儲(chǔ)到另外一個(gè)變量中,簡化了變量的引用。
- name: test name
hosts: webserver
gather_facts: True
tasks:
- name: print ansible_local
debug: var=ansible_local
- name: capture output of id command
command: id -un
register: login
ignore_errors: True
- set_fact: loginuser={{ login.stdout }}
- name: show login user
debug: var=loginuser
內(nèi)置變量

命令行傳遞變量
還可以在運(yùn)行playbook的時(shí)候在命令行傳遞變量。如果要傳遞一個(gè)包含變量的文件,可以用 $ ansible-playbook greet.yml -e @greetvars.yml 。
- name: pass a message on the command line
hosts: localhost
vars:
greeting: "you didn't specify a message"
tasks:
- name: output a message
debug: msg="{{ greeting }}"
$ ansible-playbook greet.yml -e greeting=hiya
變量優(yōu)先級(jí)
ansible在很多地方可以設(shè)置變量,盡量不要重名。優(yōu)先級(jí)由高到低如下:
- 命令行的參數(shù), 上面的
-e greeting=‘hello’。 - host_vars, group_vars中的變量,不管是在inventory中還是yaml文件中定義的。
- Facts變量
- role目錄下的
defaults/main.yml。
對(duì)于role的特別說明一下,變量優(yōu)先級(jí)最高的是命令行的參數(shù)變量,接著是playbook中調(diào)用role時(shí)傳遞的變量,再是role/vars/main.yml,然后再是play中的vars定義的變量,最后是role/defaults/main.yml中的變量。如下面所示的playbook,除了運(yùn)行時(shí)通過-e傳遞的命令行變量優(yōu)先級(jí)最高外,余下的 2 > 3 >1 > 4。
#!/usr/bin/env ansible-playbook
- name: test name
hosts: dbserver1
vars:
testvar: vars var #1
roles:
- role: database
testvar: role var #2
#####database/vars/main.yml
testvar: vars.main.yml var #3
####database/defaults/main.yml
testvar: defaults.main.yml var #4
5.3 playbook要點(diǎn)
實(shí)用模塊
- 如果想在控制機(jī)器而不是遠(yuǎn)程機(jī)器運(yùn)行命令,可以用local_action。
- 如果機(jī)器沒有啟動(dòng)起來,需要先等待機(jī)器啟動(dòng)再執(zhí)行play,用wait_for模塊。
- name: Deploy mezzanine
hosts: web
gather_facts: False
# vars & vars_files section not shown here
tasks:
- name: wait for ssh server to be running
local_action: wait_for port=22 host="{{ inventory_hostname }}" search_regex=OpenSSH
- name: gather facts
setup:
- 如果不想一次在所有的hosts都執(zhí)行,可以設(shè)置serial參數(shù)來設(shè)置每次執(zhí)行幾個(gè)host,比如升級(jí)服務(wù)器,我們不想影響服務(wù),會(huì)一個(gè)一個(gè)跑??梢栽O(shè)置max_fail_percentage來指定最大失敗的比率,比如設(shè)置為25%,則如果有4臺(tái)機(jī)器,有2臺(tái)任務(wù)執(zhí)行失敗則終止整個(gè)play,其他任務(wù)不再執(zhí)行。
- name: upgrade packages on servers behind load balancer
hosts: myhosts
serial: 1
max_fail_percentage: 25
tasks:
# tasks go here
加密數(shù)據(jù)
一些數(shù)據(jù)如db密碼等不能直接提交到代碼庫,可以提交一個(gè)加密的版本。加密文件可以用ansible-vault工具。運(yùn)行playbook的時(shí)候加上參數(shù)
ansible-vault encrypt secrets.yml
ansible-vault create secrets.yml
ansible-vault view secrets.yml
$ ansible-playbook test.yml --ask-vault-pass
$ ansible-playbook mezzanine --vault-password-file ~/password.txt
hosts格式
可以用冒號(hào):表示合并服務(wù)器組,:& 求交集等。
指定運(yùn)行的hosts可以在命令行加上 —limit 。
ansible-playbook -l 'staging:&database' playbook.yml
ansible-playbook —limit 'staging:&database' playbook.yml

Filters
filter可以用在很多方面,比如默認(rèn)值filter。如果database_host沒有定義,則HOST的值設(shè)置為localhost。
"HOST": "{{ database_host | default('localhost') }}"針對(duì)任務(wù)返回值的filter。可以的取值有 failed,changed,skipped,success等。
failed_when: result|failed-
文件路徑的filter。
vars: homepage: /usr/share/nginx/html/index.html tasks: - name: copy home page copy: src=files/{{ homepage | basename }} dest={{ homepage }} -
自定義filter。
寫一個(gè)自定義的filter,放在項(xiàng)目的 filter_plugins 目錄下即可。下面是一個(gè)用于字符串分割的filter模塊,使用時(shí)使用filter語法即可。from ansible import errors def split_string(string, seperator=' '): try: return string.split(seperator) except Exception, e: raise errors.AnsibleFilterError('split plugin error: %s, string=%s' % str(e),str(string) ) class FilterModule(object): def filters(self): return { 'split' : split_string, }
lookups
查找變量可以通過lookup實(shí)現(xiàn),支持file,redis,pipe,cvsfile等多種格式。(redis的需要安裝python的redis模塊)
復(fù)雜循環(huán)
- with_items
- with_lines
- with_fileglob
- with_dict
- ...
debug你的playbook
檢查語法:ansible-playbook --syntax-check playbook.yml
查看host列表:ansible-playbook --list-hosts playbook.yml
查看task列表:ansible-playbook --list-tasks playbook.yml
檢查模式(不會(huì)運(yùn)行): ansible-playbook --check playbook.yml
diff模式(查看文件變化): ansible-playbook --check --diff playbook.yml
從指定的task開始運(yùn)行:ansible-playbook --start-at-task="install packages" playbook.yml
逐個(gè)task運(yùn)行,運(yùn)行前需要你確認(rèn):ansible-playbook --step playbook.yml
指定tags:ansible-playbook --tags=foo,bar playbook.yml
跳過tags:ansible-playbook --skip-tags=baz,quux playbook.yml
6 角色(Roles)
6.1 角色基本結(jié)構(gòu)
roles可以簡化playbook編寫,讓playbook更清晰和方便復(fù)用。一個(gè)名為database的role的目錄結(jié)構(gòu)如下,這些目錄都是可選的,如果你的角色沒有任何handler,則不需要handlers目錄。roles的查找路徑默認(rèn)是/etc/ansible/roles,也可以在 /etc/ansible/ansible.cfg的roles_path中設(shè)置。
roles/database/tasks/main.yml
Tasks
roles/database/files/
Holds files to be uploaded to hosts
roles/database/templates/
Holds Jinja2 template files
roles/database/handlers/main.yml
Handlers
roles/database/vars/main.yml
Variables that shouldn’t be overridden
roles/database/defaults/main.yml
Default variables that can be overridden
roles/database/meta/main.yml
Dependency information about a role
6.2 pre_tasks和post_tasks
在角色執(zhí)行任務(wù)前做一些前置工作,任務(wù)執(zhí)行完后做一些后置處理。
- name: test
hosts: dbserver
vars_files:
- secrets.yml
pre_tasks:
- name: update the apt cache
apt: update_cache=yes
roles:
- role: database
post_tasks:
- name: send email
command: xxx
6.3 依賴角色
如果怕遺漏一些任務(wù),比如設(shè)置ntp之類的,可以使用依賴角色的功能。這樣在執(zhí)行你的角色任務(wù)時(shí)會(huì)先執(zhí)行依賴角色。
roles/database/meta/main.yml
dependencies:
- { role: ntp, ntp_server=ntp.ubuntu.com }
6.4 Ansible Galaxy
可以通過ansible galaxy工具方便的創(chuàng)建一個(gè)角色目錄。
ansible-galaxy init -p playbooks/roles database
如果不用-p指定路徑,那么默認(rèn)是會(huì)在當(dāng)前目錄創(chuàng)建角色的目錄結(jié)構(gòu)。創(chuàng)建后的目錄結(jié)構(gòu)如下:
playbook/roles/database
├── README.md
├── defaults
│ └── main.yml
├── files
├── handlers
│ └── main.yml
├── meta
│ └── main.yml
├── tasks
│ └── main.yml
├── templates
├── tests
│ ├── inventory
│ └── test.yml
└── vars
└── main.yml
ansible galaxy還是一個(gè)開源的角色庫,你可以在其中下載到許多其他人寫好的角色代碼或者提交自己的角色代碼。角色倉庫的使用說明在這里。
6.5 Ansible Tower
ansible tower是ansible公司提供的一套商用的web管理平臺(tái),也有試用版本,還沒有試用過,后續(xù)使用了再補(bǔ)充。
7 加速你的ansible
7.1 SSH ControlPersist
ControlMaster auto
ControlPath $HOME/.ansible/cp/ansible-ssh-%h-%p-%r
ControlPersist 60s
7.2 Pipelining(ansible的特性)
ansible通常執(zhí)行的原理是在 ~/.ansible下面創(chuàng)建一個(gè)臨時(shí)目錄(通過ssh),然后通過sftp或者scp拷貝python腳本到臨時(shí)目錄,然后執(zhí)行這個(gè)腳本代碼(再次通過ssh)。使用pipeline可以使得這些操作只要一個(gè)ssh連接去執(zhí)行python腳本。即便是開啟了ControlPersist,這個(gè)性能提升也很可觀。在配置文件中加入pipelining=true即可開啟。
需要注意的是,開啟pipeling要關(guān)閉服務(wù)器的requiretty功能。增加文件/etc/sudoers.d/disable-requiretty,其中的內(nèi)容為 Defaults:ansibleuser !requiretty,ansibleuser為你的用戶名。
7.3 Fact Cache
如果你不需要用到服務(wù)器信息,可以關(guān)閉獲取fact的功能,這樣也可以加快playbook的執(zhí)行效率。配置文件中加入 gathering = explicit即可,這樣你要獲取服務(wù)器信息,需要顯示的在play中指定。
如果要用到fact信息,可以使用fact緩存,這樣每個(gè)機(jī)器的fact信息只會(huì)獲取一次而不是每次都去獲取。fact緩存支持json,redis,memcached。如果用redis則需要在控制機(jī)上安裝python的redis模塊,自然redis也是要安裝的。
[defaults]
gathering = smart
# 24-hour timeout, adjust if needed
fact_caching_timeout = 86400
# You must specify a fact caching implementation
# JSON file implementation
fact_caching = jsonfile //或者redis,memcached
fact_caching_connection = /tmp/ansible_fact_cache
7.4 Parallelism
可以設(shè)置ANSIBLE_FORKS環(huán)境變量或者在配置文件加上forks=n來指定并行執(zhí)行的host的數(shù)目。
7.5 關(guān)于異步
ansible的1.7版本開始增加了異步參數(shù) async,也就是說執(zhí)行一個(gè)時(shí)間很長的任務(wù)時(shí),可以不用等待它結(jié)束,而是直接先執(zhí)行后面的任務(wù),在后續(xù)的play中定時(shí)檢查任務(wù)執(zhí)行結(jié)果即可。
有幾點(diǎn)注意一下,一個(gè)是async參數(shù),是指任務(wù)執(zhí)行的超時(shí)時(shí)間,如果這個(gè)時(shí)間設(shè)置的比任務(wù)執(zhí)行時(shí)間短,則任務(wù)會(huì)超時(shí)失敗。poll值為輪詢?nèi)蝿?wù)狀態(tài)的時(shí)間間隔,如果設(shè)置為0,表示啟動(dòng)并忽略,也就是說設(shè)置為0才是真正的開始異步執(zhí)行,也就是直接執(zhí)行后面的task,而為了知道異步任務(wù)執(zhí)行的結(jié)果,可以用async_status來實(shí)現(xiàn)。如果poll設(shè)置為非0值,則還是阻塞執(zhí)行的,并非異步。
- hosts: dbserver
tasks:
- name: simulate long running op (15 sec), wait for up to 45 sec, poll every 5 sec
command: /bin/sleep 15
async: 20
poll: 0
register: asynctest
- name: check async status
async_status: jid="{{ asynctest.ansible_job_id }}"
register: job_result
until: job_result.finished
retries: 30
delay: 2
8 創(chuàng)建自定義模塊
在某些情況下,可能ansible自帶的模塊不能滿足你的需求,需要自定義模塊??梢酝ㄟ^python或者bash來寫自定義模塊,符合ansible的模塊編寫標(biāo)準(zhǔn)即可,這里有很詳細(xì)的文檔。
9 Docker
docker是目前很火爆的技術(shù),它提供了一套遠(yuǎn)程API供第三方程序調(diào)用,ansible的docker模塊就是使用了這套API對(duì)docker操作。ansible用在docker上主要有兩點(diǎn):一是編排docker容器。通常一個(gè)系統(tǒng)需要很多個(gè)docker容器來支持,每個(gè)容器都運(yùn)行一個(gè)服務(wù)。服務(wù)之間需要相互通信,同時(shí)你也要保證容器啟動(dòng)的順序等,原生docker并沒有這些工具支持,ansible則是非常合適的一個(gè)選擇。二是創(chuàng)建鏡像。官方的方式是通過Dockerfile來創(chuàng)建鏡像,但是通過ansible來實(shí)現(xiàn)更加簡單方便。
基于docker的應(yīng)用的生命周期是這樣的:
1. 在本地機(jī)器創(chuàng)建docker鏡像。
2. 將docker鏡像push到registry。
3. 遠(yuǎn)程機(jī)器上將鏡像從registry上pull下來。
4. 在遠(yuǎn)程機(jī)器上啟動(dòng)容器。
使用ansible之后,則是下面這樣的:
1. 寫好用來創(chuàng)建docker鏡像的playbook。
2. 運(yùn)行playbook來創(chuàng)建鏡像。
3. 將docker鏡像推送到registry。
4. 寫好一個(gè)拉取docker鏡像并啟動(dòng)容器的playbook。
5. 執(zhí)行playbook拉取和啟動(dòng)容器。
關(guān)于ansible搭建一個(gè)完整實(shí)例和docker化將在下一篇文章中詳細(xì)描述,敬請期待。