ansible的playbook的书写语法非常丰富,再加上一些语法格式,语法类型等等,导致playbook非常强大,下面我们将说一说一些基础的语法格式,比如,when判断,loop循环,tags标签,include等等。
when判断在ansible中使用的频率非常高,比如yum模块可以自动检测软件是否已被安装,而无需人为干涉,但是有些任务就需要人为判断才可以实现。
· 比如:我们在搭建LNMP架构的时候,只有web节点才需要配置nginx仓库,此时就需要用到when判断。
· 比如:centos与ubuntu都需要安装Apache,而centos是使用yum模块安装,而Ubuntu系统是用apt模块安装,那么就需要判断主机系统,任何针对不同系统使用不同的模块。
· 比如:根据fact变量或者register变量来判断。
操作 | 举例 | 注释 |
等于(string) | ansible_machine == 'x86_64' | 当架构等于x86 |
等于 | ansible_distribution == 'CentOS' | 当发行版本为Centos |
小于 | ansible_memory_mb.nocache.free < 4 |
当内存小于4 |
大于 | ansible_memory_mb.nocache.free > 4 | 当内存大于4 |
小于等于 | <= | |
大于等于 | >= | |
不等于 | != | |
被定义 | memory is defined | |
没有被定义 | memory is not defined | |
当某个只为True | 变量名 | 当变量名的值为True |
取反 | not 变量名 | 当变量名的值不为True |
当a在b(list)中 | udcp in uos | 当uos在udcp中 |
注意:
1. when定义判断条件,当条件为真,才执行该task;
2. 多个条件可以用and或or分隔。
3. when表达式中调用变量不能用{{ }}
示例1:centos和uos两种系统,当系统为uos的时,打印系统信息。
##playbook示例:
---
- hosts: test
tasks:
#当为uos的系统的时候打印出系统信息
- name: instll nginx
debug:
msg:
- "{{ ansible_distribution }}}"
when: (ansible_distribution == 'UnionTech OS Server 20')
##执行结果:
[root@clinet ansible_1]# ansible-playbook fact_test.yml
PLAY [test] *********************************************************************************************************************************************************************************************
TASK [Gathering Facts] **********************************************************************************************************************************************************************************
ok: [192.168.194.133]
TASK [instll nginx] *************************************************************************************************************************************************************************************
skipping: [192.168.194.129]
ok: [192.168.194.133] => {
"msg": [
"UnionTech OS Server 20}"
]
}
PLAY RECAP **********************************************************************************************************************************************************************************************
192.168.194.129 : ok=0 changed=0 unreachable=0 failed=0 skipped=1 rescued=0 ignored=0
192.168.194.133 : ok=2 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
[root@clinet ansible_1]#
示例2: 其他语法示例
##and或or使用语法
when: ((A == XX) and (B == XX))
when: ((A == XX) or (B == XX))
## vars被定义
when: vars is defined
##vars没有被定义
when: vars is not defined
##为True
vars:
var1: True
when: ( var1 )
##部位True
when: (not var1)
loop用于处理很多task都要重复使用一个模块,比如一次启动10个服务,或者一次拷贝10个文件,像这样的情况我们就可使用loop。
示例1: 循环关闭nginx和mariadb服务
##playbook示例:
---
- hosts: test
tasks:
#循环安装软件
- name: stop nginx and mariadb
service:
name: '{{ item }}'
state: stopped
loop:
- nginx
- mariadb
## 执行结果:
[root@clinet ansible_1]# ansible-playbook fact_test.yml
PLAY [test] *********************************************************************************************************************************************************************************************
TASK [stop nginx and mariadb] ***************************************************************************************************************************************************************************
changed: [192.168.194.129] => (item=nginx)
changed: [192.168.194.129] => (item=mariadb)
PLAY RECAP **********************************************************************************************************************************************************************************************
192.168.194.129 : ok=1 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
[root@clinet ansible_1]#
示例2:在/tmp目录下批量创建文件
## playbook示例:
---
- hosts: test
tasks:
# 批量创建文件
- name: stop nginx and mariadb
file:
path: '/home/{{ item.path }}'
owner: '{{ item.owner }}'
group: "{{ item.group }}"
mode: "{{ item.mode }}"
state: touch
loop:
- {path: 'xhz.conf', owner: 'root', group: 'root', mode: '0755' }
- {path: 'flf.conf', owner: 'root', group: 'root', mode: '0644' }
## 执行结果:
[root@clinet ansible_1]# ansible-playbook fact_test.yml
PLAY [test] *********************************************************************************************************************************************************************************************
TASK [stop nginx and mariadb] ***************************************************************************************************************************************************************************
changed: [192.168.194.129] => (item={u'owner': u'root', u'path': u'xhz.conf', u'group': u'root', u'mode': u'0755'})
changed: [192.168.194.129] => (item={u'owner': u'root', u'path': u'flf.conf', u'group': u'root', u'mode': u'0644'})
PLAY RECAP **********************************************************************************************************************************************************************************************
192.168.194.129 : ok=1 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
[root@clinet ansible_1]# ls -l /home
total 0
drwx------. 3 uos uos 78 Oct 10 21:49 uos
[root@clinet ansible_1]#
[root@route ~]# ls -l /home/
total 0
-rw-r--r-- 1 root root 0 Nov 11 15:15 flf.conf
-rwxr-xr-x 1 root root 0 Nov 11 15:15 xhz.conf
[root@route ~]#
Handlers是一个触发器,同时是一个特殊的tasks,他无法直接运行,它需要被包含Notify的tasks通知之后才执行。比如:httpd服务的配置文件发生改变,则我们可以通过Notify通知给指定的Handlers触发器,然后执行相应的重启服务操作;如果配置文件不发生变更操作,则不会触发Handlers任务的执行。
tasks:
- name: start memcached
service:
name: memcached
state: started
notify: restart memcached
- name: start apache
service
name: httpd
state: started
notify: restart apache
handlers:
- name: restart memcached
service:
name: memcached
state: restarted
- name: restart apache
service:
name: httpd
state: restarted
##在notify中定义内容一定要和handler中定义的 - name 内容一样,这样才能达到触发的效果,否则会不生效。
其一:默认情况下,在一个playbook中,只要有task执行失败,则playbook终止,即使是与handler关联的task在失败的task之前运行成功了,handler也不会被执行(因为Handerlers是在所有的task正常执行完之后,才会执行)。如果希望在这种情况下handler仍然能够执行,则需要添加force_handlers参数,配置如下:
- hosts: all
force_handlers: yes ##
tasks:
- name: a task which always notifies its handler
command: /bin/true
notify: restart the database
- name: a task which fails because the package doesn't exist
yum:
name: notapkg
state: latest
handlers:
- name: restart the database
service:
name: mariadb
state: restarted
##如果与handler关联的task还未执行,在其前的task已经失败,整个play终止,则handler未被触发,也不会执行。
其二:默认情况下,所有task执行完毕后,才会执行各个handler,并不是执行完某个task后,立即执行对应的handler,如果你想要在执行完某些task以后立即执行对应的handler,则需要使用meta模块,示例如下
- hosts: web
tasks:
- name: install httpd
yum:
name: httpd
state: present
notify: httpd
- name: install mariadb
yum:
name: mariadb
state: present
notify: mariadb
- meta: flush_handlers ##重点
- name: install vim
yum:
name: vim
state: present
handlers:
- name: httpd
systemd:
name: httpd
state: started
- name: mariadb
systemd:
name: mariadb
state: started
这个任务使用meta模块,meta任务是一种特殊的任务,meta任务可以影响ansible的内部运行方式,上例中,meta任务的参数值为flush_handlers,”meta: flush_handlers”表示立即执行之前的task所对应handler,**什么意思呢?意思就是,在当前meta任务之前,一共有两个任务,task1与task2,它们都有对应的handler,当执行完task1与task2以后,立即执行对应的handler,而不是像默认情况那样在所有任务都执行完毕以后才能执行各个handler。
其三:多个handler对应一个task: 我们还可以在一个task中一次性notify多个handler,怎样才能一次性notify多个handler呢?你可能会尝试将多个handler使用相同的name,但是这样并不可行,因为当多个handler的name相同时,只有一个handler会被执行,所以,我们并不能通过这种方式notify多个handler,如果想要一次notify多个handler,则需要借助另一个关键字,它就是’listen’,你可以把listen理解成”组名”,我们可以把多个handler分成”组”,当我们需要一次性notify多个handler时,只要将多个handler分为”一组”,使用相同的”组名”即可,当notify对应的值为”组名”时,”组”内的所有handler都会被notify,示例如下:
---
- hosts: web
tasks:
- name: task1
file: path=/testdir/testfile
state=touch
notify: handler group1 ##
handlers:
- name: handler1
listen: handler group1
file: path=/testdir/ht1
state=touch
- name: handler2
listen: handler group1 ##
file: path=/testdir/ht2
state=touch
默认情况下,ansibke在执行一个play-book时,会执行playbook中的所有任务,然而标签功能就是用来指定运行playbook中某个特定任务的。
1. 为playbook中的task添加的标签的方式:
· 对某一个task打一个标签;
· 对某一个task打多个标签;
· 多多个task打一个标签;
2. task打完标签的使用方法:
· -t 执行指定tag标签对应的任务;
· --skip-tags: 执行除--skip-tags指定标签之外的所有任务。
示例1: 使用-t执行指定的tags标签
- hosts: test
tasks:
- name: install nfs server
yum:
name: nfs-utils
state: present
tags:
- install_nfs
- install_nfs-server
- name: start nfs
service:
name: nfs-utils
state: started
enabled: yes
tags:
- start_nfs
##执行结果
[root@clinet ansible_1]# ansible-playbook tags_test.yaml -t install_nfs
PLAY [test] *********************************************************************************************************************************************************************************************
TASK [Gathering Facts] **********************************************************************************************************************************************************************************
ok: [192.168.194.129]
TASK [install nfs server] *******************************************************************************************************************************************************************************
ok: [192.168.194.129]
PLAY RECAP **********************************************************************************************************************************************************************************************
192.168.194.129 : ok=2 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
[root@clinet ansible_1]#
示例2: --skip-tags指定不执行某个tags
- hosts: test
tasks:
- name: install nfs server
yum:
name: nfs-utils
state: present
tags:
- install_nfs
- install_nfs-server
- name: start nfs
service:
name: nfs-utils
state: started
enabled: yes
tags:
- start_nfs
##执行结果
[root@clinet ansible_1]# ansible-playbook tags_test.yaml --skip-tags install_nfs
PLAY [test] **********************************************************************************************************
TASK [start nfs] *****************************************************************************************************
changed: [192.168.194.129]
PLAY RECAP ***********************************************************************************************************
192.168.194.129 : ok=1 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
[root@clinet ansible_1]#
在变现ansible playbook的时候,我们会发现大量的playbook内容需要重复编写,各个tasks之间的功能需要相互调用才能完成各自功能,这个时候我们就可以使用include功能。
示例1:多个项目调用相同的task
比如:a项目和b项目都需重启apache2,我们就可以利用include来调用一个单独写有重启apache2的yaml文件,来完成这一个操作
1. 编写restart_apache.yml文件
##直接写task即可,不用按照playbook去去写
[root@clinet ansible_1]# cat restart_apache2.yml
- name: restart nginx
service:
name: nginx
state: started
enabled: yes
[root@clinet ansible_1]#
2. 编写playbook
- hosts: 192.168.194.129
tasks:
- name: project a install nginz
yum:
name: nginx
state: present
- name: project a start nginx
include: restart_apache2.yml
- hosts: 192.168.194.130
tasks:
- name: project a install nginx
yum:
name: nginx
state: present
- name: project a start apache2
include: restart_apache2.yml
示例2: include结合tags应用
通过指定标签tags,来说明是安装tomcat8还是tomcat9
· 1. 准备入口文件main.yml,然后包含install_tomcat8.yml和install_tomcat9.yml
· 2. 在执行main.yml时,需要通过--tags来指定安装版本。
1. 编写main.yml入口文件
[root@clinet ansible_1]# cat main.yml
- hosts: test
tasks:
- name: install tomcat8
include: install_tomcat8.yml
## 定义变量
tomcat_version=9.0.43
tomcat_install_dir=/usr/local
tags: tomcat8
- name: install tomcat9
include: install_tomcat9.yml
## 定义变量
tomcat_version=8.5.63
tomcat_install_dir=/usr/local
tags: tomcat9
[root@clinet ansible_1]#
[root@clinet ansible_1]#
2. 编写tomcat8安装文件
[root@clinet ansible_1]# cat install_tomcat8.yml
- name: debug message
debug:
msg:
- '{{ tomcat_version }}'
- '{{ tomcat_install_dir }}'
- name: install jdk-1.8
yum:
name: java-1.8.0-openjdk
state: present
- name: Download tomcat
get_url:
url: http://mirrors.hust.edu.cn/apache/tomcat/tomcat-9/v{{ tomcat_version }}/bin/apache-tomcat-{{ tomcat_version }}.tar.gz
dest: /tmp
- name: unarchive tomcat-{{ tomcat_version }}.tar.gz
unarchive:
src: /tmp/apache-tomcat-{{ tomcat_version }}.tar.gz
dest: '{{ tomcat_install_dir }}'
remote_src: yes
- name: start tomcat
shell: cd {{ tomcat_install_dir }} && mv apache-tomcat-{{ tomcat_version }} tomcat8 && cd tomcat8/bin && nohup ./startup.sh
[root@clinet ansible_1]#
[root@clinet ansible_1]#
3. 编程tomcat9安装文件
[root@clinet ansible_1]# cat install_tomcat9.yml
- name: debug message
debug:
msg:
- '{{ tomcat_version }}'
- '{{ tomcat_install_dir }}'
- name: install jdk-1.8
yum:
name: java-1.8.0-openjdk
state: present
- name: Download tomcat
get_url:
url: http://mirrors.hust.edu.cn/apache/tomcat/tomcat-9/v{{ tomcat_version }}/bin/apache-tomcat-{{ tomcat_version }}.tar.gz
dest: /tmp
- name: unarchive tomcat-{{ tomcat_version }}.tar.gz
unarchive:
src: /tmp/apache-tomcat-{{ tomcat_version }}.tar.gz
dest: '{{ tomcat_install_dir }}'
remote_src: yes
- name: start tomcat
shell: cd {{ tomcat_install_dir }} && mv apache-tomcat-{{ tomcat_version }} tomcat8 && cd tomcat8/bin && nohup ./startup.sh
[root@clinet ansible_1]#
[root@clinet ansible_1]#
4. 执行mian.yml文件,然后通过-t来指定对应版本
[root@clinet ansible_1]# ansible-playbook main.yml -t tomcat8
[root@clinet ansible_1]# ansible-playbook main.yml -t tomcat9
说明:
示例2中把安装tomcat8和tomcat9写成了两个问价,实际上没有必要,因为tomcat_version和tomcat_install_dir 变量是直接在main.yml文件中定义的。但是写为两个文件的时候是这样考虑的:tomcat_version和tomcat_install_dir变量写在include导入的yml文件中,最后发现include导入的yml文件是不支持写- hosts,vars等参数的,需要直接写tasks的内容,这一点要特别注意。
在playbook执行的过程中,难免会遇到一些错误,由于playbook遇到错误之后,不会执行之后的任务,不便于调试,此时可以使用ignore_errors来暂时忽略错误,是的playbook继续执行。
示例1: 忽略错误,继续执行后续的tasks
[root@clinet ansible_1]# cat ignoore_error.yml
- hosts: test
tasks:
- name: ignore false
command: /bin.false
ignore_errors: yes
- name: touche file
file:
path: /tmp/xhz
state: touch
[root@clinet ansible_1]#
[root@clinet ansible_1]#
[root@clinet ansible_1]#
[root@clinet ansible_1]#
[root@clinet ansible_1]# ansible-playbook ignoore_error.yml
PLAY [test] *********************************************************************************************************************************************************
TASK [ignore false] *************************************************************************************************************************************************
fatal: [192.168.194.129]: FAILED! => {"changed": false, "cmd": "/bin.false", "msg": "[Errno 2] No such file or directory", "rc": 2}
...ignoring
TASK [touche file] **************************************************************************************************************************************************
changed: [192.168.194.129]
PLAY RECAP **********************************************************************************************************************************************************
192.168.194.129 : ok=2 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=1
[root@clinet ansible_1]#
[root@clinet ansible_1]#
[root@clinet ansible_1]#
在playbook中,有时候在执行shell命令的时候,比如:netstat -ntpl 命令,每次执行的时候并未对被控主机操作实质性的改变,但是返回结果每次都是chanaged(黄色)状态,此时我们可以通过chanaged_when来改变状态为ok(绿色)
示例1: 将changed的状态改为ok
- hosts: test
tasks:
- name: info
shell: netstat -ntpl
changed_when:
- false
结束语:
playbook的语法使用不仅仅只有上面这些,还包括更加丰富的pre_task,post_task,import_include,import_tasks等等。上面这些是初学者必须要了解和掌握的,至于其他的,我们将在后面的Roles和综合案例中遇到了在给大家分享。