天天看点

Ansible 简单入门

安装:

先安装EPEL源  

yum install ansible -y

Ansible配置和测试

第一步是修改主机与配置组,文件位置 /etc/ansible/hosts, 格式是ini,添加两台主机IP 同时定义两个IP到webserver组 内容如下:

【/etc/ansible/hosts】

#web1.aa.com

#web2.aa.com

192.168.137.8

192.168.137.9

[webserver]

通过PING魔窟测试主机的连通性,分别对单主机及组进行PING操作 初夏如下结果 表示安装测试成功

<code>[root@localhost ansible]</code><code># ansible webserver -m ping -k</code>

<code>SSH password:</code>

<code>192.168.137.8 | SUCCESS =&gt; {</code>

<code>    </code><code>"changed"</code><code>: </code><code>false</code><code>,</code>

<code>    </code><code>"ping"</code><code>: </code><code>"pong"</code>

<code>}</code>

<code>192.168.137.9 | SUCCESS =&gt; {</code>

<code>[root@localhost ansible]</code><code>#</code>

<code>#运行命令如下:</code>

<code>[root@localhost ansible]</code><code># ansible webserver -a "uptime"</code>

<code>192.168.137.8 | SUCCESS | rc=0 &gt;&gt;</code>

<code> </code><code>16:43:39 up 26 min,  1 user,  load average: 0.03, 0.01, 0.00</code>

<code>127.168.137.9 | SUCCESS | rc=0 &gt;&gt;</code>

<code> </code><code>16:43:40 up 49 min,  4 </code><code>users</code><code>,  load average: 0.14, 0.06, 0.01</code>

由于主控端与被控主机未配置SSH证书信任,需要在执行ansible命令时添加 -k 参数,要求提供root密码 即在提示 SSH password:时输入 很多人更倾向于使用Linux普通用户账户进行连接并使用sudo命令实现root权限,格式为:ansible webserver -m ping -u ansible -sudo.

为了避免Ansible下发指令时输入目标主机密码,通过证书签名达到SSH无密码是一个方案,推荐使用ssk-keygen 与 ssh-copy-id来实现快速证书的生成及公钥下发,其中ssh-keygen生成一对秘钥,使用ssh-copy-id来下发生成的公钥。具体操作如下:

   在主控端主机创建秘钥 执行:ssk-keygen -t rsa, 有询问直接回车键即可 将在/root/.ssh目录下生成一对秘钥,其中 id_rsa为私钥,id_rsa.pub为公钥,公钥需要下发到被控主机用户的.ssh目录,同时要求重命名成authorized_keys文件。

<code>[root@localhost ansible]</code><code># ssh-keygen -t rsa -b 4096</code>

<code>Generating public</code><code>/private</code> <code>rsa key pair.</code>

<code>Enter </code><code>file</code> <code>in</code> <code>which</code> <code>to save the key (</code><code>/root/</code><code>.</code><code>ssh</code><code>/id_rsa</code><code>):</code>

<code>Created directory </code><code>'/root/.ssh'</code><code>.</code>

<code>Enter passphrase (empty </code><code>for</code> <code>no passphrase):</code>

<code>Enter same passphrase again:</code>

<code>Your identification has been saved </code><code>in</code> <code>/root/</code><code>.</code><code>ssh</code><code>/id_rsa</code><code>.</code>

<code>Your public key has been saved </code><code>in</code> <code>/root/</code><code>.</code><code>ssh</code><code>/id_rsa</code><code>.pub.</code>

<code>The key fingerprint is:</code>

<code>36:ca:5e:14:6b:ef:ab:12:4d:82:be:33:d0:70:3c:f4 root@localhost</code>

<code>The key's randomart image is:</code>

<code>+--[ RSA 4096]----+</code>

<code>|                 |</code>

<code>|    .            |</code>

<code>|   o o  .        |</code>

<code>|  . = E .o       |</code>

<code>|   = . +S        |</code>

<code>|  . o..=.o       |</code>

<code>|   . .o.. .      |</code>

<code>|    +... .       |</code>

<code>|     o....o.     |</code>

<code>+-----------------+</code>

接下来同步公钥文件id_rsa.pub到目标主机,推荐使用ssh-copy-id公钥拷贝工具,命令格式:

/usr/bin/ssh-copy-id [-i[identity_file]][user@]machine 本例中使用一下命令:

<code>ssh</code><code>-copy-</code><code>id</code> <code>-i </code><code>/root/</code><code>.</code><code>ssh</code><code>/id_rsa</code><code>.pub [email protected]</code>

<code>ssh</code><code>-copy-</code><code>id</code> <code>-i </code><code>/root/</code><code>.</code><code>ssh</code><code>/id_rsa</code><code>.pub [email protected]</code>

效验SSH无密码是否配置成功 ,运行 ssh [email protected] 如果直接进入目标root账号提示符,则署名配置成功。

定义主机与组规则

Ansible通过定义好的主机与组规则(Inventory)对匹配的目标主机进行远程操作,配置规则文件默认是/etc/ansible/hosts.

定义主机与组

所有定义的主机与组规则都在/etc/ansible/hosts文件中. 为ini文件格式 主机可以用域名、IP、别名进行标识,其中webserver、dbserver 为组名 紧跟着的主机为其成员 格式如下:

<code>mail.example.com</code>

<code>192.168.1.21:2135</code>

<code>[webserver]</code>

<code>foo.example.com</code>

<code>bar.example.com</code>

<code>192.168.1.22</code>

<code>[dbserver]</code>

<code>one.example.com</code>

<code>two.example.com</code>

<code>three.example.com</code>

<code>192.168.1.23</code>

其中 192.168.1.21:2135 的意思是定义一个SSH服务端口为2135的主机,当然我们也可以使用别名来描述一台主机。

jumper ansible_ssh_port=22 ansible_ssh_host=192.168.1.50

jumper为定义一个s别名,ansible_ssh_port为主机SSH服务端口,ansible_ssh_host为目标主机 更多保留主机变量如下:

ansible_ssh_host  目标主机地址

ansible_ssh_port   目标主机SSH端口 端口22无需指定

ansible_ssh_user  目标主机用户名

ansible_ssh_pass  目标主机密码

ansible_connection 目标主机连接类型 可以视local、ssh 或者paramiko

ansible_ssh_private_key_file 目标主机SSH私钥

ansible_*_interpreter  指定采用非Python的其他脚本语言 如 Ruby Perl 或者其他

组成员主机名称支持正则描述 示例如下:

<code>www[01:20].example.com</code>

<code>db-[a:f].example.com</code>

定义主机变量

主机可以指定变量,以便后面供Playbooks配置使用 比如定义主机hosts1 和 hosts2 上Apache参数http_port 及 maxRequestsPerChild,目的是让两台主机产生Apache配置文件httpd.conf差异化,定义格式如下:

<code>[atlanta]</code>

<code>host1 http_port=80 maxRequestsPerChild=808</code>

<code>host2 http_port=8080 maxRequestsPerChild=909</code>

定义组变量

组变量的作用域是覆盖组所有成员,通过定义一个新块,块名由组名+":vars" 组成,定义格式如下:

<code>host1</code>

<code>host2</code>

<code>[atlanta:vars]</code>

<code>ntp_server=ntp.atlanta.example.com</code>

<code>proxy=proxy.atlanta.example.com</code>

同时Ansible支持组嵌套组 ,通过定义一个新块,块名由组名+":children"组成,格式如下:

<code>[raleigh]</code>

<code>host3</code>

<code>[southeast:children]</code>

<code>atlanta</code>

<code>raleigh</code>

<code>[southeast:vars]</code>

<code>some_server=foo.southeast.example.com</code>

<code>halon_system_timeout=30</code>

<code>self_destruct_countdown=60</code>

<code>escape_pods=2</code>

<code>[usa:children]</code>

<code>southeast</code>

<code>northeast</code>

<code>southwest</code>

分离主机与组特定数据

为了更好规范定义的主机与组变量,Ansible支持将/etc/ansible/hosts定义的主机名与组变量单独剥离出来存放到指定文件夹中.将采用YAML格式存放,存放位置规定: "/etc/ansible/group_vars/+组名" 和 "/etc/ansible/host_vars/+主机名" 分别存放指定组名或者主机名定义的变量。如下:

/etc/ansible/group_vars/dbservers

/etc/ansible/group_vars/webservers

定义的dbserver变量格式为:

[/etc/ansible/group_vars/dbservers]

---

ntp_server:acme.example.org

database_server:storage.example.org

Ansible常用模块

远程命令模块

<code>   </code><code>ansible webserver -m </code><code>command</code> <code>-a </code><code>"free -m"</code>  <code>#远程命令</code>

<code>   </code><code>ansible webserver -m script -a </code><code>"/home/test.sh"</code> <code>#远程主机执行主控服务器脚本</code>

<code>   </code><code>ansible webserver -m shell -a </code><code>"/home/test.sh"</code>   <code>#执行远程主机脚本</code>

   2. copy模块

   实现主控端想目标主机拷贝文件

   以下例子实现拷贝/home/test.sh文件到webserver组目标主机/tmp目录下 并更新文件属主和权限

<code>ansible webserver -m copy -a </code><code>"src=/home/test.sh dest=/tmp/ owner=root group=root mode=0755"</code>

   3.    stat模块

   获取远程文件状态信息,包括atime、ctime、mtime、MD5、uid、gid等信息

   例子:

<code>ansible webserver -m stat -a </code><code>"path=/etc/sysctl.conf"</code>

   4.    get_url模块

   实现在远程主机下载指定URL到本地 支持sha256sum文件效验

    实例:

<code>ansible webserver -m get_url -a </code><code>"url=http://www.baidu.com dest=/tmp/index.html mode=0440 force=yes "</code>

  5.    yum功能

   Linux平台软件包管理操作 常见的有yum apt 管理方式

    实例 :

<code>   </code><code>ansible webserver -m yum -a </code><code>"name=curl state=latest"</code>

<code>   </code><code>ansible webserver -m apt -a </code><code>"pkg=curl state=latest"</code>

  6.    cron模块

   远程主机crontab配置

    实例:

<code>ansible webserver -m </code><code>cron</code> <code>-a </code><code>"'name=check dirs' hour='5,2' job='ls -alh &gt; /dev/null'"</code>

    结果:

<code>#Ansible: test_check</code>

<code>* 5,2 * * * </code><code>ls</code> <code>-alh &gt; </code><code>/root/ll</code><code>.txt</code>

  7.   mount模块

    远程主机分区挂载

<code>ansible webserver -m </code><code>mount</code> <code>-a </code><code>"name=/mnt/data src=/dev/sd0 fstype=ext3 opts=ro state=present"</code>

  8.    service模块

    远程主机系统服务管理

    实例:

<code>   </code><code>ansible webserver -m service -a </code><code>"name=nginx state=stopped"</code>

<code>   </code><code>ansible webserver -m service -a </code><code>"name=nginx state=restarted"</code>

<code>   </code><code>ansible webserver -m service -a </code><code>"name=nginx state=reloaded"</code>

 9.    sysctl包管理模块

    远程主机sysctl配置

<code>  </code><code>ansible webserver -m sysctl -a "name=kernel.panic value=3 sysctl_file=</code><code>/etc/sysctl</code><code>.conf checks=before reload=yessalt </code><code>'*'</code> <code>pkg.upgrade</code>

10 .    user模块

    远程主机系统用户管理

<code>    </code><code>ansible webserver -m user -a </code><code>"name=johnd comment='John Doe'"</code> <code>#添加用户</code>

<code>    </code><code>ansible webserver -m user -a </code><code>"name=johnd state=absent  remove=yes"</code> <code>#删除用户</code>

playbook介绍:

<code>---</code>

<code>- hosts: webserver</code>

<code>  </code><code>vars:</code>

<code>    </code><code>worker_processes: 4</code>

<code>    </code><code>num_cpus: 4</code>

<code>    </code><code>max_open_file: 65506</code>

<code>    </code><code>root: </code><code>/data</code>

<code>  </code><code>remote_user: root</code>

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

<code>  </code><code>- name ensure nginx is at the latest version</code>

<code>    </code><code>yum: pkg=nginx state=latest</code>

<code>  </code><code>- name: write the nginx config </code><code>file</code>

<code>    </code><code>template: src=</code><code>/home/test/nginx2</code><code>.conf dest=</code><code>/etc/nginx/nginx</code><code>.conf</code>

<code>    </code><code>notify:</code>

<code>    </code><code>- restart nginx</code>

<code>  </code><code>- name: ensure nginx is running</code>

<code>    </code><code>service: name=nginx start=started</code>

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

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

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

以上playbook定制了一个简单的Nginx软件包管理 内容包括安装 配置模板 状态管理等。下面进行说明。

定义主机和用户:

   在playbook执行时,可以为主机或组定义变量,比如指定远程登录用户。一下为webserver组定义的相关变量,变量的作用域只限于webserver组下的主机。

   hosts参数的作用为定义的操作对象,可以视主机或组,本示例定义的为webserver组,同事通过vars定义了4个变量 配置模板用到,其中remote_user 为制动远程操作的用户名,默认为root账号,支持sudo方式运行,通过添加sudo:yes即可。注意 remote_user参数在Ansible1.4或更高版本才引入。

任务列表:

    所有定义的任务列表tasks list ,playbook将按定义的配置文件自上而下的顺序执行,定义的主机都将得到相同的任务,但执行的返回结果不一定保持一致,取决于主机的环境及程序包状态。建议每个任务事件都定义一个name标签增强可读性,也便于观察输出结果了解运行的位置,默认使用action(具体的执行动作)来替换name作为输入。下列是一个简单的任务定义示例:

<code>tasks:</code>

<code>  </code><code>- name: </code><code>make</code> <code>sure nginx is running</code>

<code>  </code><code>service: name=nginx state=started</code>

  功能实际检测Nginx服务是否处于运行状态,如果没有则启动。其中name标签对下面的action进行描述,action部分可以视ansible的任意模块,本示例为service模块,参数使用key=value的格式,如 name=httpd 在定义任务时也可以引用变量 格式如下:

<code>   </code><code>- name: create a virtual host </code><code>file</code> <code>for</code> <code>{{ vhost }}</code>

<code>   </code><code>template: src=somefile.j2 dest=</code><code>/etc/httpd/conf</code><code>.d/{{ vhost }}</code>

  在playbook可以通过template模块对本地配置模板文件进行渲染并同步到目标主机。已nginx配置文件为例,定义如下:

<code>- name: write the nginx config </code><code>file</code>

<code>  </code><code>template: src=</code><code>/etc/ansible/nginx/nginx2</code><code>.conf dest=</code><code>/etc/nginx/nginx</code><code>.conf</code>

<code>  </code><code>notify:</code>

<code>  </code><code>- restart nginx</code>

其中scr=/etc/ansible/nginx/nginx2.conf位管理端模板文件的存放位置 dest=/etc/nginx/nginx.conf为目标主机nginx配置的文件的存放位置,通过下面Nginx模板文件让大家对模板的定义有个基本的概念。

<code>user nginx;</code>

<code>worker_processes {{ worker_processes}};</code>

<code>{% </code><code>if</code> <code>num_cpus == 2 %}</code>

<code>worker_cpu_affinity 01 10;</code>

<code>{% </code><code>elif</code> <code>num_cpus == 4 %}</code>

<code>worker_cpu_affinity 1000 0100 0010 0001;</code>

<code>{% </code><code>elif</code> <code>num_cpus &gt;= 8 %}</code>

<code>worker_cpu_affinity 00000001 00000010 00000100 00001000 00010000 00100000 01000000 10000000;</code>

<code>{% </code><code>else</code> <code>%}</code>

<code>{% endif %}</code>

<code>worker_rlimit_nofile {{ max_open </code><code>file</code> <code>}};</code>

Ansible会根据定义好的模板渲染成真实的配置文件 ,模板使用YAML语法 最终生成的nginx.conf如下:

<code>worker_processes 4;</code>

<code>worker_rlimit_nofile 65506;</code>

档目标主机配置文件发生变化后,通知处理程序Handlers 来触发后面的动作,比如重启nginx服务。handlers中定义的处理程序没有通知触发时时不会执行的,触发后也只会运行一次。触发时通过Handlers定义的name标签来识别的,比如下面的notify中的 restart nginx 与handlers中的 name:restart nginx保持一致。

<code>notify:</code>

<code>      </code><code>- restart nginx</code>

<code>handlers:</code>

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

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

执行playbook

    执行playbook可以通过ansible-playbook命令实现,格式: ansible-playbook playbookfiel.yml [参数] 如启用10个并行进程数执行playbook:

ansible-playbook /etc/ansible/nginx/nginx.yml -f 10

其他参数:

-u REMOTE_USER:手工指定远程执行playbook的系统用户

--syntax-check 检查playbook语法

--list-hosts playbooks 匹配到的主机列表

-T TIMEOUT 定义playbook执行超时时间

--setp 以单任务分步骤运行,方便做每一个不的确认工作。

更多参数运行 ansible-playbook -help获取。

以下是一个例子的执行结果  vsftpd.yml文件内容如下:

<code>[root@localhost ansible]</code><code># cat vsftpd.yml</code>

<code>  </code><code>- name: Install Vsftpd Server</code>

<code>    </code><code>yum: pkg=vsftpd state=latest</code>

<code>    </code><code>- restart vsftpd</code>

<code>  </code><code>- name: Check Vsftpd is Running</code>

<code>    </code><code>service: name=vsftpd state=started</code>

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

<code>      </code><code>service: name=vsftpd stated=restarted</code>

执行结果如下:

<code>[root@localhost ansible]</code><code># ansible-playbook vsftpd.yml -f 10</code>

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

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

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

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

<code>TASK [Install Vsftpd Server] ***************************************************</code>

<code>TASK [Check Vsftpd is Running] *************************************************</code>

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

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

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

角色定义

已经了解了变量,任务和处理程序的定义 有什么方法更好的进行组织或抽象 让其复用性更强、功能根据模块化?答案就是角色,角色是Ansible定制好的一种标准规范,以不同级别目录层次及文件对角色、变量、任务、处理程序等拆分、为后续功能扩展、可维护性打下基础 一个定向的角色目录结构如下:

<code>site.yml</code>

<code>webserver.yml</code>

<code>fooserver.yml</code>

<code>roles/</code>

<code>   </code><code>common/</code>

<code>       </code><code>files/</code>

<code>       </code><code>templates/</code>

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

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

<code>       </code><code>vars/</code>

<code>       </code><code>meta/</code>

<code>   </code><code>webserver/</code>

在playbook是这样引用的:

<code>[site.yml]</code>

<code>- hosts:webserver</code>

<code>  </code><code>roles:</code>

<code>      </code><code>-common</code>

<code>      </code><code>-webserver</code>

角色定制已下规范,其中X为角色名

如roles/x/tasks/main.yml 文件存在。其中列出的任务江北添加到执行队列

如roles/x/handlers/main.yml 文件存在 其中所列出的处理程序将被添加到执行队列;

如roles/x/vars/main.yml 文件存在 其中列出的变量将被添加到执行队列;

如roles/x/meta/main.yml 文件存在 所列任何作用的依赖关系将被添加到角色的列表

任何副本任务可以引用roles/x/files/无需写路径 默认相对或绝对医用;

任何脚本任务可以引用roles/x/files/无需写路径 默认相对或绝对医用;

任何模板任务可以引用文件中的roles/x/templates/无需写路径 默认相对或绝对医用;

为了便于更好的理解 以下示例以角色的形式存在。同时添加了一个公共角色common 从角色全局作用域中抽取出公共的部分,一般为系统的基础服务,比如 ntp iptables、selinux、sysctl等。本示例是针对ntp服务的管理

playbook目录结构

playbook目录包括变量定义目录group_vars、主机组定义文件hotst、全局配置文件site.yml 角色功能目录,playbook目录如下:

<code>[root@localhost playbook]</code><code># tree nginx/</code>

<code>nginx/</code>

<code>├── group_Vars</code>

<code>├── hosts</code>

<code>├── roles</code>

<code>│   ├── common</code>

<code>│   │   ├── handlers</code>

<code>│   │   │   └── main.yml</code>

<code>│   │   ├── tasks</code>

<code>│   │   ├── templates</code>

<code>│   │   │   └── ntp.conf.js</code>

<code>│   │   └── vars</code>

<code>│   │       └── main.yml</code>

<code>│   └── web</code>

<code>│       ├── handlers</code>

<code>│       │   └── main.yml</code>

<code>│       ├── tasks</code>

<code>│       └── templates</code>

<code>│           └── nginx2.conf</code>

<code>└── site.yml</code>

<code>11 directories, 9 files</code>

<code>[root@localhost playbook]</code><code>#</code>

1.新建目录nginx

2.定义主机组

     以下定义了一个业务组webserver 成员时两台主机

【nginx/hosts】

<code>  </code><code>[webserver]</code>

<code>  </code><code>192.168.137.8</code>

<code>  </code><code>192.168.137.9</code>

        注意:这个非必选配置,默认将引用/etc/ansible/hosts的参数 角色中自定义组与主机文件将通过 -i file 命令行参数调用。如 ansible-playbook -i hosts 来调用

3.定义主机组或变量

group_vars为定义变量目录,目录当中的文件名要与组名保持一致,组变量文件定义的变量作为域值受限于该组,all代表所有主机

【nginx/group_vars/all】

<code>nptserver:ntp.sjtu.edu.cn</code>

【nginx/group_vars/webserver】

<code>worker_processes:4</code>

<code>num_cpus:4</code>

<code>max_open_file:65536</code>

<code>root:</code><code>/data</code>

4. 全局配置文件site.yml

全局配置文件引用了两个角色块,角色应用方位及实现的功能功都不一样:

【nginx/site.yml】

<code>- name:apply common configuration to all nodes</code>

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

<code>    </code><code>- common</code>

<code>- name:configure and deploy the webserver and application code</code>

<code>  </code><code>hosts:webserver</code>

<code>    </code><code>- we</code>

全局配置文件site.yml引用了两个角色,一个为公共类的common,另一个为web类,分别对应nginx/common 、nginx/web目录 以此类推,可以引用更多的角色,如dbserver、nosql、Hadoop等,前提是我们要先进行定义。通常情况下一个角色对应着一个特定功能服务。通过hosts参数来绑定角色对应的主机或组。

5.    角色common的定义

角色common定义了 handlers   tasks   templates   vars 4个功能类,分别存放处理程序、任务列表、模板、变量的配置文件main.yml。需要注意的是 vars/main.yml中定义的变量优先级高于/nginx/group_vars/all定义的 可以从ansible-playbook的执行结果中得到验证。各功能模块配置文件如下:

[handlers/main.yml]

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

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

[tasks/main.yml]

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

<code>  </code><code>yum:name=ntp state=present</code>

<code>  </code> 

<code>- name:Configure ntp </code><code>file</code>

<code>  </code><code>template:src=ntp.conf.js dest</code><code>/etc/ntp</code><code>.conf</code>

<code>  </code><code>notify:restart ntp</code>

<code>- name:Start the ntp service</code>

<code>  </code><code>service: name=ntpd state=started enabled=</code><code>true</code>

<code> </code> 

<code>- name: </code><code>test</code> <code>to see </code><code>if</code> <code>selinux is running</code>

<code>  </code><code>command</code><code>:getenforce</code>

<code>  </code><code>register:sestatus</code>

<code>  </code><code>changed_when:</code><code>false</code>

其中template:src=ntp.conf.js引用是唔需要写路径。默认在上级的templates目录中查找。

[templates/ntp.conf.j2]

<code>driftfile </code><code>/var/lib/ntp/drift</code>

<code>restrict 127.0.0.1</code>

<code>restrict -6 ::1</code>

<code>server {{ ntpserver }}</code>

<code>includefile </code><code>/etc/ntp/crypto/pw</code>

<code>keys </code><code>/etc/ntp/keys</code>

此处` ntpserver `将引用vars/main.yml定义的ntpserver变量。

[vars/main.yml]

<code>ntpserver:210.72.145.44</code>

6. 角色WEB的定义

   角色web定义了handlers tasks templates三个功能类  具体如下:

<code>【handlers</code><code>/main</code><code>.yml】</code>

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

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

<code>【tasks</code><code>/main</code><code>.yml】</code>

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

<code>   </code><code>yum: pkg=nginx state=latest</code>

<code> </code><code>- name: write the nginx config </code><code>file</code>

<code>   </code><code>template: src=nginx2.conf dest=</code><code>/etc/nginx/nginx</code><code>.conf</code>

<code>   </code><code>notify:</code>

<code>   </code><code>- restart nginx</code>

<code> </code><code>- name: check nginx is running</code>

<code>   </code><code>service:name=nginx state=started</code>

<code>【template</code><code>/nginx2</code><code>.conf】</code>

<code> </code><code>user    nginx;</code>

<code> </code><code>worker_processes {{ worker_processes }};</code>

<code> </code><code>{% </code><code>if</code> <code>num_cpus == 2 %}</code>

<code> </code><code>worker_cpu_affinity 01 10;</code>

<code> </code><code>{% </code><code>if</code> <code>num_cpus == 4 %}</code>

<code> </code><code>worker_cpu_affinity 1000 0100 0010 0001;</code>

<code> </code><code>{% </code><code>if</code> <code>num_cpus &gt;= 8 %}</code>

<code> </code><code>worker_cpu_affinity 00000001 00000010 00000100 00001000 00010000 00100000 01000000 10000000;</code>

<code> </code><code>{% </code><code>else</code> <code>%}</code>

<code> </code><code>{% endif %}</code>

<code> </code><code>worker_rlimit_nofile {{ max_open_file }};</code>

<code> </code><code>....</code>

7 运行角色

cd /etc/ansible/playbook/nginx

ansible-playbook -i hosts site.yml -f 10

我自己的测试执行结果如下:

<code>[root@localhost nginx]</code><code># ansible-playbook -i hosts site.yml -f 10</code>

<code>PLAY [comment settings] ********************************************************</code>

<code>TASK [common : Install ntp] ****************************************************</code>

<code>TASK [common : write ntp config </code><code>file</code><code>] ******************************************</code>

<code>TASK [common : start ntp server] ***********************************************</code>

<code>TASK [common : check selinux diabled] ******************************************</code>

<code>PLAY [Deploy webserver] ********************************************************</code>

<code>TASK [web : Copy nginx yum repo </code><code>file</code><code>] ******************************************</code>

<code>TASK [web : Install Nginx Web Server] ******************************************</code>

<code>TASK [web : write nginx config] ************************************************</code>

<code>TASK [web : check nginx running] ***********************************************</code>

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

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

<code>[root@localhost nginx]</code><code>#</code>

目录结构和文件内容如下:

<code>├── group_vars</code>

<code>│   └── webserver</code>

<code>│   │   │   └── ntp.conf.j2</code>

<code>│       ├── files</code>

<code>│       │   └── nginx.repo</code>

文件内容如下:

<code>[root@localhost playbook]</code><code># cat nginx/group_vars/webserver</code>

<code>worker_processes: auto</code>

<code>num_cpus: 2</code>

<code>max_open_file: 65536</code>

<code>root: </code><code>/data</code>

<code>[root@localhost playbook]</code><code># cat nginx/hosts</code>

<code>192.168.137.8</code>

<code>192.168.137.9</code>

<code>[root@localhost playbook]</code><code># cat nginx/roles/common/handlers/main.yml</code>

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

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

<code>[root@localhost playbook]</code><code># cat nginx/roles/common/tasks/main.yml</code>

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

<code>  </code><code>yum: name=ntp state=present</code>

<code>- name: write ntp config </code><code>file</code>

<code>  </code><code>template: src=ntp.conf.j2 dest=</code><code>/etc/ntp</code><code>.conf</code>

<code>  </code><code>notify: restart ntp</code>

<code>- name: start ntp server</code>

<code>- name: check selinux diabled</code>

<code>  </code><code>command</code><code>: getenforce</code>

<code>  </code><code>register: sestatus</code>

<code>  </code><code>changed_when: </code><code>false</code>

<code>[root@localhost playbook]</code><code># cat nginx/roles/common/templates/ntp.conf.j2</code>

<code>[root@localhost playbook]</code><code># cat nginx/roles/common/vars/main.yml</code>

<code>ntpserver: ntp.sjtu.edu.cn</code>

<code>[root@localhost playbook]</code><code># cat nginx/roles/web/handlers/main.yml</code>

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

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

<code>[root@localhost playbook]</code><code># cat nginx/roles/web/tasks/main.yml</code>

<code>- name: Copy nginx yum repo </code><code>file</code>

<code>  </code><code>copy: src=</code><code>/root/playbook/nginx/roles/web/files/nginx</code><code>.repo dest=</code><code>/etc/yum</code><code>.repos.d/ owner=root group=root mode=0755</code>

<code>- name: Install Nginx Web Server</code>

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

<code>- name: write nginx config</code>

<code>  </code><code>template: src=nginx2.conf dest=</code><code>/etc/nginx/nginx</code><code>.conf</code>

<code>- name: check nginx running</code>

<code>[root@localhost playbook]</code><code># cat nginx/roles/web/templates/nginx2.conf</code>

<code>user  nginx;</code>

<code>worker_processes {{ worker_processes }};</code>

<code>worker_rlimit_nofile {{ max_open_file }};</code>

<code>error_log  </code><code>/var/log/nginx/error</code><code>.log warn;</code>

<code>pid        </code><code>/var/run/nginx</code><code>.pid;</code>

<code>events {</code>

<code>    </code><code>worker_connections  204800;</code>

<code>    </code><code>use epoll;</code>

<code>    </code><code>multi_accept on;</code>

<code>http {</code>

<code>    </code><code>include       </code><code>/etc/nginx/mime</code><code>.types;</code>

<code>    </code><code>default_type  application</code><code>/octet-stream</code><code>;</code>

<code>    </code><code>log_format  main  </code><code>'$remote_addr - $remote_user [$time_local] "$request" '</code>

<code>                      </code><code>'$status $body_bytes_sent "$http_referer" '</code>

<code>                      </code><code>'"$http_user_agent" "$http_x_forwarded_for"'</code><code>;</code>

<code>    </code><code>access_log  </code><code>/var/log/nginx/access</code><code>.log  main;</code>

<code>    </code><code>sendfile        on;</code>

<code>    </code><code>server_tokens off;</code>

<code>    </code><code>tcp_nopush on;</code>

<code>    </code><code>keepalive_timeout 10;</code>

<code>    </code><code>client_header_timeout 10;</code>

<code>    </code><code>client_body_timeout 10;</code>

<code>    </code><code>reset_timedout_connection on;</code>

<code>    </code><code>send_timeout 10;</code>

<code>    </code><code>#limit_conn_zone $binary_remote_addr zone=addr:10m;</code>

<code>    </code><code>#limit_conn addr 100;</code>

<code>    </code><code>gzip</code> <code>on;</code>

<code>    </code><code>gzip_min_length 1000;</code>

<code>    </code><code>gzip_comp_level 4;</code>

<code>    </code><code>include </code><code>/etc/nginx/conf</code><code>.d/*.conf;</code>

<code>[root@localhost playbook]</code><code># cat nginx/site.yml</code>

<code>- name: comment settings</code>

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

<code>     </code><code>- common</code>

<code>- name: Deploy webserver</code>

<code>  </code><code>hosts: webserver</code>

<code>    </code><code>- web</code>

获取远程主机系统信息:Facts

facts是一个非常有用的组件,类似于Saltstack的Grains功能,实现获取远程主机的系统信息,包括主机名,IP地址,操作系统,分区信息,硬件信息等。可以配合playbook实现更加个性化和灵活的要求。比如在httpd.conf模板中引用Facts的主机名信息作为ServerName参数的值。通过运行  ansible-playbook -m setup 可获取Facts信息 例如 获取192.168.137.8 的Facts信息需要运行

    ansible-playbook 192.168.137.8 -m setup  结果如下:

<code>[root@localhost ~]</code><code># ansible 192.168.137.8 -m setup</code>

<code>    </code><code>"ansible_facts"</code><code>: {</code>

<code>        </code><code>"ansible_all_ipv4_addresses"</code><code>: [</code>

<code>            </code><code>"192.168.137.8"</code>

<code>        </code><code>],</code>

<code>        </code><code>"ansible_all_ipv6_addresses"</code><code>: [</code>

<code>            </code><code>"fe80::20c:29ff:fe7a:596d"</code>

<code>        </code><code>"ansible_architecture"</code><code>: </code><code>"x86_64"</code><code>,</code>

<code>        </code><code>"ansible_bios_date"</code><code>: </code><code>"05/20/2014"</code><code>,</code>

<code>        </code><code>"ansible_bios_version"</code><code>: </code><code>"6.00"</code><code>,</code>

<code>        </code><code>"ansible_cmdline"</code><code>: {</code>

<code>            </code><code>"KEYBOARDTYPE"</code><code>: </code><code>"pc"</code><code>,</code>

<code>            </code><code>"KEYTABLE"</code><code>: </code><code>"us"</code><code>,</code>

<code>            </code><code>"LANG"</code><code>: </code><code>"en_US.UTF-8"</code><code>,</code>

<code>            </code><code>"SYSFONT"</code><code>: </code><code>"latarcyrheb-sun16"</code><code>,</code>

<code>            </code><code>"crashkernel"</code><code>: </code><code>"auto"</code><code>,</code>

<code>            </code><code>"quiet"</code><code>: </code><code>true</code><code>,</code>

<code>            </code><code>"rd_LVM_LV"</code><code>: </code><code>"vg_serv/lv_root"</code><code>,</code>

<code>            </code><code>"rd_NO_DM"</code><code>: </code><code>true</code><code>,</code>

<code>            </code><code>"rd_NO_LUKS"</code><code>: </code><code>true</code><code>,</code>

<code>            </code><code>"rd_NO_MD"</code><code>: </code><code>true</code><code>,</code>

<code>            </code><code>"rhgb"</code><code>: </code><code>true</code><code>,</code>

<code>            </code><code>"ro"</code><code>: </code><code>true</code><code>,</code>

<code>            </code><code>"root"</code><code>: </code><code>"/dev/mapper/vg_serv-lv_root"</code>

<code>        </code><code>},</code>

<code>        </code><code>"ansible_date_time"</code><code>: {</code>

<code>            </code><code>"date"</code><code>: </code><code>"2016-07-26"</code><code>,</code>

<code>            </code><code>"day"</code><code>: </code><code>"26"</code><code>,</code>

<code>            </code><code>"epoch"</code><code>: </code><code>"1469546980"</code><code>,</code>

<code>            </code><code>"hour"</code><code>: </code><code>"23"</code><code>,</code>

<code>            </code><code>"iso8601"</code><code>: </code><code>"2016-07-26T15:29:40Z"</code><code>,</code>

<code>            </code><code>"iso8601_basic"</code><code>: </code><code>"20160726T232940834874"</code><code>,</code>

<code>            </code><code>"iso8601_basic_short"</code><code>: </code><code>"20160726T232940"</code><code>,</code>

<code>            </code><code>"iso8601_micro"</code><code>: </code><code>"2016-07-26T15:29:40.835071Z"</code><code>,</code>

<code>            </code><code>"minute"</code><code>: </code><code>"29"</code><code>,</code>

<code>            </code><code>"month"</code><code>: </code><code>"07"</code><code>,</code>

<code>            </code><code>"second"</code><code>: </code><code>"40"</code><code>,</code>

<code>            </code><code>"time"</code><code>: </code><code>"23:29:40"</code><code>,</code>

<code>            </code><code>"tz"</code><code>: </code><code>"CST"</code><code>,</code>

<code>            </code><code>"tz_offset"</code><code>: </code><code>"+0800"</code><code>,</code>

<code>            </code><code>"weekday"</code><code>: </code><code>"Tuesday"</code><code>,</code>

<code>            </code><code>"weekday_number"</code><code>: </code><code>"2"</code><code>,</code>

<code>            </code><code>"weeknumber"</code><code>: </code><code>"30"</code><code>,</code>

<code>            </code><code>"year"</code><code>: </code><code>"2016"</code>

<code>        </code><code>"ansible_default_ipv4"</code><code>: {</code>

<code>            </code><code>"address"</code><code>: </code><code>"192.168.137.8"</code><code>,</code>

<code>            </code><code>"alias"</code><code>: </code><code>"eth1"</code><code>,</code>

<code>            </code><code>"broadcast"</code><code>: </code><code>"192.168.137.255"</code><code>,</code>

<code>            </code><code>"gateway"</code><code>: </code><code>"192.168.137.1"</code><code>,</code>

<code>            </code><code>"interface"</code><code>: </code><code>"eth1"</code><code>,</code>

<code>            </code><code>"macaddress"</code><code>: </code><code>"00:0c:29:7a:59:6d"</code><code>,</code>

<code>            </code><code>"mtu"</code><code>: 1500,</code>

<code>            </code><code>"netmask"</code><code>: </code><code>"255.255.255.0"</code><code>,</code>

<code>            </code><code>"network"</code><code>: </code><code>"192.168.137.0"</code><code>,</code>

<code>            </code><code>"type"</code><code>: </code><code>"ether"</code>

<code>.......................省略</code>

在模板文件中这样引用Facts信息

` ansible_device`.`sda`.`model `

` ansible_hostname `

Jinja2过滤器

使用格式:{{ 变量名 | 过滤方法 }}

示例:获取一个文件路径变量过滤出文件名

{{ path | basename }}

获取文件所处的目录名:

{{ path | dirname }}

下面为一个完整的示例,实现从/etc/profile 中过滤出文件名 profile  并输出重定向到/tmp/testshell文件中

<code>- hosts: 192.168.1.21</code>

<code>      </code><code>filename: </code><code>/etc/profile</code>

<code>     </code><code>- name: </code><code>"shell"</code>

<code>       </code><code>shell: </code><code>echo</code> <code>{{ filename | </code><code>basename</code> <code>}} &gt;&gt; </code><code>/tmp/testshell</code>

本地Facts

我们可以通过Facts来获取目标主机的系统信息,当这些信息不能满足我们功能需求时,可以通过编写自定义的facts模块来实现。当然 还有一个更简单的办法 就是通过本地facts来实现。 只需要在目标设备/etc/ansible/facts.d目录定义JSON、INI或可执行文件的JSON输出 文件扩展名使用“.fact” 这些文件都可以作为Ansible本地Facts 例如 在目标设备 192.168.137.9 定义三个变量  供以后playbook使用

【/etc/ansible/facts.d/preferences.fact】

[general]

max_memory_size=32

max_user_processes=3730

open_files=65535

在主控端运行ansible 192.168.1.21 -m setup -a "filter=ansible_local" 可以看到定义的结果 返回结果如下:

<code>[root@localhost ~]</code><code># ansible 192.168.137.9 -m setup -a "filter=ansible_local"</code>

<code>        </code><code>"ansible_local"</code><code>: {</code>

<code>            </code><code>"preferences"</code><code>: {</code>

<code>                </code><code>"general"</code><code>: {</code>

<code>                    </code><code>"max_memory_size"</code><code>: </code><code>"32"</code><code>,</code>

<code>                    </code><code>"max_user_processes"</code><code>: </code><code>"3730"</code><code>,</code>

<code>                    </code><code>"open_files"</code><code>: </code><code>"65535"</code>

<code>                </code><code>}</code>

<code>            </code><code>}</code>

<code>        </code><code>}</code>

<code>    </code><code>},</code>

<code>    </code><code>"changed"</code><code>: </code><code>false</code>

<code>[root@localhost ~]</code><code>#</code>

注册变量:

变量的另一个用途是将一条命令的运行结果保存到变量中,供后面的playbook使用 下面是一个简单的示例

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

<code>      </code><code>register: foo_result</code>

<code>      </code><code>ignore_errors:True</code>

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

<code>      </code><code>when: foo_result.rc == 5</code>

上述例子注册了一个foo_result的变量 值为shell:/usr/bin/foo的运行结果。ingore_error为忽略错误。变量注册完成后。就可以在后面的playbook中使用了。当条件语句when:foo_result.rc == 5成立时 shell:/usr/bin/bar命令才会运行,其中foo_result.rc为返回/usr/bin/foo的resultcode 返回码  例如 返回rc=0的返回码

<code>[root@localhost ~]</code><code># ansible 192.168.137.9 -a "free -m"</code>

<code>192.168.137.9 | SUCCESS | rc=0 &gt;&gt;</code>

<code>             </code><code>total       used       </code><code>free</code>     <code>shared    buffers     cached</code>

<code>Mem:          1989        601       1387          0         63        252</code>

<code>-/+ buffers</code><code>/cache</code><code>:        285       1704</code>

<code>Swap:         2015          0       2015</code>

条件语句:

有时候一个playbook的结果取决于一个变量  或者取决于上一个任务的执行结果.在某些情况下 一个变量的值可以依赖于其他变量的值 当然也会影响Ansible的执行过程。

下面主要介绍when声明:

有时候我们想跳过某些主机的执行步骤 比如符合特定版本的操作系统将不安装某个软件。在Ansible中很容易做到这一点 通过when 子句实现  其中将引用jinja2表达式 如下示例:

<code>  </code><code>- name: </code><code>"shutdown Debian flavored systems"</code>

<code>    </code><code>command</code><code>: </code><code>/sbin/shutdown</code> <code>-t now</code>

<code>    </code><code>when: ansible_os_family == </code><code>"Debian"</code>

通过定义任务的facts本地变量 ansible_os_family 操作系统版本名称是否为Debian 结果将返回BOOL类型值,为True时将执行上一条语句 command:/sbin/shutdown -t now 为False时该条语句都不会触发 。看另一个示例 通过判断一条命令执行结果做不同的处理。

<code>   </code><code>- </code><code>command</code><code>: </code><code>/bin/false</code>

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

<code>     </code><code>ingore_error: True</code>

<code>   </code><code>- </code><code>command</code><code>: </code><code>/bin/someting</code>

<code>     </code><code>when: result|failed</code>

<code>   </code><code>- </code><code>command</code><code>: </code><code>/bin/someting_else</code>

<code>     </code><code>when: result|success</code>

<code>   </code><code>- </code><code>command</code><code>: </code><code>/bin/still/someting</code>

<code>     </code><code>when: result|skipped</code>

when:result|success的意思当变量result的执行结果为成功状态时 将执行/bin/someting_else命令 其他同理。其中success为ansible内部过滤器方法 返回True代表运行成功。

循环:

通常一个任务会做很多事情,例如创建大量的用户,安装很多包 或者重复特定步骤 直到某种结果条件为止.ansible为我们提供了此支持。示例如下:

<code>- name: add several </code><code>users</code>

<code>  </code><code>user: name={{ item }} state=present </code><code>groups</code><code>=wheel</code>

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

<code>    </code><code>- testuser1</code>

<code>    </code><code>- testuser2</code>

这个示例实现了一个批量创建用户的功能 with_items会自动循环执行上面的语句 user: name=` item ` state=present groups=wheel 循环次数为with_items的元素个数。这里有两个元素 分别是testuser1 和 testuser2  会分别替换 ` item `项 这个示例与下面的语句是等价的:

<code>- name: add user testuser1</code>

<code>  </code><code>user: name=testuser1 state=present </code><code>groups</code><code>=wheel</code>

<code>- name: add user testuser2</code>

<code>  </code><code>user: name=testuser2 state=present </code><code>groups</code><code>=wheel</code>

当然 元素也支持字典的形式 如下:

<code>  </code><code>user: name={{ item.name }} state=present </code><code>groups</code><code>={{ item.</code><code>groups</code> <code>}}</code>

<code>     </code><code>- { name: </code><code>'testuser1'</code><code>,</code><code>groups</code><code>:</code><code>'wheel'</code><code>}</code>

<code>     </code><code>- { name: </code><code>'testuser2'</code><code>,</code><code>groups</code><code>:</code><code>'root'</code><code>}</code>

循环也支持list形式 不过是通过with_flattened语句实现 如下示例:

<code>packages_base:</code>

<code>    </code><code>- [ </code><code>'foo-packages'</code><code>,</code><code>'bar-packages'</code><code>]</code>

<code>packages_apps:</code>

<code>    </code><code>- [ [</code><code>'one-packages'</code><code>,</code><code>'two-packages'</code><code>] ]</code>

<code>    </code><code>- [ [</code><code>'red-packages'</code><code>],[</code><code>'blue-packages'</code><code>] ]</code>

以上定义了两个列表变量,分别是需要安装的软件包名 以便后面如下引用:

<code>- name: flattened loop demo</code>

<code>  </code><code>yum: name={{ item }} state=installed</code>

<code>  </code><code>with_flattened:</code>

<code>      </code><code>- packages_base</code>

<code>      </code><code>- packages_apps</code>

<code></code>

      本文转自flyingzf  51CTO博客,原文链接:http://blog.51cto.com/flyingzf/1828641,如需转载请自行联系原作者

继续阅读