目录



  • 四、Ansible-playbook
  • 1、YAML介绍
  • 2、Playbook基础理论
  • 3 、Playbook核心元素
  • 4、Hosts
  • 5、Remote_user
  • 6、Task
  • 7、Playbook
  • 8、Handers
  • 9、Notify
  • 10、Targs
  • 11、vars
  • 12、Templates
  • 13、When
  • 14、Item
  • 15、For
  • 16、If



四、Ansible-playbook

1、YAML介绍

1)基础知识

  • YAML是一个可读性高的用来表达资料序列的格式。YAML参考了很多语言,包括C语言、Python、Perl德国,Clark Evens在2001年首次发表了这种语言,另外Ingy dot 和Oren Ben-Kiki也是共同设计者
  • YAML Ain’t Mrakup Language ,即YAML不是XML,不过在开发语言的时候,YAML的愿意是:"Yet Another Markup Language"(仍是一种标记语言)
  • 特性
    YAML的可读性好
    YAML和脚本语言的交互性好
    YAML使用实现语言的数据类型
    YAML有一个一致的信息模型
    YAML易于实现
    YAML可以基于流来处理
    YAML表达能力强,扩展性好
  • 更多内容及规范参见http://www.yaml.org

2)语法简介

  • 在单一档案中,可用连续三个---区分多个档案(习惯首行---)。另外,可以选择性的连续...来表示档案结尾
  • 慈航开始正常些Playbook内容,一般建议写明该Playbook的功能
  • 使用#号注释代码
  • 缩进必须是统一的,空格和tab不能混用
  • 缩进的级别必须是一致的,同样的缩进代表同样的级别,程序判别配置的级别是通过缩进结合换行来实现的
  • YAML文件内容和Linux系统大小写判断方式保持一致,是区别大小写的,k/v的值均需大小写敏感
  • k/v的值可同行业可以换行,同行使用:分割
  • v可是字符串,也可以是另外一个表
  • 一个完整的代码块所需最小的元素包括:name:task
  • 一个name只能包括一个task
  • YAML文件扩展名通常为yml或yaml

3)基础用法

  • List:列表,其所有元素均使用"-"开头
    # A list of tasty fruits
    - apple
    - orange
    - strawberry
    - mango

-后面有空格

  • Dictonary:字典,通常由多个key和value构成

  • # An emplyoyee record
    name: example developer
    job: developer
    skill: elite
    也可以将key:value放置于{}内进行表示,用,分开多个

  • # An emplyoyee record
    {name: example developer,job: developer,skill: elite}
  • YAML语法和其他告诫语言类似,并且可以简单表达清单,散列表,标量等数据结构。其结构通过空格来展示,序列里面的项用-来代表,mao理的键值对用:隔开

name: John Smith

age: 40

gender: male

spourse:

name: Tom Smith

age: 37

gender: female

children:

- name: jimmy Smith

- age: 17

- name: kitty Smith

- age: 12

2、Playbook基础理论

1)简介

  • playbook 是由一个或多个play组成的 列表
  • play的主要功能在于将直线归并为一组的主机装扮实现通过ansible中的task定义好的角色。从根本来讲,所谓的task无非是调用ansible的一个module。将多个play组织在一个playbook内,即可以让它们联动起来按实现编排的机制唱一台大戏
  • playbook采用YAML语言编写

2)工作流程

ansible自动化when约束 ansible 网络自动化_shell

3 、Playbook核心元素

  • Hosts 执行的远程主机列表
  • Tasks 任务集
  • Varniables 内置变量或自定义变量在playbook中调用
  • Templates 模板,可替换模板中的变量并实现一些简单的逻辑的文件
  • Hanglers和notify结合使用,由特定条件触发的操作,满足条件方才执行,否则不执行
  • Tags 标签 制定某条任务执行,用户选择运行playbook中的部分代码,ansible具有幂等性,因此会自动跳过没有辩护的部分,即便如此,有的代码为测试其确实没有发生变化的时间依然会非常的长,此时确信其没有变化,就可以通过tags跳过这些代码片段
    ansible-playbook -t tagsname useradd.yml

4、Hosts

  • playbook中的每一个play 的目的都是为了让某个或某些主机以某个特定身份执行任务,hosts用于制定要执行执行任务的主机,须事先定义在主机清单内
  • 可以是如下形式:
    one.exaple.com
    one.exaple.com:two.exaple.com
    172.16.111.7
    172.16.111.*
  • web:db 两个组的并集
  • web:&app 两个组的交集
  • app:!db 在app且不在db组内
    - hosts: web:db

实例:

[root@ansible ansible]# vim ./test.yml

---
- hosts: web
  remote_user: root

  tasks:
    - name: create new file    #创建文件
      file: name=/data/file123 state=touch 
    - name: create new user    #创建用户
      user: name=test222 system=yes shell=/sbin/nologin
    - name: install httpd    #安装httpd
      yum: name=httpd 
    - name: copy html     #复制html
      copy: src=/usr/share/httpd/noindex/index.html dest=/var/www/html/
    - name: start service   #开启httpd服务
      service: name=httpd state=started enabled=yes
      #执行前检查剧本是否正常
   [root@ansible ansible]# ansible-playbook -C ./test.yml  

PLAY [web] *********************************************************************

TASK [Gathering Facts] *********************************************************
ok: [172.16.111.8]
ok: [172.16.111.7]

TASK [create new file] *********************************************************
changed: [172.16.111.8]
changed: [172.16.111.7]

TASK [create new user] *********************************************************
changed: [172.16.111.7]
changed: [172.16.111.8]

TASK [remove httpd] ************************************************************
ok: [172.16.111.8]
ok: [172.16.111.7]

