Ansible的管理变量、机密和事实

导读:本篇文章讲解 Ansible的管理变量、机密和事实,希望对大家有帮助,欢迎收藏,转发!站点地址:www.bmabk.com

管理变量、机密和事实

1.管理变量

1.1Ansible变量简介

Ansible支持利用变量来存储值,并在Ansible项目的所有文件中重复使用这些值。这可以简化项目的创建和维护,并减少错误的数量。

通过变量,可以轻松地在Ansible项目中管理给定环境的动态值。例如,变量可能包含下面这些值:

  • 要创建的用户
  • 要安装的软件包
  • 要重新启动的服务
  • 要删除的文件
  • 要从互联网检索的存档

1.1.1 命令变量

变量的名称必须以字母开头,并且只能包含字母、数字和下划线。

无效和有效的Ansible变量名称示例

无效的变量名称 有效的变量名称
web server web_server
remote.file remote_file
1st file file_1
file1
remoteserver$1 remote_server_1
remote_server1

1.12 定义变量

可以在Ansible项目中的多个位置定义变量。不过,这些变量大致可简化为三个范围级别:

  • 全局范围:从命令行或Ansible配置设置的变量
  • Play范围:在play和相关结构中设置的变量
  • 主机范围:由清单、事实收集或注册的任务,在主机组和个别主机上设置的变量

如果在多个级别定义了相同名称的变量,则采用优先级别最高的变量。窄范围优先于更广泛的范围:由清单定义的变量将被playbook定义的变量覆盖,后者将被命令行中定义的变量覆盖。

1.2 playbook中的变量

变量在Ansible Playbook中发挥着重要作用,因为它们可以简化playbook中变量数据的管理。

1.2.1在Playbook中定义变量

编写playbook时,可以定义自己的变量,然后在任务中调用这些值。例如,名为web_package的变量可以使用值httpd来定义。然后,任务可以使用yum模块调用该变量来安装httpd软件包。

Playbook变量可以通过多种方式定义。一种常见的方式是将变量放在playbook开头的vars块中:

- hosts: all
  vars:
    user: joe
    home: /home/joe
/ / 查看node1的/etc/hosts信息
[root@node1 ~]# cat /etc/hosts
127.0.0.1   localhost localhost.localdomain localhost4 localhost4.localdomain4
::1         localhost localhost.localdomain localhost6 localhost6.localdomain6

/ / 进入ansible清单文件目录
[root@master ~]# cd /opt/project/
[root@master project]# ls
[root@master project]# cd playbook/
ansible.cfg  hehe  inventories  playbook
[root@master playbook]# ls
files              httpd.yml  vsftpd.yml
firewalld.yml      test.yml
httpd-install.yml  user.yml

/ / 通过vars块使用变量编辑playbook
[root@master playbook]# vim test.yml 
[root@master playbook]# cat test.yml
---
- hosts: '192.168.91.129'
  gather_facts: no
  vars:
    IP: 192.168.91.130   / / 定义变量IP和NAME
    NAME: node2
  tasks:
    - name: test
      lineinfile:
        path: /etc/hosts
        line: "{{ IP }} {{ NAME }}"
        state: present
[root@master playbook]# cd ..
[root@master project]# ls
ansible.cfg  inventories  playbook

/ / 先查看主机是否ping的通
[root@master project]# ansible all -m ping
192.168.91.129 | SUCCESS => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/libexec/platform-python"
    },
    "changed": false,
    "ping": "pong"
}

/ / 使用playbook执行
[root@master project]# ansible-playbook playbook/test.yml 

PLAY [192.168.91.129] **********************************************************

TASK [test] ********************************************************************
changed: [192.168.91.129]

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

/ / /etc/hosts文件发生改变,执行成功
[root@node1 ~]# cat /etc/hosts
127.0.0.1   localhost localhost.localdomain localhost4 localhost4.localdomain4
::1         localhost localhost.localdomain localhost6 localhost6.localdomain6
192.168.91.130 node2

也可以在外部文件中定义playbook变量。此时不使用playbook中的vars块,可以改为使用vars_files指令,后面跟上相对于playbook位置的外部变量文件名称列表:

- hosts: all
  vars_files:
    - vars/users.yml

而后,可以使用YAML格式在这一/这些文件中定义playbook变量:

user: joe
home: /home/joe
[root@master project]# cd playbook/

/ / 创建一个vars目录
[root@master playbook]# mkdir vars
[root@master playbook]# ls
files          httpd-install.yml  user.yml  vsftpd.yml
firewalld.yml  test.yml           vars

/ / 在vars目录里面编辑一个runtime.yml和run.yml文件
[root@master playbook]# vim vars/runtime.yml
[root@master playbook]# cat vars/runtime.yml 
IP: 192.168.91.131
NAME: node3

[root@master playbook]# vim vars/run.yml
[root@master playbook]# cat vars/run.yml 
IP: 192.168.91.132
NAME: node4

/ / vars目录需要和test.yml文件在同一个级别才可以使用这个方法
[root@master ~]# tree /opt/project/playbook/
/opt/project/playbook/
├── files
│   └── vsftpd.conf
├── firewalld.yml
├── httpd-install.yml
├── test.yml
├── user.yml
├── vars
│   ├── run
│   ├── runtime.yml
│   └── run.yml
└── vsftpd.yml

