Ansible: What’s the difference between task, role, play and playbook?
Playbook 是 play 的列表。顶层的 playbook YAML 里只做两件事:
- 定义 play
- 使用关键字
import_playbook
从另一个 playbook YAML 文件导入 plays。
playbook 只能被 ansible-playbook
调用。
Task,用手册的话说,就是用来调用一个 ansible 模块。但是,task 不知道它应该运行在哪些主机上。
Play 用来将任务绑定到要运行的服务器上。这里的重点是强制关键字 hosts
,它告诉哪些主机受到影响以及如何受到影响。
因此,playbook 的结构和行为如下:
- Playbook: the highest level, just a list of plays
- Play: ties tasks to host lists
- Tasks: definition of a call to a module
- Besides tasks, a play may have pre-tasks, post-tasks and handlers, which are all task-like, and roles.
- Play: ties tasks to host lists
Role 是封装和共享 Ansible 信息的一种简单方法。在角色的子目录下,可以找到任务、处理程序、文件、模板、变量和默认值,这些都与该特定角色相关。
角色只定义任务,不定义这些任务将运行在哪些主机上,所以必须从 play 中引用角色。
角色还可以声明依赖关系,即在执行当前角色之前先运行其他角色。
Dry run the playbook without making actual changes
ansible-playbook -i inventory/hosts playbook.yml --check --diff -vv
Module
Modules are the main building blocks of Ansible playbooks. Although we do not generally speak of “module plugins”, a module is a type of plugin. For a developer-focused description of the differences between modules and other plugins, see Modules and plugins: what is the difference?
Modules and plugins: what is the difference?
If you are looking to add functionality to Ansible, you might wonder whether you need a module or a plugin. Here is a quick overview to help you understand what you need:
Modules are reusable, standalone scripts that can be used by the Ansible API, the ansible command, or the ansible-playbook command. Modules provide a defined interface. Each module accepts arguments and returns information to Ansible by printing a JSON string to stdout before exiting. Modules execute on the target system (usually that means on a remote system) in separate processes. Modules are technically plugins, but for historical reasons we do not usually talk about “module plugins”.
Plugins extend Ansible’s core functionality and execute on the control node within the /usr/bin/ansible process. Plugins offer options and extensions for the core features of Ansible - transforming data, logging output, connecting to inventory, and more.
Fully Qualified Connection Name (FQCN)
Namespace.Collection.Content_Name
, the full definition of a module, plugin or filter.
Plugin documentation tool
# List all docs for available plugins (default to "module")
ansible-doc -l
# List all docs for builtin modules
ansible-doc -l ansible.builtin
# List all docs for filter plugins
ansible-doc -l -t filter
# view the doc of file module
ansible-doc ansible.builtin.file
# short format
ansible-doc file
become 和 become_user
本节内容参考自 Ansible sudo – ansible become example。
如果只有 become
指令,没有 become_user
指令时,相当于执行基本的 sudo command
命令,即以 root
用户执行相应的任务。
如果希望以非 root
用户执行 ansible 任务,在 playbook 中必须同时使用 become
和 become_user
指令,这相当于执行 sudo -u someuser somecommand
命令。
执行 palybook 时,可能需要输入密码来执行 become
指令 (sudo
),ansible-playbook
命令提供了选项 --ask-become-pass
或 -K
(短格式)来提示输入密码。
ansible-playbook -i hosts playbook.yml -K
可以设置 BECOME_PASSWORD_FILE 和 DEFAULT_BECOME_ASK_PASS 选项来提供默认值
- 在
ansible.cfg
文件中设置
[defaults]
# (path) The password file to use for the become plugin. --become-password-file.
# If executable, it will be run and the resulting stdout will be used as the password.
become_password_file=a_become_password_file
[privilege_escalation]
# (boolean) Toggle to prompt for privilege escalation password.
become_ask_pass=True
- 在环境变量中设置
export ANSIBLE_BECOME_PASSWORD_FILE=a_become_password_file
export ANSIBLE_BECOME_ASK_PASS=yes
Plugin list
使用 ansible-doc -t become -l
查看可用插件列表,使用 ansible-doc -t become <plugin name>
查看具体文档和示例。
Ansible ad hoc command
临时命令非常适合很少重复的任务。例如,重启服务器、复制文件、管理包和用户等等。
ansible [pattern] -m [module] -a "[module options]"
-a
选项接受 key=value
格式或 JSON 字符串。
Example hosts
hosts 文件
[controller]
localhost ansible_connection=local
[vms]
fedora-vm ansible_connection=ssh ansible_host=10.211.55.4 ansible_user=parallels
例子 - 重启服务器
ansible 命令默认使用 ansible.builtin.command 模块。
ansible vms -i hosts -a '/sbin/reboot'
# 等同于
ansible vms -i hosts -m command -a '/sbin/reboot'
默认情况下,Ansible 仅使用 5 个并发进程。如果主机数量多于为 fork 计数设置的值,Ansible 会花费更长的时间。参数 --forks
或 -f
指定并发进程数量 ,下面使用 10 个并行分支重启 [vms] 服务器:
ansible vms -i hosts -a "/sbin/reboot" -f 10
/usr/bin/ansible
使用你当前账号运行,需要以不同用户连接,可以加上 --user
或 -u
参数
ansible vms -i hosts vms -a '/sbin/reboot' -f 10 -u username
重启服务器可能需要权限升级。可以使用 username
连接到服务器,并使用 --become
或 -b
参数以 root
用户身份运行命令,添加 --ask-become-pass
或 -K
,Ansible 会提示你输入用于权限升级的密码。
ansible vms -i hosts -a "/sbin/reboot" -f 10 -u username --become [--ask-become-pass]
在命令行指定要连接的主机
在命令行指定主机,而不通过 inventory 文件,注意 -i
参数中指定的主机最后要加逗号 ,
。
# run in local
ansible -i localhost, --connection local -m setup all
# run in host01
ansible -i host01, -u username -m setup all
# run playbook, requires 'hosts: all' in your playbook
ansible-playbook -i host01, site.yml
打印主机当前使用的 ansible 变量
ansible all -i hosts -m debug -a "var=hostvars[inventory_hostname]"
Ansible-Vault
加密字符串
ansible-vault encrypt_string 命令将字符串加密,并格式化为可包含在 playbook、roles 或 variables 文件中的格式。要创建一个加密变量,将以下三个选项传递给 ansible-vault encrypt_string 命令:
选项 | 值 | 描述 |
---|---|---|
string_to_encrypt | 要加密的字符串 | |
–name/-n | string_name_of_variable | 字符串变量名称 |
–stdin-name | string_name_of_variable | 字符串变量名称,从 stdin 读入字符串时使用 |
–vault-id | label@password_source | 密码来源(提示、文件或脚本,带或不带 vault ID) |
命令格式如下
ansible-vault encrypt_string '<string_to_encrypt>' [--name '<string_name_of_variable>'] [--vault-id <password_source>]
例如,加密字符串 ‘foooodev’,其变量名为 ‘the_dev_secret’,并且使用 a_vault_password_file 中的密码 (abc123
)
ansible-vault encrypt_string 'foooodev' --name 'the_dev_secret' --vault-id a_vault_password_file
Encryption successful
the_dev_secret: !vault |
$ANSIBLE_VAULT;1.1;AES256
61346536643430386630663636623832646162383361623737393936326361383937613537616537
3561636565343863336236636138636339393139393363620a313135393937613164643830643765
36363661386236313833646262666561363461613037646134633830646561383938633261643538
6439353735643865340a616534613665363836623137333965643062636332353766666166353437
6535
加密多行字符串,可以不在命令中输入要加密的字符串,此时会在命令行提示输入字符串
ansible-vault encrypt_string --stdin-name '<string_name_of_variable>' --vault-id <password_source>
输入字符串后,回车键入 Ctrl-D 退出。
从 Ansible 2.4 开始,--vault-id
可用于指示密码用于哪个 vault ID(’dev’、’prod’、’cloud’ 等)以及如何获取密码 (prompt, a file path, etc.),例如:
ansible-vault encrypt_string 'foooodev' --name 'the_dev_secret' --vault-id dev@a_vault_password_file
默认情况下,vault ID 标签只是一个提示,提醒使用哪个密码来加密变量或文件。Ansible 不会检查加密内容标头中的 vault ID 是否与你提供的 vault ID 匹配,详细内容参阅官方文档 Enforcing vault ID matching
可以设置 DEFAULT_VAULT_PASSWORD_FILE 和 DEFAULT_VAULT_IDENTITY 选项来提供默认值
- 在
ansible.cfg
文件中设置
[defaults]
# (string) If true, decrypting vaults with a vault id will only try the password from the matching vault-id
vault_id_match=False
# (path) The vault password file to use. Equivalent to --vault-password-file or --vault-id
# If executable, it will be run and the resulting stdout will be used as the password.
vault_password_file=a_vault_password_file
# (string) The label to use for the default vault id label in cases where a vault id label is not provided
vault_identity=dev
- 在环境变量中设置
export ANSIBLE_VAULT_PASSWORD_FILE=a_vault_password_file
export ANSIBLE_VAULT_IDENTITY=dev
查看加密字符串变量
假如之前加密的字符串变量存储在了文件 vars.yml 中,可以使用下面的命令进行解密,
ansible localhost -m debug -a "var=the_dev_secret" -e "@vars.yml" --vault-id dev@a_vault_password_file
解密 host 变量文件中的加密变量
ansible -i inventories/staging/hosts -m debug -a 'var=ssh_private_key' --vault-id staging@a_vault_password_file group_name
解密字符串
解密字符串的时候需要注意字符串的两种加密格式:
-
不带 vault id label 的格式,其对应的 vault format 版本为
1.1
$ANSIBLE_VAULT;1.1;AES256
-
带 vault id label 的格式,其对应的 vault format 版本为
1.2
$ANSIBLE_VAULT;1.2;AES256;vault-id-label
因此,解密字符串时,需要根据加密格式来决定 --vault-id
带不带 vault id label,否则解密时会报错,
-
解密不带 vault id label 格式的字符串,使用
echo
命令注意加密字符串中不要有空白echo '$ANSIBLE_VAULT;1.1;AES256 61346536643430386630663636623832646162383361623737393936326361383937613537616537 3561636565343863336236636138636339393139393363620a313135393937613164643830643765 36363661386236313833646262666561363461613037646134633830646561383938633261643538 6439353735643865340a616534613665363836623137333965643062636332353766666166353437 6535' | ansible-vault decrypt --vault-id a_vault_password_file
-
解密带 vault id label 格式的字符串,不给出的加密字符串的话,会在命令行提示输入要解密的字符串,输入字符串后回车键入 Ctrl-D 退出。
ansible-vault decrypt --vault-id dev@a_vault_password_file
Reading ciphertext input from stdin $ANSIBLE_VAULT;1.2;AES256;dev 39666338393536356364376237656337366137656333656663346330393831396230633663613165 6266343834353331633339326438326561323732646566380a343565306637633932663861316637 64343331303237656435623134326336323365343138333866333761616161376432383236666638 3239346137656331640a633732653037643663626133373737613864386566363937663966623437 3632
解密文本时的问题
注意:要解密的加密文本中不能包含任何空格,否则会解密失败。
一个常见的问题就是从 group_vars 文件中直接复制加密文本进行解密,因为这些加密文本在 group_vars 文件中都有缩进,所以直接复制粘贴进行解密就会失败。因此,我们需要先将带缩进的加密文本中的空格都去掉再进行解密。
消除字符串前后的空格有效方法是使用 xargs
命令,例如
echo ' abc ' | xargs
将会输出不带前后空格的字符串
abc
加密文本还要保持原来的行数,xargs
命令默认是读入多行后进行处理,因此还要使用 -L1
参数确保它一次处理一行。以前面的 the_dev_secret
加密变量为例,解密命令如下:
echo ' $ANSIBLE_VAULT;1.1;AES256
61346536643430386630663636623832646162383361623737393936326361383937613537616537
3561636565343863336236636138636339393139393363620a313135393937613164643830643765
36363661386236313833646262666561363461613037646134633830646561383938633261643538
6439353735643865340a616534613665363836623137333965643062636332353766666166353437
6535' | xargs -L1 | ansible-vault decrypt --vault-id prompt
在密码提示中输入 abc123
,得到解密后的内容
Vault password (default):
Decryption successful
foooodev%