TASK [copy html] ***************************************************************
changed: [172.16.111.7]
changed: [172.16.111.8]

TASK [start service] ***********************************************************
ok: [172.16.111.7]
ok: [172.16.111.8]

PLAY RECAP *********************************************************************
172.16.111.7               : ok=6    changed=3    unreachable=0    failed=0   
172.16.111.8               : ok=6    changed=3    unreachable=0    failed=0

测试没问题后执行,然后去被控端检查上述剧本中各项任务是否执行成功,验证。

5、Remote_user

remote_user:可用于Host和task中,也可以通过指定其通过sudo的方式在远程主机上执行任务,其可用于play全局或某服务;次在,甚至可以在sudo时使用sudo_user制定sudo时切换的用户

- hosts: web
  remote_user: root
tasks:
  - name: test connection
    ping:
    remote_user: white
    sudo: yes     #默认为root
    sudo_user: wang     #sudo为wang

6、Task

  • play的主题部分是task list。task list中的个任务按次序诸葛在hosts制定的所有主机下执行,即在所有主机上完成第一个任务后开始第二个。在运行紫霞而下某playbook时,如果中途发生错误,所有已执行任务都将回滚,因此,在更正playbook后重新执行一次即可。
  • task的目的是使用指定的参数执行模块,而在模块参数中可以使用变量。模块执行的是幂等的,这意味着多次之心是安全的,因为其结果均一致。
  • 每个task都应该有其name,用于playbook的执行结果输出,建议内容尽可能的清晰描述任务步骤,如果未提供name,则action的结果将用于输出
  • task:任务列表
    格式:action: module arguments
    module: arguments 建议使用
    注意:shell和command模块后面跟命令二不是key=value
    某个任务的状态在运行后为changed时,可通过notify通知给相应的handles
    任务可以通过tags打标签,而后可在ansible-playbook命令上使用-t制定进行调用
    task:
    - name: disable selinux
    command: /sbin/setenforce 0
    如果命令或脚本的退出吗不为0(命令执行失败),可以用如下方式替代:
    tasks:
    - name: run this command and ignore the result
    shell: /usr/bin/command || /bin/true
    或者使用ignore_errors来忽略错误信息:
    tasks:
    - name: run this command and ignore the result
    shell: /usr/bin/command
    ignore_errors: True

7、Playbook

  • 运行playbook的方式

ansible-playbook <filename.yml> ...[options]

  • 常见选项
    -- check只检车可能发生的改变,不真正执行等于-C
    --list-hosts 列出运行任务的主机
    --limit 主机列表指着对主机列表中的主机执行
    -v 显示过程 -vv -vvv更详细
  • 实例
    ansible-playbook file.yml --check
    ansible-playbook file.yml
    ansible-playbook file.yml --limit web
    ansible-playbook file.yml --list-tasks
[root@ansible ~]# ansible-playbook ./ansible/test.yml --list-tasks

playbook: ./ansible/test.yml

  play #1 (web): web  TAGS: []
    tasks:
      create new file TAGS: []
      create new user TAGS: []
      remove httpd    TAGS: []
      copy html   TAGS: []
      start service   TAGS: []
[root@ansible ~]# ansible-playbook ./ansible/test.yml --list-hosts

playbook: ./ansible/test.yml

  play #1 (web): web  TAGS: []
    pattern: [u'web']
    hosts (2):
      172.16.111.7
      172.16.111.

实例:

httpd.tml
- hosts: web
  remote_user: root

tasks:
  - name: install httpd
    yum: name=httpd state=present
  - name: install configure file
    copy: src=files/httpd.conf dest=/etc/httpd.conf
  - name: start service
    service: name-httpd state=started enable=yes

注意此时files使用的是相对路径,默认是当前用户家目录/下,没有files目录新建即可,把需要复制的文件复制到改文件夹内,YML脚本内即可使用相对路径

8、Handers

是task任务列表,这些task与前述的task没有本质的不同,用于当关注的资源发生变化时,才会采取一定的操作

9、Notify

此action可用于在每个play的最后被处罚,这样可以避免多次有改变每次都执行制定的操作,仅在所有的变化发生完成后一次性执行制定操作,在notify中累出的操作称为handler,也即notify中调用hander中定义的操作

[root@ansible ansible]# mkdir files
[root@ansible ansible]# cp /etc/httpd/conf/httpd.conf ./files
[root@ansible ansible]# vim httpd.yml


---
- hosts: all
  remote_user: root

  tasks:
    - name: install httpd package
      yum: name=httpd
    - name: copy conf file
      copy: src=files/httpd.conf dest=/etc/httpd/conf/ backup=yes
    - name: start service
      service: name=httpd state=started enabled=yes                                                       
      #提前修改conf文件内容 用于验证 例如端口 修改为8008
      [root@ansible ansible]# ansible-playbook -C httpd.yml 

PLAY [all] *****************************************************************************************************************************************************************

TASK [Gathering Facts] *****************************************************************************************************************************************************
ok: [172.16.111.9]
ok: [172.16.111.7]
ok: [172.16.111.8]

TASK [install httpd package] ***********************************************************************************************************************************************
changed: [172.16.111.9]
changed: [172.16.111.7]
changed: [172.16.111.8]

TASK [copy conf file] ******************************************************************************************************************************************************
changed: [172.16.111.9]
changed: [172.16.111.7]
changed: [172.16.111.8]

TASK [start service] *******************************************************************************************************************************************************
changed: [172.16.111.9]
changed: [172.16.111.8]
changed: [172.16.111.7]