2 directories, 9 files


/ / 把runtime.yml文件带入到test.yml
[root@master playbook]# vim test.yml 
[root@master playbook]# cat test.yml 
---
- hosts: '192.168.91.129'
  gather_facts: no
  vars_files:
    - vars/runtime.yml
    - vars/run.yml
  tasks:
    - name: test
      lineinfile:
        path: /etc/hosts
        line: "{{ IP }} {{ NAME }}"
        state: present
[root@master playbook]# cd ..

/ / 使用ansible执行playbook
[root@master project]# ansible-playbook playbook/test.yml 

PLAY [192.168.91.129] **********************************************************

TASK [test] ********************************************************************
changed: [192.168.91.129]

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

/ / 查看其结果,执行成功
[root@node1 ~]# cat /etc/hosts
127.0.0.1   localhost localhost.localdomain localhost4 localhost4.localdomain4
::1         localhost localhost.localdomain localhost6 localhost6.localdomain6
192.168.91.130 node2
192.168.91.131 node3
192.168.91.132 node4

1.2.2 在playbook使用变量

声明了变量后,可以在任务中使用这些变量。若要引用变量,可以将变量名放在双大括号内。在任务执行时,Ansible会将变量替换为其值。

vars:
  user: joe
  
tasks:
  # This line will read: Creates the user joe
  - name: Creates the user {{ user }}
    user:
      # This line will create the user named joe
      name: "{{ user }}"

注意:当变量用作开始一个值的第一元素时,必须使用引号。这可以防止Ansible将变量引用视为YAML字典的开头。

1.3 主机变量和组变量

直接应用于主机的清单变量分为两在类:

  • 主机变量,应用于特定主机
  • 组管理,应用于一个主机组或一组主机组中的所有主机

主机变量优先于组变量,但playbook中定义的变量的优先级比这两者更高。

若要定义主机变量和组变量,一种方法是直接在清单文件中定义。这是较旧的做法,不建议采用,但你可能会在未来的工作当中遇到。

定义192.168.91.129的ansible_user主机变量:

[webservers]
192.168.91.129 ansible_user=joe    / / 这种用起来不方便

定义webservers主机组的user组变量:

[root@master project]# vim inventories 
[root@master project]# cat inventories 
[webservers]
192.168.91.129

[webservers:vars]
user=joe
[root@master project]# vim inventories 
[root@master project]# vim inventories 
[root@master project]# cat inventories 
[webservers]
192.168.91.129    / / 只要有主机用户密码相同,就可以往里面添加

[webservers:vars]
ansible_user=root
ansible_password=123456
[root@master project]# ansible all -m ping
192.168.91.129 | SUCCESS => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/libexec/platform-python"
    },
    "changed": false,
    "ping": "pong"
}

定义webservers组的user组变量,该组由两个主机组成,每个主机组有两个服务器:

[webserver1]
node1.example.com
node2.example.com

[webserver2]
node3.example.com
node4.example.com

[webservers:children]
webservers1
webservers2

[webservers:vars]
user=joe

此做法存在一些缺点,它使得清单文件更难以处理,在同一文件中混合提供了主机和变量信息,而且采用的也是过时的语法。

1.3.1 使用目录填充主机和组变量

定义主机和主机组的变量的首选做法是在与清单文件或目录相同的工作目录中,创建group_vars和host_vars两个目录。这两个目录分别包含用于定义组变量和主机变量的文件。

建议的做法是使用host_vars和group_vars目录定义清单变量,而不直接在清单文件中定义它们。

为了定义用于servers组的组变量,需要创建名为group_vars/servers的YAML文件,然后该文件的内容将使用与playbook相同的语法将变量设置为值:

user: joe

类似的,为了定义用于特定主机的主机变量,需要在host_vars目录中创建名称与主机匹配的文件来存放主机变量。

下面的示例更加详细的说明了这一做法。例如在一个场景中,需要管理两个数据中心,并在~/project/inventory清单文件中定义数据中心主机:

[root@node1 ~]# mkdir ~/project
[root@node1 ~]# vim ~/project/inventory
[datacenter1]
node1.example.com
node2.example.com

[datacenter2]
node3.example.com
node4.example.com

[datacenters:children]
datacenter1
datacenter2

如果需要为两个数据中心的所有服务器定义一个通用值,可以为datacenters主机组设置一个组变量:

[root@node1 ~]# mkdir ~/project/groupo_vars
[root@node1 ~]# vim ~/project/groupo_vars/datacenters
package: httpd

如果要为每个数据中心定义不同的值,可以为每个数据中心主机组设置组变量

[root@node1 ~]# vim ~/project/groupo_vars/datacenter1
package: httpd
[root@node1 ~]# vim ~/project/groupo_vars/datacenter2
package: apache2

如果要为每一数据中心的各个主机定义不同的值,则在单独的主机变量文件中定义变量:

[root@node1 ~]# mkdir ~/project/host_vars
[root@node1 ~]# vim ~/project/host_vars/node1.example.com
package: httpd
[root@node1 ~]# vim ~/project/host_vars/node2.example.com
package: apache2
[root@node1 ~]# vim ~/project/host_vars/node3.example.com
package: mariadb-server
[root@node1 ~]# vim ~/project/host_vars/node4.example.com
package: mysql-server

