您现在的位置是:首页 >其他 >【运维知识进阶篇】Ansible流程控制详解网站首页其他
【运维知识进阶篇】Ansible流程控制详解
大家好,今天给大家讲解Ansible的流程控制,Ansible作为可以批量管理客户机的工具,自然是功能齐全,其条件语句、判断语句类似于shell脚本,所以我们要熟练掌握,在实际运用中灵活使用。
playbook条件语句
例如:我们在使用不同的系统的时候,可以通过判断系统来对软件包进行安装;在nfs和rsync安装过程中,客户端服务器不需要推送配置文件,用条件语句可以减少剧本的编写;在安装源码时,可以判断是否执行成功。执行成功就启动等等。
1、根据不同操作系统安装apache
[root@Ansible test]# vim test.yml
- hosts: web01
tasks:
- name: install centos httpd
yum:
name: httpd
state: present
when: ansible_facts['os_family'] == "CentOS" #官方写法
#when: ansible_distribution == "CentOS" 非官方写法
- name: install ubuntu httpd
yum:
name: apache2
state: present
when: ansible_facts['os_family'] == "Ubuntu"
#还可以使用括号对条件进行分组
tasks:
- name: "shut down CentOS 6 and Debian 7 systems"
command: /sbin/shutdown -t now
when: (ansible_facts['distribution'] == "CentOS" and ansible_facts['distribution_major_version'] == "6") or (ansible_facts['distribution'] == "Debian" and ansible_facts['distribution_major_version'] == "7") #如果当前管理机为CentOS6或者是Debin7就关机
#也可以以列表形式指定多个条件
tasks:
- name: "shut down CentOS 6 systems"
command: /sbin/shutdown -t now
when:
- ansible_facts['distribution'] == "CentOS"
- ansible_facts['distribution_major_version'] == "6"
#也可以进行条件运算
tasks:
- shell: echo "only on Red Hat 6, derivatives, and later"
when: ansible_facts['os_family'] == "RedHat" and ansible_facts['lsb']['major_release']|int >= 6 #当系统为RedHat并且版本大于等于6时进行输出
#过滤匹配返回值包含successful
tasks:
- name: Check Nginx Configure
command: /usr/sbin/nginx -t
register: result
- name: Restart web02
command: /usr/sbin/reboot
when: result.stderr_lines is match ".*successful.*" 精准匹配,通常配合正则使用
或者
when: result.stderr_lines is search "successful" 模糊匹配
2、rsync服务端推送配置文件
[root@Ansible test]# cat /ansible/rsyncd/rsyncd.yml
- hosts: all
tasks:
- name: Install Rsyncd Server
yum:
name: rsync
state: present
- name: Create www Group
group:
name: www
gid: 666
- name: Create www User
user:
name: www
uid: 666
group: www
shell: /sbin/nologin
create_home: false
- name: Scp Rsync Config
copy:
src: /ansible/rsyncd/rsyncd.conf
dest: /etc/rsyncd.conf
owner: root
group: root
mode: 0644
when: ansible_hostname == "Backup"
- name: create passwd file
copy:
content: 'rsync_backup:123'
dest: /etc/rsync.passwd
owner: root
group: root
mode: 0600
when: ansible_hostname == "Backup"
- name: Create backup Directory
file:
path: /backup
state: directory
mode: 0755
owner: www
group: www
recurse: yes
when: ansible_hostname == "Backup"
- name: Start Rsyncd Server
systemd:
name: rsyncd
state: started
when: ansible_hostname == "Backup"
3、rsync客户端推送脚本
[root@Ansible rsyncd]# cat rsyncd_kehu.yml
- hosts: all
tasks:
- name: SCP Backup Shell
copy:
src: backup.sh
dest: /root/backup.sh
when: ansible_hostname is match "web*"
[root@Ansible rsyncd]# ansible-playbook rsyncd_kehu.yml
PLAY [all] *********************************************************************
TASK [Gathering Facts] *********************************************************
ok: [nfs]
ok: [mysql]
ok: [lb01]
ok: [rsync]
ok: [lb00]
ok: [web02]
ok: [web01]
ok: [lb02]
TASK [SCP Backup Shell] ********************************************************
skipping: [rsync]
skipping: [nfs]
skipping: [lb01]
skipping: [lb02]
skipping: [mysql]
skipping: [lb00]
skipping: [web01]
skipping: [web02]
PLAY RECAP *********************************************************************
lb00 : ok=1 changed=0 unreachable=0 failed=0 skipped=1 rescued=0 ignored=0
lb01 : ok=1 changed=0 unreachable=0 failed=0 skipped=1 rescued=0 ignored=0
lb02 : ok=1 changed=0 unreachable=0 failed=0 skipped=1 rescued=0 ignored=0
mysql : ok=1 changed=0 unreachable=0 failed=0 skipped=1 rescued=0 ignored=0
nfs : ok=1 changed=0 unreachable=0 failed=0 skipped=1 rescued=0 ignored=0
rsync : ok=1 changed=0 unreachable=0 failed=0 skipped=1 rescued=0 ignored=0
web01 : ok=1 changed=0 unreachable=0 failed=0 skipped=1 rescued=0 ignored=0
web02 : ok=1 changed=0 unreachable=0 failed=0 skipped=1 rescued=0 ignored=0
[root@Ansible rsyncd]#
4、变量注册后进行判断
[root@Ansible ~]# vim /ansible/test/test.yml
- hosts: web_group
tasks:
- name: check httpd server
command: systemctl is-active httpd
ignore_errors: yes #忽视报错继续执行
register: check_httpd
-name: print check_httpd #调试完后可以取消debug
debug:
msg: "{{ check_httpd }}"
- name: httpd restart
service:
name: httpd
state: restated
when: check_httpd.rc == 0
[root@Ansible test]# ansible-playbook test.yml
PLAY [web_group] ***************************************************************
TASK [Gathering Facts] *********************************************************
ok: [web02]
ok: [web01]
TASK [check httpd server] ******************************************************
fatal: [web01]: FAILED! => {"changed": true, "cmd": ["systemctl", "is-active", "httpd"], "delta": "0:00:00.072816", "end": "2023-04-19 21:10:07.733494", "msg": "non-zero return code", "rc": 3, "start": "2023-04-19 21:10:07.660678", "stderr": "", "stderr_lines": [], "stdout": "unknown", "stdout_lines": ["unknown"]}
...ignoring
fatal: [web02]: FAILED! => {"changed": true, "cmd": ["systemctl", "is-active", "httpd"], "delta": "0:00:00.073889", "end": "2023-04-19 21:10:07.735992", "msg": "non-zero return code", "rc": 3, "start": "2023-04-19 21:10:07.662103", "stderr": "", "stderr_lines": [], "stdout": "unknown", "stdout_lines": ["unknown"]}
...ignoring
TASK [print check_httpd] *******************************************************
ok: [web01] => {
"msg": {
"changed": true,
"cmd": [
"systemctl",
"is-active",
"httpd"
],
"delta": "0:00:00.072816",
"end": "2023-04-19 21:10:07.733494",
"failed": true,
"msg": "non-zero return code",
"rc": 3,
"start": "2023-04-19 21:10:07.660678",
"stderr": "",
"stderr_lines": [],
"stdout": "unknown",
"stdout_lines": [
"unknown"
]
}
}
ok: [web02] => {
"msg": {
"changed": true,
"cmd": [
"systemctl",
"is-active",
"httpd"
],
"delta": "0:00:00.073889",
"end": "2023-04-19 21:10:07.735992",
"failed": true,
"msg": "non-zero return code",
"rc": 3,
"start": "2023-04-19 21:10:07.662103",
"stderr": "",
"stderr_lines": [],
"stdout": "unknown",
"stdout_lines": [
"unknown"
]
}
}
TASK [httpd restart] ***********************************************************
skipping: [web01]
skipping: [web02]
PLAY RECAP *********************************************************************
web01 : ok=3 changed=1 unreachable=0 failed=0 skipped=1 rescued=0 ignored=1
web02 : ok=3 changed=1 unreachable=0 failed=0 skipped=1 rescued=0 ignored=1
playbook循环语句
写循环语句可以减少剧本的编写,提高工作效率
1、启动多个服务
[root@Ansible test]# cat test.yml
- hosts: web_group
tasks:
- name: start service
systemd:
name: "{{ item }}"
state: started
with_items: #或者是用loop
- httpd
- php-fpm
- mariadb
2、循环定义变量(以列表形式)
[root@Ansible test]# cat test.yml
- hosts: web_group
tasks:
- name: ensure a list of packages instaled
yum:
name: "{{ packages }}"
vars:
packages:
- httpd
- httpd-tools
[root@Ansible test]# cat test.yml
- hosts: web_group
tasks:
- name: ensure a list of packages instaled
yum: name = "{{ item }}" state=present
with_items:
- httpd
- httpd-tools
3、字典循环
#创建用户
[root@Ansible test]# cat test.yml
- hosts: web_group
tasks:
- name: Add Users #记得提前创建组
user:
name: "{{ item.name }}"
groups: "{{ item.groups }}"
state: present
with_items:
- { name: 'www',groups: 'www'}
- { name: 'koten',groups: 'koten'}
#拷贝文件
[root@Ansible test]# cat test.yml
- hosts: web_group
tasks:
- name: copy conf and code
copy:
src: "{{ item.src }}"
dest: "{{ item.dest }}"
mode: "{{ item.mode }}"
with_items:
- { src: "./httpd.conf", dest: "/etc/httpd/conf/", mode: "0644" }
- { src: "./upload_file.php", dest: "/var/www/html/", mode: "0600" }
playbookhandlers
handler是用来执行某些条件下的任务,比如配置文件发生变化时,通过notify触发handler去重启服务。(在saltstack中也有类似的触发器,写法相对Ansible简单,只需要watch,配置文件即可)
handlers案例
[root@Ansible test]# cat test.yml
- hosts: web_group
vars:
- http_port: 8080 #定义http端口变量
tasks:
- name: Install Http Server #安装http服务
yum:
name: httpd
state: present
- name: config httpd server #配置http服务
template: #将管理机变量内容导入至客户机的这个文件
src: ./httpd.conf
dest: /etc/httpd/conf
notify: #检测到配置文件发生变化后,执行handler中的变量
- Restart Httpd Server #名字要与handlers中-name保持一致
- Restart PHP Server
- name: start httpd server
service:
name: httpd
state: started
enabled: yes
handlers: #handler中内置的变量
- name: Restart Httpd Server
systemd:
name: httpd
state: restarted
- name: Restart PHP Server
systemd:
name: php-fpm
state: restarted
注意:
1、无论多少个task通知了相同的handlers,handlers仅会在所有tasks结束后运行一次。
2、Handlers只有在其所在的任务被执行时,才会被运行;如果一个任务中定义了notify调用Handlers,但是由于条件判断等原因,该任务未被执行,那么Handlers同样不会被执行。
3、Handlers只会在每一个play的末尾运行一次;如果想在一个playbook中间运行Handlers,则需要使用meta模块来实现。例如: -meta: flush_handlers。
4、如果一个play在运行到调用Handlers的语句之前失败了,那么这个Handlers将不会被执行。我们可以使用meta模块的–force-handlers选项来强制执行Handlers,即使Handlers所在的play中途运行失败也能执行。
5、不能使用handlers替代tasks
playbook任务标签
默认情况下,Ansible在执行一个剧本时,会执行剧本里的所有任务,Ansible的tag功能,可以实现给剧本中的一个或多个任务(整个剧本也可以)打上标签,利用标签来指定这些要运行剧本中的个别任务,或不执行个别任务。
1、打标签方式
对一个task打一个标签;对一个task打多个标签;对多个task打一个标签
2、标签的使用
-t 执行指定搞定tag标签任务
-skip-tags 执行-skip-tags之外的标签任务
[root@Ansible test]# cat test.yml
- hosts: web_group
vars:
- http_port: 8080
tasks:
- name: Install Http Server
yum:
name: httpd
state: present
tags:
- install_httpd
- httpd_server
- name: configure httpd server
template:
src: ./httpd.conf
dest: /etc/httpd/conf/httpd.conf
notify: Restart Httpd Server
tags:
- config_httpd
- httpd_server
- name: start httpd server
service:
name: httpd
state: started
enabled: yes
tags: service_httpd
handlers:
- name: Restart Httpd Server
systemd:
name: httpd
state: restarted
[root@Ansible test]# ansible-playbook test.yml --list-tags #显示当前剧本的所有标签
playbook: test.yml
play #1 (web_group): web_group TAGS: []
TASK TAGS: [config_httpd, httpd_server, install_httpd, service_httpd]
[root@Ansible test]# ansible-playbook test.yml -t httpd_server #执行httpd_server标签的命令
[root@Ansible test]# ansible-playbook test.yml -t install_httpd,confiure_httpd #执行install_httpd,confiure_httpd的命令
[root@Ansible test]# ansible-playbook test.yml --skip-tags httpd_server #执行除了带httpd_server的命令
playbook文件复用
先前我们写的剧本都是一个一个去执行,我们能否通过写剧本的方法让剧本一键执行,ansible中准备了include的命令,用来动态调用task任务列表
1、只调用task:include_tasks,小剧本中只留tasks,变量需要写在单独的剧本中
[root@Ansible test]# cat task.yml
- hosts: web_group
vars:
- http_port: 8080
tasks:
- include_tasks: task_install.yml #下面可以加when判断
- include_tasks: task_configure.yml
- include_tasks: task_start.yml
handlers:
- name: Restart Httpd Server
systemd:
name: httpd
state: restarted
[root@Ansible test]# cat task_install.yml
- name: Install Http Server
yum:
name: httpd
state: present
[root@Ansible test]# cat task_configure.yml
- name: configure httpd server
template:
src: ./httpd.conf
dest: /etc/httpd/conf/httpd.conf
notify: Restart Httpd Server
[root@Ansible test]# cat task_start.yml
- name: start httpd server
service:
name: httpd
state: started
enabled: yes
2、调用整个task文件:include(新版本:import_playbook)
- include: httpd.yml
- include: nfs.yml
- include: rsync.yml
- import_playbook: httpd.yml
- import_playbook: nfs.yml
- import_playbook: rsync.yml
3、在saltstack中,叫做top file入口文件
playbook忽略错误
剧本在执行中会检测任务执行的返回状态,遇到错误就会终止剧本,但是有些时候,即使执行错误,我们也需要它继续执行。
需要加入参数:ignore_errors: yes 忽略错误
[root@Ansible test]#
- hosts: web_group
tasks:
- name: Ignore False
command: /bin/false
ignore_errors: yes
- name: touch new file
file:
path: /tmp/oldboy.txt
state: touch
playbook错误处理
当剧本执行失败时,如果在task中设置了handler也不会被执行。但是我们可以通过参数强制调用handler
1、强制调用handler
[root@Ansible test]# cat test.yml
- hosts: web_group
vars:
- http_port: 8080
force_handlers: yes #错误后强制也会调用handler
tasks:
- name: Install Http Server
yum:
name: htttpd
state: present
- name: config httpd server
template:
src: ./httpd.conf
dest: /etc/httpd/conf
notify: #检测当配置文件变化时,执行变量里的内容
- Restart Httpd Server
- Restart PHP Server
- name: start httpd server
service:
name: httpd
state: started
enabled: yes
handlers:
- name: Restart Httpd Server
systemd:
name: httpd
state: restarted
- name: Restart PHP Server
systemd:
name: php-fpm
state: restarted
2、抑制changed
被管理主机没有发生变化,可以使用参数将change状态改为ok
[root@Ansible test]# cat handler.yml
- hosts: web_group
vars:
- http_port: 8080
force_handlers: yes
tasks:
- name: shell
shell: netstat -lntup|grep httpd
register: check_httpd
changed_when: false #将客户机状态设置为false,这样当管理机执行剧本时候,就不会因为这条命令执行handler中的内容,不必再去重启服务
- name: debug
debug: msg={{ check_httpd.stdout.lines }}
[root@Ansible test]# cat test.yml
- hosts: webservers
vars:
- http_port: 8080
tasks:
- name: configure httpd server
template:
src: ./httpd.conf
dest: /etc/httpd/conf/httpd.conf
notify: Restart Httpd Server
- name: Check HTTPD
shell: /usr/sbin/httpd -t
register: httpd_check
changed_when:
- httpd_check.stdout.find('OK')
- false
- name: start httpd server
service:
name: httpd
state: started
enabled: yes
handlers:
- name: Restart Httpd Server
systemd:
name: httpd
state: restarted
我是koten,10年运维经验,持续分享运维干货,感谢大家的阅读和关注!