安裝ansible
1.yum安裝:
RHEL(Centos)7版本:
rpm -Uvh http://mirrors.zju.edu.cn/epel/7/x86_64/e/epel-release-7-8.noarch.rpm
yum install ansible
2.Apt(Ubuntu)安裝方式:
apt-get install software-properties-common
apt-add-repository ppa:ansible/ansible
apt-get update
apt-get install ansible
3.homebrew (Mac OSX)安裝方式
brew update
brew install Ansible
4.通過pip安裝:
sodu easy_install pip
pip install ansible
如果是在OS X系統(tǒng)上安裝,編譯器可能會(huì)有警告或出錯(cuò),需要設(shè)置CFLAGS、CPPFLAGS環(huán)境變量:
sudo CFLAGS=-Qunused-arguments CPPFLAGS=-Qunused-arguments pip install ansible
配置運(yùn)行環(huán)境
Ansible環(huán)境
Ansible 配置文件是以.ini格式存儲(chǔ)配置數(shù)據(jù)的,在Ansible中,幾乎所有的配置項(xiàng)都可以通過Ansible的playbook或環(huán)境變量來重新賦值。在運(yùn)行Ansible命令式,命令將會(huì)按照預(yù)先設(shè)定的順序查找配置文件,如下所示:
1)ANSIBLE_CONFIG: 首先,Ansible命令會(huì)檢查環(huán)境變量,及這個(gè)環(huán)境變量指向的配置文件。
2)./ansible.cfg: 其次,將會(huì)檢查當(dāng)前目錄下的ansible.cfg配置文件
3)~/ansible.cfg: 再次,將會(huì)檢查當(dāng)前用戶home目錄下的.ansible.cfg配置文件
- /etc/ansible/ansible.cfg: 最后,將會(huì)檢查再用軟件包管理工具安裝Ansible時(shí)自動(dòng)產(chǎn)生的配置文件。
ansible.cfg常用配置參數(shù)
1.inventory 這個(gè)參數(shù)表示資源清單inventory文件的位置,資源清單就是被管理主機(jī)列表。如:inventory = /etc/ansible/hosts
2.forks 設(shè)置默認(rèn)情況下Ansible最多能有多少個(gè)進(jìn)程同時(shí)工作,默認(rèn)設(shè)置最多5個(gè)進(jìn)程并行處理。如: forks = 5
3.sudo_user 這是設(shè)置默認(rèn)執(zhí)行命令的用戶,如: sudo_user = root
4.remote_port 這是制定連接被管節(jié)點(diǎn)的管理端口,默認(rèn)是22。除非設(shè)置了特殊的SSH端口,不然這個(gè)參數(shù)一般是不需要修改的。如:reomte_ssh = 22
5.timeout 這是設(shè)置SSH連接的超時(shí)間隔,單位是秒。配置示例如下:timeout = 60
第一條ansible命令
編輯(或創(chuàng)建)/etc/ansible/hosts文件,在其中加入被管理的遠(yuǎn)程主機(jī):
[merge]
10.0.81.31
10.0.81.32
10.0.81.33
注意:你的public SSH key必須在這些系統(tǒng)的“authorized_keys”中
使用list-hosts參數(shù)進(jìn)行驗(yàn)證:

現(xiàn)在對(duì)你的管理節(jié)點(diǎn)運(yùn)行一個(gè)命令:

