[Ansible系列⑧]ansible-playbook 基础语法

一.  简介

         ansible的playbook的书写语法非常丰富,再加上一些语法格式,语法类型等等,导致playbook非常强大,下面我们将说一说一些基础的语法格式,比如,when判断,loop循环,tags标签,include等等。

 二.  Ansible-Playbook语法

 2.1    when判断

         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)

 2.2    loop循环

         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 ~]# 

 2.3    Handlers与Notify

         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
 

2.4    tags(标签)

         默认情况下,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]#

2.5    include任务复用 

         在变现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的内容,这一点要特别注意。

2.6    Playbook异常处理

         在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]# 

 2.7    playbook状态ok处理

         在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和综合案例中遇到了在给大家分享。

你可能感兴趣的:(ansible系列,ansible,运维,linux,服务器,架构)