机密、事实和循环

1. 管理机密

1.1 Ansible Vault

当我们写的 playbook 中涉及敏感信息,如:密码。这时为了防止敏感信息泄露,就可以使用 vault 进行加密。

1.2 ansible-vault 命令行操作

1.2.1 创建加密文件

创建新的加密文件ansible-vault create

[root@ansible ~]# ansible-vault create xx.yml
New Vault password: 
Confirm New Vault password: 
[root@ansible ~]# cat xx.yml 
$ANSIBLE_VAULT;1.1;AES256
35613766303866333262313936376263636436306436366138313762663939363563316362303431
3762643965663231626562346331633230366139616563370a393863356366616437323064303361
66366239386136336238303238303963376364656663633165663635626532376265313464373266
3731313263333562390a353231323436303162373332326336343333386338333264613232393266
6635

可以创建加密文件来保存加密文件的密码,

[root@ansible ~]# cat .qq 
redhat
[root@ansible ~]# ansible-vault create --vault-password-file=.qq 111.yml
[root@ansible ~]# cat 111.yml 
$ANSIBLE_VAULT;1.1;AES256

1.2.2 查看加密文件

可以使用ansible-vault view filename命令查看Ansible Vault加密的文件

[root@ansible ~]# ansible-vault view xx.yml 
Vault password: 
---
- name: create user
  hosts: 192.168.172.142
  tasks:
    - name:
      user:
        name: jerry
        uid: 2222

--vault-password-file指向存放密码的文件,可以不用手动输密码

[root@ansible ~]# ansible-vault view  --vault-password-file=.qq xx.yml 
---
- name: create user
  hosts: 192.168.172.142
  tasks:
    - name:
      user:
        name: jerry
        uid: 2222

1.2.3 编辑现有的加密文件

要编辑现有的加密文件,Ansible Vault提供了ansible-vault edit filename命令。此命令将文件解密为一个临时文件,并允许编辑。保存时,它将复制其内容并删除临时文件。

[root@ansible ~]# ansible-vault edit xx.yml 
Vault password: 

//不用手动输入密码
[root@ansible ~]# ansible-vault edit  --vault-password-file=.qq xx.yml 

1.2.4 加密现有的文件

ansible-vault encrypt filename命令加密已存在的文件。此命令可取多个欲加密文件的名称作为参数。

[root@ansible ~]# ansible-vault encrypt anaconda-ks.cfg 
New Vault password: 
Confirm New Vault password: 
Encryption successful
[root@ansible ~]# cat anaconda-ks.cfg 
$ANSIBLE_VAULT;1.1;AES256

使用–output=OUTPUT_FILE选项,可将加密文件保存为新的名称。只能通过–output选项使用一个输入文件。

1.2.5 解密现有的文件

