首页 > 代码库 > Ansible@一个高效的配置管理工具--Ansible configure management--翻译(四)

Ansible@一个高效的配置管理工具--Ansible configure management--翻译(四)

无书面许可请勿转载

由于第三章内容较长,我将分做几个部分来翻译。

Advanced Playbooks
So far the playbooks that we have looked at are simple and just run a number of
modules in order. Ansible allows much more control over the execution of your
playbook. Using the following techniques, you should be able to perform even
the most complex deployments.
Running operations in parallel
By default, Ansible will only fork up to five times, so it will only run an operation
on five different machines at once. If you have a large number of machines, or
you have lowered this maximum fork value, then you may want to launch things
asynchronously. Ansible's method for doing this is to launch the task and then
poll for it to complete. This allows Ansible to start the job across all the required
machines while still using the maximum forks.
To run an operation in parallel, use the async and poll keywords. The async keyword
triggers Ansible to run the job in parallel, and its value will be the maximum time that
Ansible will wait for the command to complete. The value of poll indicates to Ansible
how often to poll to check if the command has been completed.
If you wanted to run updatedb across an entire cluster of machines, it might look like
the following code:
- hosts: all
tasks:
- name: Install mlocate
yum: name=mlocate state=installed
- name: Run updatedb
command: /usr/bin/updatedb
async: 300
poll: 10
You will notice that when you run the previous example on more than five machines,
the yum module acts differently to the command module. The yum module will run on
the first five machines, then the next five, and so on. The command module, however,
will run across all the machines and indicate the status once complete.
If your command starts a daemon that eventually listens on a port, you can start it
without polling so that Ansible does not check for it to complete. You can then carry
on with other actions and check for completion later using the wait_for module. To
configure Ansible to not wait for the job to complete, set the value of poll to 0 .
Finally, if the task that you are running takes an extremely long time to run, you can
tell Ansible to wait for the job as long as it takes. To do this, set the value of async
to 0 .
You will want to use Ansible's polling in the following situations:
?     You have a long-running task that may hit the timeout
?     You need to run an operation across a large number of machines
?     You have an operation for which you don't need to wait to complete
There are also a few situations where you should not use async or poll :
?     If your job acquires locks that prevent other things from running
?     You job only takes a short time to run

高级Palybook

之前我们遇到的playbook都比较简单,只需要运行一些模块就可以了。但是Ansible允许你在运行playbook的时候有更多的控制,让你可以完成最最复杂的部署任务。

并行运行任务

Ansible默认一次启用5个线程,所以只能一次只能对5台机器进行操作。如果你的机器数量超过5个,或者线程数低于5个,那么你可能会想要用异步来完成。Ansible的方法是轮询每个任务直到任务结束,这样Absible就可以在使用5个线程的情况下完成所有机器(超过5台机器)操作的任务。

启用并行,需要使用async 和poll这2个关键字,async关键字启用并行,而它的值是他的最大等待时间;poll则设置Ansible多久检查一次任务是否完成。

下面的例子,一次性更新一个群集里面所有的数据库,代码如下:

- hosts: all
tasks:
- name: Install mlocate
yum: name=mlocate state=installed

- name: Run updatedb
command: /usr/bin/updatedb
async: 300
poll: 10

当你运行这个列子的时候,yum操作是5台机器5台机器的运行,但是updatedb操作是一次性操作所有机器,并立刻返回操作状态信息。

如果启用一个daemon程序,并最终会监听一个端口,那么你可以不需要使用poll轮询,而用上一章介绍的wait_for模块,这样只需要把poll的值设置为0就可以了;如果你的操作可能要执行很长很长的时间,那么我们只需要告诉Ansible一直等待就可以了,这样只需要把asyn设置为0即可。

轮询适用的场合有:

  • 当你的任务需要运行很长时间,并且可能超时的时候
  • 当操作的机器数量很大的时候
  • 当一个任务,你不需要等待他返回结束信号的时候

不适用async 或则 poll 的场合:

  • 你的任务有依赖关系的其他任务的时候
  • 你的任务花费的时间很短的时候

