03 - 编写和运行 Playbook
实验环境
[zzh@controller ~]$ mkdir web && cd web
[zzh@controller web]$ cat > ansible.cfg <<'EOF'
[defaults]
remote_user = zzh
inventory = ./inventory
[privilege_escalation]
become = True
become_user = root
become_method = sudo
become_ask_pass = False
EOF
[zzh@controller web]$ cat > inventory <<'EOF'
controller
node1
node2
node3
node4
EOF
Playbook 介绍
adhoc 命令可以作为一次性命令对一组主机运行一项简单的任务。不过,若要真正发挥 Ansible 的能力,需要使用功能 playbook。
playbook 是一个文本文件,其中包含由一个或多个按特定顺序运行的 play 组成的列表。play 是针对清单中选定的主机运行的一组有序任务。play 可以让您将一系列冗长而复杂的手动管理任务转变为可轻松重复的例程,并且具有可预测性。
在 playbook 中,您可以将 play 内的任务序列保存为人类可读并可立即运行的形式。根据任务的编写方式,任务本身记录了部署应用或基础架构所需的步骤。
ad hoc 命令示例:
[zzh@controller web]$ ansible -m user -a "name=newbie uid=4000 state=present" node1
Playbooks 以 yaml 格式编写,通常以 yaml 和 yml 扩展名保存。
改写成 playbook:
---
- name: Configure important user consistently
hosts: node1
tasks:
- name: newbie exists with UID 4000
user:
name: newbie
uid: 4000
state: present
...
yaml 格式只使用空格缩进,对于空格的数量没有强制要求。
基本规则:
- 同一级别的元素,使用相同的缩进。
- 对于子项目,使用比父项目更多的缩进。
- 增加空白行,提高可读性。
Vim 编辑器设置
如果使用 vim 编辑器,设置 vim 环境便于编辑 Playbooks。
在 $HOME/.vimrc 文件中添加以下内容:
set ai ts=2
或者
autocmd FileType yaml set ai ts=2
效果:
- “ai”,即 “autoindex”,表示自动缩进。
- “ts”,即 “tabstop”,表示 tab 键使用 2 个空格代替。
- autocmd FileType yam,代表文件类型是 yaml 时,自动执行 “set ai ts=2”。
Playbook 编写
Playbook 示例
playbook.yaml 内容如下:
# yaml格式起始行,一般不省略
---
# Playbook中第一个play
# play具有属性:name,hosts,become,tasks,缩进一致
# name属性,用于简要描述play
- name: Enable intranet services
# hosts属性,用于定义要在哪个受管理节点执行
hosts: node1
# tasks属性,用于描述play中任务,属性是列表格式
tasks:
# 第一个任务
# 任务具有属性:涵name和模块名等。
# name属性,用于简要描述任务
- name: latest version of httpd and firewalld installed
# 指明模块名,也就是要执行的任务
yum:
# 执行要操作的rpm包名称
name:
# rpm包名称是-开头的列表格式,或者逗号分隔的列表格式
- httpd
- firewalld
# 定义软件包的状态,lastet代表升级为最新版本
state: latest
# 第二个任务
- name: test html page is installed
# copy模块,用于将content属性值写入到目标文件
copy:
content: "Welcome zzh WebSite!\n"
dest: /var/www/html/index.html
# 第三个任务
- name: firewalld enabled and running
# service模块,用于启用并启动firewalld服务
service:
name: firewalld
enabled: true
state: started
# 第四个任务
- name: firewalld permits access to httpd service
# firewalld,用于放行http服务
firewalld:
service: http
permanent: true
state: enabled
immediate: yes
# 第五个任务
- name: httpd enabled and running
# service模块,用于启用并启动httpd服务
service:
name: httpd
enabled: true
state: started
# Playbook中第二个play,-开头表示列表
- name: Test intranet web server
hosts: localhost
become: no
tasks:
- name: connect to intranet web server
# uri模块,用于测试网站是否可以访问
uri:
url: http://node1
return_content: yes
status_code: 200
# yaml格式结束行,一般省略
...
YAML 注释
在 YAML 中,编号或井号符号 (#) 右侧的所有内容都是注释。如果注释的左侧有内容,请在该编号符号的前面加一个空格。注释可用于提高可读性。
示例:
# This is YAML comment
Some data # This is also a YAML comment
YAML 单行字符串
YAML 中的字符串通常不需要放在引号里,即使字符串中包含空格。
字符串也可以用双引号或单引号括起。
this is a string
'this is another string'
"this is yet another a string"
YAML 多行字符串
-
可以使用竖线(I) 字符表示,保留字符串中的换行字符。
示例:
--- - name: test string hosts: node1 tasks: - name: test string # 用户显示变量值或者字符串 debug: msg: | Example Company 123 Main Street Atlanta, GA 30303
-
也可以使用大于号(>) 字符表示换行字符。执行时换行符使用空格代替,并且行内的引导空白将被删除。
示例:
yam
--- - name: test string hosts: node1 tasks: - name: test string # 用户显示变量值或者字符串 debug: msg: > This is an example of a long string, that will become a single sentence once folded.
这种方法通常用于将很长的字符串在空格字符处断行,使它们跨占多行来提高可读性。
YAML 字典
一组键值对的集合,又称为映射(mapping)和哈希(hashes)。
以缩进块的形式编写键值对集合,如下方所示:user 属性是字典格式,是多个键值对集合。
user:
name: zzh
uid: 1088
state: absent
字典也可以使用以花括号括起的内联块格式编写,如下方所示:
user: {name: zzh, uid: 1088, state: absent}
大多数情形中应避免内联块格式,其可读性较差。不过,当 playbook 中包含角色列表时,使用这种语法,更加容易区分 play 中包含的角色和传递给角色的变量。
某些 playbook 可能使用较旧的简写(shorthand)格式,通过将模块的键值对放在与模块名称相同的行上来定义任务。
示例:
- name: shorhand form
user: name=zzh uid=1088 state=absent
普通格式:
- name: shorhand form
user:
name: zzh
uid: 1088
state: absent
两者格式总结:
- 通常您应避免简写格式,而使用普通格式。
- 普通格式的行数较多,更容易操作。任务的关键字垂直堆叠,更容易区分。阅读 play 时,您的眼睛直接向下扫视,左右运动较少。
- 普通格式是原生的 YAML,现代文本编辑器中的语法突出显示工具可以识别,简写形式则不支持。
- 可能会在文档和他人提供的旧 playbook 中看到这种语法,而且这种语法仍然可以发挥作用。
YAML 列表
一组按次序排列的值,又称为序列(sequence)和数组(array)。
以缩进块的形式编写的键值对集合,如下方所示:
- name: latest version of httpd and firewalld installed
yum:
name:
- httpd
- firewalld
state: latest
- name: test html page is installed
copy:
content: "Welcome to the example.com intranet!\n"
dest: /var/www/html/index.html
以上有两个任务,每个任务都是多个键值对描述。其中 yum 模块操作的软件包是一个简单的名称列表。
内联格式:
name: [httpd, firewalld]
尽量避免内联格式。
Playbook 运行
运行
[zzh@controller web]$ ansible-playbook playbook.yaml
语法检查
选项 --syntax-check,只检查剧本语法,不执行剧本。
[zzh@controller web]$ ansible-playbook playbook.yaml --syntax-check
空运行
空运行,是指模拟运行,并不是真正执行。
[zzh@controller web]$ ansible-playbook playbook.yaml -C
提高输出详细程度
- -v,显示任务结果。一般情况使用 -v 即可。
- -vv,任务结果和任务配置都会显示。
- -vvv,包含关于与受管主机连接的信息。
- -vvvv,增加了连接插件相关的额外详细程度选项,包括受管主机上用于执行脚本的用户,以及所执行的脚本。
Playbook 提权
在 playbook 中指定此关键字将覆盖 /etc/ansible/ansible.cfg 文件中的设置特权升级属性
- remote_user,指定 ssh 用户
- become,启用或禁用特权升级
- become_method,启用特权升级的方法
- become_user,特殊升级的帐户
---
- name: Enable intranet services
hosts: node1
remote_user: zzh
become: true
become_method: sudo
become_user: root
tasks:
- name: latest version of httpd and firewalld installed
yum:
name:
- httpd
- firewalld
state: latest
04 - 管理变量和事实
实验环境
[zzh@controller ~]$ mkdir web && cd web
[zzh@controller web]$ cat > ansible.cfg <<'EOF'
[defaults]
remote_user = zzh
inventory = ./inventory
[privilege_escalation]
become = True
become_user = root
become_method = sudo
become_ask_pass = False
EOF
[zzh@controller web]$ cat > inventory <<'EOF'
controller
node1
node2
node3
node4
EOF
管理 VARIABLES
变量简介
ansible 利用变量来存储数据,以便在 Ansible 项目文件中重复引用,有利于简化项目的创建和维护,降低出错率。我们在 playbook 中可以针对如用户、软件包、服务、文件等进行变量定义。
变量命名规则
- 只能包含字母、数字和下划线(如包含空格、点、$ 符号都为非法变量名)
- 只能以字母开头
变量范围和优先级
ansible 项目文件中多个位置支持定义变量,主要包含三个基本范围:
- Global scope:从命令行或 Ansible 配置设置的变量。
- Play scope:在 play 和相关结构中设置的变量。
- Host scope:由清单、事实(fact)收集或注册的任务,在主机组和个别主机上设置的变量。
优先级从高到低顺序:Global -> Play -> Host。
在多个级别上定义了相同名称的变量,则采用优先级别最高的变量。
Global scope
通过选项 - e传递给 ansible 或者 ansible-playbook 命令。
[zzh@controller web]$ ansible node1 -m debug -a "msg={{ package }}" -e "package=httpd"
node1 | SUCCESS => {
"msg": "httpd"
}
[zzh@controller web]$ ansible node1 -m yum -a "name={{ package }} state=present" -e "package=httpd"
node1 | SUCCESS => {
"ansible_facts": {
"discovered_interpreter_python": "/usr/libexec/platform-python"
},
"changed": false,
"msg": "Nothing to do",
"rc": 0,
"results": [
"Installed: httpd"
]
}
Play scope
vars 声明
格式 1:
---
- name: test vars statement in play
hosts: node1
vars:
user: joe
home: /home/joe
tasks:
- name: add user {{ user }}
user:
name: "{{ user }}"
home: "{{ home }}"
state: present
- name: debug user
debug:
msg: |
username is {{ user }}
home is {{ home }}
格式 2:
ansible 2.18 版本中移除 列表格式变量。
---
- name: test vars statement in play
hosts: node1
vars:
- user: joe
- home: /home/joe
tasks:
- name: add user {{ user }}
user:
name: "{{ user }}"
home: "{{ home }}"
state: present
- name: debug user
debug:
msg: |
username is {{ user }}
home is {{ home }}
vars_files 声明
如果变量比较多,我么可以使用变量文件进行分类,然后分列别应用到 playbook 中。
示例:
---
- name: test vars statement in play
hosts: node1
vars_files:
- vars/user1.yaml
tasks:
- name: add user {{ user }}
user:
name: "{{ user }}"
home: "{{ home }}"
state: present
- name: debug user
debug:
msg: >
username is {{ user}}
home is {{ home }}
[zzh@controller web]$ mkdir vars
[zzh@controller web]$ vim vars/user1.yaml
# yaml 格式
user: user1
home: /home/user1
变量引用
可将变量名称放在双花括号 ({{}}) 内引用变量。在任务执行时, Ansible 会将变量替换为其值。
当变量用作值的第一元素时,变量引用必须使用引号(单引号或者双引号),否则会报错。
错误的示例:
---
- name: test variables play
hosts: node1
vars:
user: joe
home: /home/joe
tasks:
- name: create user
user:
name: {{ user }}
state: present
验证:
[devops@ansible web]$ ansible-playbook playbook.yaml
ERROR! We were unable to read either as JSON nor YAML, these are the errors we got from each:
JSON: Expecting value: line 1 column 1 (char 0)
Syntax Error while loading YAML.
found unacceptable key (unhashable type: 'AnsibleMapping')
The error appears to be in '/home/devops/web/playbook.yaml': line 10, column 16, but may
be elsewhere in the file depending on the exact syntax problem.
The offending line appears to be:
user:
name: {{ user }}
^ here
We could be wrong, but this one looks like it might be an issue with
missing quotes. Always quote template expression brackets when they
start a value. For instance:
with_items:
- {{ foo }}
Should be written as:
with_items:
- "{{ foo }}"
Host scope
主机变量应用于主机和主机组。主机变量优先级高于主机组变量。
主机清单中定义
较旧的做法是直接在清单文件中定义。不建议采用,但仍可能会遇到。
示例:
[servers]
node1 user=zzh
node2
[servers:vars]
user=laowang
验证:
[zzh@controller web]$ ansible servers -m debug -a 'var=user'
node1 | SUCCESS => {
"user": "zzh"
}
node2 | SUCCESS => {
"user": "laowang"
}
缺点:使得清单文件更复杂,在同一文件中混合提供了主机和变量信息。
目录分层结构定义
在项目目录中创建如下目录:
- group_vars,定义主机组变量。目录中文件名可以直接使用
主机组名
或者主机组名.yaml
。 - host_vars,定义主机变量。目录中文件名可以直接使用
主机名
或者主机名.yaml
。
示例 1:
[zzh@controller web]$ cat inventory
[servers]
node1
node2
[zzh@controller web]$ mkdir group_vars
[zzh@controller web]$ vim group_vars/servers.yaml
user: laowang
[zzh@controller web]$ mkdir host_vars
[zzh@controller web]$ vim host_vars/node1.yaml
user: zzh
验证:
[zzh@controller web]$ ansible servers -m debug -a 'var=user'
node1 | SUCCESS => {
"user": "zzh"
}
node2 | SUCCESS => {
"user": "laowang"
}
目录结构定义主机和主机组的变量是首选做法。
示例 2:
basho
[zzh@controller web]$ tree
.
├── ansible.cfg
├── group_vars
│ ├── dc
│ ├── dc1
│ └── dc2
├── host_vars
│ └── node1.yaml
├── inventory
└── playbook.yaml
[zzh@controller web]$ vim inventory
[dc1]
node1
node2
[dc2]
node3
node4
[dc:children]
dc1
dc2
[zzh@controller web]$ grep . group_vars/* host_vars/*
group_vars/dc:package: httpd
group_vars/dc1:package: httpd
group_vars/dc2:package: apache
host_vars/node1.yaml:package: mariadb-server
[zzh@controller web]$ ansible all -m debug -a 'var=package'
node1 | SUCCESS => {
"package": "mariadb-server"
}
node3 | SUCCESS => {
"package": "apache"
}
node2 | SUCCESS => {
"package": "httpd"
}
node4 | SUCCESS => {
"package": "apache"
}
主机连接特殊变量
详情参考:主机连接特殊变量。
- ansible_connection,与主机的连接类型,可以是 smart、ssh 或 paramiko。默认为 smart。
- ansible_host,要连接的主机的名称,默认值就是主机清单名称。
- ansible_port,ssh 端口号,如果不是 22。
- ansible_user,ssh 用户名。
- ansible_ssh_pass,要使用的 ssh 密码。切勿以纯文本形式存储此变量,始终使用保管库。
- ansible_ssh_private_key_file,ssh 使用的私钥文件。如果使用多个密钥并且您不想使用 SSH 代理,这很有用。
- ansible_ssh_common_args,此设置始终附加到 sftp、scp 和 ssh 的默认命令行。
- ansible_sftp_extra_args,此设置始终附加到默认的 sftp 命令行。
- ansible_scp_extra_args,此设置始终附加到默认的 scp 命令行。
- ansible_ssh_extra_args,此设置始终附加到默认的 ssh 命令行。
- ansible_become,等效于 ansible_sudo 或 ansible_su,允许强制提权。
- ansible_become_method,允许设置权限提升方法。
- ansible_become_user,等效于 ansible_sudo_user 或 ansible_su_user,允许设置您通过权限升级成为的用户。
- ansible_become_pass,等效于 ansible_sudo_pass 或 ansible_su_pass,允许您设置权限提升密码(切勿以纯文本形式存储此变量;始终使用保管库。请参阅变量和保管库)。
数组变量
除了将与同一元素相关的配置数据(软件包列表、服务列表和用户列表等)分配到多个变量外,管理员也可以使用数组变量,将多个值存储在同一变量中。
示例:
yaml
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
改写如下:
yaml
users:
bjones:
first_name: Bob
last_name: Jones
home_dir: /users/bjones
acook:
first_name: Anne
last_name: Cook
home_dir: /users/acook
数组变量引用方式一:
yaml
# Returns 'Bob'
users.bjones.first_name
# Returns '/users/acook'
users.acook.home_dir
数组变量引用方式二:
yaml
# Returns 'Bob'
users['bjones']['first_name']
# Returns '/users/acook'
users['acook']['home_dir']
引用方式总结:
- 如果使用方法一 **. 分隔符 ** 引用的关键字与 python 的功能函数同名,例如 discard、copy、add,那么就会出现问题。方法二 [‘’] 引用方式可以避免这种错误。
- 尽管两种方法都可以使用,为了减少排故难度,Ansible 中统一使用其中一种方法。
示例 1:
yaml
---
- name: test vars statement in play
hosts: node1
vars:
users:
zzh:
user_name: zzh
home_path: /home/zzh
laowang:
user_name: laowang
home_path: /home/laowang
tasks:
- name: add user {{ users.zzh.user_name }}
user:
name: '{{ users.zzh.user_name }}'
home: "{{ users.zzh.home_path }}"
- name: debug laowang
debug:
msg: >
username is {{ users['laowang']['user_name'] }}
home_path is {{ users['laowang']['home_path'] }}
示例 2:
yaml
---
- name: test vars statement in play
hosts: node1
vars:
users:
- user_name: zzh
home_path: /home/zzh1
- user_name: laowang
home_path: /home/zzh2
tasks:
- name: add user {{ users.0.user_name }}
user:
name: "{{ users.0.user_name }}"
home: "{{ users.0.home_path }}"
- name: debug {{ users[1].user_name }}
debug:
msg: "{{ users[1].user_name }}"
register 语句
register 语句捕获任务输出。 输出保存在一个临时变量中,稍后在 playbook 中可用于调试用途或者达成其他目的。
示例:
yaml
---
- name: Installs a package and prints the result
hosts: node1
tasks:
- name: Install the package
yum:
name: httpd
state: installed
register: install_result
- debug:
var: install_result
MAGIC 变量
magic 变量由 Ansible 自动设置,可用于获取与特定受管主机相关的信息。
假设当前清单内容为:
ini
controller
[webs]
node1
node2
[dbs]
node3
node4
最常用四个 Magic 变量:
-
inventory_hostname,包含清单中配置的当前受管主机的主机名称。这可能因为各种原因而与 FACTS 报告的主机名称不同。
bash
[zzh@controller web]$ ansible node1 -m debug -a 'var=inventory_hostname' node1 | SUCCESS => { "inventory_hostname": "node1" }
-
group_names,列出当前受管主机所属的所有主机组。
bash
[zzh@controller web]$ ansible node1 -m debug -a 'var=group_names' node1 | SUCCESS => { "group_names": [ "webs" ] }
-
groups,列出清单中的所有组,以及组中含有的主机。
bash
[zzh@controller web]$ ansible node1 -m debug -a 'var=groups' node1 | SUCCESS => { "groups": { "all": [ "workstation", "node1", "node2", "node3", "node4" ], "dbs": [ "node3", "node4" ], "ungrouped": [ "controller" ], "webs": [ "node1", "node2" ] } }
-
hostvars,包含所有受管主机的变量,可用于获取另一台受管主机的变量的值。如果还没有为受管主机收集 FACTS,则它不会包含该主机的 FACTS。
例如:
hostvars.controller.group_names
bash
# node1 所在主机组
116 ansible node1 -m debug -a var=group_names
# 清单中所有主机组,以及主机组中主机
117 ansible node1 -m debug -a var=groups
# 特定主机组中主机
118 ansible node1 -m debug -a var=groups.all
119 ansible node1 -m debug -a var=groups.nodes
120 ansible node1 -m debug -a var=groups.dbs
管理 SECRETS
Ansible Vault 简介
Ansible 可能需要访问密码或 API 密钥等敏感数据,此信息可能以纯文本形式存储在清单变量或其他 Ansible 文件中。任何有权访问 Ansible 文件的用户或存储这些 Ansible 文件的版本控制系统都能够访问此敏感数据。
这显然存在安全风险。Ansible 随附的 Ansible Vault 可以加密任何由 Ansible 使用的结构化数据文件,包括清单变量、playbook 中含有的变量文件、在执行 playbook 时作为参数传递的变量文件,以及 Ansible 角色中定义的变量。
ansible-vault 命令
bash
[zzh@controller web]$ ansible-vault -h
usage: ansible-vault [-h] [--version] [-v]
{create,decrypt,edit,view,encrypt,encrypt_string,rekey} ...
encryption/decryption utility for Ansible data files
positional arguments:
{create,decrypt,edit,view,encrypt,encrypt_string,rekey}
create Create new vault encrypted file
decrypt Decrypt vault encrypted file
edit Edit vault encrypted file
view View vault encrypted file
encrypt Encrypt YAML file
encrypt_string Encrypt a string
rekey Re-key a vault encrypted file
options:
--version show program's version number, config file location, configured
module search path, module location, executable location and exit
-h, --help show this help message and exit
-v, --verbose Causes Ansible to print more debug messages. Adding multiple -v will
increase the verbosity, the builtin plugins currently evaluate up to
-vvvvvv. A reasonable level to start is -vvv, connection debugging
might require -vvvv. This argument may be specified multiple times.
See 'ansible-vault <command> --help' for more information on a specific command.
ansible-vault create 和 edit 命令使用默认编辑器 vi 打开文件。您可以设置和导出 EDITOR 环境变量指定其他默认编辑器。
例如,若要将默认编辑器设为 vim, 可设置为 export EDITOR=vim
[zzh@controller web]$ export EDITOR=vim
# 或者添加到bash配置文件中
[zzh@controller web]$ echo 'export EDITOR=vim' >> ~/.bashrc
[zzh@controller web]$ source ~/.bashrc
# 创建加密文件,内容是yaml格式,例如 password: redhat
[zzh@controller web]$ ansible-vault create secret.yaml
New Vault password: `redhat`
Confirm New Vault password: `redhat`
password: redhat
[zzh@controller web]$ cat secret.yaml
$ANSIBLE_VAULT;1.1;AES256
35346165313633633862323064376139623339643639373164306339393231653637363933353339
3337386665303463633834643061643436613839316538640a356330613330313534336135616537
35363037636633633061336363326362306234376236616539613964353831303763623939353433
3334363938613131370a623166373737343737393730343863323433336366383432373036303262
63303438393936303133336635313463633863383037643039353664313432653631
# 查看加密文件
[zzh@controller web]$ ansible-vault view secret.yaml
Vault password: `redhat`
password: redhat
# 使用 --vault-password-file 选项从文件中读取加密和解密密码
[zzh@controller web]$ echo redhat > pass
[zzh@controller web]$ ansible-vault view secret.yaml --vault-password-file=pass
password: redhat
# 还可以在ansible.cfg的defaults中设置vault_password_file
[zzh@controller web]$ vim ansible.cfg
vault_password_file = ./pass
# 编辑加密文件
[zzh@controller web]$ ansible-vault edit secret.yaml --vault-password-file=pass
password: redhat123
# 解密文件
[zzh@controller web]$ ansible-vault decrypt secret.yaml --vault-password-file=pass
Decryption successful
[zzh@controller web]$ cat secret.yaml
password: redhat123
# 加密文件
[zzh@controller web]$ ansible-vault encrypt secret.yaml --vault-password-file=pass
Encryption successful
# 更改加密文件密码
[zzh@controller web]$ ansible-vault rekey secret.yaml --vault-password-file=pass
New Vault password: `redhat`
Confirm New Vault password: `redhat`
Rekey successful
# 还可以使用选项 --new-vault-password-file 指定新密码所在文件位置
[zzh@controller web]$ ansible-vault rekey secret.yaml --vault-password-file=pass --new-vault-password-file=pass-new
Rekey successful
综合试验
# ansible vault 综合实验
[zzh@controller web 16:30:48]$ vim ansible.cfg
[defaults]
inventory = ./inventory
remote_user = zzh
vault_password_file = ./passwd-for-vault
[privilege_escalation]
become=True
become_method=sudo
become_user=root
become_ask_pass=False
[zzh@controller web 16:34:20]$ vim password-for-vault
redhat123
[zzh@controller web 16:34:42]$ ls host_vars/node1/
ls: 无法访问host_vars/node1/: 没有那个文件或目录
[zzh@controller web 16:34:55]$ mkdir -p host_vars/node1
[zzh@controller web 16:36:24]$ touch host_vars/node1/vars.yml
[zzh@controller web 16:36:30]$ touch host_vars/node1/vaults.yml
[zzh@controller web 16:36:35]$ ls host_vars/node1/
vars.yml vaults.yml
[zzh@controller web 16:36:43]$ cat host_vars/node1/vars.yml
[zzh@controller web 16:36:55]$ vim host_vars/node1/vars.yml
node: NODE1
ansible_host: node1
[zzh@controller web 16:37:39]$ vim host_vars/node1/vaults.yml
user: zzh
password: redhat
host: '%'
priv: '*.*:ALL'
[zzh@controller web 16:38:33]$ ansible-vault encrypt host_vars/node1/vaults.yml
Encryption successful
[zzh@controller web 16:39:00]$ ansible-vault view host_vars/node1/vaults.yml
user: zzh
password: redhat
host: '%'
priv: '*.*:ALL'
变量管理推荐做法
- 包含敏感变量的文件可通过 ansible-vault 命令进行保护。
- 敏感变量和所有其他变量保存在相互独立的文件中。
- 管理组变量和主机变量的首选方式是在项目目录中创建子目录。
可为每个主机组或受管主机使用独立的目录。这些目录可包含多个变量文件,它们都由该主机组或受管主机使用。
示例:
[zzh@controller web]$ tree
.
├── ansible.cfg
├── group_vars
│ ├── all.yaml
│ └── servers.yaml
├── host_vars
│ └── node1
│ ├── vars.yaml
│ └── vaults.yaml
├── inventory
├── playbook.yaml
└── vault
└── mysql.yml
4 directories, 8 files
node1 的大部分变量可以放在 vars.yaml 文件中,敏感变量则可单独放在 vaults.yaml 文件中,并使用 ansible-vault 加密 vault 文件,而将 vars 文件保留为纯文本。
管理 FACTS
FACTS 介绍
FACTS 是 Ansible 在受管主机上自动检测到的变量,默认保存在内容中,只存在于本次 playbook 执行期间。
FACTS 含有主机相关的信息,可以像 play 中的常规变量一样使用。
受管主机的 facts 包括:
・主机名称・内核版本・网络接口・IP 地址・操作系统版本・各种环境变量
・CPU 数量・提供的或可用的内存・可用磁盘空间
借助 facts,可以方便地检索受管主机的状态,并根据该状态确定要执行的操作。
例如:
- 可以根据当前内核版本的 FACTS 运行条件任务,以此来重新启动服务器。
- 可以根据通过 FACTS 报告的可用内存来自定义 MySQL 配置文件。
- 可以根据 FACTS 的值设置配置文件中使用的 IPv4 地址。
通常,每个 play 在执行第一个任务之前会先自动收集 FACTS。
查看 FACTS 内容
示例 1:查看所有变量
---
- name: Dump facts
hosts: node1
tasks:
- name: Print all facts
debug:
var: ansible_facts
示例 2:查看单个变量
---
- hosts: node1
tasks:
- name: Print Ansible facts
debug:
msg: >
The default IPv4 address of {{ ansible_fqdn }}
is {{ ansible_default_ipv4.address }}
部分 FACTS
FACT | VARIABLE |
---|---|
短主机名 | ansible_facts[‘hostname’] |
完全限定的域名 | ansible_facts[‘fqdn’] |
主要 IPv4 地址(基于路由) | ansible_facts[‘default_ipv4’][‘address’] |
所有网络接口的名称列表 | ansible_facts[‘interfaces’] |
/dev/vdal 磁盘分区的大小 | ansible_facts[‘devices’][‘vda’][‘partitions’]['vda1][‘size’] |
DNS 服务器列表 | ansible_facts[‘dns’][‘nameservers’] |
当前运行的内核的版本 | ansible_facts[‘kernel’] |
setup 和 gather_facts 模块
setup 和 gather_facts 模块都可以用来收集 facts:
- gather_facts 模块,只能用来收集 facts。
- setup 模块,除了用来收集 facts,还提供额外选项:
- filter 选项,用于查看特定 facts 值。
- gather_subset 选项,用于控制收集 facts 范围。
[zzh@controller web]$ ansible -m setup node1 -a 'filter=ansible_default_ipv4'
node1 | SUCCESS => {
"ansible_facts": {
"ansible_default_ipv4": {
"address": "172.25.250.10",
"alias": "enp1s0",
"broadcast": "172.25.250.255",
"gateway": "172.25.250.254",
"interface": "enp1s0",
"macaddress": "52:54:00:00:fa:0a",
"mtu": 1500,
"netmask": "255.255.255.0",
"network": "172.25.250.0",
"type": "ether"
},
"discovered_interpreter_python": "/usr/libexec/platform-python"
},
"changed": false
}
ANSIBLE FACTS 变量注入
在 Ansible2.5 之前,FACTS 是使用前缀为 ansible_字符串的单个变量注入,而不是作为 ansible_facts 变量的一部分注入。
例如,ansible_facts [‘distribution’] 等同于 ansible_distribution。
许多较旧的 playbook 仍然使用 ansible_前缀的变量,而不是通过 ansible_ facts 变量引用。
部分 facts 变量对比:
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 同时识别新的 FACTS 命名系统(使用 ansible_facts) 和旧的 2.5 前 “作为单独变量注入的 FACTS” 命名系统。
Ansible 配置置文件 [default] 块中 inject_facts_as_vars 参数设置为 false, 可关闭旧命名系统。默认设置目前为 true。
如果设置为 false,则只能使用新的 ansible_facts.* 命名规则。在这种情况下,尝试通过旧命名空间引用 FACTS 将导致变量未定义错误。
inject_facts_as_vars 的默认值在 Ansible 的未来版本中可能会更改为 false。
关闭 FACTS 收集
关闭 FACTS 收集部分原因:
- 不使用任何 FACTS
- 希望加快 play 速度或减小 play 在受管主机上造成的负载
- 受管主机因为某种原因而无法运行 setup 模块
- 需要安装一些必备软件后再收集 FACTS
Ansible 配置文件设置
[defaults]
gathering = explicit
play 中设置
---
- name: Fact dump
hosts: node1
gather_facts: no
即使关闭以后,也可以随时使用 setup 模块收集 facts。
自定义 FACTS (忽略)
管理员可以创建自定义 FACTS, 将其存储在每个受管主机上。 这些 FACTS 整合到 setup 模块在受管主机上运行时收集的标准 FACTS 列表中。
借助自定义 FACTS,管理员可以为受管主机定义特定的值,供 play 用于填充配置文件或有条件地运行任务。动态自定义 FACTS 允许在 play 运行时以编程方式确定这些 FACTS 的值,甚至还可以确定提供哪些 FACTS。
自定义 FACTS 可以在静态文件中定义,格式可为 INI 文件或 JSON 文件。它们也可以是生成 JSON 输出的可执行脚本,如同动态清单脚本一样。
默认情況下, setup 模块:
- 从各受管主机的 /etc/ansible/facts.d 目录下的文件和脚本中加载自定义 FACTS。
- 各个文件或脚本的名称必须以.fact 结尾才能被使用。
- 动态自定义 FACTS 脚本必须输出 JSON 格式的 FACTS,而且必须是可执行文件。
[root@node1 ~]# mkdir -p /etc/ansible/facts.d/
INI 格式自定义 FACTS 文件:
[root@node1 ~]# vim /etc/ansible/facts.d/custom.fact
[packages]
web_package = httpd
db_package = mariadb_server
[users]
user1 = joe
user2 = jane
JSON 格式自定义 FACTS 文件:
{
"packages": {
"web_package": "httpd",
"db_package": "mariadb_server"
},
"users": {
"user1": "joe",
"user2": "jane"
}
}
利用临时命令在受管主机上运行 setup 模块来检查自定义 FACTS 的结构。
[zzh@controller web]$ ansible node1 -m setup -a 'filter=ansible_local'
node1 | SUCCESS => {
"ansible_facts": {
"ansible_local": {
"custom": {
"packages": {
"db_package": "mariadb_server",
"web_package": "httpd"
},
"users": {
"user1": "joe",
"user2": "jane"
}
}
},
"discovered_interpreter_python": "/usr/libexec/platform-python"
},
"changed": false
}
自定义 FACTS 的使用方式与默认 FACTS 相同。