以上示例项目project的目录结构如果包含上面所有示例文件,将如下所示:

[root@node1 ~]# tree ~/project
/root/project
├── ansible.cfg
├── group_vars
│   ├── datacenters
│   ├── datacenters1
│   └── datecenters2
├── host_vars
│   ├── node1.example.com
│   ├── node2.example.com
│   ├── node3.example.com
│   └── node4.example.com
├── inventory
└── playbook.yml

2 directories, 10 files

例子:

/ / 创建group_vars和host_vars目录组
[root@master project]# mkdir group_vars
[root@master project]# mkdir host_vars
[root@master project]# ls
ansible.cfg  host_vars   playbook
group_vars   inventories

/ / 在host_vars组创建一个ip地址进行编辑,写入用户名和密码
[root@master project]# vim host_vars/192.168.91.129
[root@master project]# cat host_vars/192.168.91.129 
ansible_user: root
ansible_password: 123456
[root@master project]# vim inventories
[root@master project]# cat inventories 
[webservers]
192.168.91.129

/ / 执行
[root@master project]# ansible all -m ping
192.168.91.129 | SUCCESS => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/libexec/platform-python"
    },
    "changed": false,
    "ping": "pong"
}

/ / 还可以在host_vars/192.168.91.129里面写一个不存在的ip
[root@master project]# vim host_vars/192.168.91.129
[root@master project]# cat host_vars/192.168.91.129
ansible_user: root
ansible_password: 123456
ip: 1.1.1.1
name: node10

/ / 直接把变量ip和nane带入进去,不需要vars
[root@master project]# vim playbook/test.yml 
[root@master project]# cat playbook/test.yml 
---
- hosts: '192.168.91.129'
  gather_facts: no
  tasks:
    - name: test
      lineinfile:
        path: /etc/hosts
        line: "{{ ip }} {{ name }}"
        state: present

/ / 再次执行
[root@master project]# ansible-playbook playbook/test.yml 

PLAY [192.168.91.129] **********************************************************

TASK [test] ********************************************************************
changed: [192.168.91.129]

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

/ / 查看结果,发现执行成功(在主机文件变量写的,没有在playbook直接写变量)
[root@node1 ~]# cat /etc/hosts
127.0.0.1   localhost localhost.localdomain localhost4 localhost4.localdomain4
::1         localhost localhost.localdomain localhost6 localhost6.localdomain6
192.168.91.130 node2
192.168.91.131 node3
192.168.91.132 node4
1.1.1.1 node10

/ / group_vars和host_vars目录组需要和inventories清单文件同级别
[root@master ~]# tree /opt/project/
/opt/project/
├── ansible.cfg
├── group_vars
├── host_vars
│   └── 192.168.91.129
├── inventories
└── playbook
    ├── files
    │   └── vsftpd.conf
    ├── firewalld.yml
    ├── httpd-install.yml
    ├── test.yml
    ├── user.yml
    ├── vars
    │   ├── run
    │   ├── runtime.yml
    │   └── run.yml
    └── vsftpd.yml

1.4 从命令行覆盖变量

清单变量可被playbook中设置的变量覆盖,这两种变量又可通过在命令行中传递参数到ansible或ansible-playbook命令来覆盖。在命令行上设置的变量称为额外变量。

当需要覆盖一次性运行的playbook的变量的已定义值时,额外变量非常有用。例如:

ansible-playbook main.yml -e "package=apache2"    /  / 最高优先级

优先级划分:

/ / 目录文件ip优先级没有playbook里面的ip优先级高
[root@master project]# cat host_vars/192.168.91.129 ansible_user: root
ansible_password: 123456
ip: 1.1.1.1
name: node10

[root@master project]# vim playbook/test.yml 
[root@master project]# cat playbook/test.yml 
---
- hosts: '192.168.91.129'
  gather_facts: no
  vars:
    ip: 2.2.2.2     / / 以这个为优先级
  tasks:
    - name: test
      lineinfile:
        path: /etc/hosts
        line: "{{ ip }} {{ name }}"
        state: present

/ / 执行
[root@master project]# ansible-playbook playbook/test.yml 

PLAY [192.168.91.129] **********************************************************

TASK [test] ********************************************************************
changed: [192.168.91.129]

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

/ / 查看结果
[root@node1 ~]# cat /etc/hosts
127.0.0.1   localhost localhost.localdomain localhost4 localhost4.localdomain4
::1         localhost localhost.localdomain localhost6 localhost6.localdomain6
192.168.91.130 node2
192.168.91.131 node3
192.168.91.132 node4
1.1.1.1 node10
2.2.2.2 node10

ansible-playbook -e “ip=3.3.3.3” playbook/test.yml:执行优先级最高,以3.3.3.3为准

[root@master project]# ansible-playbook -e "ip=3.3.3.3" playbook/test.yml 

PLAY [192.168.91.129] **********************************************************

TASK [test] ********************************************************************
changed: [192.168.91.129]

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

/ / 查看结果
[root@node1 ~]# cat /etc/hosts
127.0.0.1   localhost localhost.localdomain localhost4 localhost4.localdomain4
::1         localhost localhost.localdomain localhost6 localhost6.localdomain6
192.168.91.130 node2
192.168.91.131 node3
192.168.91.132 node4
1.1.1.1 node10
2.2.2.2 node10
3.3.3.3 node10

