1、忽略错误 ignore_errors
如果一个task出错,默认将不会继续执行后续的其它task
利用 ignore_errors:yes 可以忽略此task的错误,继续向下执行playbook其它task
[root@ansible ansible]#vim test_ignore.yml
---
- hosts: websrvs
tasks:
- name: error
command: /bin/false
ignore_errors: yes
- name: continue
command: wall continue
2、Playbook中使用handlers和notify
2.1、handlers和notify
Handlers本质是task list ,类似于MySQL中的触发器触发的行为,其中的task与前述的task并没有本质上的不同,只有在关注的资源发生变化时,才会采取一定的操作。
Notify对应的action 在所有task都执行完才会最后被触发,这样可避免多个task多次改变发生时每次都触发执行指定的操作,Handlers仅在所有的变化发生完成后一次性地执行指定操作。
在notify中列出的操作称为handler,也即notify中调用handler中定义的操作
注意:
- 如果多个task通知了相同的handlers, 此handlers仅会在所有task结束后运行一 次。
- 只有notify对应的task发生改变了才会通知handlers, 没有改变则不会触发handlers
- handlers 是在所有前面的tasks都成功执行才会执行,如果前面任何一个task失败,会导致handler跳过执行
[root@ansible ansible]#cat install_nginx.yml
---
#install nginx
- hosts: webservers
gather_facts: no
tasks:
- name: install package
yum:
name: nginx
- name: config file
copy:
src: nginx.conf
dest: /etc/nginx/nginx.conf
notify:
- restart nginx
- notify message
tags:
- config
- name: Web Content
copy:
content: "App Version {{ ansible_eth0.ipv4.address.split('.')[-1]}}"
dest: /usr/share/nginx/html/index.html
- name: start service
service:
name: nginx
state: started
enabled: yes
handlers:
- name: restart nginx
service: name=nginx state=restarted
- name: notify message
debug: msg="nginx is restarted"
2.2、force_handlers
如果不论前面的task成功与否,都希望handlers能执行,可以使用force_handlers: yes 强制执行handler
- hosts: webservers
force_handlers: yes
tasks:
- name: config file
copy:
src: nginx.conf
dest: /etc/nginx/nginx.conf
notify: restart nginx
- name: install package
yum:
name: no_exist_package
handlers:
- name: restart nginx
service:
name: nginx
state: restarted
3、Playbook中使用tags组件
默认情况下, Ansible 在执行一个 playbook 时,会执行 playbook 中所有的任务
在playbook文件中,可以利用tags组件,为特定 task 指定标签,当在执行playbook时,可以只执行特定tags的task,而非整个playbook文件
可以一个task对应多个tag,也可以多个task对应同一个tag
还有另外3个特殊关键字用于标签, tagged, untagged 和 all,它们分别是仅运行已标记,只有未标记和所有任务。
tags 主要用于调试环境
[root@ansible ansible]# vim install_httpd.yml
---
- hosts: webservers
remote_user: root
gather_facts: no
tasks:
- name: install httpd
yum:
name: httpd
- name: modify config listen port
lineinfile:
path: /etc/httpd/conf/httpd.conf
regexp: '^Listen'
line: 'Listen 8080'
- name: modify config data1
lineinfile:
path: /etc/httpd/conf/httpd.conf
regexp: '^DocumentRoot "/var/www/html"'
line: 'DocumentRoot "/data/html"'
- name: modify config data2
lineinfile:
path: /etc/httpd/conf/httpd.conf
regexp: '^<Directory "/var/www/html">'
line: '<Directory "/data/html">'
- name: mkdir website dir
file:
path: /data/html
state: directory
- name: web html
copy:
src: files/index.html
dest: /data/html/
tags: copy
- name: start service
service:
name: httpd
state: started
enabled: yes
tags: service
# 列出标签
[root@ansible ansible]# ansible-playbook --list-tags install_httpd.yml
playbook: install_httpd.yml
play #1 (webservers): webservers TAGS: []
TASK TAGS: [copy, service]
# 执行有指定标签的任务
[root@ansible ansible]# ansible-playbook -t service install_httpd.yml
# 忽略执行指定标签的task
[root@ansible ansible]# ansible-playbook --skip-tags service install_httpd.yml
# 忽略执行没有标签的任务,即只执行有标签的任务
[root@ansible ansible]# ansible-playbook install_httpd.yml --skip-tags untagged
4、Playbook中使用变量
Playbook中同样也支持变量
变量名:仅能由字母、数字和下划线组成,且只能以字母开头
变量定义
variable=value
variable: value
http_port=80
http_port: 80
变量调用方式
通过 {{ variable_name }} 调用变量,且变量名前后建议加空格,有时用”{{ variable_name }}”才生效
变量来源
- ansible 的 setup facts 远程主机的所有变量都可直接调用
- 通过命令行指定变量,优先级最高
ansible-playbook -e varname=value test.yml
- 在playbook文件中定义
vars:
var1: value1
var2: value2
- 在独立的变量YAML文件中定义
- hosts: all
vars_files:
- vars.yml
- 在主机清单文件中定义
主机(普通)变量:主机组中主机单独定义,优先级高于公共变量
组(公共)变量:针对主机组中所有主机定义统一变量
- 在项目中针对主机和主机组定义
在项目目录中创建 host_vars和group_vars目录
- 在role中定义
变量的优先级从高到低如下
-e 选项定义变量 -->playbook中vars_files --> playbook中vars变量定义 -->host_vars/主机名文件 -->主机清单中主机变量--> group_vars/主机组名文件-->group_vars/all文件--> 主机清单组变量
4.1、使用 setup 模块中变量
使用 facts 变量
本模块自动在playbook调用,生成的系统状态信息, 并将之存放在facts变量中
facts 包括的信息很多,如: 主机名,IP,CPU,内存,网卡等
facts 变量的实际使用场景案例
- 通过facts变量获取被控端CPU的个数信息,从而生成不同的Nginx配置文件
- 通过facts变量获取被控端内存大小信息,从而生成不同的memcached的配置文件
- 通过facts变量获取被控端主机名称信息,从而生成不同的Zabbix配置文件
- 通过facts变量获取被控端网卡信息,从而生成不同的主机名
# 范例
[root@ansible ansible]# ansible localhost -m setup -a 'filter="ansible_default_ipv4"'
localhost | SUCCESS => {
"ansible_facts": {
"ansible_default_ipv4": {
"address": "192.168.1.11",
"alias": "eth0",
"broadcast": "192.168.1.255",
"gateway": "192.168.1.2",
"interface": "eth0",
"macaddress": "00:0c:29:33:e3:25",
"mtu": 1500,
"netmask": "255.255.255.0",
"network": "192.168.1.0",
"type": "ether"
}
},
"changed": false
}
[root@ansible ansible]# ansible localhost -m setup -a "filter=ansible_nodename"
localhost | SUCCESS => {
"ansible_facts": {
"ansible_nodename": "ansible"
},
"changed": false
}
# 范例
# 用户和目录要提前存在
[root@ansible ansible]# vim var.yml
---
- hosts: all
remote_user: root
gather_facts: yes
tasks:
- name: create log file
file:
name: /data/{{ ansible_nodename }}.log
state: touch
owner: devop
mode: 600
# 显示 eth0 网卡的 IP 地址
[root@ansible ansible]# vim show_ip.yml
---
- hosts: all
tasks:
- name: show eht0 ip address
debug:
msg: IP address {{ ansible_eth0.ipv4.address }}
#msg: IP address {{ ansible_facts["eth0"]["ipv4"]["address"] }}
#msg: IP address {{ ansible_facts.eth0.ipv4.address }}
#msg: IP address {{ ansible_default_ipv4.address }}
#msg: IP address {{ ansible_eth0.ipv4.address }}
#msg: IP address {{ ansible_eth0.ipv4.address.split('.')[-1] }}#取IP中的
最后一个数字
[root@ansible ansible]# ansible-playbook show_ip.yml -v
Using /etc/ansible/ansible.cfg as config file
PLAY [all] ************************************************************************************************************************
TASK [Gathering Facts] ************************************************************************************************************
ok: [192.168.1.12]
ok: [192.168.1.21]
ok: [192.168.1.13]
ok: [192.168.1.11]
TASK [show eht0 ip address] ******************************************************************************************
ok: [192.168.1.13] => {
"msg": "IP address 13"
}
ok: [192.168.1.12] => {
"msg": "IP address 12"
}
ok: [192.168.1.11] => {
"msg": "IP address 11"
}
ok: [192.168.1.21] => {
"msg": "IP address 21"
}
PLAY RECAP ************************************************************************************************************************
192.168.1.11 : ok=2 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
192.168.1.12 : ok=2 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
192.168.1.13 : ok=2 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
192.168.1.21 : ok=2 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
# 修改主机名形式为 web_随机数
[root@ansible ansible]# vim hostname.yml
---
- hosts: webservers
tasks:
- name: 定义一个随机数,设定为变量,然后后续调用
shell: echo $((RANDOM%255))
register: web_number
- name: 使用debug输出变量结果
debug:
msg: "{{ web_number }}"
- name: 使用hostname模块将主机名修改为web_随机数
hostname:
name: web_{{ web_number.stdout }}
性能优化
每次执行playbook,默认会收集每个主机的所有facts变量,将会导致速度很慢,可以采用下面方法加速
- 关闭facts采集加速执行,此方法将导致无法使用facts变量
- hosts: all
gather_facts: no
- 当使用 gather_facts: no 关闭 facts,确实能加速 Ansible 执行,但是有时候又需要使用 facts 中的内容,还希望执行的速度快,这时候可以设置facts 的缓存,将facts变量信息存在redis服务器中
[root@ansible ~]# cat /etc/ansible/ansible.cfg
[defaults]
# smart 表示默认收集 facts,但 facts 已有的情况下不会收集,即使用缓存 facts
# implicit 表示默认收集 facts,要禁止收集,必须使用 gather_facts: False
# explicit 则表示默认不收集,要显式收集,必须使用gather_facts: True
gathering = smart #在使用 facts 缓存时设置为smart
fact_caching_timeout = 86400 #缓存时长
fact_caching = redis #缓存存在redis中
fact_caching_connection = 192.168.1.41:6379:0 #0表示redis的0号数据库
#若redis设置了密码
fact_caching_connection = 192.168.1.41:6379:0:password
4.2、在playbook 命令行中定义变量
vim var2.yml
---
- hosts: websrvs
remote_user: root
tasks:
- name: install package
yum: name={{ pkname }} state=present
[root@ansible ~]#ansible-playbook -e pkname=httpd var2.yml
# 可以将多个变量放在一个文件中
[root@ansible ~]#cat vars
pkname1: memcached
pkname2: vsftpd
[root@ansible ~]#vim var2.yml
---
- hosts: websrvs
remote_user: root
tasks:
- name: install package {{ pkname1 }
yum: name={{ pkname1 }} state=present
- name: install package {{ pkname2 }
yum: name={{ pkname2 }} state=present
[root@ansible ~]#ansible-playbook -e pkname1=memcached -e pkname2=httpd
var2.yml
[root@ansible ~]#ansible-playbook -e '@vars' var2.yml
4.3、在playbook文件中定义变量
此方式定义的是私有变量,即只能在当前playbook中使用,不能被其它Playbook共用
[root@ansible ~]#vim var3.yml
---
- hosts: websrvs
remote_user: root
vars:
username: user1
groupname: group1
tasks:
- name: create group {{ groupname }}
group: name={{ groupname }} state=present
- name: create user {{ username }}
user: name={{ username }} group={{ groupname }} state=present
[root@ansible ~]#ansible-playbook -e "username=user2 groupname=group2" var3.yml
# 变量的相互调用
[root@ansible ~]#cat var4.yaml
---
- hosts: websrvs
remote_user: root
vars:
collect_info: "/data/test/{{ansible_default_ipv4['address']}}/"
tasks:
- name: create IP directory
file: name="{{collect_info}}" state=directory
# 执行结果
tree /data/test/
/data/test/
└── 192.168.1.12
└── 192.168.1.13
1 directory, 0 files
# 安装多个包
[root@ansible ~]#cat install.yml
- hosts: websrvs
vars:
web: httpd
db: mariadb-server
tasks:
- name: install {{ web }} {{ db }}
yum:
name:
- "{{ web }}"
- "{{ db }}"
state: latest
4.4、使用专用的公共的变量文件
可以在一个独立的playbook文件中定义公共变量,在其它的playbook文件中可以引用变量文件中的变量
此方式比playbook中定义的变量优化级高
vim vars.yml
---
# variables file
package_name: mariadb-server
service_name: mariadb
vim var5.yml
---
#install package and start service
- hosts: dbsrvs
remote_user: root
vars_files:
- vars.yml
tasks:
- name: install package
yum: name={{ package_name }}
tags: install
- name: start service
service: name={{ service_name }} state=started enabled=yes
4.5、在主机清单中定义主机和主机组的变量
所有项目的主机变量
在inventory 主机清单文件中为指定的主机定义变量以便于在playbook中使用
[websrvs]
www1.test.org http_port=80 maxRequestsPerChild=808
www2.test.org http_port=8080 maxRequestsPerChild=909
所有项目的组(公共)变量
在inventory 主机清单文件中赋予给指定组内所有主机上的在playbook中可用的变量,如果和主机变是同名,优先级低于主机变量
# 范例
[websrvs:vars]
http_port=80
ntp_server=ntp.wang.org
nfs_server=nfs.wang.org
[all:vars]
# --------- Main Variables ---------------
# Cluster container-runtime supported: docker, containerd
CONTAINER_RUNTIME="docker"
# Network plugins supported: calico, flannel, kube-router, cilium, kube-ovn
CLUSTER_NETWORK="calico"
# Service proxy mode of kube-proxy: 'iptables' or 'ipvs'
PROXY_MODE="ipvs"
# K8S Service CIDR, not overlap with node(host) networking
SERVICE_CIDR="192.168.0.0/16"
# Cluster CIDR (Pod CIDR), not overlap with node(host) networking
CLUSTER_CIDR="172.16.0.0/16"
# NodePort Range
NODE_PORT_RANGE="20000-60000"
# Cluster DNS Domain
CLUSTER_DNS_DOMAIN="magedu.local."
[root@ansible ~]#vim /etc/ansible/hosts
[websrvs]
192.168.1.12 hname=www1 domain=test.io
192.168.1.13 hname=www2
[websrvs:vars]
mark="-"
[all:vars]
domain=wang.org
[root@ansible ~]#ansible websrvs -m hostname -a 'name={{ hname }}{{ mark }}{{ domain }}'
# 命令行指定变量:
[root@ansible ~]#ansible websrvs -e domain=magedu.cn -m hostname -a 'name={{ hname }}{{ mark }}{{ domain }}'
4.6、针对当前项目的主机和主机组的变量
上面的方式是针对所有项目都有效,而官方更建议的方式是使用ansible特定项目的主机变量和组变量
生产建议在每个项目对应的目录中创建额外的两个变量目录,分别是host_vars和group_vars
- host_vars下面的文件名和主机清单主机名一致,针对单个主机进行变量定义,格式:host_vars/hostname
- group_vars下面的文件名和主机清单中组名一致, 针对单个组进行变量定义,格式:gorup_vars/groupname
- group_vars/all文件内定义的变量对所有组都有效
4.7、register 注册变量
在playbook中可以使用register将捕获命令的输出保存在临时变量中,方便后续调用此变量,比如可以使用debug模块进行显示输出
# 利用debug 模块输出变量
[root@ansible ~]#cat register1.yml
---
- hosts: dbsrvs
tasks:
- name: get variable
shell: hostname
register: name
- name: "print variable"
debug:
msg: "{{ name }}" #输出register注册的name变量的全部信息,注意变量要加" "引起来
#msg: "{{ name.cmd }}" #显示命令
#msg: "{{ name.rc }}" #显示命令成功与否
#msg: "{{ name.stdout }}" #显示命令的输出结果为字符串形式,所有结果都放在一行里显示,适合于结果是单行输出
#msg: "{{ name.stdout_lines }}" #显示命令的输出结果为列表形式,逐行标准输出,适用于多行显示
#msg: "{{ name['stdout_lines'] }}" #显示命令的执行结果为列表形式,和效果上面相同
#msg: "{{ name.stdout_lines[0] }}" #显示命令的输出结果的列表中的第一个元素
说明:
第一个 task 中,使用了 register 注册变量名为 name ;当 shell 模块执行完毕后,会将数据放到该变量中。
第二个 task 中,使用了 debug 模块,并从变量name中获取数据。