inventory文件
主機(jī)與組
Ansible 可同時(shí)操作屬于一個(gè)組的多臺(tái)主機(jī),組和主機(jī)之間的關(guān)系通過 inventory 文件配置. 默認(rèn)的文件路徑為 /etc/ansible/hosts
/etc/ansible/hosts 文件的格式與windows的ini配置文件類似:
[webservers]
foo.example.com
bar.example.com
[dbservers]
one.example.com
two.example.com
three.example.com
方括號(hào)[]中是組名,用于對(duì)系統(tǒng)進(jìn)行分類,便于對(duì)不同系統(tǒng)進(jìn)行個(gè)別的管理。
一個(gè)系統(tǒng)可以屬于不同的組,比如一臺(tái)服務(wù)器可以同時(shí)屬于 webserver組 和 dbserver組.這時(shí)屬于兩個(gè)組的變量都可以為這臺(tái)主機(jī)所用
如果有主機(jī)的SSH端口不是標(biāo)準(zhǔn)的22端口,可在主機(jī)名之后加上端口號(hào),用冒號(hào)分隔,如下:
badwolf.example.com:5309
假設(shè)你有一些靜態(tài)IP地址,希望設(shè)置一些別名,但不是在系統(tǒng)的 host 文件中設(shè)置,又或者你是通過隧道在連接,那么可以設(shè)置如下:
jumper ansible_ssh_port=5555 ansible_ssh_host=192.168.1.50
在這個(gè)例子中,通過 “jumper” 別名,會(huì)連接 192.168.1.50:5555.
一組相似的 hostname , 可簡(jiǎn)寫如下:
[webservers]
www[01:50].example.com
數(shù)字的簡(jiǎn)寫模式中,01:50 也可寫為 1:50,意義相同.你還可以定義字母范圍的簡(jiǎn)寫模式:
[databases]
db-[a:f].example.com
對(duì)于每一個(gè)host,你還可以選擇連接類型和連接用戶名:
[targets]
localhost ansible_connection=local
other1.example.com ansible_connection=ssh ansible_ssh_user=mpdehaan
other2.example.com ansible_connection=ssh ansible_ssh_user=mdehaan
主機(jī)變量
分配變量給主機(jī)的方式:
[atlanta]
host1 http_port=80 maxRequestsPerChild=808
host2 http_port=303 maxRequestsPerChild=909
組的變量
也可以定義屬于整個(gè)組的變量:
[atlanta]
host1
host2
[atlanta:vars]
ntp_server=ntp.atlanta.example.com
proxy=proxy.atlanta.example.com
把一個(gè)組作為另一個(gè)組的子成員
可以把一個(gè)組作為另一個(gè)組的子成員,以及分配變量給整個(gè)組使用. 這些變量可以給 /usr/bin/ansible-playbook 使用,但不能給 /usr/bin/ansible 使用:
[atlanta]
host1
host2
[raleigh]
host2
host3
[southeast:children]
atlanta
raleigh
[southeast:vars]
some_server=foo.southeast.example.com
halon_system_timeout=30
self_destruct_countdown=60
escape_pods=2
[usa:children]
southeast
northeast
southwest
northwest
分文件定義 Host 和 Group 變量
在 inventory 主文件中保存所有的變量并不是最佳的方式.還可以保存在獨(dú)立的文件中,這些獨(dú)立文件與 inventory 文件保持關(guān)聯(lián). 不同于 inventory 文件(INI 格式),這些獨(dú)立文件的格式為 YAML.
假設(shè) inventory 文件的路徑為:
/etc/ansible/hosts
假設(shè)有一個(gè)主機(jī)名為 ‘foosball’, 主機(jī)同時(shí)屬于兩個(gè)組,一個(gè)是 ‘raleigh’, 另一個(gè)是 ‘webservers’. 那么以下配置文件(YAML 格式)中的變量可以為 ‘foosball’ 主機(jī)所用.依次為 ‘raleigh’ 的組變量,’webservers’ 的組變量,’foosball’ 的主機(jī)變量:
/etc/ansible/group_vars/raleigh
/etc/ansible/group_vars/webservers
/etc/ansible/host_vars/foosball
Tip: Ansible 1.2 及以上的版本中,group_vars/ 和 host_vars/ 目錄可放在 inventory 目錄下,或是 playbook 目錄下. 如果兩個(gè)目錄下都存在,那么 playbook 目錄下的配置會(huì)覆蓋 inventory 目錄的配置
Parallelism and Shell Commands
目前ansible自帶很多模塊,我們可以使用ansible-doc -l顯示所有自帶模塊,還可以通過?ansible-doc "模塊名",查看模塊的介紹以及案例。
下面舉一些常見的命令:
1.shell命令

