事实与任务控制

181 阅读5分钟

事实与任务控制

本文已参与「新人创作礼」活动,一起开启掘金创作之路。

一、Ansible管理事实

ansible事实描述

  1. Ansible事实是ansible在受管主机上自动检测到的变量

  2. 事实中包含有主机相关的信息,可以像play中的常规变量、条件、循环或依赖于从受管主机收集的值的任何其他语句那样使用

  3. 为受管主机收集的一些事实可能包括:主机名称、内核版本、网络接口、IP地址、操作系统版本、各种环境变量、CPU数量、提供的或可用的内存、可用磁盘空间等等

  4. 借助事实,可以方便地检索受管主机的状态,并根据该状态确定要执行的操作 例如:

    • 可以根据含有受管主机当前内核版本的事实运行条件任务,以此来重启服务器
    • 可以根据通过事实报告的可用内存来自定义MySQL配置文件
    • 可以根据事实的值设置配置文件中使用的IPV4地址
  5. 每个play在执行第一个任务之前会先自动与逆行setup模块来收集事实

ansible事实的示例

事实变量
短主机名ansible_facts['hostname']
完全限定域名ansible_facts['fqdn']
IPv4地址ansible_facts['default_ipv4'] ['address']
所有网络接口的名称列表ansible_facts['interfaces']
/dev/vda1磁盘分区的大小ansible_facts['devices'] ['vda'] ['partitions'] ['vda1'] ['size']
DNS服务器列表ansible_facts['dns'] ['nameservers']
当前运行的内核版本ansible_facts['kernel']

如果变量的值为散列/字典类型,则可使用两种语法来获取其值。比如:

  • ansible_facts['default_ipv4'] ['address']也可以写成ansible_facts.default_ipv4.address
  • ansible_facts['dns'] ['nameservers']也可以写成ansible_facts.dns.nameservers

查看node2主机的IP

//编写playbook
---
- hosts: all
  tasks:
    - name: print ip
      debug:
        msg:
          the IPv4 address of {{ ansible_facts.fqdn }} is {{ ansible_facts.all_ipv4_addresses }}
//执行playbook
[root@node1 test]# ansible-playbook fact.yml 

PLAY [all] **************************************************************************************************************************

TASK [Gathering Facts] **************************************************************************************************************
ok: [192.168.100.110]

TASK [print ip] *********************************************************************************************************************
ok: [192.168.100.110] => {
    "msg": "the IPv4 address of node2 is ['192.168.100.110']"
}

PLAY RECAP **************************************************************************************************************************
192.168.100.110            : ok=2    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0 

关闭事实收集

通常我们关闭事实收集是为了:

  • 不准备使用任何事实
  • 希望加快play速度
  • 希望减小play在受管主机上造成的负载
  • 受管主机因为某种原因无法运行setup模块
  • 需要安装一些必备软件后再进行事实收集

二、编写循环和条件任务

1. ansible中循环的介绍

我们在编写playbook时,不可避免的要执行一些重复性操作,比如安装软件包,批量创建用户,操作某个目录下的所有文件等。正因为ansible是一门简单的自动化语言,所以流程控制、循环语句这些编程语言的基本元素它都同样具备

1.1 loop

在playbook中使用循环,直接使用loop关键字即可

如下:

[root@node1 test]# vim test1.yml
---
- hosts: 192.168.100.110
  gather_facts: no
  tasks:
    - name: start service
      service:
        name: "{{ item }}"
        state: started
      loop:
        - httpd
        - php-fpm

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

[root@node1 test]# vim test_service.yml
test_services:
  - postfix
  - httpd

[root@node1 test]# vim test1.yml
- name: start services
  hosts: test
  vars_files:
    - test_services.yml
  tasks:
    - name: start service
      service:
        name: "{{ item }}"
        state: started
      loop: "{{ test_services }}"

执行结果如下:

[root@node1 test]# ansible-playbook test1.yml 

PLAY [192.168.100.110] **************************************************************************************************************

TASK [stop service] *****************************************************************************************************************
ok: [192.168.100.110] => (item=httpd)
ok: [192.168.100.110] => (item=php-fpm)

PLAY RECAP **************************************************************************************************************************
192.168.100.110            : ok=1    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   

1.2 在循环语句中注册变量

