第五节:Ansible剧本-流程控制

Ansible 流程控制

一、playbook 条件语句

1.1 条件语句简介

什么是条件语句

判断某个条件成立,则执行某个task

为何要用条件语句?有很多场景例如
1、比如目标主机的最小内存必须达到多少,才能执行该task
2、根据命令输出结果的不同以触发不同的task
3、根据不同目标主机的facts,执行不同的task
4、判断某个服务的配置文件是否发生变更,以确定是否需要重启服务

1.2 条件语句用法

(1)基本示例

在ansible中,使用条件判断的关键字就是when。
when关键字后面跟着的是python的表达式,在表达式中你能够使用任何的变量或者fact,当表达式的结果返回的是false,便会跳过本次的任务
示例: 判断条件中的facts变量名可以通过ansible group5 -m setup进行查询

[root@lb workspace]# cat test1.yaml 
- hosts: group3
  tasks:
    - name: Install httpd
      yum:
        name: httpd
        state: present
      when: ansible_distribution == "Rocky"

    - name: Install nginx
      yum:
        name: nginx
        state: present
      when: ansible_distribution == "CentOS"

(2)比较运算符

可用于比较数字或字符串
==
!=
>
<
>=
<=

示例

when: ansible_machine == "x86_64" 

when: ansible_memory_mb.real.free <= 512

当你用到一些facts值时,你不确定值是什么大小写是啥,你需要事先用下面的命令进行查看

# ansible group3 -m setup |less
# ansible group3 -m setup |grep -i distribution 查看出对应的值,再写条件

(3)逻辑运算符

除了比较运算符,还支持逻辑运算符:

  • and:逻辑与,当左边和右边两个表达式同时为真,则返回真
  • or:逻辑或,当左右和右边两个表达式任意一个为真,则返回真n
  • ot:逻辑否,对表达式取反
  • ():当一组表达式组合在一起,形成一个更大的表达式,组合内的所有表达式都是逻辑与的关系

示例

# 逻辑或
when: ansible_distribution == "Rocky" or ansible_distribution == "CentOS"

# 逻辑与
when: ansible_distribution_version == "9.3" and ansible_kernel == "5.14.0-362.8.1.el9_3.x86_64"

when:
  - ansible_distribution_version == "9.3"
  - ansible_kernel == "5.14.0-362.8.1.el9_3.x86_64"

# 组合
when: => 
  ( ansible_distribution == "Rocky" and ansible_distribution_major_version == "9" )
  or
  ( ansible_distribution == "CentOS" and ansible_distribution_major_version == "7")

(4) 更复杂一些的判断

可以快速看一眼,了解都有哪些能力,有个基本印象,后续用的时候临时查就行
— egon:真正高效的学习方法,并非是记住所有东西,而是有所取舍,抓重点
https://egonlin.com/?p=9836

(5)一些逻辑判断示例

示例1:判断命令的结果

====================》判断register注册变量的返回结果
- name: restart httpd if postfix is running
  hosts: group1
  tasks:
    - name: get postfix server status
      command: /usr/bin/systemctl is-active postfix
      ignore_errors: yes
      register: result

    - name: 打印debug信息,帮你看到结果,正式yaml可以去掉
      debug:
        msg: 
          - "命令的结果:{{ result }}"
          - "命令的状态码: {{ result.rc }}"
    - name: restart apache httpd based on postfix status
      service:
        name: httpd
        state: restarted
      when: result.rc == 0

示例2:判断系统版本

[root@m01 project]# vim xitong.yml
- hosts: web_group
  tasks:
    - name: Install Centos httpd
      shell: "yum install -y httpd"
      when: ansible_distribution == "CentOS"

    - name: Install Ubuntu httpd
      shell: "apt-get apache2"
      when: ansible_distribution == "Ubuntu"

示例3:判断主机名,即在目标主机上hostname查看到的结果

[root@m01 project]# cat base.yml
    - name: Create www Group
      group:
        name: www
        gid: 666
        state: present
      when: ansible_fqdn != "db01"

    - name: Create www User
      user:
        name: www
        uid: 666
        group: www
        shell: /sbin/nologin
        create_home: false
        state: present
      when: ansible_fqdn != "db01"

示例4:判断服务是否安装

