Ansible Playbook 进阶探秘:Handlers、变量、循环及条件判断全解析
192.168.60.100 | ansible.com |
192.168.60.110 | client-1.com |
192.168.60.120 | client-2.com |
192.168.60.130 | client-1.com |
一、Handlers
介绍:在发生改变时执行的操作(类似puppet通知机制)
示例:
当apache的配置文件发生改变时,apache服务才会重启。
- hosts: webservers
remote_user: root
tasks:
- name: apache
yum: name=httpd state=present
- name: apache config
copy: src=/config/httpd.conf dest=/etc/httpd/conf/httpd.conf
notify:
- start httpdhandlers:
- name: start httpd
service: name=httpd state=restarted
测试:
配置文件没有改变——
(可以看到没有重启服务的任务)
修改apache的配置——
[root@ansible playbook]# vim /config/httpd.conf #修改端口
Listen 8080
(当配置发生修改后,可以看到重启服务的任务已经被执行)
二、变量的使用
常用的变量一般就两种:
- 一种为用户自己定义的变量
- 一种为facts获取的变量(即ansible webserver -m setup查到的变量)
1.自定义变量
(1)用户在.yml文件自定义变量
示例:
[root@ansible playbook]# vim test1.yaml
- hosts: webservers
remote_user: root
vars:
- var1: "abc"
- var2: 123
tasks:
- name: test vars
shell: echo "{{ var1 }} {{ var2 }}" >> /tmp/var.txt
测试:
(定义的变量成功写入到被管控端主机的var.txt文件)
(2)通过-e参数传递变量
示例:
[root@ansible playbook]# vim test2.yaml
- hosts: webservers
remote_user: root
tasks:
- name: install package
yum: name={{ packname }} state=present
测试:
(使用-e传递参数安装namp)
(3)通过主机/主机组配置文件传递变量
主机——
示例:
[root@ansible playbook]# vim /etc/ansible/hosts
192.168.60.110 packname=nmap
[root@ansible playbook]# vim test3.yaml
- hosts: 192.168.60.110
remote_user: root
tasks:
- name: install package
yum: name={{ packname }} state=present
测试:
(参数成功传递)
主机组——
示例:
[root@ansible playbook]# vim /etc/ansible/hosts
[webservers]
192.168.60.110
192.168.60.120
192.168.60.130
[webservers:vars]
packname=nmap
[root@ansible playbook]# vim test4.yaml
测试:
(参数传递成功)
2.通过facts获取的系统的变量
(1)获取系统变量
即ansible webserver -m setup查到的变量
使用方法:
[root@ansible playbook]# ansible webservers -m setup > /tmp/fact.txt
[root@ansible playbook]# vim /tmp/fact.txt
在剧本中直接调用这些变量名就行,但是注意这些变量名是分级别的,对齐的就是同一级,比如ansible_default_ipv4和ansible_nodename就是同一级,address是ansible_default_ipv4的 下一级,要调用它就需要在ansible_default_ipv4后面加上一个“.”,就是写成这样ansible_default_ipv4.address。
(其实就是字典,以键值对的方式关联起来,这里说变量是为了帮助理解)
示例:
[root@ansible playbook]# vim facts.yaml #获取ip、主机名等系统变量信息
- hosts: webservers
remote_user: root
tasks:
- name: hostname ipaddrss
shell: echo "{{ ansible_default_ipv4.address }} {{ ansible_nodename }}" > /tmp/facts.txt
- name: fetch file /tmp/facts
fetch: src=/tmp/facts.txt dest=/tmp
测试:
[root@ansible playbook]# ansible-playbook facts.yaml
(2)本地facts(facts.d)自定义系统变量
解释:就是定义ansible webserver -m setup查到的变量
示例:
[root@client-1 ~]# mkdir -p /etc/ansible/facts.d #在被管控端创建如下目录
[root@client-1 ~]# touch /etc/ansible/facts.d/test.fact #创建文件
[root@client-1 ~]# vim /etc/ansible/facts.d/test.fact #写入要定义的变量名
[general]
test_test1=123
test_test2=abc
测试:
[root@ansible playbook]# ansible 192.168.60.110 -m setup | grep test_test
#获取自定义的系统变量
(获取成功!)
3.变量注册
介绍:在 Ansible 的 Playbook 里,变量注册是一项非常实用的功能。借助它,我们能够把某个命令的执行结果存储到变量中,方便后续使用。这一特性能够在很多场景下避免编写特定于站点的复杂逻辑。比如,你可以利用变量注册来检查系统中是否存在某个特定程序。具体操作时,只需使用 register 关键字指定用于存储结果的变量即可。通过这种方式,你可以在后续任务中根据存储的结果进行条件判断和进一步操作。
示例:
[root@ansible playbook]# vim test5.yaml
- hosts: webservers
remote_user: root
tasks:
- name: get user info
shell: grep ^root /etc/passwd
register: pass_content
- name: save info
shell: echo {{ pass_content }} > /tmp/user.txt
(获取root用户信息存进pass_content里面)
测试:
(可以看到存入客户端/tmp/user.txt文件下的信息是以键值对的形式,我们可以用来调用这些键值对,这个需要和后面要学的判断结合起来使用,单独看确实没什么用,下面是如何调用)
[root@ansible playbook]# vim test5.yaml #调用
- hosts: webservers
remote_user: root
tasks:
- name: get user info
shell: grep ^root /etc/passwd
register: pass_content
- name: save info
shell: echo {{ pass_content.stdout }} > /tmp/user1.txt
[root@ansible playbook]# ansible-playbook test5.yaml
[root@client-1 ~]# cat /tmp/user1.txt
三、剧本中的条件判断
ansible和puppet软件相同 是可以支持条件判断,使用when语句。
示例1
[root@ansible playbook]# vim test6.yaml
- hosts: webservers
remote_user: root
tasks:
- name: install tree
yum: name=tree state=present
when: ansible_nodename == "client-1.com"
- name: install nmap
yum: name=nmap state=present
when: ansible_nodename == "client-2.com"
(设置条件判断,当被管控端的主机名叫client-1.com时安装tree,叫client-2.com时安装nmap)
测试:
(可以看到条件判断将不符合条件的主机全部跳过去了)
示例2
条件判断+变量注册
[root@ansible playbook]# vim apache_test.yaml
- hosts: webservers
remote_user: root
tasks:
- name: get pkg info
shell: rpm -qa | grep httpd | awk -F'-' 'NR == 1 {print$1}'
register: pkginfo
- name: install httpd
yum: name=httpd state=present
when: pkginfo.stdout != "httpd"
测试:
[root@ansible playbook]# ansible-playbook apache_test.yaml
(由于我的三台被管控机都安装过了httpd,所以可以看到都跳过去了)
(紫色字体的警告不用管,没有影响,如果嫌烦可以按照下面的方式去掉)
[root@ansible playbook]# vim /etc/ansible/ansible.cfg
command_warnings=False
(这样执行就没有警告了)
示例3
源码部署nginx并启动服务
1.规划
基础功能:
- 本地部署,打包
- 需要判断目标主机是否已经安装nginx
扩展:
- 通过配置文件是否改动触发启动
2.准备本地源码包nginx
[root@ansible /]# wget https://nginx.org/download/nginx-1.27.4.tar.gz
(复制链接直接wget就行,这样就在本地下载下来了nginx的源码包)
[root@ansible /]# tar -xvf /nginx-1.27.4.tar.gz -C /usr/local/src/
[root@ansible /]# cd /usr/local/src/nginx-1.27.4/
[root@ansible nginx-1.27.4]# yum install gcc gcc-c++ pcre-devel zlib-devel
[root@ansible nginx-1.27.4]# useradd -m -s /sbin/nologin nginx
[root@ansible nginx-1.27.4]# ./configure --user=nginx --group=nginx --prefix=/usr/local/nginx
[root@ansible nginx-1.27.4]# make
[root@ansible nginx-1.27.4]# make install
[root@ansible local]# cd /usr/local
[root@ansible local]# tar -zcvf /playbook/nginx.tar.gz nginx/
3.编写剧本
[root@ansible playbook]# vim snginx_install.yaml
- hosts: webservers
remote_user: root
tasks:
- name: copy nginx
copy: src=/playbook/nginx.tar.gz dest=/tmp
- name: get nginx info
shell: ls -d /usr/local/nginx
register: nginx_info
ignore_errors: yes
- name: unzip nginx
shell: tar -xvf /tmp/nginx.tar.gz -C /usr/local
when: nginx_info.rc == 2
- name: add user
user: name=nginx state=present
- name: modify nginx
file: path=/usr/local/nginx owner=nginx group=nginx recurse=yes state=directory
剧本说明:
- hosts: webservers
remote_user: root
tasks:
- name: copy nginx #拷贝源码到到管控机
copy: src=/playbook/nginx.tar.gz dest=/tmp
- name: get nginx info #注册变量用于判断是否安装了nginx
shell: ls -d /usr/local/nginx
register: nginx_info
ignore_errors: yes #未安装过nginx会报错,这里用来跳过保存,继续执行剧本
- name: unzip nginx #解压安装nginx
shell: tar -xvf /tmp/nginx.tar.gz -C /usr/local
when: nginx_info.rc == 2 #判断是否安装过nginx
- name: add user #添加nginx用户
user: name=nginx state=present
- name: modify nginx #递归设置用户和所属组为nginx
file: path=/usr/local/nginx owner=nginx group=nginx recurse=yes state=directory
相关说明:
ignore_errors: yes #当某个任务执行时出现错误,ignore_errors模块等于yes时,不会让整个剧本执行中断,而是会忽略这个错误,继续执行后续的任务
nginx.rc == 2 #这个是register的返回值,当返回值等于2的时候表明/usr/local/nginx目录已经存在
recurse=yes #这个是递归的修改权限
测试:
[root@ansible playbook]# ansible-playbook snginx_install.yaml
(可以看到注册变量任务的报错,因为没有安装过nginx,但是因为添加了ignore_errors模块,成功跳过了错误,继续执行剧本后面剩下的任务)
(被管控端的所有主机安装成功)
(这时候再执行就可以看到,注册变量模块没有再报错,因为所有节点都已经安装了nginx,而且解压nginx的任务判断成功也生效,安装过nginx的被管控机没有再次执行解压nginx的任务,这就节约了在实际运维场景下的时间)
4.扩展
[root@ansible playbook]# vim snginx_install.yaml
- hosts: webservers
remote_user: root
tasks:
- name: copy nginx
copy: src=/playbook/nginx.tar.gz dest=/tmp
- name: get nginx info
shell: ls -d /usr/local/nginx
register: nginx_info
ignore_errors: yes
- name: unzip nginx
shell: tar -xvf /tmp/nginx.tar.gz -C /usr/local
when: nginx_info.rc == 2
- name: add user
user: name=nginx state=present
- name: modify nginx
file: path=/usr/local/nginx owner=nginx group=nginx recurse=yes state=directory
- name: nginx config
copy: src=/playbook/nginx.conf dest=/usr/local/nginx/conf
notify:
- restart nginxhandlers:
- name: restart nginx
shell: /usr/local/nginx/sbin/nginx -s reload
测试:
启动管控端的nginx服务——
[root@client-1 ~]# /usr/local/nginx/sbin/nginx
[root@client-2 ~]# /usr/local/nginx/sbin/nginx
[root@client-3 ~]# /usr/local/nginx/sbin/nginx
配置文件nginx.conf没有修改——
[root@ansible playbook]# ansible-playbook snginx_install.yaml
(因为配置没有修改,所以nginx服务不会被重启,这样节约了时间)
修改nginx.conf的配置,将nginx的端口设置为8080——
[root@ansible playbook]# vim nginx.conf
listen 8080;
[root@ansible playbook]# ansible-playbook snginx_install.yaml
(配置文件发生改动,管控端重启被管控端所有主机的nginx服务)
四、剧本中的循环
剧本的循环是使用with_items关键字实现的。
(这个功能使用很少,判断功能用到的比较多)
示例1
添加abc1-abc3用户
[root@ansible playbook]# vim cycle.yaml
- hosts: webservers
remote_user: root
tasks:
- name: add user
user: name={{ item }} state=present
with_items:
- abc1
- abc2
- abc3
测试:
[root@ansible playbook]# ansible-playbook cycle.yaml
(用户再被管控端创建成功!)
示例2
依次安装软件 nmap wget tree
[root@ansible playbook]# vim cycle-1.yaml
- hosts: webservers
remote_user: root
tasks:
- name: pack install
user: name={{ item }} state=present
with_items:
- tree
- namp
- vsftpd
测试:
(软件安装成功!)
ansible自动化运维工具的介绍-CSDN博客
深度剖析 ansible:从部署基础到模块运用及剧本编写-CSDN博客
Ansible 实战:Roles,运维的 “魔法函数”-CSDN博客