您现在的位置是:首页 >其他 >【运维知识进阶篇】Ansible流程控制详解网站首页其他

【运维知识进阶篇】Ansible流程控制详解

我是koten 2024-07-19 18:01:02
简介【运维知识进阶篇】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年运维经验,持续分享运维干货,感谢大家的阅读和关注!

风语者!平时喜欢研究各种技术,目前在从事后端开发工作,热爱生活、热爱工作。