PLAY RECAP *****************************************************************************************************************************************************************
172.16.111.7               : ok=4    changed=3    unreachable=0    failed=0   
172.16.111.8               : ok=4    changed=3    unreachable=0    failed=0   
172.16.111.9               : ok=4    changed=3    unreachable=0    failed=0  
#测试正常
执行后验证结果,查询被控端8008端口和80端口是否启动
[root@ansible ansible]# ansible all -m shell -a 'ss -lnt|grep :8080'
172.16.111.9 | FAILED | rc=1 >>
non-zero return code

172.16.111.8 | FAILED | rc=1 >>
non-zero return code

172.16.111.7 | FAILED | rc=1 >>
non-zero return code

[root@ansible ansible]# ansible all -m shell -a 'ss -lnt|grep :80'
172.16.111.9 | SUCCESS | rc=0 >>
LISTEN     0      128         :::8008                    :::*                  

172.16.111.8 | SUCCESS | rc=0 >>
LISTEN     0      128         :::8008                    :::*                  

172.16.111.7 | SUCCESS | rc=0 >>
LISTEN     0      128         :::8008                    :::*  
#查看可知80端口端口依然启动8008未启动,但是被控端都生成了httpd.conf的备份文件

此时我们需要做到的是,检测到conf文件有变化,服务自动重启,此时就需要handers介入

[root@ansible ansible]# vim httpd.yml 

---
- hosts: all
  remote_user: root

  tasks:
    - name: install httpd package
      yum: name=httpd
    - name: copy conf file
      copy: src=files/httpd.conf dest=/etc/httpd/conf/ backup=yes
      notify: restart service   #触发器,放在需要监控的触发动作后,: 后面是执行的东走
    - name: start service
      service: name=httpd state=started enabled=yes

  handlers:
    - name: restart service
      service: name=httpd state=restarted

此时最好再次修改httpd.conf 因为前次操作已经把httpd。conf文件已经修改了 ,修改端口为9527 ,由于安装过httpd,本次执行默认跳过安装,再次执行后查看端口

[root@ansible ansible]# ansible-playbook  httpd.yml -C

PLAY [all] *****************************************************************************************************************************************************************

TASK [Gathering Facts] *****************************************************************************************************************************************************
ok: [172.16.111.9]
ok: [172.16.111.8]
ok: [172.16.111.7]

TASK [install httpd package] ***********************************************************************************************************************************************
ok: [172.16.111.8]
ok: [172.16.111.9]
ok: [172.16.111.7]

TASK [copy conf file] ******************************************************************************************************************************************************
changed: [172.16.111.9]
changed: [172.16.111.8]
changed: [172.16.111.7]

TASK [start service] *******************************************************************************************************************************************************
ok: [172.16.111.8]
ok: [172.16.111.9]
ok: [172.16.111.7]

RUNNING HANDLER [restart service] ******************************************************************************************************************************************
changed: [172.16.111.9]
changed: [172.16.111.7]
changed: [172.16.111.8]

PLAY RECAP *****************************************************************************************************************************************************************
172.16.111.7               : ok=5    changed=2    unreachable=0    failed=0   
172.16.111.8               : ok=5    changed=2    unreachable=0    failed=0   
172.16.111.9               : ok=5    changed=2    unreachable=0    failed=0   
[root@ansible ansible]# ansible all -m shell -a 'ss -lnt|grep 80'
172.16.111.9 | FAILED | rc=1 >>
non-zero return code

172.16.111.8 | FAILED | rc=1 >>
non-zero return code

172.16.111.7 | FAILED | rc=1 >>
non-zero return code

[root@ansible ansible]# ansible all -m shell -a 'ss -lnt|grep 9527'
172.16.111.9 | SUCCESS | rc=0 >>
LISTEN     0      128         :::9527                    :::*                  

172.16.111.7 | SUCCESS | rc=0 >>
LISTEN     0      128         :::9527                    :::*                  

172.16.111.8 | SUCCESS | rc=0 >>
LISTEN     0      128         :::9527                    :::*

可以看出,handlers和notify配合使用,类似于linux系统的inotify,监控行为

10、Targs

可以使用tags增加标签,在执行YML的时候可以指定标签使用

[root@ansible ansible]# vim httpd.yml 

---
- hosts: all
  remote_user: root

  tasks:
    - name: install httpd package
      yum: name=httpd
      tags: inhttpd
    - name: copy conf file
      copy: src=files/httpd.conf dest=/etc/httpd/conf/ backup=yes
      notify: restart service
    - name: start service
      service: name=httpd state=started enabled=yes
      tages: rshttpd
  handlers:
    - name: restart service
      service: name=httpd state=restarted
[root@ansible ansible]# ansible-playbook -t rshttpd httpd.yml #-t 指定标签
 [WARNING]: Ignoring invalid attribute: tages


PLAY [all] *****************************************************************************************************************************************************************

TASK [Gathering Facts] *****************************************************************************************************************************************************
ok: [172.16.111.9]
ok: [172.16.111.8]
ok: [172.16.111.7]

PLAY RECAP *****************************************************************************************************************************************************************
172.16.111.7               : ok=1    changed=0    unreachable=0    failed=0   
172.16.111.8               : ok=1    changed=0    unreachable=0    failed=0   
172.16.111.9               : ok=1    changed=0    unreachable=0    failed=0

ansible-playbook -t rshttpd,inhttpd httpd.yml #多个标签逗号隔开

多个动作可以共用一个标签(个人感觉类似索引,关键词)

11、vars

以上操作都是所有任务依次操作,所有主机都执行,不够灵活,需要引入变量,让不同的主机可以进行不同的操作,可以做条件供以后判断等操作

变量名:只能由字母、数字和下划线组成,且只能字母开头

变量来源:

1、ansible setup facts远程主机的所有变量都可以直接调用,支持通配符

ansible web -m setup -a 'filter=*address*'

