天天看点

自动化运维工具Ansible实战(六)playbook常用的模块

(一)简述

    playbook的模块与在ansible命令行下使用的模块有一些不同。这主要是因为在playbook中会使用到一些facts变量和一些通过setup模块从远程主机上获取到的变量。有些模块没法在命令行下运行,就是因为它们需要这些变量。而且即使那些可以在命令行下工作的模块也可以通过playbook的模块获取一些更高级的功能。

(二)常用的模块

1,template模块

    在实际应用中,我们的配置文件有些地方可能会根据远程主机的配置的不同而有稍许的不同,template可以使用变量来接收远程主机上setup收集到的facts信息,针对不同配置的主机,定制配置文件。用法大致与copy模块相同。

常见的参数如下:

<code>backup:如果原目标文件存在,则先备份目标文件</code>

<code>dest:目标文件路径</code>

<code>force:是否强制覆盖,默认为</code><code>yes</code>

<code>group:目标文件属组</code>

<code>mode:目标文件的权限</code>

<code>owner:目标文件属主</code>

<code>src:源模板文件路径</code>

<code>validate:在复制之前通过命令验证目标文件,如果验证通过则复制</code>

<code>官方简单示例:</code>

<code>- template: src=</code><code>/mytemplates/foo</code><code>.j2 dest=</code><code>/etc/file</code><code>.conf owner=bin group=wheel mode=0644</code>

<code>- template: src=</code><code>/mytemplates/foo</code><code>.j2 dest=</code><code>/etc/file</code><code>.conf owner=bin group=wheel mode=</code><code>"u=rw,g=r,o=r"</code>

<code>- template: src=</code><code>/mine/sudoers</code> <code>dest=</code><code>/etc/sudoers</code> <code>validate=</code><code>'visudo -cf %s'</code>

通过以下的例子来讲解template模块与copy模块的区别。

有一个配置文件named.conf需要通过playbook的template模块来分发到主机组web1中,

<code>下面是named.conf的简单配置:</code>

<code>[root@Server5 etc]</code><code># cat named.conf </code>