Looping
Ansible allows you to repeat a module several times with different input, for
example, if you had several files that should have similar permissions set. This
can save you a lot of repetition and allows you to iterate over facts and variables.
To do this, you can use the with_items key on an action and set the value to the list
of items that you are going to iterate over. This will create a variable for the module
called item , which will be set to each item in turn as your module is iterated over.
Some modules such as yum will optimize this so that instead of doing a separate
transaction for each package, they will operate on all of them at once.Using with_items looks like this:tasks:- name: Secure config filesfile: path=/etc/{{ item }} mode=0600 owner=root group=rootwith_items:- my.cnf- shadow- fstabIn addition to looping over fixed items, or a variable, Ansible can also use whatare called lookup plugins. These plugins allow you to tell Ansible to fetch the datafrom somewhere externally. For example, you might want to upload all the files thatmatch a particular pattern, and then upload them.In this example, we upload all the public keys in a directory and then assemble theminto an authorized_keys file for the root user.tasks:#1- name: Make key directory#2file: path=/root/.sshkeys ensure=directory mode=0700owner=root group=root#3- name: Upload public keys#4copy: src=http://www.mamicode.com/{{ item }} dest=/root/.sshkeys mode=0600>

Loop循环

Anisble可以根据不同的输入重复运行一个模块很多次,比如一些权限集差不多的文件,你可以根据实际情况和变量值里迭代他们。

使用with_items关键字来定义一个循环,把你希望的数据插入一个列表作为它的值。这样会生成一个变量叫item,当你历遍列表的时候,item将被列表中的元素挨个赋值。像yum这样的模块还会自动优化这种操作,所以你可以使用循环一次性全部完成。

循环的机构类似下面的代码:

tasks:
- name: Secure config files
file: path=/etc/{{ item }} mode=0600 owner=root group=root
with_items:
- my.cnf
- shadow
- fstab

此外,Anisble还可以使用lookup插件来使用外部数据,比如你想上传所有符合一个表达式规则的文件到目的地。下面的例子中,我们上传公钥目录下的所有文件,合并到authorized_keys文件给root用户。

tasks:
- name: Make key directory
file: path=/root/.sshkeys ensure=directory mode=0700
owner=root group=root
- name: Upload public keys