-o, --one-line Try to output everything on one line.
2.復(fù)制文件

3.包和服務(wù)管理

playbook
Playbooks 是 Ansible的配置,部署,編排語言.他們可以被描述為一個(gè)需要希望遠(yuǎn)程主機(jī)執(zhí)行命令的方案,或者一組IT程序運(yùn)行的命令集合.
playbook 文件格式為YAML語法,在編寫playbook前需要對(duì)YAML有一定的了解,關(guān)于YAML語法可以通過 yaml官網(wǎng)進(jìn)行學(xué)習(xí)。
playbook 由一個(gè)或多個(gè) ‘plays’ 組成.它的內(nèi)容是一個(gè)以 ‘plays’ 為元素的列表。在 play 之中,一組機(jī)器被映射為定義好的角色.在 ansible 中,play 的內(nèi)容,被稱為 tasks,即任務(wù).在基本層次的應(yīng)用中,一個(gè)任務(wù)是一個(gè)對(duì) ansible 模塊的調(diào)用,
下面一個(gè) playbook示例,其中僅包含一個(gè) play:
---
- hosts: webservers
vars:
http_port: 80
max_clients: 200
remote_user: root
tasks:
- name: ensure apache is at the latest version
yum: pkg=httpd state=latest
- name: write the apache config file
template: src=/srv/httpd.j2 dest=/etc/httpd.conf
notify:
- restart apache
- name: ensure apache is running
service: name=httpd state=started
handlers:
- name: restart apache
service: name=httpd state=restarted
在 ansible-examples 中有很多實(shí)例,如果你希望深入學(xué)習(xí)可以在單獨(dú)的頁(yè)面打開它。
Task
每一個(gè) play 包含了一個(gè) task 列表(任務(wù)列表).一個(gè) task 在其所對(duì)應(yīng)的所有主機(jī)上(通過 host pattern 匹配的所有主機(jī))執(zhí)行完畢之后,下一個(gè) task 才會(huì)執(zhí)行.
playbook變量與引用
1.通過inventory文件定義主機(jī)以及主機(jī)變量
在/etc/ansible/hosts中定義主機(jī)組和組變量如下:
merge]
10.0.81.31
10.0.81.32
10.0.81.33
[merge:vars]
key=test
創(chuàng)建playbook文件test.yaml如下:
---
- hosts: merge
gather_facts: False
tasks:
- name: display Host Variable from hostfile
debug: msg="The key value is {{ key }}"
運(yùn)行playbook文件:

2.通過文件定義主機(jī)以及主機(jī)組變量
我們可以通過host_vars和group_vars目錄來針對(duì)主機(jī)和主機(jī)組定義變量。
使用yum安裝Ansible的配置文件在/etc/ansible/目錄下,我們?cè)谠撃夸浵滦陆╤ost_vars和group_vars目錄,如下:

3.通過ansible-playbook命令行傳入
可以通過-e 命令傳入變量
ansbile-playbook test.yaml -e "key=test"
ansible-playbook目前還支持YAML和JSON的方式傳入變量
cat var.yaml
---
key: test
cat var.json
{"key": "test"}
4.可以在playbook文件內(nèi)通過vars字段定義變量
5.可以在playbook文件內(nèi)通過vars_files字段引用變量,首先把所有的變量定義在某個(gè)文件內(nèi),然后再playbook文件內(nèi)使用vars_files參數(shù)引用這個(gè)變量文件
6.使用register內(nèi)的變量
Ansible playbook內(nèi)task之間可以互相傳遞數(shù)據(jù),比如第2個(gè)task需要獲得第1個(gè)task的執(zhí)行結(jié)果。
我們可以通過下面的方式:
vim test01.yaml
---
- hosts: merge
gather_facts: False
tasks:
- name: register variable
shell: hostname
register: info
- name: display variable
debug: msg="The key value is {{ info }}"