现有的加密文件可以通过ansible-vault decrypt filename命令永久解密。在解密单个文件时,可使用--output选项以其他名称保存解密的文件。
```shell
[root@ansible ~]# ansible-vault decrypt anaconda-ks.cfg 
Vault password: 
Decryption successful
[root@ansible ~]# cat anaconda-ks.cfg 
#version=RHEL8
ignoredisk --only-use=nvme0n1
autopart --type=lvm
# Partition clearing information
clearpart --none --initlabel
......

1.2.6 更改加密文件的密码

使用ansible-vault rekey filename命令更改加密文件的密码。此命令可一次性更新多个数据文件的密钥。

[root@ansible ~]# ansible-vault rekey xx.yml 
Vault password: 
New Vault password: 
Confirm New Vault password: 
Rekey successful

1.3 playbook和ansible vault

要运行可访问通过Ansible Vault加密的文件的playbook,需要向ansible-playbook命令提供加密密码。如果不提供密码,playbook将返回错误:

[root@ansible ~]# ansible-playbook xx.yml 
ERROR! Attempting to decrypt but no vault secrets found

可使用–vault-password-file选项指定以纯文本存储加密密码的文件。密码应当在该文件中存储为一行字符串。由于该文件包含敏感的纯文本密码,因此务必要通过文件权限和其他安全措施对其加以保护。

[root@ansible ~]# ansible-playbook --vault-password-f
ile=.qq xx.yml 

PLAY [create user] **********************************
***************************

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

TASK [user] *****************************************
***************************
changed: [192.168.172.142]

PLAY RECAP ******************************************
***************************
192.168.172.142            : ok=2    changed=1    unr
eachable=0    failed=0    skipped=0    rescued=0    i
gnored=0   

[root@lamp ~]# id jerry
uid=2222(jerry) gid=2222(jerry) groups=2222(jerry)

2. 管理事实

主控机连接被控机后,被控机上对应生成的变量称之为事实。

为受管主机收集的一些事实可能包括:

  • 主机名称
  • 内核版本
  • 网络接口
  • IP地址
  • 操作系统版本
  • 各种环境变量
  • CPU数量
  • 提供的或可用的内存
  • 可用磁盘空间
    查看为受管主机收集的事实的一种方式是,运行一个收集事实并使用debug模块显示ansible_facts变量值的简短playbook。
[root@ansible ~]# cat oo.yml 
---
- hosts: 192.168.172.142
  tasks:
    - name: view facts
      debug:
        var: ansible_facts

[root@ansible ~]# ansible-playbook oo.yml 

PLAY [192.168.172.142] *********************************************************

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

TASK [view facts] **************************************************************
ok: [192.168.172.142] => {
    "ansible_facts": {
......

debug模块的msg可以打印自定义信息。

[root@ansible ~]# cat oo.yml 
---
- hosts: 192.168.172.142
  tasks:
    - name: view facts
      debug:
        msg:
          "{{ ansible_facts['default_ipv4']['address'] }}"

[root@ansible ~]# ansible-playbook oo.yml 

PLAY [192.168.172.142] *******************************************************************************

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

TASK [view facts] ************************************************************************************
ok: [192.168.172.142] => {
    "msg": "192.168.172.142"
}

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

为了提升系统处理效率,可关闭事实。

关闭事实gather_facts: no

2.1 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’]

在playbook中使用事实时,Ansible将事实的变量名动态替换为对应的值:

[root@ansible ~]# cat oo.yml 
---
- hosts: 192.168.172.142
  tasks:
    - name: view facts
      debug:
        msg:
          The IPV4 address of {{ ansible_facts["all_ipv4_addresses"] }}


[root@ansible ~]# ansible-playbook oo.yml 

PLAY [192.168.172.142] *********************************************************

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

TASK [view facts] **************************************************************
ok: [192.168.172.142] => {
    "msg": "The IPV4 address of ['192.168.172.142']"
}

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

2.2 创建自定义事实

除了使用系统捕获的事实外,我们还可以自定义事实,并将其本地存储在每个受管主机上。这些事实整合到setup模块在受管主机上运行时收集的标准事实列表中。它们让受管主机能够向Ansible提供任意变量,以用于调整play的行为。

自定义事实可以在静态文件中定义,格式可为INI文件或采用JSON。它们也可以是生成JSON输出的可执行脚本,如同动态清单脚本一样。

有了自定义事实,我们可以为受管主机定义特定的值,供play用于填充配置文件或有条件地运行任务。动态自定义事实允许在play运行时以编程方式确定这些事实的值,甚至还可以确定提供哪些事实。

默认情况下,setup模块从各受管主机的/etc/ansible/facts.d目录下的文件和脚本中加载自定义事实。各个文件或脚本的名称必须以.fact结尾才能被使用。动态自定义事实脚本必须输出JSON格式的事实,而且必须是可执行文件。

//在受管主机上创建/etc/ansble/facts.d
[root@lamp facts.d]# cat 00.fact 
[packages]
web_package = httpd
db_package = mariadb-server

[users]
user1 = aaa
user2 = bbb

[root@ansible ~]# cat oo.yml 
---
- hosts: 192.168.172.142
  tasks:
    - name: 
      debug:
        var: ansible_facts["ansible_local"]
[root@ansible ~]# ansible-playbook oo.yml 

PLAY [192.168.172.142] *********************************************************

TASK [Gathering Facts] *********************************************************
[WARNING]: error loading fact - please check content
ok: [192.168.172.142]

TASK [debug] *******************************************************************
ok: [192.168.172.142] => {
    "ansible_facts[\"ansible_local\"]": {
        "00": {
            "packages": {
                "db_package": "mariadb-server",
                "web_package": "httpd"
            },
            "users": {
                "user1": "aaa",
                "user2": "bbb"
            }
        },
        "000": "error loading fact - please check content"
    }
}

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

同样的事实可能以JSON格式提供。以下JSON事实等同于以上示例中INI格式指定的事实。JSON数据可以存储在静态文本文件中,或者通过可执行脚本输出到标准输出:

{
  "packages": {
    "web_package": "httpd",
    "db_package": "mariadb-server"
  },
  "users": {
    "user1": "joe",
    "user2": "jane"
  }
}

自定义事实文件不能采用playbook那样的YAML格式。JSON格式是最为接近的等效格式。

自定义事实由setup模块存储在ansible_facts.ansible_local变量中。
事实按照定义它们的文件的名称来整理。例如,假设前面的自定义事实由受管主机上保存为/etc/ansible/facts.d/custom.fact的文件生成。在这种情况下,ansible_facts.ansible_local[‘custom’][‘users’][‘user1’]的值为joe。

自定义事实的使用方式与playbook中的默认事实相同:

---
- hosts: all
  tasks:
  - name: Prints various Ansible facts
    debug:
      msg: >
        The package to install on {{ ansible_facts['fqdn'] }}
        is {{ ansible_facts['ansible_local']['cutstom']['packages']['web_package'] }}

2.3 使用魔法变量

一些变量并非事实或通过setup模块配置,但也由Ansible自动设置。这些魔法变量也可用于获取与特定受管主机相关的信息。

最常用的有四个:

魔法变量说明
hostvars包含受管主机的变量,可以用于获取另一台受管主机的变量的值。
如果还没有为受管主机收集事实,则它不会包含该主机的事实。
group_names列出当前受管主机所属的所有组
groups列出清单中的所有组和主机
inventory_hostname包含清单中配置的当前受管主机的主机名称。
因为各种原因有可能与事实报告的主机名称不同

3. 循环

3.1 编写循环任务

Ansible支持使用loop关键字对一组项目迭代任务。可以配置循环以利用列表中的各个项目、列表中各个文件的内容、生成的数字序列或更为复杂的结构来重复任务。

3.1.1 简单循环

简单循环对一组项目迭代任务。loop关键字添加到任务中,将应对其迭代任务的项目列表取为值。循环变量item保存每个迭代过程中使用的值。

[root@ansible ~]# cat oo.yml 
---
- hosts: 192.168.172.142
  tasks:
    - name: 
      service:
        name: "{{ item }}"
        state: started
      loop:
        - httpd
        - mariadb
        - php-fpm
[root@ansible ~]# ansible-playbook oo.yml 

PLAY [192.168.172.142] *********************************************************

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

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

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

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

[root@ansible ~]# cat oo.yml 
run:
  - httpd
  - mariadb
[root@ansible ~]# cat xx.yml 
---
- hosts: 192.168.172.142
  vars_files:
    - oo.yml
  tasks:
    - name:
      service:
        name: "{{ item }}"
        state: started
      loop: "{{ run  }}"


[root@ansible ~]# ansible-playbook xx.yml 

PLAY [192.168.172.142] *********************************************************

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

TASK [service] *****************************************************************
ok: [192.168.172.142] => (item=httpd)
ok: [192.168.172.142] => (item=mariadb)

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

3.1.2 循环散列或字典列表

loop列表不需要是简单值列表。在以下示例中,列表中的每个项实际上是散列或字典。示例中的每个散列或字典具有两个键,即name和groups,当前item循环变量中每个键的值可以分别通过item.name和item.groups变量来检索。

[root@ansible ~]# cat xx.yml 
---
- hosts: 192.168.172.142
  tasks:
    - name: Users exist and are in the correct groups
      user:
        name: "{{ item.name  }}"
        state: present
        groups: "{{ item.groups  }}"
      loop:
        - name: aaa
          groups: root
        - name: bbb
          groups: root
        - name: ccc
          groups: root

[root@ansible ~]# ansible-playbook xx.yml 

PLAY [192.168.172.142] *********************************************************

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

TASK [Users exist and are in the correct groups] *******************************
changed: [192.168.172.142] => (item={'name': 'aaa', 'groups': 'root'})
changed: [192.168.172.142] => (item={'name': 'bbb', 'groups': 'root'})
ok: [192.168.172.142] => (item={'name': 'ccc', 'groups': 'root'})

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

3.1.3 较早样式的循环关键字

较早样式的Ansible循环

循环关键字描述
with_items行为与简单列表的loop关键字相同,例如字符串列表或散列/字典列表。
但与loop不同的是,如果为with_items提供了列表的列表,
它们将被扁平化为单级列表。循环变量item保存每次迭代过程中使用的列表项。
with_file此关键字需要控制节点文件名列表。循环变量item在每次迭代过程中保存文件列表中相应文件的内容。
with_sequence此关键字不需要列表,而是需要参数来根据数字序列生成值列表。循环变量item在每次迭代过程中保存生成的序列中的一个生成项的值。

3.1.4 将Register变量与Loop一起使用

register关键字也可以捕获循环任务的输出。

[root@ansible ~]# cat oo.yml 
---
- hosts: 192.168.172.142
  gather_facts: no
  tasks:
    - name:
      shell: "echo hello {{ item }}"
      loop:
        - aaa
        - bbb
        - ccc
        - ddd
      register: results

    - debug:
        var: results


[root@ansible ~]# ansible-playbook oo.yml 

PLAY [192.168.172.142] *********************************************************

TASK [shell] *******************************************************************
changed: [192.168.172.142] => (item=aaa)
changed: [192.168.172.142] => (item=bbb)
changed: [192.168.172.142] => (item=ccc)
changed: [192.168.172.142] => (item=ddd)

TASK [debug] *******************************************************************
ok: [192.168.172.142] => {
    "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 hello aaa",
                "delta": "0:00:00.011488",
                "end": "2021-07-26 20:44:21.222259",
                "failed": false,
                "invocation": {
                    "module_args": {
                        "_raw_params": "echo hello aaa",
                        "_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": "aaa",
                "rc": 0,
                "start": "2021-07-26 20:44:21.210771",
                "stderr": "",
                "stderr_lines": [],
                "stdout": "hello aaa",
                "stdout_lines": [
                    "hello aaa"
                ]
            },
            {
                "ansible_loop_var": "item",
                "changed": true,
                "cmd": "echo hello bbb",
                "delta": "0:00:00.009031",
                "end": "2021-07-26 20:44:22.424240",
                "failed": false,
                "invocation": {
                    "module_args": {
                        "_raw_params": "echo hello bbb",
                        "_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": "bbb",
                "rc": 0,
                "start": "2021-07-26 20:44:22.415209",
                "stderr": "",
                "stderr_lines": [],
                "stdout": "hello bbb",
                "stdout_lines": [
                    "hello bbb"
                ]
            },
            {
                "ansible_loop_var": "item",
                "changed": true,
                "cmd": "echo hello ccc",
                "delta": "0:00:00.006415",
                "end": "2021-07-26 20:44:23.367842",
                "failed": false,
                "invocation": {
                    "module_args": {
                        "_raw_params": "echo hello ccc",
                        "_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": "ccc",
                "rc": 0,
                "start": "2021-07-26 20:44:23.361427",
                "stderr": "",
                "stderr_lines": [],
                "stdout": "hello ccc",
                "stdout_lines": [
                    "hello ccc"
                ]
            },
            {
                "ansible_loop_var": "item",
                "changed": true,
                "cmd": "echo hello ddd",
                "delta": "0:00:00.008976",
                "end": "2021-07-26 20:44:24.472214",
                "failed": false,
                "invocation": {
                    "module_args": {
                        "_raw_params": "echo hello ddd",
                        "_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": "ddd",
                "rc": 0,
                "start": "2021-07-26 20:44:24.463238",
                "stderr": "",
                "stderr_lines": [],
                "stdout": "hello ddd",
                "stdout_lines": [
                    "hello ddd"
                ]
            }
        ]
    }
}

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

4. 有条件的运行任务

Ansible可使用conditionals在符合特定条件时执行任务或play。例如,可以利用一个条件在Ansible安装或配置服务前确定受管主机上的可用内存。

​ 我们可以利用条件来区分不同的受管主机,并根据它们所符合的条件来分配功能角色。Playbook变量、注册的变量和Ansible事实都可通过条件来进行测试。可以使用比较字符串、数字数据和布尔值的运算符。

以下场景说明了在Ansible中使用条件的情况:

  • 可以在变量中定义硬限制(如min_memory)并将它与受管主机上的可用内存进行比较。
  • Ansible可以捕获并评估命令的输出,以确定某一任务在执行进一步操作前是否已经完成。例如,如果某一程序失败,则将路过批处理。
  • 可以利用Ansible事实来确定受管主机网络配置,并决定要发送的模板文件(如,网络绑定或中继)。
  • 可以评估CPU的数量,来确定如何正确调节某一Web服务器。
  • 将注册的变量与预定义的变量进行比较,以确定服务是否已更改。例如,测试服务配置文件的MD5检验以和查看服务是否已更改。

4.1 条件任务语法

when语句用于有条件地运行任务。它取要测试的条件为值。如果条件满足,则运行任务。如果条件不满足,则跳过任务。

可以测试的一个最简单条件是某一布尔变量是True还是False。以下示例中的when语句导致任务仅在run_my_task为True时运行:

[root@ansible ~]# cat test.yml 
---
- hosts: http
  vars:
    run_my_task: true
  tasks:
    - name:
      yum:
        name: vsftpd
      when: run_my_task



[root@ansible ~]# ansible-playbook test.yml 

PLAY [http] ********************************************************************

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

TASK [yum] *********************************************************************
changed: [192.168.172.142]

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

以下示例测试my_service变量是否具有值。若有值,则将my_service的值用作要安装的软件包的名称。如果未定义my_service变量,则跳过任务且不显示错误。

[root@ansible ~]# cat test.yml 
---
- hosts: http
  gather_facts: no
  vars:
    my_service: vsftpd
  tasks:
    - name: "{{ my_service }} package is installed"
      yum:
        name: "{{ my_service }}"
        state: latest
      when: my_service is defined

[root@ansible ~]# ansible-playbook test.yml 

PLAY [http] ********************************************************************

TASK [vsftpd package is installed] *********************************************
ok: [192.168.172.142]

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

示例条件

操作示例
等于(值为字符串)ansible_machine == “x86_64”
等于(值为数字)max_memory == 512
小于min_memory < 128
大于min_memory > 256
小于等于min_memory <= 256
大于等于min_memory >= 512
不等于min_memory != 512
变量存在min_memory is defined
变量不存在min_memory is not defined
布尔变量是True。1、True或yes的求值为Truememory_available
布尔变量是False。0、False或no的求值为Falsenot memory_available
第一个变量的值存在,作为第二个变量的列表中的值ansible_distribution in supported_distros

在示例中,ansible_distribution变量是在Gathering Facts任务期间确定的事实,用于标识托管主机的操作系统分支。变量supported_distros由playbook创建,包含该playbook支持的操作系统分发列表。如果ansible_distribution的值在supported_distros列表中,则条件通过且任务运行。

[root@ansible ~]# cat test.yml 
---
- hosts: http
  gather_facts: yes
  vars:
    supported_distros:
      - RedHat
      - Fedora
  tasks:
    - name: 
      yum:
        name: vsftpd
        state: absent
      when: ansible_distribution in supported_distros
[root@ansible ~]# ansible-playbook test.yml 

PLAY [http] ********************************************************************

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

TASK [yum] *********************************************************************
changed: [192.168.172.142]

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

4.2 测试多个条件

一个when语句可用于评估多个条件。使用and和or关键字组合条件,并使用括号分组条件。

如果任一条件为真时满足条件语句,则应当使用or语句。例如,如果计算机上运行的是红帽企业linux或Fedora,则下述条件得到满足:

[root@ansible ~]# cat test.yml 
---
- hosts: http
  vars:
    my_service: vsftpd
  tasks:
    - name: 
      yum:
        name: "{{ my_service }}"
        state: present
      when: ansible_distribution == "Redhat" or ansible_distribution == "Fedora"

[root@ansible ~]# ansible-playbook test.yml 

PLAY [http] ********************************************************************

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

TASK [yum] *********************************************************************
skipping: [192.168.172.142]

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

使用and运算时,两个条件都必须为真,才能满足整个条件语句。

when: ansible_distribution_version == "7.5" and ansible_kernel == "3.10.0-327.el7.x86_64

when关键字还支持使用列表来描述条件列表。向when关键字提供列表时,将使用and运算组合所有条件。下面的示例演示了使用and运算符组合多个条件语句的另一方式:

when:
  - ansible_distribution_version == "7.5"
  - ansible_kernel == "3.10.0-327.el7.x86_64"

这种格式提高了可读性,而可读性是良好编写Ansible Playbook的关键目标。

通过使用括号分组条件,可以表达更复杂的条件语句。例如,如果计算机上运行的是红帽企业Linux7或Fedora28,则下述条件语句得到满足。此示例使用大于字符,这样长条件就可以在playbook中分成多行,以便于阅读。

when: >
  ( ansible_distribution == "Redhat" and
    ansible_distribution_major_version == "7" )
  or
  ( ansible_distribution == "Fedora" and
    ansible_distribution_major_version == "28" )

5. 组合循环和有条件任务

- name: install mariadb-server if enough space on root
  yum:
    name: mariadb-server
    state: latest
  loop: "{{ ansible_mounts }}"
  when: item.mount == "/" and item.size_available > 300000000

对某个任务结合使用when和loop时,将对每个项检查when语句。

---
- name: Restart HTTPD if Postfix is Running
  hosts: http
  tasks:
    - name: Get Postfix server status
      command: /usr/bin/systemctl is-active postfix     # Postfix是否在运行?
      ignore_errors: yes    # 如果它不在运行并且命令失败,则不停止处理。
      register: result      # 将模块的结果信息保存在名为result的变量中
      
    - name: Restart Apache HTTPD based on Postfix status
      service:
        name: httpd
        state: restarted
      when: result.rc == 0      # 评估Postfix任务的输出。如果systemctl命令的退出代码为0,\
                                # 则Postfix激活并且此任务重启httpd服务
  

6. 实施处理程序

6.1 ansible处理程序

使用notify语句来调用一个任务的触发,只有当任务的结果为change时,才会调用任务

tasks:
  - name: copy demo.example.conf configuratioon template      # 通知处理程序的任务
    template:
      src: /var/lib/templates/demo.example.conf.template
      dest: /etc/httpd/conf.d/demo.example.conf
    notify:         # notify语句指出该任务需要触发一个处理程序
      - restart apache     # 要运行的处理程序的名称

handlers:       # handlers关键字表示处理程序任务列表的开头
  - name: restart apache   # 被任务调用的处理程序的名称
    service:    # 用于该处理程序的模块
      name: httpd
      state: restarted

在上面的例子中,restart apache处理程序只有在template任务通知已发生更改时才会触发。一个任务可以在其notify部分中调用多个处理程序。Ansible将notify语句视为数组,并且迭代处理程序名称:

tasks:
  - name: copy demo.example.conf configuration template
    template:
      src: /var/lib/templates/demo.exammple.conf.template
      dest: /etc/httpd/conf.d/demo.example.conf
    notify:
      - restart mysql
      - restart apache

handlers:
  - name: restart mysql
    service:
      name: mariadb
      state: restarted
      
  - name: restart apache
    service:
      name: httpd
      state: restarted

6.2 调用使用处理程序的好处

使用处理程序时需要牢记几个重要事项:

  • 处理程序始终按照play的handlers部分指定的顺序运行。它们不按在任务中由notify语句列出的顺序运行,或按任务通知它们的顺序运行。
  • 处理程序通常在相关play中的所有其他任务完成后运行。playbook的tasks部分中某一任务调用的处理程序,将等到tasks下的所有任务都已处理后才会运行。
  • 处理程序名称存在于各play命名空间中。如果两个处理程序被错误地给予相同的名称,则仅会运行一个。
  • 即使有多个任务通知处理程序,该处理程序依然仅运行一次。如果没有任务通知处理程序,它就不会运行。
  • 如果包含notify语句的任务没有报告changed结果(例如,软件包已安装并且任务报告ok),则处理程序不会获得通知。处理程序将被跳过,直到有其他任务通知它。只有相关任务报告了changed状态,Ansible才会通知处理程序。

处理程序用于在任务对受管主机进行更改时执行额外操作。它们不应用作正常任务的替代。

7. 处理任务失败

7.1 管理play中的任务错误

Ansible评估任务的返回代码,从而确定任务是成功还是失败。通常而言,当任务失败时,Ansible将立即在该主机上中止play的其余部分并且跳过所有后续任务。

但有些时候,可能希望即使在任务失败时也继续执行play。例如,或许预期待定任务有可能会失败,并且希望通过有条件地运行某项其他任务来修复。

Ansible有多种功能可用于管理任务错误。

7.2 忽略任务失败

默认情况下,任务失败时play会中止。不过,可以通过忽略失败的任务来覆盖此行为。可以在任务中使用ignore_errors关键字来实现此目的。

[root@ansible ~]# cat test.yml 
---
- hosts: http
  gather_facts: no
  tasks:
    - name:
      yum:
        name: snfuxhzmc
        state: present
      ignore_errors: yes
[root@ansible ~]# ansible-playbook test.yml 

PLAY [http] ********************************************************************

TASK [yum] *********************************************************************
fatal: [192.168.172.142]: FAILED! => {"ansible_facts": {"discovered_interpreter_python": "/usr/libexec/platform-python"}, "changed": false, "failures": ["No package snfuxhzmc available."], "msg": "Failed to install some of the specified packages", "rc": 1, "results": []}
...ignoring

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

7.3 任务失败后强制执行处理程序

通常而言,如果任务失败并且play在该主机上中止,则收到play中早前任务通知的处理程序将不会运行。如果在play中设置force_handlers: yes关键字,则即使play因为后续任务失败而中止也会调用被通知的处理程序。

---
- hosts: http
  force_handlers: yes
  tasks:
    - name: a task which always notifies its handler
      command: /bin/true
      notify: restart the database
      
    - name: a task which fails because the package doesn't exist
      yum:
        name: bbbbbynumnbctvc
        state: latest
        
  handlers:
    - name: restart the database
      service:
        name: mariadb
        state: restarted
        

请记住,处理程序会在任务报告changed结果时获得通知,而在任务报告ok或failed结果时不会获得通知。

7.4 指定任务失败条件

可以在任务中使用failed_when关键字来指定表示任务已失败的条件。这通常与命令模块搭配使用,这些模块可能成功执行了某一命令,但命令的输出可能指示了失败。

tasks:
  - name: Run user creation script
    shell: /usr/local/bin/create_users.sh
    register: command_result
    failed_when: "'Password missing' in command_result.stdout"

fail模块也可用于强制任务失败。上面的场景也可以编写为两个任务:

tasks:
  - name: Run user creation script
    shell: /usr/local/bin/create_users.sh
    register: command_result
    ignore_errors: yes
    
  - name: Report script failure
    fail:
      msg: "The password is missing in the output"
    when: "'Password missing' in command_result.stdout"

7.5 指定何时任务报告 “Changed” 结果

当任务对托管主机进行了更改时,会报告 changed 状态并通知处理程序。如果任务不需要进行更改,则会报告ok并且不通知处理程序。

changed_when关键字可用于控制任务在何时报告它已进行了更改。

[root@ansible ~]# cat test.yml 
---- hosts: http
  tasks:
    - name: 
      yum:
        name: vsftpd
        state: absent
      changed_when: yes

[root@ansible ~]# ansible-playbook test.yml 

PLAY [http] ********************************************************************

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

TASK [yum] *********************************************************************
changed: [192.168.172.142]

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

7.6 Ansible块和错误处理

在playbook中,块是对任务进行逻辑分组的子句,可用于控制任务的执行方式。

- name: block example
  hosts: web
  tasks:
    - name: installing and configuring Yum versionlock plugin
      block:
      - name: package needed by yum
        yum:
          name: yum-plugin-versionlock
          state: present
      - name: lock version of tadata
        lineinfile:
          dest: /etc/yum/pluginconf.d/versionlock.list
          line: tzdata-2020j-1
          state: present
      when: ansible_distribution == "Redhat"

通过块,也可结合rescue和always语句来处理错误。如果块中的任何任务失败,则执行其rescue块中的任务来进行恢复。在block子句中的任务以及rescue子句中的任务(如果出现故障)运行之后,always子句中的任务运行。总结:

  • block:定义要运行的主要任务.
  • rescue:定义要在block子句中定义的任务失败时运行的任务.
  • always:定义始终都独立运行的任务,不论block和rescue子句中定义的任务是成功还是失败.

以下示例演示了如何在playbook中实施块。

[root@ansible ~]# cat test.yml 
---
- hosts: http
  tasks:
    - name: test
      block:
        - shell: echo 'hello' > /root/xx
      rescue:
        - shell: echo 'yes' > /root/xx

      always:
        - shell: echo 'no' >> /root/xx
[root@ansible ~]# ansible-playbook test.yml 

PLAY [http] ********************************************************************

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

TASK [shell] *******************************************************************
changed: [192.168.172.142]

TASK [shell] *******************************************************************
changed: [192.168.172.142]

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

[root@apache ~]# cat xx 
hello
no
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
对于计算机专业的学生而言,参加各类比赛能够带来多方面的益处,具体包括但不限于以下几点: 技能提升: 参与比赛促使学生深入学习和掌握计算机领域的专业知识与技能,如编程语言、算法设计、软件工程、网络安全等。 比赛通常涉及实际问题的解决,有助于将理论知识应用于实践中,增强问题解决能力。 实践经验: 大多数比赛都要求参赛者设计并实现解决方案,这提供了宝贵的动手操作机会,有助于积累项目经验。 实践经验对于计算机专业的学生尤为重要,因为雇主往往更青睐有实际项目背景的候选人。 团队合作: 许多比赛鼓励团队协作,这有助于培养学生的团队精神、沟通技巧和领导能力。 团队合作还能促进学生之间的知识共享和思维碰撞,有助于形成更全面的解决方案。 职业发展: 获奖经历可以显著增强简历的吸引力,为求职或继续深造提供有力支持。 某些比赛可能直接与企业合作,提供实习、工作机会或奖学金,为学生的职业生涯打开更多门路。 网络拓展: 比赛是结识同行业人才的好机会,可以帮助学生建立行业联系,这对于未来的职业发展非常重要。 奖金与荣誉: 许多比赛提供奖金或奖品,这不仅能给予学生经济上的奖励,还能增强其成就感和自信心。 荣誉证书或奖状可以证明学生的成就,对个人品牌建设有积极作用。 创新与研究: 参加比赛可以激发学生的创新思维,推动科研项目的开展,有时甚至能促成学术论文的发表。 个人成长: 在准备和参加比赛的过程中,学生将面临压力与挑战,这有助于培养良好的心理素质和抗压能力。 自我挑战和克服困难的经历对个人成长有着深远的影响。 综上所述,参加计算机领域的比赛对于学生来说是一个全面发展的平台,不仅可以提升专业技能,还能增强团队协作、沟通、解决问题的能力,并为未来的职业生涯奠定坚实的基础。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值