2、在/etc/ansible/hosts中定义

普通变量:主机组中主机单独定义,优先级高于公共变量

公共变量:针对主机组中所有主机定义统一变量

#对所有主机的hostname可以修改  根据名字区分
[root@ansible ansible]# vim httpd1.yml 

---
- hosts: web
  remote_user: root

  tasks:
    - name: set hostname
      hostname: name=www{{http_port}}.white.com
#此时这样执行,hostname均可以被修改,我们发现该hostname内 www和.white.com都是web内左右主机共用的  那就可以用在hosts内设置变量
[root@ansible ansible]# vim /etc/ansible/hosts 
[web]
172.16.111.7
172.16.111.8
[web:vars]     #新增web组变量
nodename=www
damainname=white.com
[root@ansible ansible]# vim httpd1.yml 
#根据设置配置剧本
---
- hosts: all
  remote_user: root

  tasks:
    - name: set hostname
      hostname: name={{nodename}}{{http_port}}.{{domainname}}
~                                                     [root@ansible ansible]# ansible-playbook httpd1.yml

3、通过命令行制定变量,优先级最高

ansible-playbook -e varname=value

[root@ansible ansible]# vim app.yml
---
- hosts: app
  remote_user: root

  tasks:
    - name: install  package
      yum: name={{ pkname}}    #设置变量名
    - name: start service
      service: name={{ pkname}} state=started  enabled=yes   #调用变量


[root@ansible ansible]# ansible-playbook -e 'pkname=vsftpd' app.yml #执行时需要 -e给变量赋值 
#结果如下
PLAY [app] **********************************************************************************

TASK [Gathering Facts] **********************************************************************
ok: [172.16.111.8]
ok: [172.16.111.7]
ok: [172.16.111.9]

TASK [install  package] *********************************************************************
ok: [172.16.111.9]
ok: [172.16.111.7]
ok: [172.16.111.8]

TASK [start service] ************************************************************************
ok: [172.16.111.9]
ok: [172.16.111.8]
ok: [172.16.111.7]

PLAY RECAP **********************************************************************************
172.16.111.7               : ok=3    changed=0    unreachable=0    failed=0   
172.16.111.8               : ok=3    changed=0    unreachable=0    failed=0   
172.16.111.9               : ok=3    changed=0    unreachable=0    failed=0   

[root@ansible ansible]# ansible app -m shell -a 'ss -lnt|grep 21'  #检查验证
172.16.111.9 | SUCCESS | rc=0 >>
LISTEN     0      32          :::21                      :::*                  

172.16.111.7 | SUCCESS | rc=0 >>
LISTEN     0      32          :::21                      :::*                  

172.16.111.8 | SUCCESS | rc=0 >>
LISTEN     0      32          :::21                      :::*

装多个安装包可以设置多个变量

tasks:
    - name: install  package
      yum: name={{ pkname1}}    #设置变量名1
    - name: install  package
      yum: name={{ pkname2}}    #设置变量名2
[root@ansible ansible]# ansible-playbook -e 'pkname1=vsftpd pkname2=httpd' app.yml  #多个变量

4、在playbook内定义

vars:

- var1: value1

- var2: value2

[root@ansible ansible]# vim app1.yml 

---
- hosts: app
  remote_user: root
  vars:
    - pkname1: httpd
    - pkname2: vsftp

  tasks:
    - name: install  package
      yum: name={{ pkname1}}
      yum: name={{ pkname2}}
    - name: start service
      service: name={{ pkname1}} state=started enabled=yes
      service: name={{ pkname2}} state=started enabled=yes
[root@ansible ansible]# ansible-playbook app1.yml 
#由于在脚本内已经定义了变量,直接执行即可

5、在role内定义

后面介绍

6、在独立的YML文件内定义

对于变量管理,由于变量有多重方式可以定义,不同人习惯会导致变量混乱等,故可以考虑吧变量放在同一文件内,使用的时候在文件内修改,剧本中调用该文件

[root@ansible ansible]# vim var.yml #配置变量文件

var1: httpd
var2: vsftpd
[root@ansible ansible]# vim testvars.yml
---
- hosts: web
  remote_user: root
  vars_files:     #调用变量文件
    - var.yml

  tasks:
    - name: install packages
      yum: name={{ var1}}
    - name: create file
      file : name=/tmp/{{ var2}}.log state=touch
[root@ansible ansible]# ansible-playbook testvars.yml 

[root@ansible ansible]# ansible web -m shell -a 'rpm -q httpd'   #验证结果
 [WARNING]: Consider using yum, dnf or zypper module rather than running rpm

172.16.111.8 | SUCCESS | rc=0 >>
httpd-2.4.6-80.el7.centos.1.x86_64

172.16.111.7 | SUCCESS | rc=0 >>
httpd-2.4.6-80.el7.centos.1.x86_64
[root@ansible ansible]# ansible web -m shell -a 'ls /tmp/vsftpd.log'
172.16.111.7 | SUCCESS | rc=0 >>
/tmp/vsftpd.log

172.16.111.8 | SUCCESS | rc=0 >>
/tmp/vsftpd.log

以上可以用来解决较为简单的命令

12、Templates

  • 文本文件,嵌套有脚本(使用模板变成语言编写)
  • jinja2语言,使用字面量,有下面形式
    字符串:使用单引号或双信号
    数字:整数,浮点数
    列表:[item1,item2,...]
    元组:(item1,item2,...)
    字典:{key1:value1,key2:value2,...}
    布尔型:true/false
  • 算数运算:+、-、*、/、//、%、**
    //整除,不留小数和余数 **指数
  • 比较运算:==,!=,>,>=,<,<=
  • 逻辑运算:and,or,not
  • 流表达式:For If When

