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