总结:-e创建变量优先级最高,其次是playbook里面的,最后是host_vars目录文件里面的变量

1.5 使用数组作为变量

除了将同一元素相关的配置数据(软件包列表、服务列表和用户列表等)分配到多个变量外,也可以使用数组。这种做法的一个好处在于,数组是可以浏览的。

例如,假设下列代码片段:

user1_first_name: Bob
user1_last_name: Jones
user1_home_dir: /users/bjones
user2_first_name: Anne
user2_last_name: Cook
user2_home_dir: /users/acook

这将可以改写成名为users的数组:

users:
  bjones:
    first_name: Bob
    last_name: jones
    home_dir: /users/bjones
  acook:
    first_name: Anne
    last_name: Cook
    home_dir: /users/acook

然后可以使用以下变量来访问用户数据:

# Returns 'Bob'
users.bjones.first_name

# Returns '/users/acook'
users.acook.home_dir

用数组的方式存储变量和引用的例子:

/ / 重新赋值
[root@master project]#  vim host_vars/192.168.91.129 
[root@master project]#  cat host_vars/192.168.91.129 
ansible_user: root
ansible_password: 123456

info:
  node5:
    ip: 4.4.4.4
    domain: www.example.com
  node6:
    ip: 5.5.5.5
    domain: web.example.com

/ / 编辑test.yml文件
[root@master ~]# vim /opt/project/playbook/test.yml 
[root@master ~]# cat /opt/project/playbook/test.yml  
---
- hosts: '192.168.91.129'
  gather_facts: no
  tasks:
    - name: test
      lineinfile:
        path: /etc/hosts
        line: "{{ info.node5.ip }} {{ info.node5.domain }}"
        state: present

/ / 查看结果
[root@node1 ~]# cat /etc/hosts
127.0.0.1   localhost localhost.localdomain localhost4 localhost4.localdomain4
::1         localhost localhost.localdomain localhost6 localhost6.localdomain6
192.168.91.130 node2
192.168.91.131 node3
192.168.91.132 node4
1.1.1.1 node10
2.2.2.2 node10
3.3.3.3 node10
4.4.4.4 www.example.com

由于变量被定义为Python字典,因此可以使用替代语法:

# Returns 'Bob'
users['bjones']['first-name']    / / 这种方法很推荐,playbook大多数使用为中括号这样的,上面的点号不是很推荐

# Returns '/users/acook'
users['acook']['home_dir']

如果键名与python方法或属性的名称(如discard、copy和add)相同,点表示法可能会造成问题。使用中括号表示法有助于避免冲突和错误。

但要声明的是,上面介绍的两种语法都有效,但为了方便故障排除,建议在任何给定Ansible项目的所有文件中一致地采用一种语法,不要混用。

1.6 使用已注册变量捕获命令输出

可以使用register语句捕获命令输出。输出保存在一个临时变量中,然后在playbook中可用于调试用途或者达成其他目的,例如基于命令输出的特定配置。

以下playbook演示了如何为调试用途捕获命令输出:

---
- name: Installs a package and prints the result
  hosts: all
  tasks:
    - name: Install the package
      yum:
        name: httpd
        state: installed
      register: install_result
                                                        
    - debug: var=install_result

运行该playbook时,debug模块用于将install_result注册变量的值转储到终端。

/ / 加入register语句和debug模块把结果放入变量里面并打印变量
[root@master ~]# vim /opt/project/playbook/test.yml 
[root@master ~]# cat /opt/project/playbook/test.yml 
---
- hosts: '192.168.91.129'
  gather_facts: no
  tasks:
    - name: test
      command: "echo hello world"
      register: result

    - debug: var=result

/ / 执行
[root@master project]# ansible-playbook playbook/test.yml

PLAY [192.168.91.129] **********************************************************

TASK [test] ********************************************************************
changed: [192.168.91.129]

TASK [debug] *******************************************************************
ok: [192.168.91.129] => {
    "result": {
        "ansible_facts": {
            "discovered_interpreter_python": "/usr/libexec/platform-python"
        },
        "changed": true,
        "cmd": [
            "echo",
            "hello",
            "world"
        ],
        "delta": "0:00:00.002311",
        "end": "2021-07-22 14:25:15.890678",
        "failed": false,
        "rc": 0,
        "start": "2021-07-22 14:25:15.888367",
        "stderr": "",
        "stderr_lines": [],
        "stdout": "hello world",
        "stdout_lines": [
            "hello world"
        ]
    }
}

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

把date的结果写入到/tmp/abc文件中

[root@master ~]# vim /opt/project/playbook/test.yml 
[root@master ~]# cat /opt/project/playbook/test.yml 
---
- hosts: '192.168.91.129'
  gather_facts: no
  tasks:
    - name: test
      command: "date"
      register: result

    - name: create file
      shell: "echo {{ result['stdout'] }} > /tmp/abc"

/ / 执行
[root@master project]# ansible-playbook playbook/test.yml

PLAY [192.168.91.129] **********************************************************

TASK [test] ********************************************************************
changed: [192.168.91.129]

TASK [create file] *************************************************************
changed: [192.168.91.129]

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

查看结果
[root@node1 ~]# cat /tmp/abc
Thu Jul 22 14:39:40 CST 2021

创建用户runtime1