<code>options {</code>

<code>listen-on port 53 {</code>

<code>127.0.0.1;</code>

<code>192.168.180.5;</code>

<code>};</code>

<code>listen-on-v6 port 53 { ::1; };</code>

<code>directory </code><code>"/var/named"</code><code>;</code>

<code>dump-</code><code>file</code> <code>"/var/named/data/cache_dump.db"</code><code>;</code>

<code>statistics-</code><code>file</code> <code>"/var/named/data/named_stats.txt"</code><code>;</code>

<code>memstatistics-</code><code>file</code> <code>"/var/named/data/named_mem_stats.txt"</code><code>;</code>

<code>zone </code><code>"."</code> <code>IN {</code>

<code>type</code> <code>hint;</code>

<code>file</code> <code>"named.ca"</code><code>;</code>

<code>include </code><code>"/etc/named.rfc1912.zones"</code><code>;</code>

<code>include </code><code>"/etc/named.root.key"</code><code>;</code>

<code>zone </code><code>"internal.example.com"</code> <code>IN {</code>

<code>type</code> <code>slave;</code>

<code>file</code> <code>"slaves/internal.example.com"</code><code>;</code>

<code>masters { 192.168.180.4; };</code>

<code>{ {group_names } }</code>

下面开始编写templated.yml的配置

<code>[root@Monitor ansible]</code><code># vim templated.yml              </code>

<code>- name: copy configure </code><code>file</code> <code>to server web1 group</code>

<code>  </code><code>hosts: web1</code>

<code>  </code><code>tasks:          </code>

<code>      </code><code>- template: src=</code><code>/etc/ansible/named</code><code>.conf dest=</code><code>/etc/named</code><code>.conf</code>

<code>      </code><code>- copy: src=</code><code>/etc/ansible/named</code><code>.conf dest=</code><code>/etc/named2</code><code>.conf</code>

执行结果如下:

<code>[root@Monitor ansible]</code><code># ansible-playbook templated.yml </code>

<code>PLAY [copy configure </code><code>file</code> <code>to server web1 group] ********************************</code>

<code>TASK [setup] *******************************************************************</code>

<code>ok: [Server6]</code>

<code>ok: [Server5]</code>

<code>TASK [template] ****************************************************************</code>

<code>TASK [copy] ********************************************************************</code>

<code>changed: [Server6]</code>

<code>changed: [Server5]</code>

<code>PLAY RECAP *********************************************************************</code>

<code>Server5                    : ok=3    changed=1    unreachable=0    failed=0   </code>

<code>Server6                    : ok=3    changed=1    unreachable=0    failed=0</code>

去server5和server6上查看刚才拷贝的文件,发现name2.conf通过copy的没有任何改变,通过template模块拷贝的name.conf会有变量的更改。下面的是server5的name.conf

<code>[root@Server5 etc]</code><code># cat /etc/named.conf </code>

<code>{ {group_names }}</code>

2,set_fact模块

    set_fact模块可以自定义facts,这些自定义的facts可以通过template或者变量的方式在playbook中使用。如果你想要获取一个进程使用的内存的百分比,则必须通过set_fact来进行计算之后得出其值,并将其值在playbook中引用。

#####下面是一个配置mysql innodb buffer size的示例:

<code>[root@Monitor ansible]</code><code># vim set_fact.yml </code>

<code>- name: Configure MySQL</code>

<code>  </code><code>tasks:</code>

<code>    </code><code>- name: </code><code>install</code> <code>MySql</code>

<code>      </code><code>yum: name=mysql-server state=installed</code>

<code>    </code><code>- name: Calculate InnoDB buffer pool size</code>

<code>      </code><code>set_fact: innodb_buffer_pool_size_mb=</code><code>"{{ ansible_memtotal_mb / 2 }}"</code>

<code>    </code><code>- name: Configure MySQL </code>

<code>      </code><code>template: src=</code><code>/etc/ansible/my</code><code>.conf dest=</code><code>/etc/my</code><code>.cnf owner=root group=root mode=0644 </code>

<code>      </code><code>notify: restart mysql </code>

<code>    </code><code>- name: Start MySQL </code>

<code>      </code><code>service: name=mysqld state=started enabled=</code><code>yes</code> 

<code>  </code><code>handlers: </code>

<code>    </code><code>- name: restart mysql </code>

<code>      </code><code>service: name=mysqld state=restarted</code>

####配置mysql的配置文件my.cnf

<code>[root@Monitor ansible]</code><code># vim /etc/ansible/my.conf</code>

<code># {{ ansible_managed }}</code>

<code>[mysqld]</code>

<code>datadir=</code><code>/var/lib/mysql</code>

<code>socket=</code><code>/var/lib/mysql/mysql</code><code>.sock</code>

<code># Disabling symbolic-links is recommended to prevent assorted</code>

<code>security risks</code>

<code>symbolic-links=0</code>

<code># Configure the buffer pool</code>

<code>innodb_buffer_pool_size = {{ innodb_buffer_pool_size_mb|int }}M</code>

<code>[mysqld_safe]</code>

<code>log-error=</code><code>/var/log/mysqld</code><code>.log</code>

<code>pid-</code><code>file</code><code>=</code><code>/var/run/mysqld/mysqld</code><code>.pid</code>

<code>#######查看执行结果</code>

<code>[root@Monitor ansible]</code><code># ansible-playbook set_fact.yml </code>

<code>PLAY [Configure MySQL] *********************************************************</code>

<code>TASK [</code><code>install</code> <code>MySql] ***********************************************************</code>

<code>TASK [Calculate InnoDB buffer pool size] ***************************************</code>

<code>TASK [Configure MySQL] *********************************************************</code>

<code>TASK [Start MySQL] *************************************************************</code>

<code>fatal: [Server5]: FAILED! =&gt; {</code><code>"changed"</code><code>: </code><code>false</code><code>, </code><code>"failed"</code><code>: </code><code>true</code><code>, </code><code>"msg"</code><code>: </code><code>"MySQL Daemon failed to start.\nStarting mysqld:  [FAILED]\r\n"</code><code>}</code>

<code>fatal: [Server6]: FAILED! =&gt; {</code><code>"changed"</code><code>: </code><code>false</code><code>, </code><code>"failed"</code><code>: </code><code>true</code><code>, </code><code>"msg"</code><code>: </code><code>"MySQL Daemon failed to start.\nStarting mysqld:  [FAILED]\r\n"</code><code>}</code>

<code>        </code><code>to retry, use: --limit @</code><code>/etc/ansible/set_fact</code><code>.retry</code>

<code>Server5                    : ok=4    changed=0    unreachable=0    failed=1   </code>

<code>Server6                    : ok=4    changed=0    unreachable=0    failed=1</code>

在server5和server6服务器上查看该配置

<code>[root@Server5 etc]</code><code># vim my.cnf </code>

<code># Ansible managed</code>

<code>innodb_buffer_pool_size = 934M</code>

3,pause模块

    pause模块是在playbook执行的过程中暂停一定时间或者提示用户进行某些操作

<code>常用参数:</code>

<code>minutes:暂停多少分钟</code>

<code>seconds:暂停多少秒</code>

<code>prompt:打印一串信息提示用户操作</code>

简单的实例:

<code>[root@Monitor ansible]</code><code># vim pause.yml</code>

<code> </code><code>- name: wait on user input</code>

<code>   </code><code>pause: prompt=</code><code>"Warning! Detected slight issue. ENTER to continue CTRL-C a to quit"</code> 

<code> </code><code>- name: timed wait</code>

<code>  </code><code>pause: seconds=30</code>

4,wait_for模块

在playbook的执行过程中,等待某些操作完成以后再进行后续操作

<code>connect_timeout:在下一个任务执行之前等待连接的超时时间</code>

<code>delay:等待一个端口或者文件或者连接到指定的状态时,默认超时时间为300秒,在这等待的300s的时间里,wait_for模块会一直轮询指定的对象是否到达指定的状态,delay即为多长时间轮询一次状态。</code>

<code>host:wait_for模块等待的主机的地址,默认为127.0.0.1</code>

<code>port:wait_for模块待待的主机的端口</code>

<code>path:文件路径,只有当这个文件存在时,下一任务才开始执行,即等待该文件创建完成</code>

<code>state:等待的状态,即等待的文件或端口或者连接状态达到指定的状态时,下一个任务开始执行。当等的对象为端口时,状态有started,stoped,即端口已经监听或者端口已经关闭;当等待的对象为文件时,状态有present或者started,absent,即文件已创建或者删除;当等待的对象为一个连接时,状态有drained,即连接已建立。默认为started</code>

<code>timeout:wait_for的等待的超时时间,默认为300秒</code>

<code>#####示例</code>

<code>[root@Monitor ansible]</code><code># cat /etc/ansible/wait_for.yml </code>

<code>- name: create task</code>

<code>  </code><code>hosts: 192.168.180.2</code>

<code> </code> 

<code>        </code><code>- wait_for: port=8080 state=started     </code><code>#等待8080端口已正常监听,才开始下一个任务,直到超时</code>

<code>        </code><code>- wait_for: port=8081 delay=10    </code><code>#等待8000端口正常监听,每隔10s检查一次,直至等待超时</code>

<code>        </code><code>- wait_for: host=0.0.0.0 port=8000 delay=10 state=drained    </code><code>#等待8000端口直至有连接建立</code>

<code>        </code><code>- wait_for: host=0.0.0.0 port=8000 state=drained exclude_hosts=10.2.1.2,10.2.1.3    </code><code>#等待8000端口有连接建立,如果连接来自10.2.1.2或者10.2.1.3,则忽略。</code>

<code>        </code><code>- wait_for: path=</code><code>/tmp/foo</code>    <code>#等待/tmp/foo文件已创建</code>

<code>        </code><code>- wait_for: path=</code><code>/tmp/foo</code> <code>search_regex=completed    </code><code>#等待/tmp/foo文件已创建,而且该文件中需要包含completed字符串</code>

<code>        </code><code>- wait_for: path=</code><code>/var/lock/file</code><code>.lock state=absent    </code><code>#等待/var/lock/file.lock被删除</code>

<code>        </code><code>- wait_for: path=</code><code>/proc/3466/status</code> <code>state=absent        </code><code>#等待指定的进程被销毁</code>

<code>        </code><code>- local_action: wait_for port=22 host=</code><code>"{{ ansible_ssh_host | default(inventory_hostname) }}"</code> <code>search_regex=OpenSSH delay=10    </code><code>#等待openssh启动,10s检查一次</code>

执行过程为:

<code>[root@Monitor ansible]</code><code># ansible-playbook wait_for.yml</code>

<code>PLAY [create task] *************************************************************</code>

<code>ok: [192.168.180.2]</code>

<code>TASK [wait_for] ****************************************************************</code>

<code>ok: [192.168.180.2 -&gt; localhost]</code>

<code>192.168.180.2              : ok=10   changed=0    unreachable=0    failed=0</code>

5,assemble模块

用于组装文件,即将多个零散的文件,合并一个大文件

<code>src:原文件(即零散文件)的路径</code>

<code>dest:合并后的大文件路径</code>

<code>group:合并后的大文件的属组</code>

<code>owner:合并后的大文件的属主</code>

<code>mode:合并后的大文件的权限</code>

<code>validate:与template的validate相同,指定命令验证文件</code>

<code>ignore_hidden:组装时,是否忽略隐藏文件,默认为no,该参数在2.0版本中新增</code>

示例:

<code>[root@Monitor ansible]</code><code># vim assemble.yml </code>

<code>- hosts: web1</code>

<code>    </code><code>- name: Make a Directory </code><code>in</code> <code>/opt</code>

<code>      </code><code>file</code><code>: path=</code><code>/opt/sshkeys</code> <code>state=directory owner=root group=root mode=0700</code>

<code>    </code><code>- name: Copy SSH keys over</code>

<code>      </code><code>copy: src=playbook/{{ item }}.pub dest=</code><code>/opt/sshkeys/</code><code>{{ item }}.pub owner=root group=root mode=0600</code>

<code>      </code><code>with_items:</code>

<code>        </code><code>- dan</code>

<code>        </code><code>- kate</code>

<code>        </code><code>- mal</code>

<code>    </code><code>- name: Make the root </code><code>users</code> <code>SSH config directory</code>

<code>      </code><code>file</code><code>: path=</code><code>/root/</code><code>.</code><code>ssh</code> <code>state=directory owner=root group=root mode=0700</code>

<code>    </code><code>- name: Build the authorized_keys </code><code>file</code>

<code>      </code><code>assemble: src=</code><code>/opt/static/</code> <code>dest=</code><code>/root/</code><code>.</code><code>ssh</code><code>/authorized_keys</code> <code>owner=root group=root mode=0700   </code><code>#将/opt/sshkeys目录里所</code>

<code>有的文件合并到</code><code>/root/</code><code>.</code><code>ssh</code><code>/authorized_keys</code><code>一个文件中</code>

######执行过程

<code>[root@Monitor ansible]</code><code># ansible-playbook assemble.yml </code>

<code>PLAY [web1] ********************************************************************</code>

<code>TASK [Make a Directory </code><code>in</code> <code>/opt</code><code>] ************************************************</code>

<code>TASK [Copy SSH keys over] ******************************************************</code>

<code>failed: [Server6] (item=mal) =&gt; {</code><code>"failed"</code><code>: </code><code>true</code><code>, </code><code>"item"</code><code>: </code><code>"mal"</code><code>, </code><code>"msg"</code><code>: </code><code>"Unable to find 'playbook/mal.pub' in expected paths."</code><code>}</code>

<code>failed: [Server6] (item=kate) =&gt; {</code><code>"failed"</code><code>: </code><code>true</code><code>, </code><code>"item"</code><code>: </code><code>"kate"</code><code>, </code><code>"msg"</code><code>: </code><code>"Unable to find 'playbook/kate.pub' in expected paths."</code><code>}</code>

<code>failed: [Server6] (item=dan) =&gt; {</code><code>"failed"</code><code>: </code><code>true</code><code>, </code><code>"item"</code><code>: </code><code>"dan"</code><code>, </code><code>"msg"</code><code>: </code><code>"Unable to find 'playbook/dan.pub' in expected paths."</code><code>}</code>

<code>failed: [Server5] (item=kate) =&gt; {</code><code>"failed"</code><code>: </code><code>true</code><code>, </code><code>"item"</code><code>: </code><code>"kate"</code><code>, </code><code>"msg"</code><code>: </code><code>"Unable to find 'playbook/kate.pub' in expected paths."</code><code>}</code>

<code>failed: [Server5] (item=dan) =&gt; {</code><code>"failed"</code><code>: </code><code>true</code><code>, </code><code>"item"</code><code>: </code><code>"dan"</code><code>, </code><code>"msg"</code><code>: </code><code>"Unable to find 'playbook/dan.pub' in expected paths."</code><code>}</code>

<code>failed: [Server5] (item=mal) =&gt; {</code><code>"failed"</code><code>: </code><code>true</code><code>, </code><code>"item"</code><code>: </code><code>"mal"</code><code>, </code><code>"msg"</code><code>: </code><code>"Unable to find 'playbook/mal.pub' in expected paths."</code><code>}</code>

<code>        </code><code>to retry, use: --limit @</code><code>/etc/ansible/assemble</code><code>.retry</code>

<code>Server5                    : ok=2    changed=0    unreachable=0    failed=1   </code>

<code>Server6                    : ok=2    changed=0    unreachable=0    failed=1</code>

6,add_host模块

在playbook执行的过程中,动态的添加主机到指定的主机组中

<code>groups</code><code>:添加主机至指定的组</code>

<code>name:要添加的主机名或IP地址</code>

示例

<code>[root@Monitor ansible]</code><code># vim add_host.yml</code>

<code>- name: add a host to group webservers</code>

<code>    </code><code>- add_host name={{ ip_from_ec2 }} group=web1 foo=23    </code><code>#添加主机到webservers组中,主机的变量foo的值为42</code>

7,group_by模块

playbook执行的过程中,动态的创建主机组

<code>- name: Create operating system group</code>

<code>  </code><code>hosts: all</code>

<code>    </code><code>- group_by: key=os_{{ ansible_distribution }}           </code><code>#在playbook中设置一个新的主机组</code>

<code>- name: Run on CentOS hosts only</code>

<code>  </code><code>hosts: os_CentOS</code>

<code>    </code><code>- name: Install Apache</code>

<code>      </code><code>yum: name=httpd state=latest</code>

<code>- name: Run on Ubuntu hosts only</code>

<code>  </code><code>hosts: os_Ubuntu</code>

<code>      </code><code>apt: pkg=apache2 state=latest</code>

执行过程:

<code>root@Monitor ansible]</code><code># ansible-playbook group_by.yml </code>

<code>PLAY [Create operating system group] *******************************************</code>

<code>ok: [192.168.180.6]</code>

<code>ok: [192.168.180.5]</code>

<code>ok: [192.168.180.4]</code>

<code>ok: [192.168.180.23]</code>

<code>ok: [192.168.180.10]</code>

<code>TASK [group_by] ****************************************************************</code>

<code>PLAY [Run on CentOS hosts only] ************************************************</code>

<code>TASK [Install Apache] **********************************************************</code>

<code>changed: [192.168.180.10]</code>

8,debug模块

调试模块,用于在调试中输出信息

<code>msg:调试输出的消息</code>

<code>var:将某个任务执行的输出作为变量传递给debug模块,debug会直接将其打印输出</code>

<code>verbosity:debug的级别</code>

<code>[root@Monitor ansible]</code><code># vim debug.yml              </code>

<code># Example that prints the loopback address and gateway for each host- debug: msg="System {{ inventory_hostname }} has uuid</code>

<code> </code><code>{{ ansible_product_uuid }}"</code>

<code>- debug: msg=</code><code>"System {{ inventory_hostname }} has gateway {{ ansible_default_ipv4.gateway }}"</code>

<code>        </code><code>when: ansible_default_ipv4.gateway is defined</code>

<code>- shell: </code><code>/usr/bin/uptime</code> 

<code>        </code><code>register: result</code>

<code>- debug: var=result verbosity=2    </code><code>#直接将上一条指令的结果作为变量传递给var,由debug打印出result的值</code>

<code>- name: Display all variables</code><code>/facts</code> <code>known </code><code>for</code> <code>a host</code>

<code>        </code><code>debug: var=hostvars[inventory_hostname] verbosity=4</code>

9,fail模块

用于终止当前playbook的执行,通常与条件语句组合使用,当满足条件时,终止当前play的运行。可以直接由failed_when取代。

<code>选项只有一个:</code>

<code>msg:终止前打印出信息</code>

<code>[root@Monitor ansible]</code><code># vim fail.yml </code>

<code>- fail: msg=</code><code>"The system may not be provisioned according to the CMDB status."</code>

<code>  </code><code>when: cmdb_status != </code><code>"to-be-staged"</code>

备注:以后会继续修改有不妥之处

本文转自 lqbyz 51CTO博客,原文链接:http://blog.51cto.com/liqingbiao/1968916