ansible之playbook与roles

ansible之playbook介绍与roles,yaml简介...

playbook基础

playbook概念

​ playbook是由一个或多个play组成的列表;play的作用:将预定义的一组主机,装扮成事先通过ansible中的task定义好的角色,task是调用ansible的module,将多个play组织在一个playbook中,让其按照编排的顺序执行

​ yaml语言编写

playbook执行逻辑

image-20201008173331897

yaml概念

yaml之于json

类比:markdown之于html,都提高了后者的写的便捷性;

https://yaml.org/

​ yaml是一种可读性强,用来表达资料序列的格式,作者clark evans

​ yaml ain`t markup language

or

​ yet another markup language

  • 可读性好
  • 和脚本语言结合性好
  • 有一个一致的信息模型
  • 易于实现
  • 可基于流处理
  • 表达能力强,扩展性好

yaml语法

  • 同一个yaml文件,用---区分多个档案
  • #为注释
  • 缩进必须统一!空格和tab不能混用
  • 缩进级别需一致
  • yaml区分大小写
  • 对应playbook,一个完整代码块至少包含name和task
  • 一个name只能有一个task
  • 文件扩展名.yml或.yaml

yaml文件示例:

list列表

- apple
- mango
- orange

dictionary字典

name:wang
job:sre leader
字典的等同写法
(name:wang,job:sre leader)


yaml的playbook示例

​ 不仅有tasks还有pre_tasks和post_tasks

---
    - hosts: 192.168.100.59,192.168.100.65
      remote_user: root
      pre_tasks: 
        - name: set epel repo for Centos 7
          yum_repository: 
            name: epel7
            description: epel7 on CentOS 7
            baseurl: http://mirrors.aliyun.com/epel/7/$basearch/
            gpgcheck: no
            enabled: True

      tasks: 
# install nginx and run it
        - name: install nginx
          yum: name=nginx state=installed update_cache=yes
        - name: start nginx
          service: name=nginx state=started

      post_tasks: 
        - shell: echo "deploy nginx over"
          register: ok_var
        - debug: msg="{{ ok_var.stdout }}"

常见数据交换格式

  • xml
  • json
  • yaml

image-20201008174605585

playbook组成

核心元素

  • hosts:远程主机列表

  • tasks:任务集

  • variables:内置变量和自定义变量

  • templates:模版

  • handler结合notity:触发条件和对应操作

  • tags:通过标签,用于跳过playbook中某些代码段

    • ansible-playbook -t tagsname useradd.yml
      

hosts

​ hosts定义了在哪些远程主机上,以某个特定用户身份执行任务,需要在主机清单中实现定义;

192.168.80.102
www.b.com
bbs.b.com

web:db 或
web:&db 且,取交集
web:!db 在web组,不在db组的

示例:
- hosts: websrvs:dbsrvs

remote_user

​ 以远程主机上的哪个用户执行任务,用于host和task中,也可以用来执行sudo后的身份

- hosts: web
 remote_user: root #指定全局的remoteuser为 root
 tasks:
  - name: test-ping
    ping: 
    remote_user: wang #该ping任务用wang用户,且使用sudo,sudo身份为root,且默认为root
    sudo: yes
    sudo_user: root
    

tasks列表和actions

​ play的主体部分为task lists,任务列表中定义的任务,按照次序在各个hosts中顺序执行,在hosts中定义的所有主机上,

​ task作用在于根据定义的参数、变量,调用并执行相应的模块,具有幂等性,因此多次执行是安全的

​ 每个task都应该设置合理,明确,见名知意的名字

写task的2种格式:

action: module arguments
modules: arguments (建议)
#shell和command模块后跟的arguments是命令,而非kv类型的键值对

某个任务执行后状态为changed,可以结合notify通知给handler做进一步处理,

任务可以通过tags打标签,结合ansible-playbook的 -t参数进行调用或跳过调用

tasks:
 - name: disable selinux
   command: /sbin/setenforce 0
# 任务名为关闭selinux,
# 调用command模块,执行的命令为/sbin/setfnforce 0

​ 若命令或脚本的退出码不为0,可以采用以下方式忽略错误

tasks:
 - name: test-ignore-error
   shell: /usr/bin/some-command || /bin/true
   
或者:

tasks:
 - name: test-ignore-error-2
   shell: /usr/bin/some-command
   ignore_errors: true

playbook层级

  • playbook可以包含多个play
    • 一个play至少包含,一个hosts,一个tasks
      • 一个tasks包含多个task,每个task基本都是调用的一个个ansible模块

hosts

​ 定义要执行任务的主机,支持通配符,正则匹配,来表示一批相同特征的主机;

remote_user

remote_user是ssh连接到各个节点的用户身份,不一定是执行任务的身份;become相关参数可以切换为别的身份来执行任务,没指定时默认是用remote_user执行任务

​ 如:可以remote_user是ssh连接过去,用nginx的身份启动nginx进程,借助become-user=nginx实现

​ remote_user的连接认证,可以通过ssh免密认证,指定密钥文件认证,指定密码认证,密码和密码文件一般可以写在主机清单的变量中,可以命令行传入!

tasks

​ ps:shell和command模块的不具有幂等性的解决方法,以初始化mysql数据库为例:

tasks:
 - name: init mysql datadir
   file: path=/data/mysql state=directory owner=mysql group=mysql mode=0755
   shell: "/usr/local/mysql/bin/mysql_install_db --datadir=/data/mysql --user=mysql creates=/mysql/data/ibdata1"

# 借助了creates参数实现;
# 第一次/mysql/data/ibdata1不存在,会支持初始化数据库目录的动作;之后再次执行,由于ibdata1文件已经存在,ansible不会再次执行该步动作;

# 介绍如下,shell和command参数,都可以通过该2个参数控制,实现类似的【幂等性】
[root@host2 ~]# ansible-doc -s shell
- name: Execute shell commands on targets
  shell:
      creates:               # A filename, when it already exists, this step will *not* be
                               run.

      removes:               # A filename, when it does not exist, this step will *not* be
                               run.

运行playbook

ansible-playbook filename.yaml ... [options]

常见options
--check -C
假装执行,列出如果执行会发生的变化,并不真正的在主机上执行
--list-hosts
列出要执行任务的主机
--list-tags
列出tag
--list-tasks
列出task
--limit 主机列表
只对主机列表里的主机执行
-v -vv -vvv
显示不同等级的执行过程信息

playbook vs shellscripts

shell脚本

#!/bin/bash

yum install -y --quiet httpd

cp /tmp/httpd.conf /etc/httpd/conf/httpd.conf

cp /tmp/vhosts.conf /etc/httpd/conf.d/

service httpd start 
chkconfig httpd on

playbook

---
- hosts: all
  remote_user: root
  tasks:
   - name: "安装httpd"
     yum: name=httpd
   - name: "复制配置文件"
     copy: src=/tmp/httpd.conf dest=/etc/httpd/conf/
    - name: "复制虚拟主机配置文件"
      copy: src=/tmp/vhosts/conf dest=/etc/httpd/conf.d/
     - name: "启动,设置开启启动"
       service: name=httpd state=started enabled=yes

playbook示例

1、创建用户

[root@host2 ansible-playbooks]# cat sysuser.yaml 
- hosts: all
  remote_user: root
  tasks:
   - name: create mysql user
     user: name=mysql system=yes uid=36
   - name: create a group
     group: name=httpd system=yes
# 创建yaml文件如上:

# 先检查执行结果
[root@host2 ansible-playbooks]# ansible-playbook -C sysuser.yaml 

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

TASK [Gathering Facts] *********************************************************************
ok: [192.168.80.102]
ok: [192.168.80.103]

TASK [create mysql user] *******************************************************************
changed: [192.168.80.103]
changed: [192.168.80.102]

TASK [create a group] **********************************************************************
changed: [192.168.80.103]
changed: [192.168.80.102]

PLAY RECAP *********************************************************************************
192.168.80.102             : ok=3    changed=2    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   
192.168.80.103             : ok=3    changed=2    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   

# 列出所有任务
[root@host2 ansible-playbooks]# ansible-playbook --list-tasks sysuser.yaml 

playbook: sysuser.yaml

  play #1 (all): all	TAGS: []
    tasks:
      create mysql user	TAGS: []
      create a group	TAGS: []
 
# 列出所有主机
[root@host2 ansible-playbooks]# ansible-playbook --list-hosts sysuser.yaml 

playbook: sysuser.yaml

  play #1 (all): all	TAGS: []
    pattern: [u'all']
    hosts (2):
      192.168.80.102
      192.168.80.103

# 最终执行
[root@host2 ansible-playbooks]# ansible-playbook sysuser.yaml

2、安装httpd

[root@host2 ansible-playbooks]# cat httpd.yaml 
- hosts: all
  remote_user: root
  tasks:
   - name: install httpd
     yum: name=httpd state=present
   - name: copy conf file
     copy: src=/tmp/httpd.conf dest=/etc/httpd/conf/
   - name: start service
     service: name=httpd state=started enabled=yes

1,确定要执行的主机,为all
2,默认task,先收集信息,确认主机都可达
3,后续3个task,为用户定义任务,
4,最终,每个host的执行结果,4个task都ok,2个changed,0个失败,0个不可达,0个跳过,忽略等等
# 最终执行
[root@host2 ansible-playbooks]# ansible-playbook httpd.yaml 

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

TASK [Gathering Facts] *********************************************************************
ok: [192.168.80.103]
ok: [192.168.80.102]

TASK [install httpd] ***********************************************************************
changed: [192.168.80.102]
changed: [192.168.80.103]

TASK [copy conf file] **********************************************************************
ok: [192.168.80.103]
ok: [192.168.80.102]

TASK [start service] ***********************************************************************
changed: [192.168.80.103]
changed: [192.168.80.102]

PLAY RECAP *********************************************************************************
192.168.80.102             : ok=4    changed=2    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   
192.168.80.103             : ok=4    changed=2    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0

notify与handlers

​ 根据幂等性,执行某task后,状态可能会changed或其他,当某个task执行后返回状态为changed,**即该步任务对目标主机造成了某些改变;**changed这个状态可以被notify捕捉,从而触发对应的handler动作;

​ 举例说明:

  1. 在template和copy2个task后,紧跟着定义的一个notify,意思是:这2个task执行后为changed的话,该notify会被触发,后执行其中的handlers进行后续操作
  2. changed状态发生情况:第一次执行;或,要复制的2个文件hash值发送变化时,为changed,此时notify会触发;
  3. notify的执行为所有task执行完毕之后,才会根据自己的task状态决定是不是触发handlers,且有多次只执行一次
  4. 例如:2个文件都改变,template和copy的执行状态都为changed,2个notify都会被触发,但是其中相同的handler:restart-nginx只会被执行一次;
tasks:
 - name: copy template conf file to remote_hosts
   template: src=/tmp/nginx.conf.j2 dest=/etc/nginx/nginx.conf
   notify:
    - restart-nginx
    - test-web
   copy=: src=/tmp/index.html dest=/usr/share/nginx/html/index.html
   notify: 
    - restart-nginx

handlers:
 - name: restart-nginx
   service: name=nginx stated=restarted
  - name: test-web
    shell: curl -I http:IP/index.html |grep 200 || /bin/false

​ 给某预期的情况定义notify,该notify发生时,触发handlers定义的操作,handler本质也还是tasks列表

[root@host2 ansible-playbooks]# vim test-handlers.yaml 
[root@host2 ansible-playbooks]# cat !$
cat test-handlers.yaml
- hosts: 192.168.80.102
  remote_user: root
  tasks:
   - name: add group nginx
     group: name=nginx state=present
   - name: add user nginx
     user: name=nginx state=present group=nginx
   - name: install nginx
     yum: name=nginx state=present
   - name: config
     copy: src=/tmp/nginx.txt dest=/etc/nginx/nginx.conf
     notify:
      - restart-nginx
      - check-nginx-process
  handlers:
   - name: restart-nginx
     service: name=nginx state=restarted enabled=yes
   - name: check-nginx-process
     shell: killall -0 nginx > /tmp/nginx.log

handlers定义和tasks同等级,本质也是一个一个的task
notify定义在某个task的尾部,通过引用handlers名字调用handlers

playbook与tags

[root@host2 ansible-playbooks]# cat test-tag.yaml 
- hosts: 192.168.80.103
  remote_user: root
  tasks:
   - name: install nginx
     yum: name=nginx state=present
   - name: copy conf file
     copy: src=/tmp/nginx.txt dest=/root
     tags: conf
   - name: start nginx
     service: name=httpd state=started enabled=yes
     tags: service
# 给不同的task打上不同的tag,执行时,可以利用tag挑选出同类的任务执行

[root@host2 ansible-playbooks]# ansible-playbook -t conf -C  test-tag.yaml 

PLAY [192.168.80.103] **********************************************************************

TASK [Gathering Facts] *********************************************************************
ok: [192.168.80.103]

TASK [copy conf file] **********************************************************************
changed: [192.168.80.103]

PLAY RECAP *********************************************************************************
192.168.80.103             : ok=2    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0  

playbook传参3种方式

---

- hosts: test
  tasks:
   - yum: name=httpd state=present
   - yum: 
      name: httpd
      state: installed
    - yum:
      args:
       name: httpd
       state: installed

playbook执行过程!

  1. 先收集各个节点的信息,gather facts
  2. 执行一个个play
  3. 执行play中定义的一个个task
  4. 根据幂等性,不需要任务的task会不执行
  5. 最后有play recap,整体执行信息汇总输出
  6. 默认是同步阻塞,

playbook变量

变量分类

​ 大致分为七类:

  1. 模块执行结果的输出,注册为变量
  2. 在本地,直接定义字典类型的变量:/etc/ansible/facts.d/*.fact
  3. role中专门定义变量的文件var
  4. 命令行传递变量
  5. 借助with_items迭代,将多个task的结果赋值给一个变量
  6. inventory主机清单中,定义的主机变量、主机组变量,
  7. 内置变量

变量定义、注册

命名

​ 字母、数字、下划线组成,必须字母开头;

格式

​ k=v

调用方式

​ 通过{{ var_name }} 调用变量,**注意前后空格,**有时需要"{{ var_name }}"才生效

​ ansible-playbook test.yaml -e "host=www user=wang"

1、变量来源

  1. ansible setup facts 可以调用远程主机的所有变量

  2. 在/etc/ansible/hosts定义

    1. 公共变量:针对主机组所有主机生效
    2. 普通变量:对某主机组中主机单独定义
  3. 通过命令行指定变量,优先级最高

    1. ansible-playbook -e var=value
  4. playbook中定义

    1. vars:
       - var1: v1
       - var2: v2
      
  5. 在独立的变量yaml文件中定义

  6. 在role中定义

使用setup中变量

# 利用setup模块,可以收集,远程主机各种信息,如:地址,系统版本,内核版本,硬件架构等;
# 在执行playbook的默认第一步行为就是:gather facts,收集信息
# 这些信息,在后续的playbook,jinja2模块中,都可以直接引用;

[root@host2 ~]# ansible 192.168.80.102 -m setup |less
192.168.80.102 | SUCCESS => {
    "ansible_facts": {
        "ansible_all_ipv4_addresses": [
            "192.168.80.102", 
            "192.168.10.102"
        ], 
        "ansible_all_ipv6_addresses": [
            "fe80::eb02:a6b5:be84:952", 
            "fe80::7822:f591:feb0:47ea"
        ], 
        "ansible_apparmor": {
            "status": "disabled"
        }, 
        "ansible_architecture": "x86_64", 
        "ansible_bios_date": "07/29/2019", 
        "ansible_bios_version": "6.00", 
        "ansible_cmdline": {
            "BOOT_IMAGE": "/vmlinuz-3.10.0-862.el7.x86_64", 
            "biosdevname": "0", 
            "net.ifnames": "0", 
            "quiet": true, 
            "rhgb": true, 
            "ro": true, 
            "root": "UUID=046ebf1d-861c-42bb-a48f-9641dfc9afec"
        }, 
        
# 结合filter参数,可以过滤需要的变量
[root@host2 ~]# ansible 192.168.80.102 -m setup -a "filter=*arch*"
192.168.80.102 | SUCCESS => {
    "ansible_facts": {
        "ansible_architecture": "x86_64", 
        "ansible_userspace_architecture": "x86_64", 
        "discovered_interpreter_python": "/usr/bin/python"
    }, 
    "changed": false
}
[root@host2 ~]# ansible 192.168.80.102 -m setup -a "filter=*bios*"
192.168.80.102 | SUCCESS => {
    "ansible_facts": {
        "ansible_bios_date": "07/29/2019", 
        "ansible_bios_version": "6.00", 
        "discovered_interpreter_python": "/usr/bin/python"
    }, 
    "changed": false
}

​ 使用fqdn变量

[root@host2 vardir]# cat setup.yaml 
- hosts: all
  remote_user: root
  tasks:
   - name: create log file
     file: name=/var/log/{{ ansible_fqdn }} state=touch

# 通过调用setup中ansibl_fqdn变量,获取每个主机的主机名,并创建文件

[root@host2 vardir]# ansible-playbook setup.yaml 

[root@host3 ~]# ll /var/log/host3.b.com 
-rw-r--r-- 1 root root 0 Oct  9 16:45 /var/log/host3.b.com
[root@host4 ~]# ll /var/log/host4.b.com 
-rw-r--r-- 1 root root 0 Oct  9 16:45 /var/log/host4.b.com

命令行-e传变量值

yaml中定义变量名,命令行中传值

[root@host2 vardir]# cat var-pkname.yaml 
- hosts: all
  remote_user: root
  tasks:
   - name: install pk
     yum: name={{ pkname }} state=present

[root@host2 vardir]# ansible-playbook  -e pkname=libaio var-pkname.yaml 

vars批量定义变量

在playbook文件中,vars定义一批变量

[root@host2 vardir]# cat vars.yaml 
- hosts: all
  remote_user: root
  vars:
   - username: user1
   - groupname: group1
  tasks:
   - name: create-u
     user: name={{ username }} state=present
   - name: create-g
     group: name={{ groupname }} state=present

主机变量

​ 在inventory主机清单中,定义主机时,给主机添加的变量

语法格式;
[websrvs] 
www1.magedu.com http_port=80 maxRequestsPerChild=808
www2.magedu.com http_port=8080 maxRequestsPerChild=909 

示例:
[root@host2 vardir]# vim /etc/ansible/hosts
[test]
192.168.80.102 http_port=80
192.168.80.103 http_port=8080

[root@host2 vardir]# cat var.yaml 
---
- hosts: test
  remote_user: root
  tasks:
   - name: test1
     debug: msg="var is {{ http_port }}"


[root@host2 vardir]# ansible-playbook var.yaml 

PLAY [test] **********************************************************************************************************************************

TASK [Gathering Facts] ***********************************************************************************************************************
ok: [192.168.80.103]
ok: [192.168.80.102]

TASK [test1] *********************************************************************************************************************************
ok: [192.168.80.102] => {
    "msg": "var is 80"
}
ok: [192.168.80.103] => {
    "msg": "var is 8080"
}

主机组变量

语法格式:
[websrvs]
www1.magedu.com
www2.magedu.com 
 
[websrvs:vars] 
ntp_server=ntp.magedu.com 
nfs_server=nfs.magedu.com 

示例;
#定义的var变量,该test组内主机,都可以引用,为组内共享变量
[root@host2 vardir]# vim /etc/ansible/hosts 
[test]
192.168.80.102 http_port=80
192.168.80.103 http_port=8080

[test:vars]
var=hello-test

[root@host2 vardir]# cat var.yaml 
---
- hosts: test
  remote_user: root
  tasks:
   - name: test1
     debug: msg="var is {{ http_port }}{{ var }}"


[root@host2 vardir]# ansible-playbook var.yaml 

PLAY [test] **********************************************************************************************************************************

TASK [Gathering Facts] ***********************************************************************************************************************
ok: [192.168.80.103]
ok: [192.168.80.102]

TASK [test1] *********************************************************************************************************************************
ok: [192.168.80.102] => {
    "msg": "var is 80hello-test"
}
ok: [192.168.80.103] => {
    "msg": "var is 8080hello-test"
}

普通变量

语法格式:
[websrvs]
192.168.99.101 http_port=8080 hname=www1 
192.168.99.102 http_port=80    hname=www2 

示例:
定义的hname为每个主机独有的变量
[root@host2 vardir]# vim /etc/ansible/hosts 
[test]
192.168.80.102 http_port=80 hname=www1
192.168.80.103 http_port=8080 hname=www2

[root@host2 vardir]# cat var.yaml 
---
- hosts: test
  remote_user: root
  tasks:
   - name: test1
     debug: msg="var is {{ http_port }}{{ var }}-{{ hname }}"


[root@host2 vardir]# ansible-playbook var.yaml 

PLAY [test] **********************************************************************************************************************************

TASK [Gathering Facts] ***********************************************************************************************************************
ok: [192.168.80.102]
ok: [192.168.80.103]

TASK [test1] *********************************************************************************************************************************
ok: [192.168.80.102] => {
    "msg": "var is 80hello-test-www1"
}
ok: [192.168.80.103] => {
    "msg": "var is 8080hello-test-www2"
}

公共变量

​ 同,组变量

[websvrs:vars] 
http_port=808
mark=“_” 

[websrvs]
192.168.99.101 http_port=8080 hname=www1
192.168.99.102 http_port=80 hname=www2 

ansible  websvrs  –m hostname –a ‘name={{ hname }}{{ mark }}{{ http_port }}’ 

命令行变量

-e选项,传入变量,采用hostname模块修改主机名,因为变量一致,所以修改后主机名一致,一般还需要引入每个主机自己的独有变量,如可以在定义主机清单的时候定义
[root@host2 vardir]# ansible test -e role=www -m hostname -a 'name={{ role }}.host.com'
192.168.80.103 | CHANGED => {
    "ansible_facts": {
        "ansible_domain": "host.com", 
        "ansible_fqdn": "www.host.com", 
        "ansible_hostname": "www", 
        "ansible_nodename": "www.host.com", 
        "discovered_interpreter_python": "/usr/bin/python"
    }, 
    "changed": true, 
    "name": "www.host.com"
}
192.168.80.102 | CHANGED => {
    "ansible_facts": {
        "ansible_domain": "host.com", 
        "ansible_fqdn": "www.host.com", 
        "ansible_hostname": "www", 
        "ansible_nodename": "www.host.com", 
        "discovered_interpreter_python": "/usr/bin/python"
    }, 
    "changed": true, 
    "name": "www.host.com"
}

ansible test -e role=www -m hostname -a 'name={{ role }}.{{ host }}.com'

变量文件

# 以yaml或json格式的文件,定义变量
 [root@host2 vardir]# cat vars.yaml 
---
var1: httpd
var2: nginx

# 在playbook中,用vars_files引用文件路径,然后就可以按需引用其中的变量,
# 文件路径,可以绝对,可以相对
[root@host2 vardir]# cat vars-use.yaml 
- hosts: all
  remote_user: root
  vars_files:
   - vars.yaml
  tasks:
   - name: create httpd log
     file: name=/root/{{ var1 }}.log state=touch
   - name: create nginx log
     file: name=/root/{{ var2 }}.log state=touch
   
 
 [root@host2 vardir]# ansible-playbook vars-use.yaml 

定义本地facts

​ ansible会自动收集/etc/ansible/facts.d/*.fact文件中数据到facts中,**以ansible_local作为顶级key,**支持文件格式为ini,json

​ eg:

[root@host2 facts.d]# pwd
/etc/ansible/facts.d
[root@host2 facts.d]# cat my.fact 
{
	"family": {
		"dad": {
			"name": "zhangsan",
      			"age": "666"
		},
     		"mom": {
			"name": "lisi",
  			"age": "233"
		}
	}
}
# 定义本地facts如上
# 过滤时发现,本地fact在ansibl_facts下一级,并以ansible_local作为次级key,
# 注意:my为本地fact的文件名,要加上
[root@host2 facts.d]# ansible localhost -m setup -a 'filter=ansible_local'
localhost | SUCCESS => {
    "ansible_facts": {
        "ansible_local": {
            "my": {
                "family": {
                    "dad": {
                        "age": "666", 
                        "name": "zhangsan"
                    }, 
                    "mom": {
                        "age": "233", 
                        "name": "lisi"
                    }
                }
            }
        }
    }, 
    "changed": false
}

# 引用路径:
[root@host2 facts.d]# cat /root/var_jason.yaml 
---
- hosts: localhost
  tasks:
   - shell: echo hello-world
     register: var1
   - debug: var=var1
   - debug: var=var1['stdout']
   - debug: var=var1.stdout_lines[0]
   - debug: var=ansible_eth1.ipv6[0].address
   - debug: var=ansible_local.my.family.dad.name

register注册变量

#register捕捉标准输出的信息,注册为变量var1
#再通过debug模块输出该变量

[root@host2 ~]# cat reg.yaml 
---
- hosts: localhost
  tasks:
   - shell: echo hello
     register: var1
   - debug: var=var1

set_fact注册变量

[root@host2 ~]# cat set.yaml 
---
- hosts: localhost
  tasks:
   - shell: echo hello
   - set_fact: var1="my var"
   - debug: var=var1

变量引用

变量过滤

​ 每个节点的收集的facts都是json格式数据,通过setup模块的filter参数可以过滤出特定key的fact,如下过滤ipv4相关的变量信息

[root@host2 ~]# ansible 192.168.80.102 -m setup -a 'filter=*ipv4'
192.168.80.102 | SUCCESS => {
    "ansible_facts": {
        "ansible_default_ipv4": {
            "address": "192.168.80.102", 
            "alias": "eth0", 
            "broadcast": "192.168.80.255", 
            "gateway": "192.168.80.2", 
            "interface": "eth0", 
            "macaddress": "00:0c:29:1b:4e:70", 
            "mtu": 1500, 
            "netmask": "255.255.255.0", 
            "network": "192.168.80.0", 
            "type": "ether"
        }
    }, 
    "changed": false
}

引用json字典

[root@host2 ~]# cat var_jason.yaml 
---
- hosts: 192.168.80.102
  tasks:
   - shell: echo hello-world
     register: var1
   - debug: var=var1
   - debug: var=var1['stdout']

# shell这个task执行后结果,标准输出为hello-world,用register捕捉,注册为变量,变量名为var1,
# debug模块,输出该变量,输出为json数据,并附带了其他信息,组成了var1的一组字典信息;

# 第2个debug模块,通过调用该变量的key为stdout,输出其值,
TASK [debug] *******************************************************************************
ok: [192.168.80.102] => {
    "var1": {
        "changed": true, 
        "cmd": "echo hello-world", 
        "delta": "0:00:00.004388", 
        "end": "2020-10-15 10:33:01.317302", 
        "failed": false, 
        "rc": 0, 
        "start": "2020-10-15 10:33:01.312914", 
        "stderr": "", 
        "stderr_lines": [], 
        "stdout": "hello-world", 
        "stdout_lines": [
            "hello-world"
        ]
    }
}

TASK [debug] *******************************************************************************
ok: [192.168.80.102] => {
    "var1['stdout']": "hello-world"
}

引用json数组

​ 以上面输出为例,stdout_line的值部分又是一个数组列表;

[root@host2 ~]# cat var_jason.yaml 
---
- hosts: 192.168.80.102
  tasks:
   - shell: echo hello-world
     register: var1
   - debug: var=var1
   - debug: var=var1['stdout']
   - debug: var=var1.stdout_lines[0]
# 数据用数字的数组下标引用


TASK [debug] *******************************************************************************
ok: [192.168.80.102] => {
    "var1['stdout']": "hello-world"
}

TASK [debug] *******************************************************************************
ok: [192.168.80.102] => {
    "var1.stdout_lines[0]": "hello-world"
}

eg2:

ipv6": [
                {
                    "address": "fe80::eb02:a6b5:be84:952", 
                    "prefix": "64", 
                    "scope": "link"
                }
            ], 



# facts输出中,ansible_eth1的ipv6字典项值为数组,每个数组值又为一组字典
# 引用方式如下
cat var_jason.yaml 
---
- hosts: 192.168.80.102
  tasks:
   - shell: echo hello-world
     register: var1
   - debug: var=var1
   - debug: var=var1['stdout']
   - debug: var=var1.stdout_lines[0]
   - debug: var=ansible_eth1.ipv6[0].address
   
TASK [debug] *******************************************************************************
ok: [192.168.80.102] => {
    "ansible_eth1.ipv6[0].address": "fe80::7822:f591:feb0:47ea"
}

引用facts变量

​ 收集后数据为json数组,顶级有2个key,一个是ansible_facts,一个是changed,节点信息都在ansible_facts中,引用时,ansible_facts可以省略,可以直接从其下一级的key开始引用

如:ansible_eth0,"ansible_architecture": "x86_64", 
    "ansible_bios_date": "07/29/2019", 
    "ansible_bios_version": "6.00", 
    "ansible_cmdline": {

变量输出

​ 采用debug模块的var或msg参数可以输出,变量,做调试之用

[root@host2 ~]# cat var_jason.yaml 
---
- hosts: localhost
  tasks:
   - debug: 'msg="ip-value is: {{ ansible_eth0.ipv4.address }}"'
   - debug: var=ansible_eth0.ipv4.address
# 注意,msg外部要加上单引号

输出结果如下:

[root@host2 ~]# ansible-playbook var_jason.yaml 

PLAY [localhost] ***************************************************************************

TASK [Gathering Facts] *********************************************************************
ok: [localhost]

TASK [debug] *******************************************************************************
ok: [localhost] => {
    "msg": "ip-value is: 192.168.80.101"
}

TASK [debug] *******************************************************************************
ok: [localhost] => {
    "ansible_eth0.ipv4.address": "192.168.80.101"

模板template

https://docs.ansible.com/ansible/latest/collections/ansible/builtin/template_module.html

概念

template功能:根据模块文件,动态生成对应的配置文件

  • templa文件必须放在templates目录下,以.j2结尾

  • yaml或yml文件需和templates目录平级,目录结构如下:

    • ./
      temnginx.yaml
      templates
          nginx.conf.j2
      

​ 文本文件;嵌套有脚本(使用模版编程语言编写),jinja2语言,有如下形式:

  • 字符串:单引号或双引号
  • 数字:整数、浮点数
  • 列表:[item1,item2,...]
  • 元组:(item1,item2)
  • 字典:[k1:v1,k2:v2]
  • 布尔型:true,false
  • 算术运算: + - * / // % **
  • 比较运算:== != > >= < <=
  • 逻辑运算:and or not
  • 流表达式: for if when

示例算术运算

​ 如下为,根据每个主机的cpu核数不同,利用templates模版文件,生成不同work_processor配置选项的示例

采用了算术运算

[root@host2 template]# ansible test -m setup |grep processor
        "ansible_processor": [
        "ansible_processor_cores": 2, 
        "ansible_processor_count": 2, 
        "ansible_processor_threads_per_core": 1, 
        "ansible_processor_vcpus": 4, 
        "ansible_processor": [
        "ansible_processor_cores": 1, 
        "ansible_processor_count": 2, 
        "ansible_processor_threads_per_core": 1, 
        "ansible_processor_vcpus": 2, 
# 2个主机,一个为4核,一个为2核        
# 根据如下目录结构,编写对应文件
[root@host2 template]# tree
.
├── temnginx.yaml
└── templates
    └── nginx.conf.j2

# yaml文件,为playbook,其中利用templates模块调用了nginx.conf的模版文件    
[root@host2 template]# cat temnginx.yaml 
- hosts: test
  remote_user: root
  tasks:
   - name: template config to remote-hosts
     template: src=nginx.conf.j2 dest=/etc/nginx/nginx.conf

# 模版文件如下,为原有nginx.conf修改而来,修改了其中worker_processes,利用了setup变量每个主机的cpu核数,的平方做为参数值
[root@host2 template]# head templates/nginx.conf.j2 
user nginx;
worker_processes {{ ansible_processor_vcpus**2 }};

# 成功执行
[root@host2 template]# ansible-playbook temnginx.yaml 

PLAY [test] **********************************************************************************************************************************

TASK [Gathering Facts] ***********************************************************************************************************************
ok: [192.168.80.102]
ok: [192.168.80.103]

TASK [template config to remote-hosts] *******************************************************************************************************
changed: [192.168.80.103]
changed: [192.168.80.102]

PLAY RECAP ***********************************************************************************************************************************
192.168.80.102             : ok=2    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   
192.168.80.103             : ok=2    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   
# 查看2台主机,生成的配置文件,分别为4,和16,为2和4的平方
[root@host4 ~]# head /etc/nginx/nginx.conf 

user nginx;
worker_processes 4;
error_log /var/log/nginx/error.log;
pid /run/nginx.pid;


[root@host3 ~]# head /etc/nginx/nginx.conf

user nginx;
worker_processes 16;
error_log /var/log/nginx/error.log;
pid /run/nginx.pid;

示例条件判断

​ 根据变量,facts,或此前任务的执行结果来做某task执行的前提时,需要用when实现,直接在某task后添加when子句+条件即可

​ jinja2语法示例:

tasks:
 - name: "shutdown redhat flavored systems"
   command: /sbin/shudown -h now
   when: ansible_os_family == "redhat"

通过os版本做判断条件

[root@host2 template]# cat when.yaml 
- hosts: test
  remote_user: root
  tasks:
   - name: add group nginx
     tags: user
     group: name=nginx state=present
   - name: add user nginx
     user: name=nginx state=present group=nginx
   - name: install nginx
     yum: name=nginx state=present
   - name: stop nginx
     service: name=nginx state=stoped
     when: ansible_distribution_major_version == "7"
     
# 在执行stop nginx时,加了when条件,只有版本是7的centos上的nginx会被停止     

示例迭代with_items

​ 有需要重复执行的任务,可以采用迭代机制;迭代的引用固定变量名为item,task中使用with_items给定要迭代的元素列表

​ 列表格式:字符串,字典

1、迭代创建用户

在task中用with_items定义列表,用item变量引用,然后创建2个用户

[root@host2 template]# cat items.yaml 
- hosts: 192.168.80.102
  remote_user: root
  tasks:
   - name: add serveral users;
     user: name={{ item }} state=present groups=wheel
     with_items:
      - testuser1
      - testuser2

testuser1:x:1003:1004::/home/testuser1:/bin/bash
testuser2:x:1004:1005::/home/testuser2:/bin/bash
[root@host3 ~]# cat /etc/passwd

2、迭代copy多个文件

[root@host2 template]# touch /etc/file1

[root@host2 template]# touch /etc/file2

[root@host2 template]# cat !$
cat /root/copy-multi.yaml
---
- hosts: test
  remote_user: root
  tasks:
   - name: create two file and copy
     copy: src={{ item }} dest=/tmp
     with_items:
      - /etc/file1
      - /etc/file2


[root@host3 ~]# ll /tmp/file*
-rw-r--r-- 1 root root 0 Oct 10 15:57 /tmp/file1
-rw-r--r-- 1 root root 0 Oct 10 15:57 /tmp/file2

3、迭代安装rpm包

[root@host2 template]# cat rpm-install.yaml 
- hosts: test
  remote_user: root
  tasks:
   - name: install rpms
     yum: name={{ item }} state=present
     with_items:
      - memcached
      - php-fpm

4、迭代-嵌套-子变量

# item元素为字典形式,通过其key再引用每个key的值;

[root@host2 template]# cat !$
cat diedai.yaml
- hosts: test
  remote_user: root
  tasks:
   - name: add groups
     group: name={{ item }} state=present
     with_items:
      - group1
      - group2
      - group3
   - name: add users
     user: name={{ item.name }} group={{ item.group }} state=present
     with_items:
      - { name: 'user1', group: 'group1' }
      - { name: 'user2', group: 'group2' }
      - { name: 'user3', group: 'group3' }


[root@host2 template]# ansible-playbook diedai.yaml 

PLAY [test] ********************************************************************************

TASK [Gathering Facts] *********************************************************************
ok: [192.168.80.103]
ok: [192.168.80.102]

TASK [add groups] **************************************************************************
ok: [192.168.80.102] => (item=group1)
ok: [192.168.80.103] => (item=group1)
changed: [192.168.80.103] => (item=group2)
changed: [192.168.80.102] => (item=group2)
changed: [192.168.80.103] => (item=group3)
changed: [192.168.80.102] => (item=group3)

TASK [add users] ***************************************************************************
changed: [192.168.80.103] => (item={u'group': u'group1', u'name': u'user1'})
changed: [192.168.80.102] => (item={u'group': u'group1', u'name': u'user1'})
changed: [192.168.80.102] => (item={u'group': u'group2', u'name': u'user2'})
changed: [192.168.80.103] => (item={u'group': u'group2', u'name': u'user2'})
changed: [192.168.80.102] => (item={u'group': u'group3', u'name': u'user3'})
changed: [192.168.80.103] => (item={u'group': u'group3', u'name': u'user3'})



[root@host3 ~]# id user1
uid=1002(user1) gid=1003(group1) groups=1003(group1)
[root@host3 ~]# id user2
uid=1005(user2) gid=1006(group2) groups=1006(group2)
[root@host3 ~]# id user3
uid=1006(user3) gid=1007(group3) groups=1007(group3)

template的for循环 if判断

template文件中的if判断语法,

​ 示例格式:for遍历vhost,if做条件判断,根据其模版生成不同的server虚拟主机配置段

{% for vhost in nginx_vhosts %}

server {

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

{% if vhost.server_name if defined %}
server_name {{ vhost.server_name }}
{% endif %}

{% if vhost.root is defined %}
root {{ vhost.root }};
{% endif %}

}

{% endfor %}

示例template for和if

yaml文件和对应j2模版如下:

[root@host2 nginx-web]# tree 
.
├── temnginx.yaml
└── templates
    └── nginx.conf.j2

[root@host2 nginx-web]# cat temnginx.yaml 
- hosts: all 
  remote_user: root
  vars:
   nginx_vhosts:
    - web1:
      listen: 8080
      server_name: "web1.b.com"
      root: "/data/web1"
    - web2:
      listen: 8080
      server_name: "web2.b.com"
      root: "/data/web2"
    - web3:
      listen: 80
      server_name: "web3.b.com"
      root: "/data/web3"
  tasks:
   - name: copy template conf file
     template: src=nginx.conf.j2 dest=/etc/nginx/nginx.conf

# 用了for循环,生成3个server虚拟主机配置段
# 用了if判断
# 用了默认值设置
[root@host2 nginx-web]# cat templates/nginx.conf.j2 
{% for vhost in nginx_vhosts %}

server {
{% if vhost.listen is defined %}
listen {{ vhost.listen }};
{% endif %}

server_name {{ vhost.server_name }};
root {{ vhost.root | default('/data') }};
}
{% endfor  %}

查看,生成的配置文件

[root@host2 nginx-web]# ansible-playbook temnginx.yaml 
# 执行;
[root@host3 ~]# cat /etc/nginx/nginx.conf

server {
listen 8080;

server_name web1.b.com;
root /data/web1;
}

server {
listen 8080;

server_name web2.b.com;
root /data/web2;
}

server {
listen 80;

server_name web3.b.com;
root /data/web3;
}
# 和yaml中,定义的vars变量一一对应;
# 模版的好处:只修改变量部分,可以很高效的生成别的配置文件,
# var可以采用其他方法定义,如单独的var文件
[root@host2 nginx-web]# cat temnginx.yaml 
- hosts: all 
  remote_user: root
  vars:
   nginx_vhosts:
    - web1:
      listen: 8080
      server_name: "web1.b.com"
      root: "/data/web1"
    - web2:
      listen: 8080
      server_name: "web2.b.com"
      root: "/data/web2"
    - web3:
      listen: 80
      server_name: "web3.b.com"
      root: "/data/web3"

采用默认值情况;web3的root变量没定义;

[root@host2 nginx-web]# cat temnginx.yaml 
- hosts: all 
  remote_user: root
  vars:
   nginx_vhosts:
    - web1:
      listen: 8080
      server_name: "web1.b.com"
      root: "/data/web1"
    - web2:
      listen: 8080
      server_name: "web2.b.com"
      root: "/data/web2"
    - web3:
      listen: 80
      server_name: "web3.b.com"
  tasks:
   - name: copy template conf file
     template: src=nginx.conf.j2 dest=/etc/nginx/nginx.conf
     
# web3的root采用默认字段,为/data     
[root@host3 ~]# cat /etc/nginx/nginx.conf

server {
listen 8080;

server_name web1.b.com;
root /data/web1;
}

server {
listen 8080;

server_name web2.b.com;
root /data/web2;
}

server {
listen 80;

server_name web3.b.com;
root /data;
}     

roles

概念

​ ansible自1.2后引入的特性;用于层次化,结构化,组成playboo,roles能够根据层次型结构自动加载变量文件,task,handler等;

​ 使用roles时需要在playbook中用include导入即可,

roles就是将变量、文件、任务、模版、处理器按照一定组织结构放在单独目录中,并 用include导入的一种机制,

一般用于构建,基于主机构建服务的场景中,也可以构建守护进程

​ 复杂场景,适合使用roles,代码复用度高

简单说:role就是拆解大的playbook的一种方式,以一定规则组成目录结构,可以复用其中的小的task或play,通过include导入即可;

roles目录结构

image-20201010181654569

playbook.yaml
roles/
	project/
		tasks/
		files/
		vars/
		templates/
		handlers/
		default/
		meta/
		后2者不常用
files:存放由copy,或script模块,调用的文件
templates:template模块查找模版文件的目录
tasks:定义task,role的基本元素,至少应包括一个名为main.yaml的文件;其他的文件通过include包含

handlers:至少包含一个main.yaml,其他文件用include导入

vars:定义变量,至少有一个main.yaml,其他用include导入

meta:定义当前角色的特殊设定,和其依赖,至少一个main.yaml,其他文件include导入

default:设定默认变量时,用此目录的main.yaml文件

创建role步骤

  1. 创建以roles命令的目录;
  2. 在roles目录中分别创建以各个角色名称命名的目录,如webservers等
  3. 在每个角色的目录中分别创建:files、handlers、meta、taskstemplates、vars等目录,没有用到的目录可以创建为空,也可以不创建
  4. 在playbook中,调用各个角色;
#示例目录结构如下,
playbook为nginx-role.yaml
roles目录下有一个role目录,为nginx
nginx目录下,有一系列固定结构的目录
[root@host2 workspace]# tree .
.
├── nginx-role.yaml
└── roles
    └── nginx
        ├── files
        │   └── main.yaml
        ├── tasks
        │   ├── groupadd.yaml
        │   ├── install.yaml
        │   ├── main.yaml
        │   ├── restart.yaml
        │   └── useradd.yaml
        └── vars
            └── main.yaml

playbook调用roles

方法1:

- hosts: webservers
  remote_user: root
  roles:
   - mysql
   - memcached
   - nginx
   

方法2:

- hosts:
  remote_user:
  roles;
   - mysql
   - { role: nginx,username: nginx }
   键role指定调用nginx这个role
   后续是给username这个变量传值为nginx

方法3:

roles:
 - { role: nginx,username: nginx,when: ansible_distribution_major_version == '7' }
 #加入了when条件判断

完整roles架构示例

# 顶层的playbook中,定义了调用了2个role
[root@host2 workspace]# cat nginx-role.yaml 
- hosts: test
  remote_user: root
  roles:
   - nginx
   - httpd


# 以nginx的role为例,task的main.yaml调用了一个个小的task文件
[root@host2 workspace]# cat roles/nginx/tasks/main.yaml 
- include: groupadd.yaml
- include: useradd.yaml
- include: install.yaml
- include: restart.yaml
- include: filecp.yaml

# tasks中其他yaml文件,将是将原来的task拆分,写到一个个文件中而已
[root@host2 workspace]# cat roles/nginx/tasks/groupadd.yaml 
- name: add group nginx
  group: name=nginx state=present

[root@host2 workspace]# cat roles/nginx/tasks/filecp.yaml 
- name: file copy
  copy: src=tom.conf dest=/tmp/tom.conf
  
# task中copy或scripts调用的文件,都放在files目录下  
[root@host2 workspace]# cat roles/nginx/files/tom.conf 

playbook中 tags使用

ansible-playbook --tags="nginx.httpd,mysql" nginx-role.yaml
#只调用带有对应标签的role

# nginx-role.yaml中
- hosts: test
  remote_user: root
  roles:
   - { role: nginx,tags: [ 'nginx','web' ],when: ansible_distribution_major_version == '6' }
   - { role: httpd,tags: [ 'httpd','web' ] }
   - { role: mysql,tags: [ 'mysql','db' ] }
   - { role: mariadb,tags: [ 'mysql','db' ] }
   - { role: php }
# tags是列表形式,注意括号前后的空格   

实验:playbook编译安装httpd

tasks各个步骤

  1. get_url模块:下载源码包:apr apr-util httpd
  2. yum模块:下载依赖包和编译工具:gcc gcc++ zlib-devel openssl-devel等
  3. unarchive模块,解压源码包
  4. shell模块,依次编译apr apr-util httpd
  5. user模块,创建apache用户
  6. copy模块或template,准备配置文件,服务管理脚本等,
  7. shell模块,path变量配置,配置文件语法检测,服务启动等

参考yaml

[root@host2 httpd]# cat httpd.yaml 
- hosts: 192.168.80.102
  remote_user: root
  tasks:
   - name: down source tar
     get_url: url="{{ item }}" dest=/root/pkg
     with_items:
      - https://mirrors.aliyun.com/apache/apr/apr-1.6.5.tar.gz
      - https://mirrors.aliyun.com/apache/apr/apr-util-1.6.1.tar.gz
      - https://mirrors.aliyun.com/apache/httpd/httpd-2.4.46.tar.gz
     run_once: True
   - name: unarchive
     unarchive: src="/root/pkg/{{ item }}" dest=/root
     with_items:
      - httpd-2.4.46.tar.gz
      - apr-1.6.5.tar.gz
      - apr-util-1.6.1.tar.gz

   - name: install pcre prce-devel
     yum: name="{{ item }}" state=present
     with_items:
      - pcre
      - pcre-devel
      - expat-devel
   - name: compile apr 
     shell: cd /root/apr-1.6.5 && ./configure --prefix=/usr/local/apr && make && make install
   - name: compile apr-util
     shell: cd /root/apr-util-1.6.1 && ./configure --prefix=/usr/local/apr-util --with-apr=/usr/local/apr && make && make install
   - name: complie httpd
     shell: |
            cd /root/httpd-2.4.46
            ./configure --prefix=/usr/local/apache --sysconfig=/etc/apache \
            --enable-mpms-shared=all \
            --with-z --with-pcre \
            --with-apr=/usr/local/apr \
            --with-apr-util=/usr/local/apr-util \
            --with-mpm-event
             make && make install
           

ansible执行过程

执行过程总结

  1. 读取配置文件,默认/etc/ansible/ansible.cfg
  2. 加载inventory文件,包括主机变量,主机组变量
  3. 执行默认,第一个任务,收集各个节点的信息
    1. 建立连接,获取家目录信息
    2. 将要执行的收集任务,放在临时文件
    3. 将临时文件传输到被控节点的临时目录
    4. ssh连接到远端执行收集任务
    5. 删除任务文件
    6. 收集信息返回给ansible端,此处各种变量可以被之后各步骤引用!
    7. 关闭连接
  4. 第二个任务,此处开始,为用户定义的任务
    1. 建立连接,获取家目录信息
    2. 将要执行的任务放到临时文件中
    3. 将临时文件,sftp传输到远端节点
    4. ssh连接到远端执行任务
    5. 删除任务文件
    6. 执行结果返回给ansible端,ansible输出到屏幕
    7. 关闭连接
  5. 后续任务...,遵从同样的步骤

假设有10个任务,主机数量较多的时候,ansible会将主机分批次执行第一个任务,直到所有主机都执行完第一个任务后,再开始第二个任务,也是分批次执行,以此类推;

直到所有节点,执行完所有任务,ansible才会释放当前shell,此乃ansible默认的同步模式;并发默认为5 ,-f可以指定并发数

ansible优化速度

配置开启ssh长连接

​ 为避免需要长时间执行的任务,中途断开,可以配置长连接时间久一点;

[ssh_connection]

# ssh arguments to use
# Leaving off ControlPersist will result in poor performance, so use
# paramiko on older platforms rather than removing it, -C controls compression use
#ssh_args = -C -o ControlMaster=auto -o ControlPersist=60s

开启pipline

​ ansible在执行一个任务期间,将任务文件放到临时文件中后,会sftp复制传输到远端,然后ssh连接到远端执行该任务文件,共2次ssh连接,pipline为openssh支持的特性,可以在一次ssh连接中完成上述2步,

​ 配置文件:/etc/ansible/ansible.cfg

# Enabling pipelining reduces the number of SSH operations required to
# execute a module on the remote server. This can result in a significant
# performance improvement when enabled, however when using "sudo:" you must
# first disable 'requiretty' in /etc/sudoers
#
# By default, this option is disabled to preserve compatibility with
# sudoers configurations that have requiretty (the default on many distros).
#
#pipelining = False

修改ansible执行策略

​ ansible默认fork为5,假设共20台,一次5台执行任务,假设其中一台主机性能好,早早完成,该空出的并发名额会等待,其他四个小伙伴一起结束,再一起分配下一个5台主机执行任务,造成浪费

​ ansible 2.0后,通过strategy设置为free,默认值是linear,使得空出来的名额,可以立即分配给后面的主机开始执行,

- hosts: all
  strategy: free
  tasks:
  ...

设置facts缓存

​ 收集各个节点的facts为默认行为,主机数量多时,收集facts会消耗一定时间;虽然可以禁止改行为,但其中变量一般很有用,所以,设置facst缓存很有必要:

# plays will gather facts by default, which contain information about
# the remote system.
#
# smart - gather by default, but don't regather if already gathered
# implicit - gather by default, turn off with gather_facts: False
# explicit - do not gather by default, must say gather_facts: True
#gathering = implicit
默认行为收集,在playbook中,可以通过gather_facts改变默认行为
设置为smart为启用缓存,在空闲时收集,并缓存;

缓存有redis和jsonfile两种形式;

## jsonfile示例
gathering=smart
fact_caching_timeout =86400
fact_caching=jsonfile
fact_caching_connection= /path/to/factdir
## 缓存1天,位置在/path/to/factdir目录下

## 
[root@host2 httpd]# ll /tmp/factdir/
total 48
-rw-r--r-- 1 root root 24549 Oct 14 16:26 192.168.80.102
-rw-r--r-- 1 root root 24365 Oct 14 16:26 192.168.80.103
[root@host2 httpd]# cat /tmp/factdir/192.168.80.102 | python -m json.tool

查看ansible执行过程示例

​ ansible或ansible-playbook的执行中,加入-vvv参数可以显示不同详细等级的执行信息,如下:

[root@host2 httpd]# ansible-playbook -vvv  httpd.yaml 

# 先确认ansible读取的配置文件,插件路径,python解释器路径,可执行程序路径等

ansible-playbook 2.9.13
  config file = /etc/ansible/ansible.cfg
  configured module search path = [u'/root/.ansible/plugins/modules', u'/usr/share/ansible/plugins/modules']
  ansible python module location = /usr/lib/python2.7/site-packages/ansible
  executable location = /usr/bin/ansible-playbook
  python version = 2.7.5 (default, Apr 11 2018, 07:36:10) [GCC 4.8.5 20150623 (Red Hat 4.8.5-28)]
Using /etc/ansible/ansible.cfg as config file
host_list declined parsing /etc/ansible/hosts as it did not pass its verify_file() method
script declined parsing /etc/ansible/hosts as it did not pass its verify_file() method
auto declined parsing /etc/ansible/hosts as it did not pass its verify_file() method
Parsed /etc/ansible/hosts inventory source with ini plugin

# 找出有几个play,
PLAYBOOK: httpd.yaml *************************************************************************************************************************
1 plays in httpd.yaml

PLAY [192.168.80.102] ************************************************************************************************************************
# 默认行为,收集节点信息,
TASK [Gathering Facts] ***********************************************************************************************************************
task path: /root/httpd/httpd.yaml:1
<192.168.80.102> ESTABLISH SSH CONNECTION FOR USER: root

参考链接

updatedupdated2021-03-092021-03-09
加载评论