info的結(jié)果是一段python字典數(shù)據(jù),里面存儲(chǔ)著很多信息,包括執(zhí)行時(shí)間狀態(tài)變化輸出等。
變量的優(yōu)先級(jí)
如果同樣名稱的變量在多個(gè)地方都有定義,那么采納是有個(gè)確定的順序,如下:
1.xtra vars (在命令行中使用 -e)優(yōu)先級(jí)最高,如:ansible-playbook -e var=value
2.然后是在inventory中定義的連接變量(比如ansible_ssh_user)
3.接著是大多數(shù)的其它變量(命令行轉(zhuǎn)換,play中的變量,included的變量,role中的變量等)
4.然后是在inventory定義的其它變量
5.然后是由系統(tǒng)發(fā)現(xiàn)的facts
6.然后是 "role默認(rèn)變量", 這個(gè)是最默認(rèn)的值,很容易喪失優(yōu)先權(quán)
playbook 循環(huán)
1.標(biāo)準(zhǔn)loops
example:
---
- hosts: merge
gather_facts: False
tasks:
- name: debug loops
debug: msg="name ----------> {{ item }}"
with_items:
- one
- two

with_items的值是python list數(shù)據(jù)結(jié)構(gòu), 可以理解為每個(gè)task會(huì)循環(huán)讀取list里面的值,然后key的名稱是item
在copy和tempalate模塊里面,你能夠使用ansible去查找一組的文件.然后默認(rèn)使用第一個(gè)文件.這能夠讓你達(dá)到效果是,當(dāng)?shù)谝粋€(gè)文件不存在時(shí),會(huì)查找第二個(gè)文件,如此類推知道最后一個(gè)文件還不存在就報(bào)fail.使用first_available_file這個(gè)關(guān)鍵字便可以到上述效果.
---
- 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
2.嵌套loops
---
- hosts: merge
gather_facts: False
tasks:
- name: debug loops
debug: msg="name ----------> {{ item[0] }} ---------> {{ item[1] }}"
with_nested:
- ['A']
- ['a','b','c']