配置文一般防止在yml文件同级的目录的templates下 配置文件以.js结尾

以下使用nginx做为案例

准备环境 nginx.conf为模板文件

#创建模板文件
[root@ansible ansible]# cp /etc/nginx/nginx.conf ~/ansible/templates/nginx.conf.j2
#编写脚本
[root@ansible ansible]# vim testtemplated.yml

---
- hosts: web
  remote_user: root

  tasks:
  - name: install package
    yum: name=nginx
  - name: copy templated
    template: src=nginx.conf.j2 dest=/etc/nginx/nginx.conf  #调用模板
  - name: start service
    service: name=nginx state=started enabled=yes
#-C测试
[root@ansible ansible]# ansible-playbook testtemplated.yml -C
#测试正常后取消-C 执行
[root@ansible ansible]# ansible web -m shell -a 'ps -ef|grep nginx'
172.16.111.7 | SUCCESS | rc=0 >>
root      24383      1  0 23:22 ?        00:00:00 nginx: master process /usr/sbin/nginx -c /etc/nginx/nginx.conf
nginx     24384  24383  0 23:22 ?        00:00:00 nginx: worker process
root      24435  24430  0 23:23 pts/0    00:00:00 /bin/sh -c ps -ef|grep nginx
root      24437  24435  0 23:23 pts/0    00:00:00 grep nginx

172.16.111.8 | SUCCESS | rc=0 >>
root      28866      1  0 23:22 ?        00:00:00 nginx: master process /usr/sbin/nginx -c /etc/nginx/nginx.conf
nginx     28867  28866  0 23:22 ?        00:00:00 nginx: worker process
root      28918  28913  0 23:23 pts/0    00:00:00 /bin/sh -c ps -ef|grep nginx
root      28920  28918  0 23:23 pts/0    00:00:00 grep nginx
#查看结果,被控端nginx已经开启,注意进程数

修改nginx.conf后再继续测试,(默认nginx.conf的worker_processes 1;和CPU核心数相等,有的版本是auto,本次测试nginx版本为1.14,默认是1)

#查询setup自带的CPU变量
[root@ansible ~]# ansible web -m setup|grep "cpu"
        "ansible_processor_vcpus": 2, 
        "ansible_processor_vcpus": 2, 
#修改nginx.conf.j2文件
[root@ansible ansible]# vim templates/nginx.conf.j2 
worker_processes  {{ansible_processor_vcpus*2}};
#由于此前已经安装并启动了nginx,此时需要更改配置文件后重启服务,可以借助前面使用的handlers+notify来完成
[root@ansible ansible]# vim testtemplated.yml 

---
- hosts: web
  remote_user: root

  tasks:
  - name: install package
    yum: name=nginx
  - name: copy templated
    template: src=nginx.conf.j2 dest=/etc/nginx/nginx.conf
    notify: restart service
  - name: start service
    service: name=nginx state=started enabled=yes

  handlers:
  - name: restart service
    service: name=nginx state=restarted
#-C测试正常后执行
[root@ansible ansible]# ansible-playbook testtemplated.yml

#再次查看nginx进程数
 [root@ansible ansible]# ansible web -m shell -a 'ps -ef|grep nginx'
172.16.111.8 | SUCCESS | rc=0 >>
root      29793      1  0 23:34 ?        00:00:00 nginx: master process /usr/sbin/nginx -c /etc/nginx/nginx.conf
nginx     29794  29793  0 23:34 ?        00:00:00 nginx: worker process
nginx     29795  29793  0 23:34 ?        00:00:00 nginx: worker process
nginx     29796  29793  0 23:34 ?        00:00:00 nginx: worker process
nginx     29797  29793  0 23:34 ?        00:00:00 nginx: worker process
root      29862  29857  0 23:35 pts/0    00:00:00 /bin/sh -c ps -ef|grep nginx
root      29864  29862  0 23:35 pts/0    00:00:00 grep nginx

172.16.111.7 | SUCCESS | rc=0 >>
root      25398      1  0 23:34 ?        00:00:00 nginx: master process /usr/sbin/nginx -c /etc/nginx/nginx.conf
nginx     25399  25398  0 23:34 ?        00:00:00 nginx: worker process
nginx     25400  25398  0 23:34 ?        00:00:00 nginx: worker process
nginx     25401  25398  0 23:34 ?        00:00:00 nginx: worker process
nginx     25402  25398  0 23:34 ?        00:00:00 nginx: worker process
root      25463  25458  0 23:35 pts/0    00:00:00 /bin/sh -c ps -ef|grep nginx
root      25465  25463  0 23:35 pts/0    00:00:00 grep nginx
#进程数都有所增加

扩展:此时被控端的web组内(node1,node2)的nginx的服务都是80端口,如果有需求更换不同的端口(node1使用8080,node2使用9080),可以为hosts内进行配置

[root@ansible ansible]# vim /etc/ansible/hosts
[web]
172.16.111.7 http_port=8080  #配置变量
172.16.111.8 http_port=9080  #配置变量
[root@ansible ansible]# vim templates/nginx.conf.j2 
server {
    listen       {{ http_port}};
#    listen      [::]:{{ http_port}} default_server
    server_name  _;

    #charset koi8-r;
    #access_log  /var/log/nginx/host.access.log  main;

    location / {
        root   /usr/share/nginx/html;
        index  index.html index.htm;
    }
}
#注意 默认nginx1.14内么有上述server 需要自己添加或者从conf.d/default.conf内自己复制,可能是作者希望使用include进行管理
[root@ansible ansible]# ansible-playbook testtemplated.yml 