[root@master ~]# vim /opt/project/playbook/test.yml 
[root@master ~]# cat /opt/project/playbook/test.yml 
---
- hosts: '192.168.91.129'
  gather_facts: no
  tasks:
    - name: create user
      user:
        name: runtime1
        state: present
      register: result

    - name: create file
      shell: "echo {{ result }} > /tmp/abc"

/ / 执行
[root@master project]# ansible-playbook playbook/test.yml

PLAY [192.168.91.129] **********************************************************

TASK [create user] *************************************************************
ok: [192.168.91.129]

TASK [create file] *************************************************************
changed: [192.168.91.129]

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

/ / 查看的结果有点多,所以可以进行提取
[root@node1 ~]# cat /tmp/abc
{name: runtime1, state: present, append: False, move_home: False, changed: False, uid: 1000, group: 1000, comment: , home: /home/runtime1, shell: /bin/bash, ansible_facts: {discovered_interpreter_python: /usr/libexec/platform-python}, failed: False}


/ / 如果我们想查看shell,可以提取值
[root@master ~]# vim /opt/project/playbook/test.yml 
[root@master ~]# cat /opt/project/playbook/test.yml 
---
- hosts: '192.168.91.129'
  gather_facts: no
  tasks:
    - name: create user
      user:
        name: runtime1
        state: present
      register: result

    - name: create file
      shell: "echo {{ result['shell'] }} > /tmp/abc"

/ / 执行
root@master project]# ansible-playbook playbook/test.yml

PLAY [192.168.91.129] **********************************************************

TASK [create user] *************************************************************
ok: [192.168.91.129]

TASK [create file] *************************************************************
changed: [192.168.91.129]

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

/ / 查看到shell
[root@node1 ~]# cat /tmp/abc
/bin/bash

2.管理机密

2.1 Ansible Vault

Ansible可能需要访问密码或API密钥等敏感数据,以便能配置受管主机。通常,此信息可能以纯文本形式存储在清单变量或其他Ansible文件中。但若如此,任何有权访问Ansible文件的用户或存储这些Ansible文件的版本控制系统都能够访问此敏感数据。这显示存在安全风险。

Ansible提供的Ansible Vault可以加密和解密任何由Ansible使用的结构化数据文件。若要使用Ansible Vault,可通过一个名为ansible-vault的命令行工具创建、编辑、加密、解密和查看文件。Ansible Vault可以加密任何由Ansible使用的结构化数据文件。这可能包括清单变量、playbook中含有的变量文件、在执行playbook时作为参数传递的变量文件,或者Ansible角色中定义的变量。

2.1.1 创建加密的文件

要创建新的加密文件,可使用ansible-vault create filename命令。该命令将提示输入新的vault密码,然后利用默认编辑器vi打开文件。我们可以设置和导出EDITOR环境变量,通过设置和导出指定其他默认编辑器。例如,若要将默认编辑器设为nano,可设置为export EDITOR=nano

[root@master ~]# ansible-vault create secret.yml
New Vault password: redhat
Confirm New Vault password: redhat

创建加密文件

[root@master ~]# cd /opt/project/
[root@master project]# ansible-vault create runtime.yml
New Vault password: 
Confirm New Vault password: 
[root@master project]# ls
ansible.cfg  group_vars  host_vars  inventories  packages  playbook  runtime.yml
[root@master project]# file runtime.yml 
runtime.yml: ASCII text
[root@master project]# cat runtime.yml 
$ANSIBLE_VAULT;1.1;AES256
61623334316266343830396566663664636664306532316634343235373435633862653437333336
6264323966663730343239613964376637633632663330300a396130623261363535396530653366
63363163613938383139333535386362383966316264396236616530363737643261656663366532
3661656265633538620a653663633464393639303036646235366138663062346131316638306264
3734

我们还可以用vault密码文件来存储vault密码,而不是通过标准输入途径输入vault密码。这样做需要使用文件权限和其他方式来严密保护该文件。

ansible-vault create --vault-password-file=vault-pass secret.yml

给文件加密

[root@master project]# vim .vault_password
[root@master project]# cat .vault_password 
ffasfafasadfafaaafasd
[root@master project]# ls
ansible.cfg  group_vars  host_vars  inventories  packages  playbook
[root@master project]# ansible-vault create --vault-password-file=.vault_password group_vars/webservers.yml
[root@master project]# cat group_vars/webservers.yml 
$ANSIBLE_VAULT;1.1;AES256
33666665356534396633376165336334383131363461306538313965323830663134613933366633
3632363064366131623263646330316438373062333133320a653330616433333832323732353231
33323765326435663065653466333730616236333264383931386465366539346234656432336437
3236346566383465360a323139353939346337383133616239633935656334633638623061326665
3762

2.1.2 查看加密的文件

可以使用ansible-vault view filename命令查看Ansible Vault加密的文件,而不必打开它进行编辑。

ansible-vault view secret.yml

查看时需要输入加密文件的加密密码。

/ / 查看密码
[root@master ~]# cat /opt/project/.vault_password 
ffasfafasadfafaaafasd

/ / 输入密码
[root@master project]# ansible-vault view group_vars/webservers.yml 
Vault password: 

/ / 查看加密文件结果
[root@master project]# ansible-vault view group_vars/webservers.yml 
Vault password: 
port: 8080

方便查看,可以直接指向密码去查看