3.條件判斷l(xiāng)oops
有時(shí)候執(zhí)行一個(gè)task之后,我們需要檢測(cè)這個(gè)task的結(jié)果是否達(dá)到了預(yù)想狀態(tài),如果沒有就需要退出整個(gè)playbook執(zhí)行,這個(gè)時(shí)候我們就需要對(duì)某個(gè)task結(jié)果一直循環(huán)檢測(cè)了,如下所示:
---
- hosts: merge
gather_facts: False
tasks:
- name: debug loops
shell: cat /root/test.txt
register: host
until: host.stdout.startswith("test")
retries: 5
delay: 5
5秒執(zhí)行一次 cat /root/test.txt將結(jié)果register給host,然后判斷host.stdout.startwith的內(nèi)容是不是test字符串開頭,如果條件成立,此task運(yùn)行完成,如果條件不成立,5秒以后重試,5此后還不滿足條件,此task運(yùn)行失敗。
其他的循環(huán)還有:散列l(wèi)oops、文件匹配loops、隨機(jī)選擇loops、文件優(yōu)先匹配loops、register loops
playbook lookups
Ansible還支持從外部數(shù)據(jù)拉取信息,比如我們可以從數(shù)據(jù)庫(kù)拉取信息,然后賦值給一個(gè)變量。
1.lookups file
---
- hosts: merge
gather_facts: False
vars:
contents: "{{ lookup('file', '/root/openrc') }}"
tasks:
- name: debug lookups
debug: msg="The contents is {% for i in contents.split("\n") %} {{ i }} {% endfor %}"
2.lookups password
lookup('password', 'file_path')
它會(huì)對(duì)傳入的內(nèi)容進(jìn)行加密處理
playbook conditionals
目前Ansible所有conditionals方式都是通過使用when進(jìn)行判斷,when的值是一個(gè)條件表達(dá)式,如果條件判斷成立,這個(gè)task就執(zhí)行某個(gè)操作,否則,該task不執(zhí)行。
tasks:
- name: "shutdown Debian flavored systems"
command: /sbin/shutdown -t now
when: ansible_os_family == "Debian"
如果想查看哪些facts變量可以引用,可以在命令行上通過調(diào)用setup module命令可以查看
ansible hostname -m setup
TAGS
如果你有一個(gè)很大的playbook,而你只想run其中的某個(gè)task,這個(gè)時(shí)候tags是你的最佳選擇。
一、最常見的使用形式:
tasks:
- yum: name={{ item }} state=installed
with_items:
- httpd
- memcached
tags:
- packages
- template: src=templates/src.j2 dest=/etc/foo.conf
tags:
- configuration
此時(shí)若你希望只run其中的某個(gè)task,這run 的時(shí)候指定tags即可:
ansible-playbook example.yml --tags "configuration,packages" #run 多個(gè)tags
ansible-playbook example.yml --tags packages # 只run 一個(gè)tag
相反,也可以跳過某個(gè)task
ansible-playbook example.yml --skip-tags configuration
二、tags 和role 結(jié)合使用
tags 這個(gè)屬性也可以被應(yīng)用到role上,例如:
roles:
- { role: webserver, port: 5000, tags: [ 'web', 'foo' ] }
三、tags和include結(jié)合使用
- include: foo.yml tags=web,foo
這樣,fool.yml 中定義所有task都將被執(zhí)行
四、系統(tǒng)中內(nèi)置的特殊tags:
always、tagged、untagged、all 是四個(gè)系統(tǒng)內(nèi)置的tag,有自己的特殊意義。
always: 指定這個(gè)tag 后,task任務(wù)將永遠(yuǎn)被執(zhí)行,而不用去考慮是否使用了--skip-tags標(biāo)記
tagged: 當(dāng) --tags 指定為它時(shí),則只要有tags標(biāo)記的task都將被執(zhí)行,--skip-tags效果相反
untagged: 當(dāng) --tags 指定為它時(shí),則所有沒有tag標(biāo)記的task 將被執(zhí)行,--skip-tags效果相反
all: 這個(gè)標(biāo)記無需指定,ansible-playbook 默認(rèn)執(zhí)行的時(shí)候就是這個(gè)標(biāo)記.所有task都被執(zhí)行
Roles
怎樣組織 playbook 才是最好的方式呢?簡(jiǎn)單的回答就是:使用 roles ! Roles 基于一個(gè)已知的文件結(jié)構(gòu),去自動(dòng)的加載某些 vars_files,tasks 以及 handlers?;?roles 對(duì)內(nèi)容進(jìn)行分組,使得我們可以容易地與其他用戶分享 roles 。
一個(gè)項(xiàng)目的結(jié)構(gòu)如下:
site.yml
webservers.yml
fooservers.yml
roles/
common/
files/
templates/
tasks/
handlers/
vars/
defaults/
meta/
webservers/
files/
templates/
tasks/
handlers/
vars/
defaults/
meta/
如果 roles 目錄下有文件不存在,這些文件將被忽略。比如 roles 目錄下面缺少了 ‘vars/’ 目錄,這也沒關(guān)系。
當(dāng)一些事情不需要頻繁去做時(shí),你也可以為 roles 設(shè)置觸發(fā)條件,像這樣:
---
- hosts: webservers
roles:
- { role: some_role, when: "ansible_os_family == 'RedHat'" }
Ansible最佳實(shí)踐
優(yōu)化Ansible速度
1.開啟SSH長(zhǎng)連接
如果被管理機(jī)器的SSH -V版本高于5.6時(shí),我們可以直接在ansible.cfg文件中設(shè)置SSH長(zhǎng)連接。設(shè)置參數(shù)如下:
sh_args = -o ControlMaster=auto -o Controlpersist=5d
Controlpersist=5d 設(shè)置整個(gè)長(zhǎng)連接保持時(shí)間為5天
2.開啟pipelining
如果開啟了pipelining ,生成好的本地Python腳本PUT到遠(yuǎn)端服務(wù)器的過程會(huì)在SSH的會(huì)話中進(jìn)行,這樣可以大大提高整個(gè)執(zhí)行效率。在ansible.cfg中設(shè)置:
pipelining = True
3.設(shè)置facts緩存
如果playbook不需要facts信息,可以在playbook中設(shè)置gather_facts:Fasle來提高playbook效率。但是如果我們既想在每次執(zhí)行playbook的時(shí)候能搜集facts,又想加速這個(gè)收集過程,那么就需要配置facts緩存了。
可以在ansible.cfg中配置fact緩存使用redis:
[defaults]
gathering = smart
fact_caching = redis #jsonfile等,使用json 需要指定fact_caching_connection指定存放路徑
fact_caching_timeout = 86400
灰度發(fā)布與檢測(cè)
1.語法檢測(cè)
在編寫完playbook或者role之后一定要養(yǎng)成進(jìn)行語法檢測(cè)的習(xí)慣,直接使用ansible-playbook命令的 --syntax-check參數(shù)即可。
2.灰度發(fā)布
進(jìn)行預(yù)運(yùn)行之前,我們需要把一個(gè)或者多個(gè)task使用delegate_to參數(shù)指定到一臺(tái)設(shè)備上進(jìn)行測(cè)試。
Ansible與openstack
---
- hosts: localhost
gather_facts: False
vars:
auth_url: http://10.0.84.52:35357/v3
login_username: admin
login_tenant_name: admin
login_password:
image_id: 63a7ea8d-2fc4-442b-8455-261b93aeb909
keypair_name: mykey
private_net: 1b3c98f2-7cbb-459f-bb1f-3dbb4facee13
flavor_id: 1
connection: local
tasks:
- name: create compute
nova_compute:
auth_url: "{{ auth_url }}"
login_username: "{{ login_username }}"
login_password: "{{ login_password }}"
login_tenant_name: "{{ login_tenant_name }}"
security_groups: default
state: present
name: "test-xue-01"
image_id: "{{ image_id }}"
key_name: "{{ keypair_name }}"
flavor_id: "{{ flavor_id }}"
region_name: RegionOne
nics:
- net-id: "{{ private_net }}"
register: openstack
- name: pull floating ip address
local_action: floating
auth_url={{ auth_url }} username={{ login_username }} password={{ login_password }} tenant={{ login_tenant_name }}
register: floating_ip
- name: bond flaoting ip to instance
local_action: bondip_openstack
auth_url={{ auth_url }} username={{ login_username }} password={{ login_password }} tenant={{ login_tenant_name }} instance_id={{ openstack.id }}floating_ip={{ floating_ip.res }}
更多資料可以查看:openstack-ansible
幾個(gè)實(shí)用的ansible命令:
1.管理域名解析器:
- name: Create DNS File
register: config_resolv
template: src=resolv.conf.j2 dest=/etc/resolv.conf
tags: firsthyper_init
由于我使用了role來組織playbook,roleresolv.conf.j2在相應(yīng)role的templates目錄下.
2.安裝常用軟件:
- name: Install ntp,puppet,rsync
register: install_package
yum: name={{item}} state=latest
with_items:
- ntpdate
- puppet
- rsync
3.同步時(shí)間
- name: Sync time
register: sync_time
shell: systemctl stop ntpd && sleep 5 && /usr/sbin/ntpdate {{ ntp_server }} 1 >/dev/null 2>&1 && systemctl start ntpd
ntp_server定義在group_vars中.
4.查看數(shù)據(jù)庫(kù)日志文件名稱
- name: Fetch Mysql Master Log File
register: master_log_file
shell: mysql -e "show master status" | grep mysql | awk '{print $1}'
delegate_to: "{{ mysql_master }}"
參考:
Ansible中文權(quán)威指南
“詭跡” 博客
Ansible自動(dòng)化運(yùn)維技術(shù)與最佳實(shí)踐