Ansible 笔记

 

Ansible: What’s the difference between task, role, play and playbook?

Playbookplay 的列表。顶层的 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.

Role 是封装和共享 Ansible 信息的一种简单方法。在角色的子目录下,可以找到任务、处理程序、文件、模板、变量和默认值,这些都与该特定角色相关。

角色只定义任务,不定义这些任务将运行在哪些主机上,所以必须从 play 中引用角色。

角色还可以声明依赖关系,即在执行当前角色之前先运行其他角色。

查看 Playbook example

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 用户执行相应的任务。

ansible_become

如果希望以非 root 用户执行 ansible 任务,在 playbook 中必须同时使用 becomebecome_user 指令,这相当于执行 sudo -u someuser somecommand 命令。

ansible_become_and_become_user

执行 palybook 时,可能需要输入密码来执行 become 指令 (sudo),ansible-playbook 命令提供了选项 --ask-become-pass-K (短格式)来提示输入密码。

ansible-playbook -i hosts playbook.yml -K

可以设置 BECOME_PASSWORD_FILEDEFAULT_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_FILEDEFAULT_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%