[root@master project]# ansible-vault view --valut-password-file=.vault_password group_vars/webservers.yml

2.1.3 编辑现有的加密文件

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

ansible-vault edit secret.yml

编辑时需要输入加密文件的加密密码。

edit子命令始终重写文件,因此只应在进行更改时使用它。要查看文件的内容而不进行更改时,应使用view子命令。.

/ / 编辑现有的加密文件
[root@master project]# ansible-vault edit --valut-password-file=.vault_password group_vars/webservers.yml

/ / 查看编辑结果:8080变80
[root@master project]# ansible-vault view --valut-password-file=.vault_password group_vars/webservers.yml
port: 80

2.1.4 加密现有的文件

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

ansible-vault encrypt secret1.yml secret2.yml
/ / 加密清单文件inventories
[root@master project]# ls
ansible.cfg  group_vars  host_vars  inventories  packages  playbook

/ / 没有加密之前,看的明白
[root@master project]# cat inventories 
[webservers]
192.168.91.129

/ / 使用encrypt成功加密
[root@master project]# ansible-vault encrypt --vault-password-file=.vault_password inventories 
Encryption successful

/ / 加密之后看不明白了
[root@master project]# cat inventories 
$ANSIBLE_VAULT;1.1;AES256
35646232383836636134613339626562633766373263643464343664633032643162313737396536
3139373239613632383136616231646537303565336632610a616165333766316164653065666130
34613739313765393661353830373331383762383531316534366533353065663836653064633363
3565633630316534650a366331363564656565316539313966663261333966363332316135333632
39616437633033633061623431653162613536333832633430613964303465666265

/ / 查看加密文件用view
[root@master project]# ansible-vault view --vault-password-file=.vault_password inventories 
[webservers]
192.168.91.129

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

2.1.5 解密现有的文件

现有的加密文件可以通过ansible-vault decrypt filename命令永久解密。在解密单个文件时,可使用**–output**选项以其他名称保存解密的文件。

ansible-vault decrypt secret1.yml --output=secret1-decrypted.yml

解密使用decrypt

/ / 进行解密清单文件
[root@master project]# ansible-vault decrypt --vault-password-file=.vault_password inventories 
Decryption successful

/ / 查看结果,解密成功
[root@master project]# cat inventories 
[webservers]
192.168.91.129

2.1.6 更加加密文件的密码

使用ansible-vault rekey filename命令更改加密文件的密码。此命令可一次性更新多个数据文件的密钥。它将提示提供原始密码和新密码。

ansible-vault rekey secret.yml
/ / 更改现有密码
[root@master project]# ansible-vault rekey group_vars/mysql 
Vault password: 
New Vault password: 
Confirm New Vault password: 
Rekey successful

在使用vault密码文件时,请使用–new-vault-password-file选项:

ansible-vault rekey --new-vault-password-file=NEW_VAULT_PASSWORD_FILE secret.yml

2.2 playbook和ansible vault

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

[root@master project]# ansible-playbook site.yml
ERROR: A Vault password must be specified to decrypt vars/api_key.yml

要为playbook提供vault密码,可使用–vault-id选项。例如,要以交互方式提供vault密码,请使用下例中所示的–vault-id @prompt:

[root@master project]# ansible-playbook  --vault-id @prompt site.yaml 
Vault password (default): 

PLAY [firewalld] **********************************************************************************************

TASK [Gathering Facts] ****************************************************************************************

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

[root@master project]# vim .ansible_password 
[root@master project]# ansible-playbook  --vault-password-file=.ansible_password site.yaml 

PLAY [firewalld] **********************************************************************************************

TASK [Gathering Facts] ****************************************************************************************

也可以使用ANSIBLE_VAULT_PASSWORD_FILE环境变量,指定密码文件的默认位置。

[root@master project]# cat site.yaml 
$ANSIBLE_VAULT;1.1;AES256
66643763316139346463623432613930653432663637303839643036643436616331663063303365
6264306163303730393235376432643231373034303134660a613736343030396338633266386632
31636636633064653162616134626566643338366435646262376238373166393033333261616631
3834663366373939320a656461363037656139333531653864323134333133363131396333353863
[root@master project]# cat .ansible_password 
123
[root@master project]# export ANSIBLE_VAULT_PASSWORD_FILE=.ansible_password 
[root@master project]# ansible-playbook     site.yaml 

PLAY [firewalld] **********************************************************************************************

TASK [Gathering Facts] ****************************************************************************************

从Ansible2.4开始,可以通过ansible-playbook使用多个Ansible Vault密码。要使用多个密码,需要将多个**–vault-id–vault-password-file选项传递给ansible-playbook**命令。

[root@master project]# ansible-vault  encrypt site.yaml 
New Vault password: 
Confirm New Vault password: 
Encryption successful

[root@master project]# ansible-vault  encrypt inventory 
New Vault password: 
Confirm New Vault password: 
Encryption successful

[root@master project]# ansible-playbook  --vault-id one@prompt  --vault-id two@prompt   site.yaml 
Vault password (one): 
Vault password (two): 

PLAY [firewalld] **********************************************************************************************

TASK [Gathering Facts] ****************************************************************************************