#执行脚本后查看结果(主要是查看端口变量是否成功  使用 ss -lnt)
[root@ansible ansible]# ansible web -m shell -a 'ss -lnt'
172.16.111.8 | SUCCESS | rc=0 >>
State      Recv-Q Send-Q Local Address:Port               Peer Address:Port              
LISTEN     0      128          *:9080                     *:*                  
LISTEN     0      100    127.0.0.1:25                       *:*                  
LISTEN     0      128          *:80                       *:*                  
LISTEN     0      128          *:22                       *:*                  
LISTEN     0      100        ::1:25                      :::*                  
LISTEN     0      128         :::22                      :::*                  

172.16.111.7 | SUCCESS | rc=0 >>
State      Recv-Q Send-Q Local Address:Port               Peer Address:Port              
LISTEN     0      100    127.0.0.1:25                       *:*                  
LISTEN     0      128          *:8080                     *:*                  
LISTEN     0      128          *:80                       *:*                  
LISTEN     0      128          *:22                       *:*                  
LISTEN     0      100        ::1:25                      :::*                  
LISTEN     0      128         :::22                      :::* 
#查看结果可知 8080  9080端口已经开启

此外,还可以在playbook内写入变量

[root@ansible ansible]# vim testtemplated.yml 
---
- hosts: web
  remote_user: root
  vars:      #配置变量
    - http_port: 88
  tasks:
  - name: install package
    yum: name=nginx
  - name: copy templated
    template: src=nginx.conf.j2 dest=/etc/nginx/nginx.conf
    notify: restart service
  - name: start service
    service: name=nginx state=started enabled=yes

  handlers:
  - name: restart service
    service: name=nginx state=restarted
#保存修改 执行  -C测试  执行(执行信息省略)
[root@ansible ansible]# ansible-playbook testtemplated.yml  
#检验结果
[root@ansible ansible]# ansible web -m shell -a 'ss -lnt'
172.16.111.8 | SUCCESS | rc=0 >>
State      Recv-Q Send-Q Local Address:Port               Peer Address:Port              
LISTEN     0      128          *:88                       *:*                  
LISTEN     0      100    127.0.0.1:25                       *:*                  
LISTEN     0      128          *:80                       *:*                  
LISTEN     0      128          *:22                       *:*                  
LISTEN     0      100        ::1:25                      :::*                  
LISTEN     0      128         :::22                      :::*                  

172.16.111.7 | SUCCESS | rc=0 >>
State      Recv-Q Send-Q Local Address:Port               Peer Address:Port              
LISTEN     0      128          *:88                       *:*                  
LISTEN     0      100    127.0.0.1:25                       *:*                  
LISTEN     0      128          *:80                       *:*                  
LISTEN     0      128          *:22                       *:*                  
LISTEN     0      100        ::1:25                      :::*                  
LISTEN     0      128         :::22                      :::*   
#端口都被修改为88

验证了最开始的优先级的概念

命令行-e>playbook剧本>hosts内普通变量>hosts内的公共(组)变量

13、When

  • 条件测试:如果需要根据变量,facts或此前任务的执行结果来作为某task执行与否的提前提时需要用到条件测试,通过when语句实现,在task中使用,jinja2语法格式
  • 在task后增加when子句即可实现条件测试;when语句支持jinja2表达式语法
  • 示例:
    tasks:
    - name: "shutdown RedHat"
    command: /sbin/shutdown -h now
    when: ansible_os_family == "RedHat"

#ansible_os_family 是setup自带变量

[root@ansible ~]# ansible 172.16.111.7 -m setup -a "filter=ansible_os_family"   #查看系列
172.16.111.7 | SUCCESS => {
    "ansible_facts": {
        "ansible_os_family": "RedHat"
    }, 
    "changed": false
}
#" "内的内容就是匹配到的内容,以后可根据此进行判断等操作

举例:在上次操作texttemplated.yml中,加入when判断,判断被控端hostname是node2的时候才复制配置文件并重启服务

[root@ansible ansible]# cp testtemplated.yml testwhen.yml
#修改脚本
[root@ansible ansible]# vim testwhen.yml 

---
- hosts: web
  remote_user: root
  vars:
    - http_port: 8888
  tasks:
  - name: install package
    yum: name=nginx
  - name: copy templated
    template: src=nginx.conf.j2 dest=/etc/nginx/nginx.conf
    when: ansible_hostname == "node2"     #判断条件 可以多个task多个判断
    notify: restart service
  - name: start service
    service: name=nginx state=started enabled=yes

  handlers:
  - name: restart service
    service: name=nginx state=restarted
#检测后执行
[root@ansible ansible]# ansible-playbook testwhen.yml 
#查看端口是否修改
[root@ansible ansible]# ansible all -m shell -a 'ss -lnt'
  172.16.111.8 | SUCCESS | rc=0 >>
State      Recv-Q Send-Q Local Address:Port               Peer Address:Port              
LISTEN     0      128          *:8888                     *:*                  
LISTEN     0      100    127.0.0.1:25                       *:*                  
LISTEN     0      128          *:80                       *:*                  
LISTEN     0      128          *:22                       *:*                  
LISTEN     0      100        ::1:25                      :::*                  
LISTEN     0      128         :::22                      :::*                  

172.16.111.7 | SUCCESS | rc=0 >>
State      Recv-Q Send-Q Local Address:Port               Peer Address:Port              
LISTEN     0      128          *:88                       *:*                  
LISTEN     0      100    127.0.0.1:25                       *:*                  
LISTEN     0      128          *:80                       *:*                  
LISTEN     0      128          *:22                       *:*                  
LISTEN     0      100        ::1:25                      :::*                  
LISTEN     0      128         :::22                      :::*                  

