playbook:
playbook 由一个或多个 ‘plays’ 组成.它的内容是一个以 ‘plays’ 为元素的列表.
在 play 之中,一组机器被映射为定义好的角色.在 ansible 中,play 的内容,被称为 tasks,即任务.在基本层次的应用中,一个任务是一个对 ansible 模块的调用
Playbooks 的格式是YAML,语法做到最小化. 首先来介绍下YAML语法
YAML:
每一个 YAML 文件都是从一个列表开始. 列表中的每一项都是一个键值对, 通常它们被称为一个 “哈希” 或 “字典”. 所以, 我们需要知道如何在 YAML 中编写列表和字典. YAML 还有一个比较奇怪的地方. 所有的 YAML 文件(无论和 Ansible 有没有关系)开始行都应该是 ---. 这是 YAML 格式的一部分, 表明一个文件的开始.
列表中的所有成员都开始于相同的缩进级别, 并且使用一个 "- " 作为开头(一个横杠和一个空格):
---
#一个关于变成语言的列表
- python
- c++
- java
对应的Python 结果为:[‘python’,’c++’,’java’]
再来看一个块结构的表示方法
-
- python
- c++
- java
-
- mysql
- mongodb
对应的python结果:
[[‘python’,’c++’,’java’],[‘mysql’,’mongodb’]]
一个字典是由一个简单的 键: 值 的形式组成(这个冒号后面必须是一个空格):
---
# 一个出版书籍的记录
publish:
name: Publisher
city: chengdu
province: sichuan
author:
name: zhf
age: 32
对应的python结果:
{‘publish’:{‘name’: ’Publisher’,’city’: ’chengdu’,’province’: ’sichuan’},’author’:{‘name’: ‘zhf’,’age’: 32}}
字典也可以使用缩进形式来表示, 如果你喜欢这样的话:
{name: Publisher,city: chengdu,province: sichuan}
从上面这些可以看出,YAML其实和JSON的格式很接近
回到playbook我们来看下tasks列表
每一个 play 包含了一个 task 列表(任务列表).一个 task 在其所对应的所有主机上(通过 host pattern 匹配的所有主机)执行完毕之后,下一个 task 才会执行.有一点需要明白的是(很重要),在一个 play 之中,所有 hosts 会获取相同的任务指令,这是 play 的一个目的所在,也就是将一组选出的 hosts 映射到 task
在运行 playbook 时(从上到下执行),如果一个 host 执行 task 失败,这个 host 将会从整个 playbook 的 rotation 中移除. 如果发生执行失败的情况,请修正 playbook 中的错误,然后重新执行即可.
下面我们来看一个task的具体例子:
---
- hosts: webservers
remote_user: root
tasks:
- name: for playbook test
apt: name=curl state=latest
在这个例子中,host的地址为webservers中定义的主机IP。remote_user: root代表已root用户执行
tasks:就是具体的任务 其中name为任务的名称, apt 就是ansible的远程命令模块 其中包含2个参数。name=curl代表要下载的软件名称,state=latest表示下载最新的
运行结果如下:任务成功执行并且在192.168.0.9上安装了curl软件包。其中TASK[for playbook test]就是我们这个任务的名称
root@zhf-linux:/home/zhf/zhf/python_prj/auto_manintance# ansible-playbook test.yml -f 10
PLAY***************************************************************************
TASK[setup]*******************************************************************
ok:[192.168.0.9]
TASK[for playbook test]*******************************************************
changed:[192.168.0.9]
PLAYRECAP*********************************************************************
192.168.0.9 : ok=2 changed=1 unreachable=0 failed=0
我们再来看另外一个例子:在这个yml文件中。task中采用了command模块。command模块和shell模块比较特殊,它们不使用key=value的格式而是直接使用命令。但是还是带有自有的一些参数。比如chdi/home/zhf/zhf就是指定切换到的目录。后面直接跟上rmtcpdumpresult.txt的命令删除文件
---
-hosts: webservers
remote_user:root
tasks:
-name: for playbook test
command:chdir=/home/zhf/zhf rm tcpdumpresult.txt
运行结果:
PLAY***************************************************************************
TASK[setup]*******************************************************************
ok:[192.168.0.9]
TASK[for playbook test]*******************************************************
changed:[192.168.0.9]
[WARNING]:Consider using file module with state=absent rather than running rm
PLAYRECAP*********************************************************************
192.168.0.9 : ok=2 changed=1 unreachable=0 failed=0
在这个提示中,有一个warning:Considerusing file module with state=absent rather than running rm
这个的意思是让我们使用file模块然后设置state=absent就可以删除文件。如果改成file模块使用的话yaml改成如下:
file:path=/home/zhf/zhf state=absent
我们再看一个template的模块调用方法。在做很多功能配置的时候,涉及到大量的配置文件的修改和替换。template就是用来完成这一功能的。官方翻译:template使用了Jinjia2格式作为文件模版,进行文档内变量的替换的模块。它的每次使用都会被ansible标记为”changed”状态。
例子:这个讲实现将本端的test1.yml文件内容渲染到远端主机的test1.yml文件中去
---
- hosts: webservers
remote_user: root
tasks:
- name: use template function
template: src=/home/zhf/zhf/python_prj/auto_manintance/test1.yml dst=/home/zhf/zhf/test1.yml
其实在task中都是调用ansible中的命令模块来触发操作的。ansible有很多模块。如果在主机上查询的花可以使用ansible-doc-s file的方式进行查询。ansible-doc是查询命令。-s后面接的是模块命令
或者也可以到网上查询网址是:http://docs.ansible.com/ansible/latest/modules_by_category.html
这里面有所有的ansible命令介绍。
在YAML中还可以使用变量。通过vars:定义变量。在具体使用变量的过程中两个双括号包含变量。例子如下:
---
-hosts: webservers
vars:
filename:tcpdumpresult.txt
remote_user:root
tasks:
-name: for playbook test
command:chdir=/home/zhf/zhf rm {{ filename }}
下面将介绍下handlers;
当我们再完成一些服务配置的时候往往会重启服务或者进行恢复。所以当远端系统被人改动时,可以重放 playbooks 达到恢复的目的。当发生改动时’notify’ actions 会在 playbook 的每一个 task 结束时被触发
下面的这个例子是在安装vsftpd后重启动这个服务。类似的只要执行结果中changed=1的都会触发notify操作。
---
- hosts: webservers
remote_user: root
tasks:
- name: for notify function test
apt: name=vsftpd state=latest
notify:
- restart vsftpd
include语句和角色:
首先来看下include语句,在用playbook实现一个很复杂的功能的时候,有可能会写一个很大的文件。而且这些文件的某些task在其他yml文件中也是被重用的。这样就没必要在每个yml中都去实现这些功能,这样效率也很低,我们应该想在编写软件代码的时候去组织这些文件。这里就需要用到include语句。include可以引用其他yml文件中的play。这种方式也被称为封装,将某些具体的功能给封装起来。我们来看一个例子,以前面删除文件的play来做示例。
首先来看test.yml。在task里面没有实现具体的操作。而是包含了test1.yml文件
---
-hosts: webservers
remote_user:root
tasks:
-name: to verify include function
include:test1.yml
再来看下test1.yml文件。里面没有了hosts和remote_user等配置,而只是由普通的task列表组成。
---
-name: test1.yml to delete the file
command:rm /home/zhf/test2.txt
来看下执行结果:
root@zhf-linux:/home/zhf/zhf/python_prj/auto_manintance#ansible-playbook test.yml -f 10
PLAY***************************************************************************
TASK[setup]*******************************************************************
ok:[192.168.0.9]
TASK[to verify include function]**********************************************
included:/home/zhf/zhf/python_prj/auto_manintance/test1.yml for 192.168.0.9
TASK[test1.yml to delete the file]********************************************
changed:[192.168.0.9]
[WARNING]:Consider using file module with state=absent rather than running rm
PLAYRECAP*********************************************************************
192.168.0.9 : ok=3 changed=1 unreachable=0 failed=0
从task的name来看,先执行test.yml.然后调用test1.yml文件执行
我们在include的时候还可以往里面传递参数
tasks:
-name: to verify include function
include:test1.yml path=/home/zhf/
然后在test1.yml中通过{{path }}来使用path.同样的你也可以在handler中去引用文件
notify:
-include: test1.yml
Role(角色):
现在已经学过 tasks和 handlers,那怎样组织playbook才是最好的方式呢?简单的回答就是:使用roles! Roles 基于一个已知的文件结构,去自动的加载某些vars_files,tasks以及 handlers。基于roles对内容进行分组,使得我们可以容易地与其他用户分享roles。
我们来看一个例子:在一个工程里面有2个角色。一个是dele一个是添加。分别实现删除文件和添加文件。我们建立如下文件目录:
1group_vars: 其中定义全局变量,所有的角色都可以使用
2hosts定义主机的参数,否则默认使用/etc/ansible/hosts的参数。自定义参数通过ansible-playbook-i hosts来调用
3role: 角色文件夹。其中包含2个角色add和dele.在个2角色下分别有handlers,tasks,templates和vars文件夹。里面包含了各自的yml文件。角色中的vasrs里面的变量优先级高于group_vars中的变量
4test.yml为整个程序的入口。从test.yml中引用具体的角色
一 首先我们来定义hosts文件。在里面添加主机IP
[webservers]
192.168.0.9
二 全局配置文件 test.yml. 在里面定义了2个角色分别是add和dele. 将分别对这个2角色进行调用
---
- name: add function for role test
hosts: webservers
roles:
- add
- name: dele function for dele test
hosts: webservers
roles:
- dele
三 角色add的定义:
[handers/main.yml]
- name: restart ftp
service: name=vsftpd state=restarted
[tasks/main.yml]
首先新建一个ftp_confg.conf文件。然后通过template命令将本段的ftp配置渲染到主机上的新建的文件
其中template中的src不用制定路径,默认在上级的templates目录中去寻找.这里有一点很奇怪。template和command不能写在同一个任务下面。否则会报错。提示语法错误
-name: add the configration of FTP
command:chdir=/home/zhf touch ftp_config.conf.j2
-name: to template the file
template:src=ftp.conf.j2 dest=/home/zhf/ftp_config.conf.j2
[vars/main.yml]
定义FTP服务主机IP
---
ftpserver:192.168.0.9
定义模板文件
[templates/ftp.conf.j2]
server{{ ftpserver }}
四 角色dele的定义:
task中删除掉之前建立的ftp_config.conf.j2
[task/main.yml]
-name: dele the configration of FTP
command:chdir=/home/zhf rm ftp_config.conf.j2
[handlers/main.yml]
-name: after dele the conf then restart FTP
service:name=vsftpd state=restarted
运行结果如下:
root@zhf-linux:/home/zhf/zhf/python_prj/role_test#ansible-playbook -i hosts test.yml -f 10
PLAY[add function for role test]**********************************************
TASK[setup]*******************************************************************
ok:[192.168.0.9]
TASK[add : add the configration of FTP]***************************************
changed:[192.168.0.9]
[WARNING]:Consider using file module with state=touch rather than running
touch
TASK[add : to template the file]**********************************************
changed:[192.168.0.9]
PLAY[dele function for dele test]*********************************************
TASK[setup]*******************************************************************
ok:[192.168.0.9]
TASK[dele : dele the configration of FTP]*************************************
changed:[192.168.0.9]
[WARNING]:Consider using file module with state=absent rather than running rm
PLAYRECAP*********************************************************************
192.168.0.9 : ok=5 changed=3 unreachable=0 failed=0
最后来介绍下playbook的循环和条件执行方法:
通常一个任务会做很多事情。如创建大量的用户和文件。安装很多包等。通过with_items方法可以帮助我们循环执行任务。参考下面的例子:
command:chdir=/home/zhf touch {{ item }}.txt这句中的item来自与with_items中的变量分别是test4和test5
root@zhf-linux:/home/zhf/zhf/python_prj/auto_manintance#cat test2.yml
---
-hosts: webservers
remote_user:root
tasks:
-name: create file by batch
command:chdir=/home/zhf touch {{ item }}.txt
with_items:
-test4
-test5
这个示例与下面是等价的:
-name: create file by batch
command:chdir=/home/zhf touch test4.txt
-name: create file by batch
command:chdir=/home/zhf touch test5.txt
另外还可以嵌套循环:
-name: create file by batch
command:chdir=/home/zhf touch {{ item[0] }}.txt rm {{ item[1] }}
with_nested:
-[‘test3’,’test4’]
-[‘test5’]
对哈希使用循环:
假如你有以下变量:
---
users:
alice:
name: Alice Appleworth
telephone: 123-456-7890
bob:
name: Bob Bananarama
telephone: 987-654-3210
你想打印出每个用户的名称和电话号码.你可以使用 with_dict
来循环哈希表中的元素:
tasks:
- name: Print phone records
debug: msg="User {{ item.key }} is {{ item.value.name }} ({{ item.value.telephone }})"
with_dict: "{{users}}"
对文件列表使用循环:
---
- hosts: all
tasks:
# first ensure our target directory exists
- file: dest=/etc/fooapp state=directory
# copy each file over that matches the given pattern
- copy: src={{ item }} dest=/etc/fooapp/ owner=root mode=600
with_fileglob:
- /playbooks/files/fooapp/*
其他循环方式可以参考:http://ansible-tran.readthedocs.io/en/latest/docs/playbooks_loops.html#looping-over-hashes