1、Playbook的介绍php
Playbook是Ansible的配置,部署,编排语言。他们能够被描述为一个须要但愿远程主机执行命令的方案,或者一组IT程序运行的命令集合。mysql
当执行一些简单的改动时ansible命令是很是有用的,然而它真的做用在于它的脚本能力。当对一台机器作环境初始化的时候每每须要不止作一件事情,这时使用playbook会更加适合。经过playbook你能够一次在多台机器执行多个指令。经过这种预先设计的配置保持了机器的配置统一,并很简单的执行平常任务。linux
Playbook还开创了不少特性,它能够容许你传输某个命令的状态到后面的指令,如你能够从一台机器的文件中抓取内容并附为变量,而后在另外一台机器中使用,这使得你能够实现一些复杂的部署机制,这是ansible命令没法实现的。nginx
2、YAML介绍web
Ansible使用标准的YAML解析器,使用YAML文件语法便可书写playbook。sql
YAML是一个可读性高的用来表达资料序列的格式,YAML参考了其余多种语言,包括:XML、C语言、Python、Perl以及电子邮件格式RFC2822等。Clark Evans在2001首次发表了这种语言。shell
Playbook组成结构apache
Inventory #定义管理主机(清单文件)centos
Modules #定义模块安全
Ad Hot Commands
Playbooks
Variables #变量元素,可传递给Tasks/Templates使用;
Tasks #任务元素,即调用模块完成任务;
Templates #模板元素,可根据变量动态生成配置文件;
Hadlers #处理器元素,一般指在某条件知足时触发的操做;
Roles #角色元素
执行一个Playbook
使用Playbook时经过ansible-playbook命令使用,它的参数和ansible命令相似,如参数-k(–ask-pass) 和-K (–ask-sudo) 来询问ssh密码和sudo密码,-u指定用户,这些指令也能够经过规定的单元写在playbook里。ansible-playbook的简单使用方法:
ansible-playbook /etc/ansible/roles/sites.yml
也能够并行执行playbook,这里的示例是并行的运行playbook,并行的级别是10个进程:
ansible-playbook /etc/ansible/roles/sites.yml -f 10
playbook的写法例子:
分别讲解playbook语言的多个特性:
2、Playbook基础组件
主机(hosts)和用户(users)
Playbook中的每个play的目的都是为了让某个或某些主机以某个指定的用户的身份执行任务,hosts用于指定要执行任务的主机,其能够是一个或者多个有冒号分割主机组,这和前面的ansible命令提到的hosts使用同样的语法,remote_user则用于指定远程主机上的执行任务的用户,如上面实例中:
不过,remote_user也能够用于task中:
- hosts: web
remote_user: root
tasks:
- name: test connection
ping:
remote_user: yourname
也能够经过指定其经过sudo的方式远程主机上执行任务,其可用于play全局或某task,此外,甚至能够在sudo时使用sudo_user指定sudo时切换的用户。
- hosts: web
remote_user: root
sudo:yes
sudo_user:yourname
若是须要在使用sudo时指定密码,可在运行ansible-playbook命令时加上--ask-sudo-pass(-K).若是使用sudo时,playbook疑似被挂起,多是在sudo prompt处被卡主了,这时可执行Control-C杀死卡住的任务,从新运行一次。
Tasks列表
play的主体部分是task列表,task列表中的各任务按次序逐个在hosts中指定的主机上执行,即在全部主机上完成第一个任务后再开始第二个任务,在运行playbook时(从上到下执行),若是一个hosts执行task失败,这个host将会从整个playbook的rotation中移除,若是发生执行失败的状况,请修正playbook中的错误,而后从新执行便可。
Task的目的是使用指定的参数执行模块,而在模块参数中可使用变量,模块执行是幂等的,这意味着屡次执行是安全的,由于其结果一致。
对于command 模块和shell 模块,重复执行playbook,其实是重复运行一样的命令。若是执行的命令相似于‘chmod’或者‘setsebool’这种命令,这没有任何问题,也可使用一个叫作‘creates’的flag使得这两个module变得具备‘幂等’特性(不是必要的。)
每个task必须有一个名称name,这样在运行playbook时,从其输出的任务信息中能够很好的辨别出是属于哪个task的,若是没有定义name,‘action’的值将会用做输出信息中的标记特定的task。
定义一个task,之前有一种格式:‘action:module options’ (可能在一些老的playbook中还能见到),如今推荐使用常见的格式:“module:options”,本文档使用的就是这种格式。
- hosts: web
vars: #定义变量
http_port: 80
max_client: 200 #定义变量是使用两个花括号括起来,中间最好有空格,{{ http_port }}
remote_user: root
task:
- name: ensure apache is at the latest version
yum: name=httpd state=latest
意思是:针对web组执行一个task使用root用户执行,而这个task任务是“ensure apache is at the latest version”,而后调用yum模块进行httpd的检测,这里的模块参数的含义都跟前面说的经常使用模块介绍时是同样的,详情查看前面的经常使用模块介绍
在众多模块中,只有command和shell模块仅须要给定一个列表无需使用key-value格式,例如:
tasks:
- name: disable selinux
command: /sbin/setenforce 0
在使用command和shell模块时,咱们须要关心返回码信息,若是有一条命令,它的成功执行的返回码不是0(ansible会终止运行),咱们可使用以下方式替代,强制返回成功:
tasks:
- name: run this command and ignore the result
shell: /usr/sbin/somecommand || /bin/true
或者使用ignore_error来忽略错误信息:
tasks:
- name: run this command and ignore the result
shell: /usr/bin/somecommand
ignore_errors: True
变量(variables)和模板(template)
在Playbook中能够直接定义变量(固然前面咱们也在inventory中添加变量),以下所示:
- hosts: web
vars:
- package:httpd
其中vars是固定格式,而package就是变量名,httpd就是变量值。
定义变量名的时候注意一些规范,变量名能够是字母、下划线、数字,但必须是以字母开头,变量名包含中线、空格、点和以数字开头的变量名都是不规范的。
对于定义好的变量能够在task或者template中使用,但通常playbook中定义的变量都是给模板使用的,Ansible使用jijia2模板框架,这个后面讲,如今只须要知道变量能够在task和template中使用便可,以下在task中调用变量:
tasks:
- name: ensure apache is at the lastest version
yum: name={{ package }} state=latest
在task或template中引用变量都是使用双花括号,中间引入变量名便可,如:{{ VAR_NAME }}.
另外说明,在playbook中调用的变量不只仅是在play中自定义的变量,也能够是ansible中所定义的全部变量,好比说在ansible中有一个setup模块,就是用来获取客户端全部的信息,如内存、CPU、IP、主机名、磁盘等信息,这些信息都是key-value格式的,每当咱们执行playbook时首先会须要执行setup模块,也就意味着task或template中引用setup模块输出的变量。如:ansible_all_ipv4_addresses均可以在task或者template使用{{}}去引用。
引用变量的方法除了上述的方式外,还能够在hosts中定义变量,此外还能够在命令行使用ansible-playbook -e 指定变量 playbook.yml,例如:
ansible-playbook -e http_port=80 ceshi.yml
最后能够在roles中定义变量
Handlers和notify
Handlers其实挺好理解的就是发生改变时执行提早定义好的操做。
上面咱们提到过,模块具备幂等性,因此当远端系统被人改动时,能够重放playbook达到恢复的目的,playbook自己能够识别这种改动,而且有一个基本的event system(系统事件),能够响应这种改动。
(当发生改动时)'notify' 动做会在playbook的每个task结束时被触发,并且即便有多个不一样的task通知改动的发生'notify' 动做只会触发一次。
这里有一个例子,当一个文件的内容被改动时,重启两个services:
- name: write the apache config file
copy: src=/srv/httpd.conf dest=/etc/httpd/conf/httpd.conf
notify:
- restart httpd
"notify"下列出的便是handlers。
handlers也是一些task的列表,经过名字来引用,它们和通常的task并无什么区别,Handlers是由通知者进行notify,若是没有被notify,handlers不会执行,无论有多少个通知者进行了notify,等到play中的全部task执行完成以后,handlers也只会被执行一次。
这里是一个handlers的示例:
handlers:
- name: restart apache #这里和notify保持一致
service: name=httpd state=restarted
下面给一个安装服务而后使用Handlers的例子:
- hosts:web
remote_user:root
task:
- name: ensure apache is at the latest version
yum: name=httpd state=latest
- name: write the apache config file
copy: src=/srv/httpd.conf dest=/etc/httpd/conf/httpd.conf
- name: ensure apache is running
service: name=httpd state=started
这个play就作了三个task,第一是检测httpd是不是最新版,若是不是就进行安装,第二就是从服务器copy一份定义好的配置文件到目标主机;第三就是启动httpd服务器。
首先须要在ansible服务器上提供一个/srv/httpd.conf配置文件,否则执行时就会报错,而后使用ansible-playbook执行。
ansible-playbook sites.yml
若是修改了配置文件,是否是须要reload或restart后你的配置文件才能生效。可是上面的配置就算你改完 配置文件再次执行playbook,配置文件虽然过去了,可是ansible幂等性,配置文件是没法生效的,而handlers就是为了解决此类问题而生的,咱们能够给copy文件的那个task定义一个handlers,一旦由配置文件改动就调用提早定义的handlers进行处理,以下:
- hosts: test
remote_user: root
tasks:
- name: ensure apache is at the latest version
yum: name=httpd state=latest
- name: write the apache config file
copy: src=/srv/httpd.conf dest=/etc/httpd/conf/httpd.conf
notify:
- restart httpd
- name: ensure apache is running
service: name=httpd state=started
handlers:
- name: restart httpd
service: name=httpd state=restarted
这个时候再次修改配置文件而后执行playbook,当执行到第二个任务后检测到定义了notify,当整个play的task都执行完毕后,就会找handlers,匹配到名称后就会执行对应的模块。
注意:notify定义的restart httpd 必须与handlers定义的name名称相同,否则会报:not found in any of the known handlers
Register
常常在playbook中,存储某个命令的结果在变量中,以备往后访问是颇有用的,而‘register’就是用来决定把结果存储在哪一个变量中,结果参数能够用在模板中,动做条目,或者when语句,像下面的例子:
- name: test play
hosts: all
tasks:
- shell: cat /etc/motd
register: motd_contents
- shell: echo "motd contains the word hi"
when: motd_contents.stdout.find('hi') != -1
就像上面展现的那样,这个注册后的参数的内容为字符串’stdout’是能够访问。这个注册了之后的结果,若是像上面展现的,能够转化为一个list(或者已是一个list),就能够在任务中的”with_items”中使用,“stdout_lines”在对象中已经能够访问了,固然若是你喜欢也能够调用“home_dirs.stdout.split()” , 也能够用其它字段切割:
- name: registered variable usage as a with_items list
hosts: all
tasks:
- name: retrieve the list of home directories
command: ls /home
register: home_dirs
- name: add home dirs to the backup spooler
file: path=/mnt/bkspool/{{ item }} src=/home/{{ item }} state=link
with_items: home_dirs.stdout_lines
# same as with_items: home_dirs.stdout.split()
提示与技巧:
在playbook执行输出信息的底部,能够找到关于托管节点的信息。
其中:
ok:表示执行成功但没作过任何变更的任务;
changed:表示执行成功但作过变更的任务;
unreachable:表示没法到达的任务;
failed:表示执行失败的任务。
若是你想看到执行成功的模块输出信息,使用--verbose便可,(不然只有执行失败的才会由输出信息)
ansible-playbook paaybook.yml --verbose
若是安装了cowsay软件包,ansible playbook的输出已经进行普遍的升级,能够尝试一下!
在执行一个playbook以前,想看看这个playbook的执行会影响到哪些hosts,你能够这样作:
ansible-playbook paybook.yml --list-hosts
在执行一个playbook以前,想看看这个playbook是否有语法错误,能够这样作:
ansible-playbook -C playbook.yml 或
ansible-playbook playbook.yml --check 这样不会执行结果,只会查看playbook是否有语法错误。
3、Playbook的其它特性
条件测试
若是须要根据变量、facts(setup)或此前任务的执行结果来做为某task执行与否的前提时要用到条件测试,在Playbook中条件测试使用when子句,在task后添加when子句便可使用条件测试:when子句支持jinjia2表达式或句法,例如:
tasks:
- name: "shutdown Debian"
command: /sbin/shutdown -h now
when: ansible_os_family == "Debian"
或者多条件判断:
tasks:
- name: “shutdown Centos6 system”
command: /sbin/shutdown -t now
when:
- ansible_distribution == "Centos"
- ansible_distribution_major_version == "6"
意思就是若是对方的系统是centos而且版本是6就进行关机处理,ansible_os_family这个变量是从facts中提取的。
也可使用组名条件判断:
tasks:
- name: "shut down CentOS 6 and Debian 7 systems"
command: /sbin/shutdown -t now
when: (ansible_distribution == "CentOS" and ansible_distribution_major_version == "6") or
(ansible_distribution == "Debian" and ansible_distribution_major_version == "7")
再如:
- host: web
remote_user: root
vars:
- http_port: 80
tasks:
- name: install package
yum: name=nginx
- name: copy template for centos7
template: src=/etc/ansible/httpd.conf dest=/etc/httpd/conf/httpd.conf #template通常在playbook.yml同级目录中建立一个templates目录,这样的话在这里就能够直接写文件template文件名不用再添加路径,固然若是没有template目录的话这里也能够写template文件所在的绝对路径。
when: ansible_distribution_major_version == "7"
notify: restart service
- name: copy template for centos6
template: src=/etc/ansible/httpd.conf6.j2 dest=/etc/httpd/conf/httpd.conf
when: ansible_distribution_major_version == "6"
notify: restart service
- name: start service
service: name=httpd state=started enabled=yes
handlers:
- name: restart service
service: name=httpd state=restarted
另外也能够自定义变量,当值为某某时执行什么动做,以下:
- hosts: all
vars:
exist: "True"
tasks:
- name: creaet file
command: touch /tmp/test.txt
when: exist | match("True")
- name: delete file
command: rm -rf /tmp/test.txt
when: exist | match("False")
意思是,当exist变量值为True时就建立文件,当值为False时就删除文件。
迭代:
当有须要重复性执行的任务时,可使用迭代机制,其使用格式为:"将须要迭代的内容定义为item变量引用,并经过with_items语句指明迭代的元素列表便可",例如:
tasks:
- name: "Install Packages"
yum: name={{ item }} state=latest
with_items:
- httpd
- mysql-server
- php
事实上,with_items中可使用元素还能够为hashes,例如添加用户和组:
tasks:
- name: "Add users"
user: name={{ item.name }} state=present groups={{ item.groups }}
with_items:
- { name:'test1', groups:'wheel'}
- { name:'test2', groups:'root'}
其中引用变量时前缀item变量是固定的,而item后跟的键名就是在with_items中定义的字典键名。
迭代功能在作批量处理时很是有用,好比系统初始化时通常要安装不少初始化的包就可使用迭代了。
更多功能查看官网。
tags
在一个playbook中,咱们通常会定义不少个task,若是咱们只想执行某个task或多个task时就可使用tags标签功能了,格式以下:
cat ceshi.yml
- hosts: web
vars:
http_port: 80
max_clients: 200
remote_user: root
tasks:
- name: ensure apache is at the latest version
yum: name=httpd state=latest
- name: write the apache config file
template: src=/srv/httpd.j2 dest=/etc/httpd.conf
notify:
- restart apache
- name: ensure apache is running
service: name=httpd state=started
tags:
- hosts
handlers:
- name: restart apache
service: name=httpd state=restarted
为复制hosts文件定义了一个tags,tags为hosts。那么在执行此playbook时可经过ansible-playbook命令使用–tags选项能实现仅运行指定的tasks而非全部的,以下:
查看tags
事实上,不光能够为单个或多个task指定同一个tags。playbook还提供了一个特殊的tags为always。做用就是当使用always当tags的task时,不管执行哪个tags时,定义有always的tags都会执行。
cat ceshi.yml
- hosts: web
vars:
http_port: 80
max_clients: 200
remote_user: root
tasks:
- name: ensure apache is at the latest version
yum: name=httpd state=latest
- name: write the apache config file
template: src=/srv/httpd.j2 dest=/etc/httpd.conf
tags:
- copy
notify:
- restart apache
- name: ensure apache is running
service: name=httpd state=started
tags:
- always
handlers:
- name: restart apache
service: name=httpd state=restarted
当你了解了Playbook的以上基础知识后基本可使用playbook来编排一些自动化任务了,上面介绍了playbook的Host、user、task、variables、template等基本组成元素的使用,以及条件测试、迭代、tags等特性,下面就开始介绍playbook中很是重要的一个概念roles,另外对于playbook的template元素,不光能够简单的调用变量,而且支持各类操做符,在使用playbook时,模板也是一个不可或缺的功能。
ansible-playbook的shell模块中的管道符‘|’的使用方法
ansibl下的
- name: Run expect to wait for a successful PXE boot via out-of-band CIMC
shell: | #在另外一个解释器下执行
set timeout 300
spawn ssh admin@{{ cimc_host }}
expect "password:"
send "{{ cimc_password }}\n"
expect "\n{{ cimc_name }}"
send "connect host\n"
expect "pxeboot.n12"
send "\n"
exit 0
args:
executable: /usr/bin/expect
delegate_to: localhost