[root@m01 project]# cat php.yml
- hosts: web_group
  tasks:
    - name: Tar php Package
      unarchive:
        src: /project/package/php.tar.gz
        dest: /tmp/

    #使用shell模块,检查php是否安装,将结果赋值给注册的变量
    - name: Check php Install Status
      shell: "rpm -qa | grep php | wc -l"
      register: get_php_instll_status

    #调用注册的变量,当变量中stdout_lines为0的时候,才会安装php
    - name: Install php Server
      shell: "yum localinstall -y /tmp/*.rpm"
      when: get_php_instll_status.stdout_lines == 0

示例5:使用列表的形式判断系统版本

[root@m01 project]# vim startserver.yml
- hosts: web_group
  tasks:
    - name: Start CentOS 6 Server
      shell: "/etc/init.d/httpd start"
      when:
        - ansible_distribution == "CentOS"
        - ansible_distribution_major_version == "6"

    - name: Start CentOS 7 Server
      shell: "systemctl start httpd"
      when:
        - ansible_distribution == "CentOS"
        - ansible_distribution_major_version == "7"

示例6:多条件and连接与列表形式效果一样

[root@m01 project]# vim startserver.yml
- hosts: web_group
  tasks:
    - name: Start CentOS 6 Server
      shell: "/etc/init.d/httpd start"
      when: (ansible_distribution == "CentOS") and (ansible_distribution_major_version == "6")

    - name: Start CentOS 7 Server
      shell: "systemctl start httpd"
      when: (ansible_distribution == "CentOS") and (ansible_distribution_major_version == "7")

示例7:判断服务是否启动

- hosts: web_group
  tasks:
    - name: Check Httpd Server
      command: systemctl is-active httpd
      ignore_errors: yes
      register: check_httpd

    - name: debug outprint
      debug: var=check_httpd

    - name: Httpd Restart
      service:
        name: httpd
        state: restarted
      when: check_httpd.rc == 0

二、playbook 循环语句

2.1 简介

1、什么是循环

循环就是重复做某件事,对应loop语法(Ansible2.5版本中新增)

2、为何要用循环

我们在编写playbook的时候,不可避免的要执行一些重复性操作,比如
1、安装软件包
2、批量创建用户
3、操作某个目录下的所有文件等
循环语法就是用来实现这些重复性动作的,没有循环语法你只能将实现某段功能的代码重写多遍造成冗余

2.2 循环语句用法

在Ansible 2.5版本中新增语法loop循环,等价于with_list。
大多数时候,with_xxx的循环都可以通过一定的手段转换成loop循环,
所以从Ansible 2.5版本之后,原来经常使用的with_items循环都可以尝试转换成loop。

在playbook中使用循环,直接使用loop关键字即可。
示例1:启动httpd和crond服务:

- hosts: group1
  tasks:
    - name: httpd and crond are running
      service:
        name: "{{ item }}"  # item是固定的变量名,你当然可以不引用
        state: restarted
      loop:
        - crond
        - httpd

也可以将loop循环的列表提前赋值给一个变量,然后在循环语句中调用:

#cat test_services.yml
test_services:
  - crond
  - httpd

# cat install_pkgs.yml 
- hosts: group1
  vars_files:
    - test_services.yml
  tasks:
    - name: postfix and crond are running
      service:
        name: "{{ item }}"
        state: started
      loop: "{{ test_services }}"

一个更复杂的示例

- hosts: group1
  tasks:
  # 1、先创建了一个www组
  - name: add www group
    group:
      name: www

  # 2、然后循环创建了两个用户
  - name: add several users
    user:
      name: "{{ item.name }}"
      state: present
      groups: "{{ item.groups }}"
    loop:
      - { name: 'egon1', groups: 'temp_group' } # temp_group不存在的话会报错
      - { name: 'egon2', groups: 'www' }

此外还有语法:loop_control,自行了解吧

2.3 旧的循环语法

1、定义循环安装服务

[root@m01 project]# vim yum.yml 
- hosts: group1
  gather_facts: no
  tasks:
    - name: Install Redis Server
      yum:
        name: "{{ package }}"
        state: present
      vars:
        package:
          - redis
          - httpd

2、定义循环启动服务

#错误写法: 不用试了,试不成功的
[root@m01 project]# vim start.yml
- hosts: web_group
  tasks:
    - name: Start Server
      systemd:
        name: "{{ package }}"
        state: started
      vars:
        package:
          - redis
          - httpd

#正确写法
[root@m01 project]# vim start.yml
- hosts: web_group
  tasks:
    - name: Start Server
      systemd:
        name: "{{ item }}"
        state: started
      with_items:
        - redis
        - httpd

3、字典定义变量

