Ansible@一个高效的配置管理工具--Ansible configure management--翻译(三)
未经书面许可。请勿转载
一张图简单概括
Simple Playbooks
Ansible is useful as a command-line tool for making small changes. However, its real
power lies in its scripting abilities. While setting up machines, you almost always
need to do more than one thing at a time. Ansible provides for this by using a tool
called playbook. Using playbooks, you can perform many actions at once, and
across multiple systems. They provide a way to orchestrate deployments, ensure a
consistent configuration, or simply perform a common task.
Playbooks are expressed in YAML, and for the most part, Ansible uses a standard
YAML parser. This means that you have all the features of YAML available to you
as you write them. For example, you can use the same commenting system as you
would in YAML. Many lines of a playbook can also be written and represented in
YAML data types. See http://www.yaml.org/ for more information.
Playbooks also open up many opportunities. They allow you to carry the state
from one command to the next. For example, you can grab the content of a file on
one machine, register it as a variable, and then use that on another machine. This
allows you to make complex deployment mechanisms that will be impossible with
the Ansible command alone. Additionally, each module tries to be idempotent; you
should be able to run a playbook several times and changes will only be made if they
need to be.
The command to execute a playbook is ansible-playbook . It accepts arguments
similar to the Ansible command-line tool. For example, -k ( --ask-pass ) and -K
( --ask-sudo ) make it prompt for the SSH and sudo passwords, respectively; -u
can be used to set the user to use SSH. However, these options can also be set inside
the playbooks themselves in the target section. For example, to use the play named
example-play.yml , you can use the following command:
$ ansible-playbook example-play.yml
The Ansible playbooks are made up of one or more plays. A play consists of three
sections: the target section, the variable section, and finally the bit that does all the
real work, the task section. You can include as many plays as you like in a single
YAML file.
• The target section defines hosts on which the play will be run, and how it
will be run. This is where you set the SSH username and other SSH-related
settings.
• The variable section defines variables which will be made available to the
play while running.
• The task section lists all the modules in the order that you want them to be
run by Ansible.
A full example of an Ansible play looks like the following code snippet:
---
- hosts: localhost
user: root
vars:
motd_warning: 'WARNING: Use by ACME Employees ONLY'
tasks:
- name: setup a MOTD
copy: dest=/etc/motd content={{ motd_warning }}
第二章 简单的playbooks
使用命令行工具来标记一些改变,Ansible显示出了他的优势。
可是,它真正强大的地方在于它的脚本能力。当我们配置机器的时候,通常须要在同一时间内做很多操作。Anisble提供了一个工具叫playbooks。使用playbooks。我们就能够一次性、对多台机器进行多个操作动作。这样的部署方法。能够确保配置的一致性。或者用来执行一些常见的任务。
playbooks使用yaml文件。多数情况下。Ansible使用了标准的yaml解析,这意味这你能够使用全部yaml的特性,比方注解规则,很多其它信息能够查看http://www.yaml.org/
Playbooks also open up many opportunities.(这句不知道该怎样翻译,只是从以下的语句应该能够反推它的意思)这能够让我们把一个状态信息从一个命令带到下一个命令。
比方:我们能够从一台机器上获取一个文件的内容,将其注冊成一个变量。应用到另外一台机器上。与单命令行模式相比,这样的特性能够让我们制作一个更加复杂的部署任务,我们能够执行一个playbook非常多次,可是配置的改变仅仅有在须要的时候才会生效。
执行playbook的命令是ansible-playbook,它接受的參数和命令行工具相似,比方-k 。--ask-sudo 是询问ssh和sudo密码。-u指定连接用户。
更进一步的是,playbook能够把这些參数写进脚本的相应选项中去。
执行一个名叫example-play.yml:
$ ansible-playbook example-play.yml
一个playbook能够有一个或者多个操作,每一个操作有三个选项。
- 目标选项:定义哪些主机来执行任务,怎样执行任务,这也是你制定SSH-related和ssh參数的地方
- 变量选项:定义在任务执行过程中须要启用那些变量
- 任务选项:须要被执行的Ansible模块列表
以下是一个完整的playbook样例:
---
- hosts: localhost
user: root
vars:
motd_warning: 'WARNING: Use by ACME Employees ONLY'
tasks:
- name: setup a MOTD
copy: dest=/etc/motd content={{ motd_warning }}
The target section
The target section looks like the following code snippet:
- hosts: webservers
user: root
This is an incredibly simple version, but likely to be all you need in most cases. Each
play exists within a list. As per the YAML syntax, the line must start with a dash. The
hosts that a play will be run on must be set in the value of hosts . This value uses the
same syntax as the one used when selecting hosts using the Ansible command line,
which we discussed in the previous chapter. The host-pattern-matching features of
Ansible were also discussed in the previous chapter. In the next line, the user tells
the Ansible playbook which user to connect to the machine as.
目标选项
目标选项的配置看起来就像以下这样:
- hosts: webservers
user: root
这是一个超级简单的版本号,可是在大多数情况下你都会须要它。每一个操作都有一个列表。就像每一个YAML语法都是以破折号開始。
每一个受管远程主机须要进行操作的话都须要指定一个host值,这个值跟我们在第一章讨论的在命令行工具中使用时候用到host是一样的;下一行指定了连接远程受管主机使用的用户名。以下是一些能够在目标选项中能够指定的其它值。
上面几个解释过了。
connection:同意你指定ssh parmiko或者local这三种传输方式
gather_facts:默认每次连接都会执行setup。假设不须要用到变量能够制定这个字段为false
The variable section
Here you can define variables that apply to the entire play on all machines. You can
also make Ansible prompt for variables if they weren't supplied in the command
line. This allows you to make easily maintainable plays, and prevents you from
changing the same thing in several parts of the play. This also allows you to have
all the configuration for the play stored at the top, where you can easily read and
modify it without worrying about what the rest of the play does.
Variables in this section of a play can be overridden by machine facts (those that are set
by modules), but they themselves override the facts you set in your inventory. So they
are useful to define defaults that you may collect in a module later, but they can't be
used to keep defaults for inventory variables as they will override those defaults.
Variable declarations, called vars , look like the values in the target section and
contain a YAML dictionary or a list. An example looks like the following code
snippet:
vars:
apache_version: 2.6
motd_warning: 'WARNING: Use by ACME Employees ONLY'
testserver: yes
Variables can also be loaded from external YAML files by giving Ansible a list of
variable files to load. This is done in a similar way using the vars_files directive.
Then simply provide the name of another YAML file that contains its own dictionary.
This means that instead of storing the variables in the same file, they can be stored
and distributed separately, allowing you to share your playbook with others.
Using vars , the files look like the following code snippet in your playbook:
vars_files:
/conf/country-AU.yml
/conf/datacenter-SYD.yml
/conf/cluster-mysql.yml
In the previous example, Ansible looks for country-AU.yml , datacenter-SYD.yml ,
and cluster-mysql.yml in the conf folder. Each YAML file looks similar to the
following code snippet:
---
ntp: 'ntp1.au.example.com'
TZ: 'Australia/Sydney'
Finally you can make Ansible ask the user for each variable interactively. This
is useful when you have variables that you don't want to make available for
automation, and instead require human input. One example where this is useful is
prompting for the passphrases used to decrypt secret keys for the HTTPS servers.
You can instruct Ansible to prompt for variables with the following code snippet:
vars_prompt:
- name: 'https_passphrase'
prompt: 'Key Passphrase'
private: yes
In the previous example, https_passphrase is where the entered data will be
stored. The user will be prompted with Key Passphrase , and because private
is set to yes , the value will not be printed on the screen as the user enters it.
You can use variables, facts, and inventory variables with the help of: {{
variablename }} , ${variablename} , or simply $variablename . You can even
refer to complex variables, such as dictionaries, with a dotted notation. For example,
a variable named httpd , with a key in it called maxclients , will be accessed as
{{ httpd.maxclients }} . This works with facts from the setup module too. For
example, you can get the IPv4 address of a network interface called eth0 using {{
ansible_eth0.ipv4.address }} .
Variables that are set in the variable section do not survive between different plays
in the same playbook. However, facts gathered by the setup module or set by set_
fact do. This means that if you are running a second play on the same machines, or
a subset of the machines in an earlier play, you can set gather_facts in the target
section to false . The setup module can sometimes take a while to run, so this can
dramatically speed up plays, especially in plays where the serial is set to a low value.
变量选项
你能够在playbook里面定义适用于全部机器的全局变量。
假设在命令行中没有提供的话。你还能够让Anisble提示变量。
这样的特性使得维护“操作”变得非常easy。避免在一个“操作”中不停的改动同一个事物,把他放在配置的最上面。在阅读和改动的时候也不会影响到“操作”的其它设置。在变量选项中的变量会被清单中的变量(就是模块中设置的变量)覆盖。但它同一时候又会被库存清单中的fact覆盖。所以最好在收集一个模块后定义这些默认值,可是这些默认值不能用来作为库存变量的默认值保存,由于他们还是会覆盖它。
和目标选项一样,变量选项的配置也像一个列表或者字典
vars:
apache_version: 2.6
motd_warning: 'WARNING: Use by ACME Employees ONLY'
testserver: yes
给出一个变量的文件列表,变量还能够通过他们导入外部定义好的变量。也能够提供一个文件夹,然后指出里面的文件。
这意味这我们不须要把变量都放在一个文件里面。他们能够分开存储,然后共享给全部playbooks。
从其它文件导入变量
vars_files:
/conf/country-AU.yml
/conf/datacenter-SYD.yml
/conf/cluster-mysql.yml
这些文件里的设置例如以下:
---
ntp: 'ntp1.au.example.com'
TZ: 'Australia/Sydney'
最后,你还看让Anisble来询问变量,当你不想自己主动提供这些变量的时候,让用户手工输入。
比方:比方一个https应用相关的脚本,你能够让用户手工指定解密密钥。
vars_prompt: #手工确认的变量
- name: 'https_passphrase' #输入数据存储的地方
prompt: 'Key Passphrase' #手工确认的数据
private: yes #当这个參数设置为yes的时候,用户的输入不会显示到屏幕上
在使用variables, facts, inventory variables的时候。用{{variablename }} , ${variablename} , 或者$variablename .来表示。这个变量的语法跟字典一样。能够通过‘.’来訪问属性。
比方https变量有一个叫做maxclients的属性,我们能够使用{{ httpd.maxclients }}来訪问。这是通过setup模块来实现的,通过{{ansible_eth0.ipv4.address }} 来訪问。
变量在同一个playbooks中的不同‘操作’中,不会被保存。所以我们在同一台机器执行第二次playkook的时候,或者在一个子集中执行之前的playbook的时候,能够gather_facts设置成false。setup模块比較耗时,所以这样会加快我们的playbook的执行速度。特别当我们的线程数设置的比較低的时候。
The task section
The task section is the last section of each play. It contains a list of the actions that
you want Ansible to perform in the order you want them to be performed. There are
several ways in which you can represent each module's configuration. We suggest
you try to stick with one as much as possible, and use the others only when required.
This makes your playbooks easier to read and maintain. The following code snippet
is what a task section looks like with all three styles shown:
tasks:
- name: install apache
action: yum name=httpd state=installed
- name: configure apache
copy: src=files/httpd.conf dest=/etc/httpd/conf/httpd.conf
- name: restart apache
service:
name: httpd
state: restarted
Here we see the three different styles being used to install, configure, and start
the Apache web server as it will look on a CentOS machine. The first task shows
you how to install Apache using the original syntax, which requires you to call the
module as the first keyword inside an action key. The second task copies Apache's
configuration file into place using the second style of the task. In this style, you use
the module name in place of the action keyword and its value simply becomes its
argument. This form is the one recommended by the Ansible authors. Finally the
last task, the third style, shows how to use the service module to restart Apache. In
this style, you use the module name as the key, as usual, but you also supply the
arguments as a YAML dictionary. This can come in handy when you are providing a
large number of arguments to a single module, or if the module wants the arguments
in a complex form, such as the Cloud Formation module.
Note that names are not required for tasks. However, they make good documentation
and allow you to refer to each task later on if required. This will become useful
especially when we come to handlers. The names are also outputted to the console
when the playbook is run, so that the user can tell what is happening. If you don't
provide a name, Ansible will just use the action line of the task or the handler.
Unlike other configuration management tools, Ansible does not provide
a fully featured dependency system. This is a blessing and a curse; with
a complete dependency system, you can get to a point where you are
never quite sure what changes will be applied to particular machines.
Ansible, however, does guarantee that your changes will be executed
in the order they are written. So, if one module depends on another
module that is executed before it, simply place one before the other in
the playbook.
任务选项
任务选项是每一个‘操作’的最后一个选项,它包括一系列的模块,Ansible有非常多地方能够配置模块,可是我们建议你在这里配置。能够使得我们的脚本更加易维护和理解。
以下这个任务选项的样例包括三个不同的类型:
tasks:
- name: install apache
action: yum name=httpd state=installed
- name: configure apache
copy: src=files/httpd.conf dest=/etc/httpd/conf/httpd.conf
- name: restart apache
service:
name: httpd
state: restarted
上述样例包括三个不同的形式来配置任务,在centos操作系统上安装、配置、重新启动apache。
第一个任务用Ansible的原始语法来安装apache,这须要我们用模块名来做为action key,第一个參数。第二个任务把把apache的配置文件从源辅助到目的地,用模块名做为key,values就是他的參数,这样的形式是Ansible作者推荐的形式。最后一个任务、第三种形式,用来重新启动apache服务,使用模块名做为key,使用YAML 字典作为value。当一个模块须要非常多參数的时候,第三种形式就能够派上用场了。比方配置Cloud Formation模块的时候。
name參数不是必须的,然而提供name能够让脚本文档变的更好,更easy调用这个任务。在兴许我们使用handlers.处理程序的时候,特别实用。
假设你不提供name值,Ansile会使用一整行的任务命令或者处理程序。
注意:Anisble不像其它配置管理工具那样有一个完整的依赖关系系统。这有好也有坏,当有依赖系统的时候。我们仅仅需关心我们须要的操作,可是那样的话,任务执行的顺序就可能不是我们希望的顺序了。所以在Anisble中,我们须要手工制定任务顺序,而且由自己来考虑依赖关系。
The handlers section
The handlers section is syntactically the same as the task section and supports the
same format for calling modules. The modules in the handlers section are not run
unless they are called by tasks. They are called only when the task they were called
from records that they changed something. You simply add a notify key to the task
with the value set to the name of the task.
Handlers are run when Ansible has finished running the task list. They are run in
the order that they are listed in the handlers section, and even if they are called
multiple times in the task section, they will run only once. This is often used to
restart daemons after they have been upgraded and configured. The following
play demonstrates how you will upgrade an ISC DHCP server to the latest version,
configure it, and set it to start at boot. If this playbook is run on a server where the
ISC DHCP daemon is already running the latest version and the config files are not
changed, the handler will not be called and DHCP will not be restarted.
---
- hosts: dhcp
tasks:
- name: update to latest DHCP
action: yum name=dhcp state=latest
notify: restart dhcp
- name: copy the DHCP config
action: copy src=dhcp/dhcpd.conf dest=/etc/dhcp/dhcpd.conf
notify: restart dhcp
- name: start DHCP at boot
action: service name=dhcpd state=started enabled=yes
handlers:
- name: restart dhcp
action: service name=dhcpd state=restarted
Each handler can only be a single module, but you can notify a list of handlers from
a single task. This allows you to trigger many handlers from a single step in the task
list. For example, if you have just checked out a new version of a Django application,
you might set a handler to migrate the database, deploy the static files, and restart
Apache. You can do this by simply using a YAML list on the notify action. This
might look something like the following code snippet:
---
- hosts: qroud
tasks:
- name: checkout Qroud
action: git repo=git@github.com:smarthall/Qroud.git
dest=/opt/apps/Qroud force=no
notify:
- migrate db
- generate static
- restart httpd
handlers:
- name: migrate db
action: command chdir=/opt/apps/Qroud ./manage.py migrate –all
- name: generate static
action: command chdir=/opt/apps/Qroud ./manage.py
collectstatic -c –noinput
- name: restart httpd
action: service name=httpd state=restarted
You can see that the git module is used to check out some public GitHub code, and
if that caused anything to change, it triggers the migrate db , generate static , and
restart httpd actions.
处理程序-handler选项
handler选项更其它选项一样也支持模块调用,可是handler中的模块仅仅有在被任务调用的时候才会被执行。这仅仅须要在任务选项中加入notify參数,然后指定handlers选项中的列出的name值。
handler处理程序仅仅有在任务列表中的任务全部被执行完毕之后才会被调用,而且就算在任务中调用了非常多次的handler,它也仅仅会执行一次。这通经常使用来在安装、升级、配置完毕之后用来重新启动服务或者应用。
以下的样例是在服务器上更新dhcp到最新版本号,配置它。然后把它加入到启动列表中去。假设目标机器上dhcp已经是最新版本号了,那么配置不会被更新,handler处理程序不会被调用,服务也不会被重新启动。
---
- hosts: dhcp
tasks:
- name: update to latest DHCP
action: yum name=dhcp state=latest
notify: restart dhcp
- name: copy the DHCP config
action: copy src=dhcp/dhcpd.conf dest=/etc/dhcp/dhcpd.conf
notify: restart dhcp
- name: start DHCP at boot
action: service name=dhcpd state=started enabled=yes
handlers:
- name: restart dhcp
action: service name=dhcpd state=restarted
每一个handler处理程序能够仅仅包括一个模块,可是你能够在任务选项中的一个任务中指定多个handler处理程序,这意味着你能够在任务列表中的一个任务完毕之后,触发一系列的handler处理程序。比方在你检查一个django应用程序的版本号之后,会设置一系列如迁移数据库、部署静态文件、重新启动apache的handler列表。你也能够在notify你填上一个yaml文件,文件的内容例如以下:
---
- hosts: qroud
tasks:
- name: checkout Qroud
action: git repo=git@github.com:smarthall/Qroud.git #使用git更新程序版本号
dest=/opt/apps/Qroud force=no
notify:
- migrate db #迁移数据库
- generate static #部署静态文件
- restart httpd #重新启动apache服务
handlers:
- name: migrate db
action: command chdir=/opt/apps/Qroud ./manage.py migrate –all
- name: generate static
action: command chdir=/opt/apps/Qroud ./manage.py
collectstatic -c –noinput
- name: restart httpd
action: service name=httpd state=restarted
The playbook modules
Using modules in playbooks is a little bit different from using them in the command
line. This is mainly because we have many facts available from the previous modules
and the setup module. Certain modules don't work in the Ansible command line
because they require access to those variables. Other modules will work in the
command-line version, but are able to provide enhanced functionalities when used
in a playbook.
Playbook的模块
在playbooks中使用模块跟在命令行中使用有一点点不一样,这主要是由于在playbooks中有很多从setup模块或者之前的模块中获取的数据要处理。
有些模块在命行中无法使用,是由于他们须要訪问变量,另一些能够在命令行中使用的模块在palybooks中使用时。拥有了更加强大的功能。
The template module
One of the most frequently used examples of a module that requires facts from
Ansible is the template module. This module allows you to design an outline of a
configuration file and then have Ansible insert values in the right places. In reality,
the Jinja2 templates can be much more complicated than this, including things
such as conditionals, for loops, and macros. The following is an example of a Jinja2
configuration file for configuring BIND:
# {{ ansible_managed }}
options {
listen-on port 53 {
127.0.0.1;
{% for ip in ansible_all_ipv4_addresses %}
{{ ip }};
{% endfor %}
};
listen-on-v6 port 53 { ::1; };
directory
"/var/named";
dump-file
"/var/named/data/cache_dump.db";
statistics-file "/var/named/data/named_stats.txt";
memstatistics-file "/var/named/data/named_mem_stats.txt";
};
zone "." IN {
type hint;
file "named.ca";
};
include "/etc/named.rfc1912.zones";
include "/etc/named.root.key";
{# Variables for zone config #}
{% if 'authorativenames' in group_names %}
{% set zone_type = 'master' %}
{% set zone_dir = 'data' %}
{% else %}
{% set zone_type = 'slave' %}
{% set zone_dir = 'slaves' %}
{% endif %}
zone "internal.example.com" IN {
type {{ zone_type }};
file "{{ zone_dir }}/internal.example.com";
{% if 'authorativenames' not in group_names %}
masters { 192.168.2.2; };
{% endif %}
};
The first line merely sets up a comment that shows which template the file came
from, the host, modification time of the template, and the owner. Putting this
somewhere in the template as a comment is a good practice, and it ensures that
people know what they should edit if they wish to alter it permanently. In the fifth
line, there is a for loop. For loops go through all the elements of a list once for each
item in the list. It optionally assigns the item to the variable of your choice so that
you can use it inside the loop. This one loops across all the values in ansible_all_
ipv4_addresses , which is a list from the setup module that contains all the IPv4
addresses that the machine has. Inside the for loop, it simply adds each of them into
the configuration to make sure BIND will listen on that interface.
Line 24 of the preceding code snippet has a comment. Anything in between {#
and #} is simply ignored by the Jinja2 template processor. This allows you to add
comments in the template that do not make it into the final file. This is especially
handy if you are doing something complicated, setting variables within the template,
or if the configuration file does not allow comments.
In the very next line we can see an if statement. Anything between {% if %} and {%
endif %} is ignored if the statement in the if tag is false. Here we check if the value
authorativenames is in the list of group names that apply to this host. If this is true,
the next two lines set two custom variables. zone_type is set to master and zone_dir
is set to data. If this host is not in the authorativenames group, zone_type and
zone_dir will be set to slave and slaves , respectively.
In line 33, we start the configuration of the zone. We set the type to the variable we
created earlier, and the location to zone_dir . Finally we check again if the host is in
the authorativenames groups, and if it isn't, we configure its master to a particular
IP address.
To get this template to set up an authorative nameserver, you need to create a group
in your inventory file named authorativenames and add some hosts under it. How
to do this was discussed back in Chapter 1, Getting Started with Ansible.
You can simply call the templates module and the facts from the machines will be
sent through, including the groups the machine is in. This is as simple as calling any
other module. The template module also accepts similar arguments to the copy
module such as owner, group, and mode.
---
- name: Setup BIND
host: allnames
tasks:
- name: configure BIND
template: src=templates/named.conf.j2 dest=/etc/named.conf
owner=root group=named mode=0640
模板模块
模板模块是Ansible中最经常使用的模块之中的一个。
它能够让你设计一个框架式的配置文件。怎样把Anisble须要的值插入到合适的位置。当中Jinja2模板尤为复杂,当中能够包括条件、循环、宏。以下是Jinja2做的bind配置的模板
# {{ ansible_managed }}
options {
listen-on port 53 {
127.0.0.1;
{% for ip in ansible_all_ipv4_addresses %}
{{ ip }};
{% endfor %}
};
listen-on-v6 port 53 { ::1; };
directory
"/var/named";
dump-file
"/var/named/data/cache_dump.db";
statistics-file "/var/named/data/named_stats.txt";
memstatistics-file "/var/named/data/named_mem_stats.txt";
};
zone "." IN {
type hint;
file "named.ca";
};
include "/etc/named.rfc1912.zones";
include "/etc/named.root.key";
{# Variables for zone config #}
{% if 'authorativenames' in group_names %}
{% set zone_type = 'master' %}
{% set zone_dir = 'data' %}
{% else %}
{% set zone_type = 'slave' %}
{% set zone_dir = 'slaves' %}
{% endif %}
zone "internal.example.com" IN {
type {{ zone_type }};
file "{{ zone_dir }}/internal.example.com";
{% if 'authorativenames' not in group_names %}
masters { 192.168.2.2; };
{% endif %}
};
第一行凝视能够用于现实模板文件的出处、相应主机、改动时间、文件属主等信息。凝视是一个好习惯。这样当其它人或者你自己要改动文件的时候,能够确定自己在改动什么内容。第五行是一个for循环。for循环会历遍列表中的每一个元素。你能够有选择的将循环中的值赋给你指定的变量。本例中循环历遍的变量是ansible_all_ipv4_addresses。它是setup模块从远程受管主机获得的关于远程主机所拥有的。全部IP地址信息的一个列表,循环结束后我们得到这些IP地址,然后把这些IP地址加入到bind的监听端口上。
第24行,模板的处理程序会自己主动忽略{# #} 之间的信息,在定义一些复杂的变量的时候能够使用这个来当凝视(有些配置文件不同意凝视)。假设在if标签中语法參数被设置成false,那么不论什么在{% if %} 和 {%endif %}之间的配置都会被忽略。在这里我们检查authorativenames时候在group_names列表中,怎样存在,则设置{% set zone_type = 'master' %} {% set zone_dir = 'data' %} 否则设置{% set zone_type = 'slave' %} {% set zone_dir = 'slaves' %}。
第33行,我们開始配置bind的区域,而且将之前设置的变量zone_type应用进去,最后再一次检查authorativenames时候在group_names中,假设不存在。就将maser设置成固定的ip地址。要使这个模板文件生效,我们须要建立一个authorative nameserver,在设备清单文件里创建一个叫authorative nameserver的组。并加入一些主机(远程受管主机)----第一章内容已经介绍过怎样完毕这些配置。
使用模板模块就能够非常easy将命令传递到组里面全部的机器,调用模板模块也和调用其它模块一样简单。此外。模板模块和copy模块一样也有owner, group, mode这些參数。
The set_fact module
The set_fact module allows you to build your own facts on the machine inside
an Ansible play. These facts can then be used inside templates or as variables in the
playbook. Facts act just like arguments that come from modules such as the setup
module: in that they work on a per-host basis. You should use this to avoid putting
complex logic into templates. For example, if you are trying to configure a buffer to
take a certain percentage of RAM, you should calculate the value in the playbook.
The following example shows how to use set_fact to configure a MySQL server
to have an InnoDB buffer size of approximately half of the total RAM available on
the machine:
---
#1
- name: Configure MySQL
#2
hosts: mysqlservers
#3
tasks:
#4
- name: install MySql
#5
yum: name=mysql-server state=installed
#6
- name: Calculate InnoDB buffer pool size
#7
set_fact: innodb_buffer_pool_size_mb="{{ ansible_memtotal_mb /
2 }}"
#8
- name: Configure MySQL
#9
template: src=templates/my.cnf.j2 dest=/etc/my.cnf owner=root
group=root mode=0644
#10
notify: restart mysql
#11
- name: Start MySQL
#12
service: name=mysqld state=started enabled=yes
handlers:
#14
- name: restart mysql
#15
service: name=mysqld state=restarted
#13
#16
The first task here simply installs MySQL using yum. The second task creates a fact
by getting the total memory of the managed machine, dividing it by two, losing any
non-integer remainder, and putting it in a fact called innodb_buffer_pool_size_
mb . The next line then loads a template into /etc/my.cnf to configure MySQL.
Finally, MySQL is started and set to start at boot time. A handler is also included to
restart MySQL when its configuration changes.
The template then only needs to get the value of innodb_buffer_pool_size and
place it into the configuration. This means that you can re-use the same template
in places where the buffer pool should be one-fifth of the RAM, or one-eighth, and
simply change the playbook for those hosts. In this case, the template will look
something like the following code snippet:
# {{ ansible_managed }}
[mysqld]
datadir=/var/lib/mysql
socket=/var/lib/mysql/mysql.sock
# Disabling symbolic-links is recommended to prevent assorted
security risks
symbolic-links=0
# Settings user and group are ignored when systemd is used.
# If you need to run mysqld under a different user or group,
# customize your systemd unit file for mysqld according to the
# instructions in http://fedoraproject.org/wiki/Systemd
# Configure the buffer pool
innodb_buffer_pool_size = {{
innodb_buffer_pool_size_mb|default(128) }}M
[mysqld_safe]
log-error=/var/log/mysqld.log
pid-file=/var/run/mysqld/mysqld.pid
You can see that in the previous template, we are simply putting the variables we
get from the play into the template. If the template doesn't see the innodb_buffer_
pool_size_mb fact, it simply uses a default of 128.
set_fact模块
注:(set_fact模块的中文译名感觉都不是非常合适,所以保留英文)
set_fact模块能够让你在远程受管机器上执行脚本的过程中来计算我们须要的值,这些值能够被用在模板或者变量中。
这些值有点相似setup模块中的參数,仅仅只是setup模块是以单台主机为单位的。
setup模块的这样的机制能够避免让模板逻辑变得过于复杂。比方,我们须要在一个playbook中计算mysql服务器的innodb_buffer_pool_size_mb,他的值能够依据远程受管主机的可用内存来计算。
以下的样例在一个playbook中设置innodb_buffer_pool_size_mb的值大约为远程受管主机的可用内存的一半:
---
- name: Configure MySQL
hosts: mysqlservers
tasks:
- name: install MySql
yum: name=mysql-server state=installed
- name: Calculate InnoDB buffer pool size
set_fact: innodb_buffer_pool_size_mb="{{ ansible_memtotal_mb /
2 }}"
- name: Configure MySQL
template: src=templates/my.cnf.j2 dest=/etc/my.cnf owner=root
group=root mode=0644
notify: restart mysql
- name: Start MySQL
service: name=mysqld state=started enabled=yes
handlers:
- name: restart mysql
service: name=mysqld state=restarted
第一个任务通yum 来安装mysql。第二个任务有一个set_fact 来计算InnoDB buffer pool size。第三个任务设置一个模板模板来设置my。cnf。并调用notify重新启动mysql。第四个任务设置mysql加入到启动列表。第五个任务重新启动mysql。
这个模板仅仅须要InnoDB buffer pool size的值来放进配置文件,这样我们就能够把这个模板给多个机器用,五分之中的一个、八分之中的一个内存都能够。以下是这个模板的代码:
# {{ ansible_managed }}
[mysqld]
datadir=/var/lib/mysql
socket=/var/lib/mysql/mysql.sock
# Disabling symbolic-links is recommended to prevent assorted
security risks
symbolic-links=0
# Settings user and group are ignored when systemd is used.
# If you need to run mysqld under a different user or group,
# customize your systemd unit file for mysqld according to the
# instructions in http://fedoraproject.org/wiki/Systemd
# Configure the buffer pool
innodb_buffer_pool_size = {{
innodb_buffer_pool_size_mb|default(128) }}M
[mysqld_safe]
log-error=/var/log/mysqld.log
pid-file=/var/run/mysqld/mysqld.pid
我们把从脚本(playbook)中得到的值放到模板中来填充innodb_buffer_pool_size。假设脚本中找不到这个值,就使用默认值128.
The pause module
The pause module stops the execution of a playbook for a certain period of time. You
can configure it to wait for a particular period, or you can make it prompt the user to
continue. While effectively useless when used from the Ansible command line, it can
be very handy when used inside a playbook.
Generally, the pause module is used when you want the user to provide
confirmation to continue, or if manual intervention is required at a particular point.
For example, if you have just deployed a new version of a web application to a
server, and you need to have the user check manually to make sure it looks okay
before you configure them to receive production traffic, you can put a pause there.
It is also handy to warn the user of a possible problem and give them the option of
continuing. This will make Ansible print out the names of the servers and ask the
user to press Enter to continue. If used with the serial key in the target section, it
will ask once for each group of hosts that Ansible is running on. This way you can
give the user the flexibility of running the deployment at their own pace while they
interactively monitor the progress.
Less usefully, this module can simply wait for a specified period of time. This is
often not useful as you usually don't know how long a particular action may take,
and guessing may have disastrous outcomes. You should not use it for waiting for
networked daemons to start up; you should use the wait_for module (described
in the next section) for this task. The following play demonstrates using the pause
module first in the user interactive mode and then in the timed mode:
---
- hosts: localhost
tasks:
- name: wait on user input
pause: prompt="Warning! Detected slight issue. ENTER to
continue CTRL-C a to quit."
- name: timed wait
pause: seconds=30
pause-暂停模块
暂停模块能够让我们在playbook中暂停一段时间,能够指定一个时间段,或者提示用户继续。在命令行模式中,这没什么用,可是在playbook中。这非常实用处。
暂停模块通常被用在当我我们须要用户来提供一个确认来继续的时候,或者在一个特殊的时间点手动确认。
比方你更新一个web应用程序之后。你须要用户在接受用户的连接之前,手工确认一切Ok。这也能够用来i提示用户一些可能出现的问题。并提供选项继续。Ansible会打印出服务器的名字,要求用户确认之后继续。
假设在目标选项中设置了串行參数。Ansible会询问组里面的每一个主机。这样的方式能够让用户在部署的时候。灵活的控制整个节奏,并监控整个交互过程。
另外,这个模块还能够简单地等待指定的时间。但这通常不是非常有效果,由于通常你不知道一个特定的‘操作’须要多少时间,而且这还有可能带来灾难性的结果。最好不用使用pause模块来等待一个网络进程的启动。wait_for模块更加合适来做这个。(我们将在下一小节介绍)。
以下的样例首先示范了交互模式下pause模块的使用,然后介绍了timed模式下的使用:
---
- hosts: localhost
tasks:
- name: wait on user input
pause: prompt="Warning! Detected slight issue. ENTER to
continue CTRL-C a to quit."
- name: timed wait
pause: seconds=30
The wait_for module
The wait_for module is used to poll a particular TCP port and not continue until that
port accepts a remote connection. The polling is done from the remote machine. If you
only provide a port, or set the host argument to localhost , the poll will try to connect
to the managed machine. You can utilize local_action to run the command from the
controller machine and use the ansible_hostname variable as your host argument to
make it try and connect to the managed machine from the controller machine.
This module is particularly useful for daemons that can take a while to start, or
things that you want to run in the background. Apache Tomcat ships with an init
script that when you try to start it immediately returns, leaving Tomcat starting in
the background. Depending on the application that Tomcat is configured to load,
it might take anywhere between two seconds to 10 minutes to fully start up and be
ready for connections. You can time your application's start up and use the pause
module. However, the next deployment may take longer or shorter, and this can
break your deployment mechanism. With the wait_for module, you have Ansible
to recognize when Tomcat is ready to accept connections. The following is a play that
does exactly this:
---
- hosts: webapps
tasks:
- name: Install Tomcat
yum: name=tomcat7 state=installed
- name: Start Tomcat
service: name=tomcat7 state=started
- name: Wait for Tomcat to start
wait_for: port=8080 state=started
After the completion of this play, Tomcat should be installed, started, and ready to
accept requests. You can append further modules to this example and depend on
Tomcat being available and listening.
wait_for 模块
wait_for模块用检測一个tcp端口时候准备好接收远程连接,这是由远程主机来完毕的。
假设你仅仅指定了端口,或者主机參数被设置为localhost。它会尝试连接远程受管主机。你能够用local_action參数来指定从控制机器来执行命令,并使用ansible_hostname做为主机參数来连接远程受管主机。这个模块在后台执行某些程序。或者启动某些进程须要一些时间的时候特别实用。
比方:tomcat有个init脚本在你执行它的时候。它将立马返回。实际上tomcat是在后台启动。启动的时间取决于应用程序的配置和载入。它须要2秒或者10分钟来完毕启动。
尽管你能够使用pause暂停模块来指定一个时间。可是下次启动或者执行所须要的时间可能又不一样了。这样做的话,会破坏我们的部署计划。可是,使用wait_for模块。我们能够指定Ansible来确认tomcat是否已经准备好接受连接。以下是样例:
---
- hosts: webapps
tasks:
- name: Install Tomcat
yum: name=tomcat7 state=installed
- name: Start Tomcat
service: name=tomcat7 state=started
- name: Wait for Tomcat to start
wait_for: port=8080 state=started
上述playbook安装、启动tomcat之后。等待tomcat程序准备好监听,而且能够接受连接,我们就能够在这个palybook的基础上扩展很多其它的模块使用了。
The assemble module
The assemble module combines several files on the managed machine and saves
them to another file on the managed machine. This is useful in playbooks when you
have a config file that does not allow includes, or globbing in its includes. This is
useful for the authorized_keys file for say, the root user. The following play will
send a bunch of SSH public keys to the managed machine, then make it assemble
them all together and place it in the root user's home directory:
---
- hosts: all
tasks:
- name: Make a Directory in /opt
file: path=/opt/sshkeys state=directory owner=root group=root
mode=0700
- name: Copy SSH keys over
copy: src=keys/{{ item }}.pub dest=/opt/sshkeys/{{ item }}.pub
owner=root group=root mode=0600
with_items:
- dan
- kate
- mal
- name: Make the root users SSH config directory
file: path=/root/.ssh state=directory owner=root group=root
mode=0700
- name: Build the authorized_keys file
assemble: src=/opt/sshkeys dest=/root/.ssh/authorized_keys
owner=root group=root mode=0700
By now this should all look familiar. You may note the with_items key in the task
that copies the keys over, and the {{ items }} variable. These will be explained
later in Chapter 3, Advanced Playbooks, but all you need to know now is that whatever
item you supply to the with_items key is substituted into the {{ items }} variable,
similar to how a for loop works. This simply lets us easily copy many files to the
remote host at once.
The last task shows the usage of the assemble module. You pass the directory
containing the files to be concatenated into the output as the src argument, and then
pass dest as the output file. It also accepts many of the same arguments ( owner ,
group , and mode ) as the other modules that create files. It also combines the files in
the same order as the ls -1 command lists them. This means you can use the same
approach as udev and rc.d , and prepend numbers to the files to ensure that they end
up in the correct order.
assemble-组装模块
assemble-组装模块把多个受管主机的文件合并成一个文件,当配置文件不同意包括的时候,这非常实用,特别是在设置root用户的authorized_keys文件的时候。以下的样例就是把多个ssh公钥文件合并之后放到受管主机的root文件夹之下:
---
- hosts: all
tasks:
- name: Make a Directory in /opt
file: path=/opt/sshkeys state=directory owner=root group=root
mode=0700
- name: Copy SSH keys over
copy: src=keys/{{ item }}.pub dest=/opt/sshkeys/{{ item }}.pub
owner=root group=root mode=0600
with_items:
- dan
- kate
- mal
- name: Make the root users SSH config directory
file: path=/root/.ssh state=directory owner=root group=root
mode=0700
- name: Build the authorized_keys file
assemble: src=/opt/sshkeys dest=/root/.ssh/authorized_keys
with_items 关键字将在下一章介绍。如今你仅仅须要知道他能够将一个列表赋值给item參数。就想循环一样。这样我们就非常easy的合并多个文件了。
最后assemble-组装模块使用src dest參数将src文件夹下的全部文件都合并成dest以下的一个文件。和其它创建文件的模块一样。assemble-组装模块也有owner group mode等属性。而且,文件合并的顺序和ls -l命令的顺序一样,这样我们确定文件的顺序和我们预期的一样了,就像udev 、rc.d这些方法一样。
The add_host module
The add_host module is one of the most powerful modules that is available in
playbooks. add_host lets you dynamically add new machines inside a play. You
can do this by using the uri module to get a host from your CMDB and then adding
it to the current play. This module will also add your host to a group, dynamically
creating that group if it does not already exist.
The module simply takes a hostname and a groups argument, which are rather self-
explanatory, and sets the hostname and groups. You can also send extra arguments
and these are treated in the same way in which extra values in the inventory file are
treated. This means you can set ansible_ssh_user , ansible_ssh_port , and so on.
add_host 加入主机模块
add_host 加入主机模块是playbook中一个强大的模块,它能够让你动态的加入受管主机到一个play中。我们能够使用uri模块从CMDB中或者主机信息然后加入他们。它还能够将主机加到组里面。假设组不存在的话还会自己主动创建它。这个模块仅须要主机名和组名2个參数,跟主机库存清单的配置一样。我们还能够加入扩展的參数像ansible_ssh_user , ansible_ssh_port等等。
The group_by module
In addition to creating hosts dynamically in your play, you can also create groups.
The group_by module can create groups based on the facts about the machines,
including the ones you set up yourself using the add_fact module explained
earlier. The group_by module accepts one argument, key , which takes the name
of a group the machine will be added to. Combining this with the use of variables,
you can make the module add a server to a group based on its operating system,
visualization technology, or any other fact that you have access to. You can then use
this group in the target section of any subsequent plays, or in templates.
So if you want to create a group that groups the hosts by operating system, you will
call the module as follows. You can then use these groups to install packages using
the right packager, for example:
---
- name: Create operating system group
hosts: all
tasks:
- group_by: key=os_{{ ansible_distribution }}
- name: Run on CentOS hosts only
hosts: os_CentOS
tasks:
- name: Install Apache
yum: name=httpd state=latest
- name: Run on Ubuntu hosts only
hosts: os_Ubuntu
tasks:
- name: Install Apache
apt: pkg=apache2 state=latest
group_by模块
除了在play中动态创建主机之外,我们还能够创建组。
group_by 模块能够让我们依据主机的真实特性来进行分组,真实特性能够通过add_fact来实现(前面已经介绍过set_fact)。group_by模块仅仅接受一个參数,key。相同组名的机器就被分到一个组里面。
假设我们在这里使用变量,我们就能够把同一个操作系统类型、同一个拓扑结构的、或者其它我们希望的拥有相同特性的主机分成一组,组能够在子play中、模板中的目标选项被使用。
比方你将同一个操作系统类型的主机分成一组,你就能够使用相同的安装包管理器来安装软件:
---
- name: Create operating system group
hosts: all
tasks:
- group_by: key=os_{{ ansible_distribution }}
- name: Run on CentOS hosts only
hosts: os_CentOS
tasks:
- name: Install Apache
yum: name=httpd state=latest
- name: Run on Ubuntu hosts only
hosts: os_Ubuntu
tasks:
- name: Install Apache
apt: pkg=apache2 state=latest