ansible默认只会创建5个进程,所以一次任务只能同时控制5台机器执行。那如果在使用ansible的过程中有大量的机器需要控制,或者在使用中想减少进程数,以上情况下,可以采取异步执行。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
在以上的例子中,如果all包括5台以上的机器。install mlocate这个任务会先在5台机器上跑,完成之后再继续下面的机器。而run updatedb任务会一次性在所有机器上都执行,然后监听这个任务的回调结果。
如果某一个任务的command是控制机器开启一个进程放到后台,那就可以不用检查这个任务是否完成了,我们只需要继续其他的动作,最后使用wait_for这个模块去检查之前的进程是否按预期中开启了便可。只需要将poll的值设置为0,便可以按照上面的要求配置。ansible不等待job的完成。
最后,还有一种需求是有一个task,他需要运行很长的时间(例如,需要将远程主机重启),那我们需要设置一直等待这个job完成,这个时候就需要将async的值设置为0便可以。
总结:
在ansible中可以通过不同的输入去重复的执行同一个模块。例子:我们需要管理几个相同权限的文件,我们可以使用for循环迭代一个facts或者variables去减少重复操作。
使用with_items这个关键字就可以完成迭代一个列表。列表中的每个变量都叫做item。例如:
tasks:
- name: secure config files
file: path=/etc/{{ item }} mode=0600 owner=root group=root
with_items:
- my.cnf
- fstab
- shadow
以上例子中,可以修改几个相同权限,用户和组的文件
例子二:
tasks:
- name: yum
yum : name={{ item }} state=installed
with_items:
- tree
- net-tools
- vim
- lsof
以上例子中,可以私用with_items安装一系列的包,而不用些好多个yum。
lookup插件,这些插件可以让ansible从外部获取数据。
一个例子:我们希望通过一种特定模式上传文件。即上传所有的public keys到一个目录,然后聚合他们到authroized_keys文件
tasks:
- name: make key directory
file: path=/root/.sshkeys ensure=directory mode=0700 owner=root group=root
- name: upload public keys
copy: src={{ item }} dest=/root/.sshkeys mode=0600 owner=root group=root
with_filelob:
- keys/*.pub
- name: Assemble keys into authorized_keys file
assemble: src=/root/.sshkeys dest=/root/.ssh/authorized_keys mode=0600 owner=root group=root
loop模块的使用场景:
在一些模块中有一些机制可以跳过本次模块的运行。但某些时候,我们并不能使用拥有跳过机制的模块,所以我们需要使用条件语句去配置跳过模块,这样情况下我们就可以选择使用不同的包管理和不同的文件系统,并且我们还可以使用set_facts这个模块做成更多的差异配置。
我们可以使用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: unexcepted 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 unexcepted conditions
pause: prompt="unexpected os"
when: ansible_os_family != "Redhat"
可以使用条件语句做跳过动作的场景:
默认ansible的所有tasks是在我们的配置的管理机器上面运行。当在一个独立的集群里配置,以上情况是适用的。然而有一些情况下,某些任务运行的状态是需要传递给其他机器的,在同一任务需要再其他机器上执行的,以上情况下,就可以使用tasks委托。
使用delegate_to关键字便可以配置任务在其他机器上执行。其他模块还是在所有配置的管理机器上运行的,当到了这个关键字的任务就是使用委托的机器上运行。而facts还是适用于当前的host。
一个例子(使用get_url模块去下载一个web集群的配置):
---
- name: fetch confgiuraction 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: loaclhost
如上例子委托localhost执行任务,我们可以使用一种更便捷的方式来实现。
---
- name: fetch confgiuraction from all webservers
hosts: webservers
tasks:
- name: get config
local_action: get_url dest=configs/{{ ansible_hostname }} url=http://{{ ansible_hostname }}/diagnostic/config
delegate_to: loaclhost
委托不限于localhost,可以是hosts里面的任何一个host。
委托的适用场景:
hostvars允许我们在当前任务中应用所有host的变量。当setup模块没有运行的时候,只有这些变量将是可用的。
${hostvars.hostname.fact}可以访问其他复杂的变量、 ${hostvars.ns1,ansible_distribution}可以得到ns1这个server的LINUX发行版本。
如下示例,设置一个dns_master变量,这是ns1的server的ip。这个变量可以在所有机器上调用。
---
- name: setup slaves
hosts: slavename
tasks:
- name: get the master
set_fact: dns_master= "{{ hostvars.ns1.ansible_default_ipv4.address }}"
- name: configure BIND
template: dest=/etc/named.conf src/templates/named.conf.j2
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_eth0.ipv4.address }} state=present
groups变量实际上不是hosts变量的列表,它只是hosts的name列表,如果我们需要调用host里面的变量还需要配合hostvars使用。
group_names是当前host所属的组的列表。这可以用于在条件语句中调用成员的group关系,或者用于debugging。通常这个变量用于跳过一些task或者在模板中用于条件语句的变量。
例子(我们拥有两套sshd配置文件,一套用于安全性更加严谨的,一个安全性普通的,然后根据group_name来分配host到那个sshd下):
---
- name: setup sshd
hosts: sshservers
tasks:
- name: for secure machines
set_fact: sshconfig=files/ssh/sshd_config_secure
when: "'secure' in group_name"
- name: for non-secure machines
set_fact: sshconfig=files/ssh/sshd_config_default
when: "'secure' not in group_name"
- name: copy over the config
copy: src={{ sshconfig }} dest=/tmp/sshd_config
inventory_hostname是机器的hostname。此变量可以帮助初始化机器和改变hostname。
此变量类似与inventory_hostname变量,只是它截取第一个句点的前面的字符,例如:hostname是host.example.com就会截取到host。
此变量是inventory文件的路径,包括目录名与文件名。
类似inventory_dir变量,但此变量只包含文件名。
所有的模块可以把变量作为参数的一部分。通过使用“{{ }}”来使用变量。例如,test变量就是“{{ test }}”,这样就可以通过变量加载特定的文件。
例:我们通过不同的机器architecture选择不同的NRPE配置文件。
---
- name: configure nrpe for the right architecture
hosts: all
user: root
tasks:
- name: copy in the correct nrpe config file
copy: src=files/nrpe.{{ ansible_architecture }}.conf dest=/etc/nagios/nrpe.conf
在copy和template模块里面。我们可以使用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 dest={{ item }}
first_available_file:
- files/apache/{{ absible_os_family }}-{{ ansible_architecture }}.cfg
- files/apache/default-{{ ansible_architecture }}.cfg
- files/apache/default.cfg
有一些命令经常需要依赖环境变量,但在ansible中很容易实现。例(现在需要远程控制一个机器文件到对象存储服务器[以aws的s3为例],那么就需要配置一些密钥之类的东西):
---
- name: upload a remote five via s3
hosts: ansibletest
user: root
tasks:
- name: setup epel
command: rpm -ivh http://download.fedoraprohect.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 --buvket=my-test-bucket --key={{ ansible_hostname }}/fstab --body=/etc/fstab --region=eu-wext-1
environment:
AWS_ACCESS_KEY_ID:********************
AWS_SECRET_ACCESS_KEY:*****************
需要配置环境变量的场景:
几乎所有的模块都是会outputs一些东西,甚至debug模块也会。大多数我们会使用的结果变量是changed。这个changed变量决定了是否要直接handlers和输出的颜色是什么。然而,结果变量还有一些其他的用处,例如,我需要将一个结果变量保存起来,在我的playbook的其他地方进行使用。例:
---
- 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的场景:
默认情况下,ansible将尝试管理playbook中所有的机器。对于滚动更新用例,可以使用serial关键字定义ansible一次应管理多少主机,还可以将它定义为百分比,表示每次并行执行的主机数占总数的比例。
---
- host: all
name: example
serial: 2 #每次只同时处理2个主机
user: root
tasks:
- name: task one
command: hostname
---
- host: all
name: example
serial: "20%" #每次只同时处理20%主机
user: root
tasks:
- name: task one
command: hostname