172.16.111.9 | SUCCESS | rc=0 >>
State      Recv-Q Send-Q Local Address:Port               Peer Address:Port              
LISTEN     0      100    127.0.0.1:25                       *:*                  
LISTEN     0      128          *:22                       *:*                  
LISTEN     0      100        ::1:25                      :::*                  
LISTEN     0      128         :::22                      :::*     
#查看nginx服务是否开启
[root@ansible ansible]# ansible all -m shell -a 'ps -ef|grep nginx'
172.16.111.9 | SUCCESS | rc=0 >>
root       8463   8462  0 17:11 pts/0    00:00:00 /bin/sh -c ps -ef|grep nginx
root       8465   8463  0 17:11 pts/0    00:00:00 grep nginx

172.16.111.7 | SUCCESS | rc=0 >>
root      28374      1  0 00:18 ?        00:00:00 nginx: master process /usr/sbin/nginx -c /etc/nginx/nginx.conf
nginx     28375  28374  0 00:18 ?        00:00:00 nginx: worker process
nginx     28376  28374  0 00:18 ?        00:00:00 nginx: worker process
nginx     28377  28374  0 00:18 ?        00:00:00 nginx: worker process
nginx     28378  28374  0 00:18 ?        00:00:00 nginx: worker process
root      29899  29894  0 00:55 pts/0    00:00:00 /bin/sh -c ps -ef|grep nginx
root      29901  29899  0 00:55 pts/0    00:00:00 grep nginx

172.16.111.8 | SUCCESS | rc=0 >>
root      33658      1  0 00:47 ?        00:00:00 nginx: master process /usr/sbin/nginx -c /etc/nginx/nginx.conf
nginx     33659  33658  0 00:47 ?        00:00:00 nginx: worker process
nginx     33660  33658  0 00:47 ?        00:00:00 nginx: worker process
nginx     33661  33658  0 00:47 ?        00:00:00 nginx: worker process
nginx     33662  33658  0 00:47 ?        00:00:00 nginx: worker process
root      33852  33843  0 00:55 pts/0    00:00:00 /bin/sh -c ps -ef|grep nginx
root      33854  33852  0 00:55 pts/0    00:00:00 grep nginx

#结果:node1(172.16.111.7)的端口未发生变化,node2(172.16.111.8)的nginx端口被修改为了8888,node3(172.16.111.9)不在web组内,没有任何操作,测试成功

14、Item

可以使用循环迭代来操作多个重复动作,比如批量创建文件等

[root@ansible ansible]# vim testitem.yml
---
- hosts: all
  remote_user: root

  tasks:
    - name: create files
      file: name=/data/{{ item}} state=touch
      when: ansible_hostname == "node3"
      with_items:
        - file1
        - file2
        - file3
    - name: install some software
      yum: name={{ item}}
      with_items:
        - sysstat
        - lrzsz
        - dstat
#测试后执行
[root@ansible ansible]# ansible-playbook testitem.yml
#验证
[root@ansible ansible]# ansible all -m shell -a 'rpm -q sysstat dstat lrzsz'
#所有被控端都安装了sysstat dstat lrzsz三个软件
[root@ansible ansible]# ansible all -m command -a 'ls -l  /data'
172.16.111.9 | SUCCESS | rc=0 >>
total 0
-rw-r--r--. 1 root root 0 Oct 25 18:27 file1
-rw-r--r--. 1 root root 0 Oct 25 18:27 file2
-rw-r--r--. 1 root root 0 Oct 25 18:27 file3

172.16.111.8 | SUCCESS | rc=0 >>
total 0
-rw-r--r--. 1 root root  0 Oct 25 15:22 file123
drwxr-xr-x. 3 root root 21 Oct 22 16:33 svndata

172.16.111.7 | SUCCESS | rc=0 >>
total 0
-rw-r--r--. 1 root root  0 Oct 25 15:22 file123
drwxr-xr-x. 3 root root 21 Oct 22 17:10 svndata
#只有node3创建了file1 file2 file3文件

需求:创建三个组,创建三个用户,并且把每个用户分别放入一个组

(user1属于g1,user2属于g2,user3属于g3)

[root@ansible ansible]# vim testgroup.yml

---
- hosts: all
  remote_user: root

  tasks:
    - name: add some groups
      group: name={{ item}} state=present
      with_items:
        - g1
        - g2
        - g3

    - name: add some user
      user: name={{ item.name}} group={{ item.group}} state=present
      with_items:
        - { name: 'user1', group: 'g1'}    #类似于键值对一个K对应一个V
        - { name: 'user2', group: 'g2'}
        - { name: 'user3', group: 'g3'}
#item.name语法记住 
#执行结果验证
[root@ansible ansible]# ansible all -m shell -a 'getent passwd#该命令结果过长
[root@ansible ansible]# ansible all -m shell -a 'tail -3 /etc/passwd'
172.16.111.9 | SUCCESS | rc=0 >>
user1:x:1002:1002::/home/user1:/bin/bash
user2:x:1003:1003::/home/user2:/bin/bash
user3:x:1004:1004::/home/user3:/bin/bash

172.16.111.8 | SUCCESS | rc=0 >>
user1:x:1002:1002::/home/user1:/bin/bash
user2:x:1003:1003::/home/user2:/bin/bash
user3:x:1004:1004::/home/user3:/bin/bash

172.16.111.7 | SUCCESS | rc=0 >>
user1:x:1003:1003::/home/user1:/bin/bash
user2:x:1004:1004::/home/user2:/bin/bash
user3:x:1005:1005::/home/user3:/bin/bash

15、For

类似shell等的shell循环

语法

{% for vhost in nginx/-vhosts %}