注意:@prompt前面的vaultIDonetwo可以是任何字符,甚至可以完全省略它们。不过,如果在使用ansible-vault命令加密文件时使用**–vault-id id选项,则在运行ansible-playbook时,将最先尝试匹配ID的密码。如果不匹配,将会尝试用户提供的其他密码。没有ID的vaultID@prompt实际上是default@prompt的简写,这意味着提示输入vaultIDdefault**的密码。

2.2.1 变量文件管理的推荐做法

若要简化管理,务必要设置Ansible项目,使敏感变量和其他变量保存在相互独立的文件中。然后,包含敏感变量的文件可通过ansible-vault命令进行保护。

管理组变量和主机变量的首选方式是在playbook级别上创建目录。group_vars目录通常包含名称与它们所应用的主机组匹配的变量文件。host_vars目录通常包含名称与它们所应用的受管主机名称匹配的变量文件。

不过,除了使用group_vars和host_vars中的文件外,也可对每一主机组或受管主机使用目录。这些目录可包含多个变量文件,它们都由该主机组或受管主机使用。例如,在playbook.yml的以下项目目录中,webservers的主机组的成员将使用group_vars/webservers/vars文件中的变量,而192.168.91.129将使用host_vars/192.168.91.129/vars和host_vars/192.168.91.129/vault中的变量:

.
├── ansible.cfg
├── group_vars
│   └── webservers
│       └── vars
├── host_vars
│   └── 192.168.91.129
│       ├── vars
│       └── vault
├── inventory
└── playbook.yml

在这种情况中,其好处在于用于192.168.91.129的大部分变量可以放在vars文件中,敏感变量则可单独放在vault文件中保密。然后使用ansible-vault加密vault文件,而将vars文件保留为纯文本。

在本例中,host_vars/192.168.91.129目录内使用的文件名没有什么特别之处。该目录可以包含更多文件,一些由Ansible Vault加密,另一些则不加密。

Playbook变量(与清单变量相对)也可通过Ansible Vault保护。敏感的playbook变量可以放在单独的文件中,此文件通过Ansible Vault加密,并能vars_files指令包含在该playbook中。这也是推荐做法,因为playbook变量的优先级高于清单变量。

如果需要在playbook中使用多个vault密码,请确保每个加密文件分配一个vaultID,并在运行playbook时输入具有该vaultID的匹配密码。这可确保在解密vault加密文件时先选择正确的密码,这比强制Ansible尝试用户提供的所有vault密码直至找到正确的密码要快。

3. 管理事实

3.1 描述Ansible事实

Ansible事实是Ansible在受管主机上自动检测到的变量。事实中包含有与主机相关的信息,可以像play中的常规变量、条件、循环或依赖于从受管主机收集的值的任何其他语句那样使用。

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

  • 主机名称
  • 内核版本
  • 网络接口
  • IP地址
  • 操作系统版本
  • 各种环境变量
  • CPU数量
  • 提供的或可用的内存
  • 可用磁盘空间

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

  • 可以根据含有受管主机当前内核版本的事实运行条件任务,以此来重启服务器
  • 可以根据通过事实报告的可用内存来自定义MySQL配置文件
  • 可以根据事实的值设置配置文件中使用的IPv4地址

通常,每个play在执行第一个任务之前会先自动运行setup模块来收集事实。

查看为受管主机收集的事实的一种方式是,运行一个收集事实并使用debug模块显示ansible_facts变量值的简短playbook。

[root@master project]# vim site.yaml 

---
  - name: Fact dump
    hosts: all
    tasks:
      - name:  Print all facts
        debug:
          var: ansible_facts

运行该playbook时,事实将显示在作业输出中:

[root@master project]# ansible-playbook     site.yaml 

PLAY [Fact dump] ***************************************************************

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

