设为首页 收藏本站
查看: 2924|回复: 0

[经验分享] Ansible详细配置管理工具(三)

[复制链接]
累计签到:1 天
连续签到:1 天
发表于 2014-7-25 09:34:12 | 显示全部楼层 |阅读模式
SouthEast.jpg


    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提示变量。这种特性使得维护“操作”变得很简单,避免在一个“操作”中不停的修改同一个事物,把他放在配置的最上面,在阅读和修改的时候也不会影响到“操作”的其他设置。在变量选项中的变量会被清单中的变量(就是模块中设置的变量)覆盖。但它同时又会被库存清单中的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可以让脚本文档变的更好,更容易调用这个任务,在后续我们使用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的组,并添加一些主机(远程受管主机)----第一章内容已经介绍过如何完成这些配置。

使用模板模块就可以很简单将命令传递到组里面所有的机器,调用模板模块也和调用其他模块一样简单。此外,模板模块和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的基础上扩展更多的模块使用了。
[python] view plaincopyprint?在CODE上查看代码片派生到我的代码片

    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参数,就想循环一样,这样我们就很容易的合并多个文件了。

最后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




运维网声明 1、欢迎大家加入本站运维交流群:群②:261659950 群⑤:202807635 群⑦870801961 群⑧679858003
2、本站所有主题由该帖子作者发表,该帖子作者与运维网享有帖子相关版权
3、所有作品的著作权均归原作者享有,请您和我们一样尊重他人的著作权等合法权益。如果您对作品感到满意,请购买正版
4、禁止制作、复制、发布和传播具有反动、淫秽、色情、暴力、凶杀等内容的信息,一经发现立即删除。若您因此触犯法律,一切后果自负,我们对此不承担任何责任
5、所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其内容的准确性、可靠性、正当性、安全性、合法性等负责,亦不承担任何法律责任
6、所有作品仅供您个人学习、研究或欣赏,不得用于商业或者其他用途,否则,一切后果均由您自己承担,我们对此不承担任何法律责任
7、如涉及侵犯版权等问题,请您及时通知我们,我们将立即采取措施予以解决
8、联系人Email:admin@iyunv.com 网址:www.yunweiku.com

所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其承担任何法律责任,如涉及侵犯版权等问题,请您及时通知我们,我们将立即处理,联系人Email:kefu@iyunv.com,QQ:1061981298 本贴地址:https://www.yunweiku.com/thread-22674-1-1.html 上篇帖子: Ansible详细配置管理工具(二) 下篇帖子: Ansible详细配置管理工具(四) 管理工具
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

扫码加入运维网微信交流群X

扫码加入运维网微信交流群

扫描二维码加入运维网微信交流群,最新一手资源尽在官方微信交流群!快快加入我们吧...

扫描微信二维码查看详情

客服E-mail:kefu@iyunv.com 客服QQ:1061981298


QQ群⑦:运维网交流群⑦ QQ群⑧:运维网交流群⑧ k8s群:运维网kubernetes交流群


提醒:禁止发布任何违反国家法律、法规的言论与图片等内容;本站内容均来自个人观点与网络等信息,非本站认同之观点.


本站大部分资源是网友从网上搜集分享而来,其版权均归原作者及其网站所有,我们尊重他人的合法权益,如有内容侵犯您的合法权益,请及时与我们联系进行核实删除!



合作伙伴: 青云cloud

快速回复 返回顶部 返回列表