copy: src=http://www.mamicode.com/{{ item }} dest=/root/.sshkeys mode=0600
owner=root group=root
with_fileglob:
- keys/*.pub


- name: Assemble keys into authorized_keys file
assemble: src=http://www.mamicode.com/root/.sshkeys dest=/root/.ssh/authorized_keys
mode=0600 owner=root group=root

Repeating模块适用的场景有:

  • 重复一个类似配置的模块很多次的时候
  • 历遍列表(根据实际情况fact)中所有的值的时候
  • 当要创建很多文件然后用assemble模块合并到一个大文件的时候
  • 使用with_fileglob复制一个目录下所有模式匹配的文件的时候

Conditional execution
Some modules, such as the copy module, provide mechanisms to configure it to skip
the module. You can also configure your own skip conditions that will only execute
the module if they resolve to true . This can be handy if your servers use different
packaging systems or have different filesystem layouts. It can also be used with the
set_fact module to allow you to compute many different things.
To skip a module, you can use the when key; this lets you provide a condition. If the
condition you set resolves to false , then the module will be skipped. The value that
you assign to when is a Python expression. You can use any of the variables or facts
available to you at this point.---- name: Install VIMhosts: alltasks:- name: Install VIM via yumyum: name=vim-enhanced state=installedwhen: ansible_os_family == "RedHat"<p>- name: Install VIM via aptapt: name=vim state=installedwhen: ansible_os_family == "Debian"</p>- name: Unexpected OS familydebug: msg="OS Family {{ ansible_os_family }} is notsupported" fail=yeswhen: not ansible_os_family == "RedHat" or ansible_os_family== "Debian"This feature can be used to pause at a particular point and wait for theuser intervention to continue. Normally when Ansible encounters anerror, it will simply stop what it is doing without running any handlers.With this feature, you can add the pause module with a condition on itthat triggers in unexpected situations. This way the pause module willbe ignored in a normal situation, but in unexpected circumstances it willallow the user to intervene and continue when it is safe to do so.The task would look like this:name: pause for unexpected conditionspause: prompt="Unexpected OS"when: ansible_os_family != "RedHat"There are numerous uses of skipping actions; here are a few suggestions:?     Working around differences in operating systems?     Prompting a user and only then performing actions that they request?     Improving performance by avoiding a module that you know won't changeanything but may take a while to do so?     Refusing to alter systems that have a particular file present?     Checking if custom written scripts have already been run

条件执行

有些像copy这样的模块提供skip忽略机制,你也可以配置你自己的条件来跳过某些模块的执行。当你的服务器使用不同的操作系统、文件类型的时候特别有用。你可以根据set_fact模块的值来做不同的事情。

跳过一个模块的执行,使用when这个关键字来提供一个条件,如果条件为假,跳过执行,使用python表达式作为它的值,在这里你可以使用任何变量或则任何可能的fact。

下面的例子就是根据操作系统确定是使用apt还是yum来执行安装操作,当os信息无法确定的时候,就打印一个消息。


---
- 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"
根据这个特性,我们还可以添加pause模块来让用户进行确认以继续,在符合我们设定的条件的正常情况下,pause模块不会被执行,只有异常情况下才会被调用并请求用户确认以继续任务,代码如下

name: pause for unexpected conditions
pause: prompt="Unexpected OS"
when: ansible_os_family != "RedHat"

条件执行适用的场景和建议:

  • 当要在不同的操作系统上进行操作的时候
  • 提示用户,然后再执行操作请求

  • 当执行一个需要较长时间的查询任务的时候
  • 当硬盘空间超过警戒线的时候禁止更新
  • 确认自定义脚本是否已经运行

Task delegation
Ansible, by default, runs its tasks all at once on the configured machine. This is great
when you have a whole bunch of separate machines to configure, or if each of your
machines is responsible for communicating its status to the other remote machines.
However, if you need to perform an action on a different host than the one Ansible is
operating on, you can use a delegation.
Ansible can be configured to run a task on a different host than the one that is being
configured using the delegate_to key. The module will still run once for every
machine, but instead of running on the target machine, it will run on the delegated
host. The facts available will be the ones applicable to the current host. Here, we
show a playbook that will use the get_url option to download the configuration
from a bunch of web servers.
---
- 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

If you are delegating to the localhost , you can use a shortcut when defining the
action that automatically uses the local machine. If you define the key of the action
line as local_action , then the delegation to localhost is implied. If we were to
have used this in the previous example, it would be slightly shorter and look like this:
---
- name: Fetch configuration from all webservers
hosts: webservers
tasks:
- name: Get config
local_action: get_url dest=configs/{{ ansible_hostname
}}.cfg url=http://{{ ansible_hostname
}}/diagnostic/config

Delegation is not limited to the local machine. You can delegate to any host that is in
the inventory. Some other reasons why you might want to delegate are:
?	 Removing a host from a load balancer before deployment
?	 Changing DNS to direct traffic away from a server you are about to change
?	 Creating an iSCSI volume on a storage device
?	 Using an external server to check that access outside the network works

任务的委派

Ansible默认在配置的机器上执行任务,当你有一大票机器需要配置或则每个设备都可达的情况下很有用。但是,当你需要在另外一个Ansible控制机器上运行任务的时候,就需要用到任务委派了。

使用delegate_to关键字就可以委派任务到其他机器上运行,同时可用的fact也会使用委派机器上的值。下面的例子使用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

当你委派给本机的时候,还可以使用更快捷的方法local_action,代码如下:

---
- name: Fetch configuration from all webservers
hosts: webservers


tasks:
- name: Get config
local_action: get_url dest=configs/{{ ansible_hostname
}}.cfg url=http://{{ ansible_hostname
}}/diagnostic/config
你可以委派任务给设备清单上的任意机器,下面是使用任务委派的一些场景:

  • 在部署之前将一个主机从一个负载均衡集群中删除
  • 当你要对一个主机做改变之前去掉相应dns的记录
  • 当在一个存储设备上创建iscsi卷的时候
  • 当使用外部的主机来检测网络出口是否正常的时候