TASK [Print all facts] *********************************************************
ok: [httpd] => {
    "ansible_facts": {
        "all_ipv4_addresses": [
            "192.168.91.129"
        ],
        "all_ipv6_addresses": [
            "fe80::9a5e:dc1f:f2df:9b52",
            "fe80::f514:dbec:d189:c2c8"
        ],
        "ansible_local": {},
        "apparmor": {
            "status": "disabled"

Playbook以JSON格式显示ansible_facts变量的内容。

下表显示了可能从受管节点收集的并可在playbook中使用的一些事实:
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

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

---
[root@master project]# vim site.yaml 

---
  - name: Fact
    hosts: all
    tasks:
      - name:  Prints various Ansible facts
        debug:
          msg: >
              The default IPv4 address of {{ ansible_facts.fqdn }}
              is {{ ansible_facts.default_ipv4.address }}
              
[root@master project]# ansible-playbook     site.yaml 

PLAY [Fact] ********************************************************************

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

TASK [Prints various Ansible facts] ********************************************
ok: [httpd] => {
    "msg": "The default IPv4 address of httpd is 192.168.91.129"
}

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

3.2 Ansible事实作为变量注入

在Ansible2.5之前,事实是作为前缀为字符串ansible_的单个变量注入,而不是作为ansible_facts变量的一部分注入。例如,ansible_facts[‘distribution’]事实会被称为ansible_distribution。

许多较旧的playbook仍然使用作为变量注入的事实,而不是在ansible_facts变量下创建命名空间的新语法。我们可以使用临时命令来运行setup模块,以此形式显示所有事实的值。以下示例中使用一个临时命令在受管主机192.168.91.129上运行setup模块:

[root@master project]# ansible all -m setup 
httpd | SUCCESS => {
    "ansible_facts": {
        "ansible_all_ipv4_addresses": [
            "192.168.91.129"
        ],
        "ansible_all_ipv6_addresses": [
            "fe80::9a5e:dc1f:f2df:9b52",
            "fe80::f514:dbec:d189:c2c8"
        ],
        "ansible_apparmor": {
            "status": "disabled"
        },
        "ansible_architecture": "x86_64",
        "ansible_bios_date": "07/22/2020",
        "ansible_bios_version": "6.00",

选定的Ansible事实名称比较

ansible_facts形式 旧事实变量形式
ansible_facts[‘hostname’] ansible_hostname
ansible_facts[‘fqdn’] ansible_fqdn
ansible_facts[‘default_ipv4’][‘address’] ansible_default_ipv4[‘address’]
ansible_facts[‘interfaces’] ansible_interfaces
ansible_facts[‘devices’][‘vda’][‘partitions’][‘vda1’][‘size’] ansible_devices[‘vda’][‘partitions’][‘vda1’][‘size’]
ansible_facts[‘dns’][‘nameservers’] ansible_dns[‘nameservers’]
ansible_facts[‘kernel’] ansible_kernel

目前,Ansible同时识别新的事实命名系统(使用ansible_facts)和旧的2.5前“作为单独变量注入的事实”命名系统。

将Ansible配置文件的**[default]部分中inject_facts_as_vars参数设置为False**,可关闭旧命名系统。默认设置目前为True

inject_facts_as_vars的默认值在Ansible的未来版本中可能会更改为False。如果设置为False,则只能使用新的**ansible_facts.***命名系统引用Ansible事实。所以建议大家一开始就要适应这种方式。

3.3 关闭事实收集

有时我们不想为play收集事实。这样做的原因可能有:

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

以上种种原因导致我们可能想要永久或暂时关闭事实收集的功能,要为play禁用事实收集功能,可将gather_facts关键字设置为no:

[root@master project]# vim site.yml 

---
  - name: This play gathers no facts automatically
    hosts: all
    gather_facts: no

即使play设置了加粗样式gather_facts: no,也可以随时通过运行使用setup模块的任务来手动收集事实:

[root@master project]# vim site.yml
---
  - name: This play gathers no facts automatically
    hosts: all
    gather_facts: no
    tasks:
      - name:  get gather_facts
        setup:
      - name: debug
        debug:
          var: ansible_facts

[root@master project]# ansible-playbook     site.yml 

PLAY [This play gathers no facts automatically] ********************************

TASK [get gather_facts] ********************************************************
ok: [httpd]

TASK [debug] *******************************************************************
ok: [httpd] => {
    "ansible_facts": {
        "all_ipv4_addresses": [
            "192.168.91.129"
        ],
        "all_ipv6_addresses": [
            "fe80::9a5e:dc1f:f2df:9b52",
            "fe80::f514:dbec:d189:c2c8"
        ]

3.4 创建自定义事实

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

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

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

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

以下是采用INI格式编写的静态自定义事实文件。INI格式的自定义事实文件包含由一个部分定义的顶层值,后跟用于待定义的事实的键值对:

[packages]
web_package = httpd
db_package = mariadb-server

[users]
user1 = gg
user2 = dd

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

{
  "packages": {
    "web_package": "httpd",
    "db_package": "mariadb-server"
  },
  "users": {
    "user1": "gg",
    "user2": "dd"
  }
}

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

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

可以利用临时命令在受管主机上运行setup模块来检查自定义事实的结构。

[root@master project]# ansible all -m setup 
httpd | SUCCESS => {
    "ansible_facts": {
        "ansible_all_ipv4_addresses": [
            "192.168.91.129"
        ],
        "ansible_all_ipv6_addresses": [
            "fe80::9a5e:dc1f:f2df:9b52",
            "fe80::f514:dbec:d189:c2c8"
        ],
        "ansible_apparmor": {
            "status": "disabled"
        },
        "ansible_architecture": "x86_64",
        "ansible_bios_date": "07/22/2020",
        "ansible_bios_version": "6.00",

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

[root@node1 ~]# mkdir -p /etc/ansible/facts.d
[root@node1 ~]# vi  /etc/ansible/facts.d/custom.fact

{
"packages": {
    "web_package": "httpd",
    "db_package": "mariadb-server"
  },
  "users": {
     "user1": "gg",
     "user2": "dd"
  }
}

[root@master project]# ansible all -m setup | less

        "ansible_local": {
            "custom": {
                "packages": {
                    "db_package": "mariadb-server",
                    "web_package": "httpd"
                },
                "users": {
                    "user1": "gg",
                    "user2": "dd"
                }
            }
        },

3.5 使用魔法变量

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

最常用的有四个:

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

另外还有许多其他的“魔法变量”。有关更多信息,请参见以下链接:
https://docs.ansible.com/ansible/latest/user_guide/playbooks_variables.html#variable-precedence-where-should-i-put-a-variable。

若要深入了解它们的值,一个途径是使用debug模块报告特定主机的hostvars变量的内容:

ansible 192.168.91.129 -m debug -a 'var=hostvars["localhost"]'

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/5650.html

(0)
小半的头像小半

相关推荐

极客之音——专业性很强的中文编程技术网站,欢迎收藏到浏览器,订阅我们!