server {

listen {{ vhost.listen| default('80 default_server') }}

{% endfor %}

需求:生成nginx.conf的server标签 listen端口不同

#创建conf文件
[root@ansible ansible]# vim templates/for1.conf.j2

{% for port in ports%}   #调用变量
server {
        listen {{ port}}
}
{% endfor %}
#创建执行脚本 
[root@ansible ansible]# vim testfor.yml

---
- hosts: web
  remote_user: root
  vars:
    ports:
      - 81
      - 82
      - 83

  tasks:
    - name: copy conf
      template: src=for1.conf.j2 dest=/data/for1.conf
      #调用模板,模板内调用YML脚本内定义的变量
[root@ansible ansible]# ansible-playbook testfor.yml
[root@ansible ansible]# ansible web -m shell -a 'ls -l /data'
172.16.111.8 | SUCCESS | rc=0 >>
total 4
-rw-r--r--. 1 root root  0 Oct 25 15:22 file123
-rw-r--r--. 1 root root 66 Oct 28 20:17 for1.conf
drwxr-xr-x. 3 root root 21 Oct 22 16:33 svndata

172.16.111.7 | SUCCESS | rc=0 >>
total 4
-rw-r--r--. 1 root root  0 Oct 25 15:22 file123
-rw-r--r--. 1 root root 66 Oct 28 20:17 for1.conf
drwxr-xr-x. 3 root root 21 Oct 22 17:10 svndata

[root@ansible ansible]# ansible web -m shell -a 'less /data/for1.conf'
172.16.111.8 | SUCCESS | rc=0 >>
server {
    listen 81
}
server {
    listen 82
}
server {
    listen 83
}

172.16.111.7 | SUCCESS | rc=0 >>
server {
    listen 81
}
server {
    listen 82
}
server {
    listen 83
}
#使用字典的方式来修改
[root@ansible ansible]# vim testfor.yml 

---
- hosts: web
  remote_user: root
  vars:
    ports:
      - listen_port: 81
      - listen_port: 82
      - listen_port: 83

  tasks:
    - name: copy conf
      template: src=for1.conf.j2 dest=/data/for1.conf
[root@ansible ansible]# vim templates/for1.conf.j2 

{% for port in ports %}
server {
        listen {{ port.listen_port}}
}
{% endfor %}

#执行效果与上面相同,如果是多键值对的时候建议使用此方法,如果只是单一的值,推荐上面方法

多键值对示例

#配置剧本文件
[root@ansible ansible]# vim testfor2.yml 

---
- hosts: web
  remote_user: root
  vars:
    ports:
      - web1:
        port: 86
        name: web1.white.com
        rootdir: /data/website1
      - web2:
        port: 87
        name: web2.white.com
        rootdir: /data/website2
      - web3:
        port: 88
        name: web3.white.com
        rootdir: /data/website3

  tasks:
    - name: copy conf
      template: src=for2.conf.j2 dest=/data/for2.conf

#配置conf文件
[root@ansible ansible]# vim templates/for2.conf.j2 

{% for p in ports %}
server {
        listen {{ p.port }}
        servername {{p.name }}
        documentroot {{ p.rootdir }}
}
{% endfor %}         
#验证后执行
[root@ansible ansible]# ansible-playbook testfor2.yml 
#验证执行结果
[root@ansible ansible]# ansible web -m shell -a 'ls /data'
172.16.111.8 | SUCCESS | rc=0 >>
file123
for1.conf
for2.conf
svndata

172.16.111.7 | SUCCESS | rc=0 >>
file123
for1.conf
for2.conf
svndata
[root@ansible ansible]# ansible 172.16.111.7 -m shell -a 'less /data/for2.conf' 
172.16.111.7 | SUCCESS | rc=0 >>
server {
    listen 86
        servername web1.white.com
        documentroot /data/website1
}
server {
    listen 87
        servername web2.white.com
        documentroot /data/website2
}
server {
    listen 88
        servername web3.white.com
        documentroot /data/website3
}

16、If

判断语句,与shell内相同,不多做介绍

语法

{% if vhost.server_name is defined %}

server_name {{vhost.server_name }};

{% endif %}

{% if vhost.root is defined 80 %}

root {{ vhost.root }};

{% endif %}

[root@ansible ansible]# vim testif1.yml 

---
- hosts: web
  remote_user: root
  vars:
    ports:
      - web1:
        port: 86
        #name: web1.white.com   #注释
        rootdir: /data/website1
      - web2:
        port: 87
        name: web2.white.com
        rootdir: /data/website2
      - web3:
        port: 88
        #name: web3.white.com   #注释
        rootdir: /data/website3

  tasks:
    - name: copy conf
      template: src=for3.conf.j2 dest=/data/for3.conf
#对p.name进行判断,没有则不生成servername
[root@ansible ansible]# vim templates/for3.conf.j2 

{% for p in ports %}
server {
        listen {{ p.port }}
{% if p.name is defined %}
        servername {{ p.name }}
{% endif %}
        documentroot {{ p.rootdir }}
}
{% endfor %}
#验证执行
[root@ansible ansible]# ansible-playbook testif1.yml 

[root@ansible ansible]# ansible web -m shell -a 'less /data/for3.conf'
172.16.111.8 | SUCCESS | rc=0 >>
server {
    listen 86
        documentroot /data/website1
}
server {
    listen 87
        servername web2.white.com
        documentroot /data/website2
}
server {
    listen 88
        documentroot /data/website3
}

172.16.111.7 | SUCCESS | rc=0 >>
server {
    listen 86
        documentroot /data/website1
}
server {
    listen 87
        servername web2.white.com
        documentroot /data/website2
}
server {
    listen 88
        documentroot /data/website3
}
#只有87端口的server有servername  别的由于被注释,if判断不存在 就不创建servername