[root@m01 project]# cat user.yml 
- hosts: group1
  tasks:
    - name: Create Some Group
      group:
        name: "{{ item.name }}"
        gid: "{{ item.gid }}"
        state: present
      with_items:
        - { name: "lhf", gid: "777" }
        - { name: "test", gid: "888" }
        - { name: "egon", gid: "999" }

    - name: Create Some User
      user:
        name: "{{ item.name }}"
        uid: "{{ item.uid }}"
        group: "{{ item.group }}"
        shell: "{{ item.shell }}"
        create_home: "{{ item.create_home }}"
      with_items:
        - { name: "lhf", uid: "777", group: "lhf", shell: "/sbin/nologin", create_home: "false" }
        - { name: "test", uid: "888", group: "test", shell: "/bin/bash", create_home: "false" }
        - { name: "egon", uid: "999", group: "egon", shell: "/bin/bash", create_home: "true" }

4、在Ansible 2.5以前更多的旧循环语法见:https://egonlin.com/?p=9843

三、playbook 任务标签

3.1.标签的作用

在大型项目当中,通常一个playbook会有非常多的task。而我们每次执行这个playbook时,都会将所有task运行一遍。而事实上,在实际使用过程中,我们可能只是想要执行其中的一部分任务而已,并不想把整个playbook完整跑一遍。这个时候就需要用到tags。

Ansible的标签(tag)功能可以给单独任务甚至整个playbook打上标签,
然后我们利用这些标签来指定要运行playbook中的个别任务,或不执行指定的任务。

3.2 标签使用示例

控制端准备配置文件

cat > /workspace/welcome.conf << "EOF"
<LocationMatch "^/+$">
    Options -Indexes
    ErrorDocument 403 /.noindex.html
</LocationMatch>
<Directory /usr/share/httpd/noindex>
    AllowOverride None
    Require all granted
</Directory>
Alias /.noindex.html /usr/share/httpd/noindex/index.html
Alias /poweredby.png /usr/share/httpd/icons/apache_pb3.png
Alias /system_noindex_logo.png /usr/share/httpd/icons/system_noindex_logo.png

EOF

示例

- hosts: group5
  tasks:
    - name: Install httpd
      yum: 
        name: httpd
        state: present
      tags: install_httpd

    - name: Cofiguration httpd
      copy:
        src: /workspace/welcome.conf
        dest: /etc/httpd/conf.d/welcome.conf
      tags: conf_httpd
      notify:
        - restart httpd

    - name: Start httpd
      service:
        name: httpd
        state: started
        enabled: true
      tags: start_httpd
  handlers:
    - name: restart httpd
      systemd:
        name: httpd
        state: restarted

在上面的示例中,我们为两个task定义了三个tags:

  • install_httpd
  • conf_httpd
  • start_httpd

查看标签

ansible-playbook 1.yml --list-tags

指定某个tag运行

ansible-playbook  1.yml --tags "install_httpd"

一次指定多个tag执行:

ansible-playbook  1.yml --tags "conf_httpd,start_httpd"

排除指定tag的task

ansible-playbook  1.yml --skip-tags "install_httpd" # 除了它其他task都执行

3.3.打标签的方式

1.对一个task打一个标签
2.对多个task打一个标签
3.对一个task打多个标签

1、对一个task打一个标签

- hosts: nginx
    - name: Config Nginx Server
      copy:
        src: /etc/nginx/nginx.conf
        dest: /etc/nginx/
      notify: restart_nginx
      tags: reconf_nginx

2、对多个task打一个标签

- hosts: nginx
    - name: Config Nginx Server
      copy:
        src: /etc/nginx/nginx.conf
        dest: /etc/nginx/
      notify: restart_nginx
      tags: reconf_nginx

    - name: Config Nginx wordpress
      copy:
        src: /project/conf/linux.wp.com.conf
        dest: /etc/nginx/conf.d/
      notify: reload_nginx
      when: (ansible_fqdn == "web01") or (ansible_fqdn == "web02")
      tags: reconf_nginx

3、对一个task打多个标签

- hosts: nginx
    - name: Config Nginx Server
      copy:
        src: /etc/nginx/nginx.conf
        dest: /etc/nginx/
      notify: restart_nginx
      # 格式1
      tags: 
        - reconf_nginx
        - reconfig_nginx
      # 格式2
      # tags: ["reconf_nginx","reconfig_nginx"]

      # 格式3:
      # tags: ["reconf_nginx","reconfig_nginx"]

了解高级用法:https://egonlin.com/?p=9850

上一篇
下一篇
Copyright © 2022 Egon的技术星球 egonlin.com 版权所有 帮助IT小伙伴学到真正的技术