register关键字也可以捕获循环任务的输出。以下代码片段显示了循环任务中register变量的结构:

[root@node1 test]# vim loop_register.yml
---
- hosts: 192.168.100.110
  gather_facts: no
  tasks:
    - name: looping echo task
      shell: 'echo this is my item: {{ item }}'
      loop:
        - one
        - two
      register: echo_results
      
    - name: show echo_results variable
      debug:
        var: echo_results

执行语句,可以看到变量的返回结果为一个字典列表

  [root@node1 test]# ansible-playbook loop_register.yml
 PLAY [192.168.100.110] **************************************************************************************************************

TASK [looping echo task] ************************************************************************************************************
changed: [192.168.100.110] => (item=one)
changed: [192.168.100.110] => (item=two)

TASK [show echo_results variable] ***************************************************************************************************
ok: [192.168.100.110] => {
    "echo_results": {
        "changed": true,
        "msg": "All items completed",
        "results": [
            {
                "ansible_facts": {
                    "discovered_interpreter_python": "/usr/libexec/platform-python"
                },
                "ansible_loop_var": "item",
                "changed": true,
                "cmd": "echo this is my item: one",
                "delta": "0:00:00.003536",
                "end": "2021-07-25 05:07:34.249739",
                "failed": false,
                "invocation": {
                    "module_args": {
                        "_raw_params": "echo this is my item: one",
                        "_uses_shell": true,
                        "argv": null,
                        "chdir": null,
                        "creates": null,
                        "executable": null,
                        "removes": null,
                        "stdin": null,
                        "stdin_add_newline": true,
                        "strip_empty_ends": true,
                        "warn": true
                    }
                },
                "item": "one",
                "rc": 0,
                "start": "2021-07-25 05:07:34.246203",
                "stderr": "",
                "stderr_lines": [],
                "stdout": "this is my item: one",
                "stdout_lines": [
                    "this is my item: one"
                ]
            },
            {
                "ansible_loop_var": "item",
                "changed": true,
                "cmd": "echo this is my item: two",
                "delta": "0:00:00.003148",
                "end": "2021-07-25 05:07:34.550064",
                "failed": false,
                "invocation": {
                    "module_args": {
                        "_raw_params": "echo this is my item: two",
                        "_uses_shell": true,
                        "argv": null,
                        "chdir": null,
                        "creates": null,
                        "executable": null,
                        "removes": null,
                        "stdin": null,
                        "stdin_add_newline": true,
                        "strip_empty_ends": true,
                        "warn": true
                    }
                },
                "item": "two",
                "rc": 0,
                "start": "2021-07-25 05:07:34.546916",
                "stderr": "",
                "stderr_lines": [],
                "stdout": "this is my item: two",
                "stdout_lines": [
                    "this is my item: two"
                ]
            }
        ]
    }
}

PLAY RECAP **************************************************************************************************************************
192.168.100.110            : ok=2    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   

1.3 旧循环列表

在ansible2.5以前,playbook通过不同的循环语句以实现不同的循环,这些语句使用with_作为前缀。这些语法目前仍然兼容,但在未来的某个版本会逐步废弃

循环语句关键字描述
with_items简单的列表循环
with_nested嵌套循环
with_dict循环字典
with_fileglob循环指定目录中的所有文件
with_lines循环一个文件中的所有行
with_sequence生成一个自增的整数序列,可以指定起始值和结束值以及步长。参数以key=value的形式指定,format指定输出的格式。数字可以是十进制、十六进制、八进制
with_subelement遍历子元素
with_together遍历数据并行集合

在没学会使用循环之前,如果想要在同一主机中创建四个文件,那么可能会编写如下playbook

[root@server4 ~]# vim test2.yml
---
- hosts: 192.168.100.110
  gather_facts: no
  tasks:
  - file:
      path: "/opt/a"
      state: touch
  - file:
      path: "/opt/b"
      state: touch
  - file:
      path: "/opt/c"
      state: touch
  - file:
      path: "/opt/d"
      state: touch

我们重复写了file模块4次,其实每次只改变了参数path的值,如果使用循环可以改写成如下

[root@server4 ~]# vim test2.yml
---
- hosts: 192.168.100.110
  gather_facts: no
  tasks:
  - file:
      path: "{{ item }}"
      state: touch
    loop: 
      - a
     - b
     - c
     - d