如何构建您的清单

Ansible 使用称为清单的主机或基础设施中的托管节点列表或组列表来自动化任务。您可以在命令行中传递主机名,但大多数 Ansible 用户都会创建清单文件。您的清单定义了您自动化的托管节点,并使用组以便您可以同时在多台主机上运行自动化任务。定义清单后,您可以使用 模式 选择您希望 Ansible 运行的目标主机或组。

最简单的清单是一个包含主机和组列表的单个文件。此文件的默认位置为 /etc/ansible/hosts。您可以使用 -i <path> 选项在命令行中指定不同的清单文件,或在配置中使用 inventory 指定。

Ansible 清单插件 支持各种格式和来源,使您的清单灵活且可定制。随着清单的扩展,您可能需要多个文件来组织您的主机和组。以下是在 /etc/ansible/hosts 文件之外的三个选项

  • 您可以创建一个包含多个清单文件的目录。请参阅 在目录中组织清单。这些可以使用不同的格式(YAML、ini 等)。

  • 您可以动态获取清单。例如,您可以使用动态清单插件列出在一个或多个云提供商中的资源。请参阅 使用动态清单

  • 您可以使用多个清单源,包括动态清单和静态文件。请参阅 传递多个清单源

注意

以下 YAML 代码段包含省略号,表示它们是较大 YAML 文件的一部分。您可以在 YAML 基础知识 中了解有关 YAML 语法的更多信息。

清单基础:格式、主机和组

您可以使用多种格式创建清单文件,具体取决于您拥有的清单插件。最常见的格式是 INI 和 YAML。一个基本的 INI /etc/ansible/hosts 可能如下所示

mail.example.com

[webservers]
foo.example.com
bar.example.com

[dbservers]
one.example.com
two.example.com
three.example.com

方括号中的标题是组名,用于对主机进行分类,并确定您何时以及出于何种目的控制哪些主机。组名应遵循与 创建有效的变量名 相同的准则。

以下是 YAML 格式的相同基本清单文件

ungrouped:
  hosts:
    mail.example.com:
webservers:
  hosts:
    foo.example.com:
    bar.example.com:
dbservers:
  hosts:
    one.example.com:
    two.example.com:
    three.example.com:

默认组

即使您没有在清单文件中定义任何组,Ansible 也会创建两个默认组:allungroupedall 组包含每个主机。ungrouped 组包含除了 all 之外没有其他组的所有主机。每个主机将始终至少属于 2 个组(allungroupedall 和某些其他组)。例如,在上面的基本清单中,主机 mail.example.com 属于 all 组和 ungrouped 组;主机 two.example.com 属于 all 组和 dbservers 组。尽管 allungrouped 始终存在,但它们可以是隐式的,并且不会出现在诸如 group_names 之类的组列表中。

主机在多个组中

您可以将每个主机放在多个组中。例如,亚特兰大数据中心中的生产 Web 服务器可能包含在名为 [prod][atlanta][webservers] 的组中。您可以创建跟踪以下内容的组

  • 什么 - 应用程序、堆栈或微服务(例如,数据库服务器、Web 服务器等)。

  • 哪里 - 数据中心或区域,以与本地 DNS、存储等通信(例如,东部、西部)。

  • 何时 - 开发阶段,以避免在生产资源上进行测试(例如,prod、test)。

扩展之前的 YAML 清单以包含什么、何时和何地将如下所示

ungrouped:
  hosts:
    mail.example.com:
webservers:
  hosts:
    foo.example.com:
    bar.example.com:
dbservers:
  hosts:
    one.example.com:
    two.example.com:
    three.example.com:
east:
  hosts:
    foo.example.com:
    one.example.com:
    two.example.com:
west:
  hosts:
    bar.example.com:
    three.example.com:
prod:
  hosts:
    foo.example.com:
    one.example.com:
    two.example.com:
test:
  hosts:
    bar.example.com:
    three.example.com:

您可以看到 one.example.com 存在于 dbserverseastprod 组中。

对组进行分组:父子组关系

您可以在组之间创建父子关系。父组也称为嵌套组或组的组。例如,如果所有生产主机都已在诸如 atlanta_proddenver_prod 之类的组中,则可以创建一个包含这些较小组的 production 组。这种方法减少了维护工作,因为您可以通过编辑子组来添加或删除父组中的主机。

要为组创建父子关系

  • 在 INI 格式中,使用 :children 后缀

  • 在 YAML 格式中,使用 children: 条目

以下与上面显示的清单相同,使用 prodtest 组的父组进行了简化。这两个清单文件为您提供相同的结果

ungrouped:
  hosts:
    mail.example.com:
webservers:
  hosts:
    foo.example.com:
    bar.example.com:
dbservers:
  hosts:
    one.example.com:
    two.example.com:
    three.example.com:
east:
  hosts:
    foo.example.com:
    one.example.com:
    two.example.com:
west:
  hosts:
    bar.example.com:
    three.example.com:
prod:
  children:
    east:
test:
  children:
    west:

子组有一些需要注意的属性

  • 任何作为子组成员的主机都自动成为父组的成员。

  • 组可以有多个父级和子级,但不能有循环关系。

  • 主机也可以属于多个组,但在运行时,每个主机只有一个**实例**。Ansible 会合并来自多个组的数据。

添加主机范围

如果有很多主机具有类似的模式,可以将其添加为一个范围,而不是单独列出每个主机名。

在 INI 文件中

[webservers]
www[01:50].example.com

在 YAML 文件中

# ...
  webservers:
    hosts:
      www[01:50].example.com:

在定义主机数字范围时,可以指定步长(序列号之间的增量)。

在 INI 文件中

[webservers]
www[01:50:2].example.com

在 YAML 文件中

# ...
  webservers:
    hosts:
      www[01:50:2].example.com:

上面的示例将使子域名 www01、www03、www05、…、www49 匹配,但不会匹配 www00、www02、www50 等,因为步长(增量)为每步 2 个单位。

对于数字模式,可以根据需要包含或删除前导零。范围是包含性的。您还可以定义字母范围。

[databases]
db-[a:f].example.com

传递多个清单源

可以通过从命令行传递多个清单参数或配置ANSIBLE_INVENTORY来同时定位多个清单源(目录、动态清单脚本或清单插件支持的文件)。当您希望同时针对通常分开的环境(如暂存和生产)执行特定操作时,这非常有用。

要从命令行定位两个清单源

ansible-playbook get_logs.yml -i staging -i production

在目录中组织清单

您可以将多个清单源合并到一个目录中。最简单的版本是使用多个文件而不是单个清单文件的目录。当一个文件变得太长时,单个文件变得难以维护。如果您有多个团队和多个自动化项目,每个团队或项目使用一个清单文件可以让每个人轻松找到与他们相关的宿主和组。

您还可以将多个清单源类型组合到清单目录中。这对于组合静态和动态主机并将其作为一个清单进行管理非常有用。以下清单目录组合了清单插件源、动态清单脚本和包含静态主机的文件。

inventory/
  openstack.yml          # configure inventory plugin to get hosts from OpenStack cloud
  dynamic-inventory.py   # add additional hosts with dynamic inventory script
  on-prem                # add static hosts and groups
  parent-groups          # add static hosts and groups

您可以按如下方式定位此清单目录

ansible-playbook example.yml -i inventory

您也可以在 ansible.cfg 文件中配置清单目录。有关更多详细信息,请参阅 配置 Ansible

管理清单加载顺序

Ansible 根据文件名按 ASCII 顺序加载清单源。如果您在一个文件或目录中定义父组,在其他文件或目录中定义子组,则必须先加载定义子组的文件。如果父组先加载,您将看到错误 Unable to parse /path/to/source_of_parent_groups as an inventory source

例如,如果您有一个名为 groups-of-groups 的文件,该文件定义了一个名为 production 的组,并在名为 on-prem 的文件中定义了子组,则 Ansible 无法解析 production 组。要避免此问题,您可以通过向文件添加前缀来控制加载顺序。

inventory/
  01-openstack.yml          # configure inventory plugin to get hosts from OpenStack cloud
  02-dynamic-inventory.py   # add additional hosts with dynamic inventory script
  03-on-prem                # add static hosts and groups
  04-groups-of-groups       # add parent groups

您可以在 清单设置示例 中找到有关如何组织清单和对主机进行分组的示例。

向清单添加变量

您可以在清单中存储与特定主机或组相关的变量值。首先,您可以直接在主清单文件中向主机和组添加变量。

为了简单起见,我们在文档中介绍了在主清单文件中添加变量的方法。但是,将变量存储在单独的主机和组变量文件中是描述系统策略的一种更稳健的方法。在主清单文件中设置变量只是一种简写。有关在“host_vars”目录中的各个文件中存储变量值的指南,请参阅 组织主机和组变量。有关详细信息,请参阅 组织主机和组变量

将变量分配给一台机器:主机变量

您可以轻松地将变量分配给单个主机,然后在剧本中使用它。您可以在清单文件中直接执行此操作。

在 INI 文件中

[atlanta]
host1 http_port=80 maxRequestsPerChild=808
host2 http_port=303 maxRequestsPerChild=909

在 YAML 文件中

atlanta:
  hosts:
    host1:
      http_port: 80
      maxRequestsPerChild: 808
    host2:
      http_port: 303
      maxRequestsPerChild: 909

非标准 SSH 端口等唯一值非常适合用作主机变量。您可以通过在主机名后添加冒号和端口号来将其添加到 Ansible 清单中。

badwolf.example.com:5309

连接变量也适用于主机变量。

[targets]

localhost              ansible_connection=local
other1.example.com     ansible_connection=ssh        ansible_user=myuser
other2.example.com     ansible_connection=ssh        ansible_user=myotheruser

注意

如果您在 SSH 配置文件中列出了非标准 SSH 端口,则 openssh 连接将找到并使用它们,但 paramiko 连接将不会。

清单别名

您还可以使用主机变量在清单中定义别名。

在 INI 文件中

jumper ansible_port=5555 ansible_host=192.0.2.50

在 YAML 文件中

# ...
  hosts:
    jumper:
      ansible_port: 5555
      ansible_host: 192.0.2.50

在此示例中,针对主机别名“jumper”运行 Ansible 将连接到 192.0.2.50 的 5555 端口。有关进一步自定义与主机的连接,请参阅 行为清单参数

在 INI 格式中定义变量

使用 key=value 语法在 INI 格式中传递的值的解释方式不同,具体取决于声明的位置。

  • 当与主机内联声明时,INI 值被解释为 Python 字面结构(字符串、数字、元组、列表、字典、布尔值、None)。主机行每行接受多个 key=value 参数。因此,它们需要一种方法来指示空格是值的一部分,而不是分隔符。包含空格的值可以加引号(单引号或双引号)。有关详细信息,请参阅 Python shlex 解析规则

  • 当在 :vars 部分中声明时,INI 值被解释为字符串。例如 var=FALSE 将创建一个等于“FALSE”的字符串。与主机行不同,:vars 部分每行只接受一个条目,因此 = 之后的所有内容都必须是该条目的值。

如果在 INI 清单中设置的变量值必须是特定类型(例如,字符串或布尔值),请始终使用过滤器在任务中指定类型。在使用变量时,不要依赖在 INI 清单中设置的类型。

考虑使用 YAML 格式作为清单源,以避免对变量的实际类型产生混淆。YAML 清单插件会一致且正确地处理变量值。

将变量分配给多台机器:组变量

如果一个组中的所有主机共享一个变量值,则可以立即将该变量应用于整个组。

在 INI 文件中

[atlanta]
host1
host2

[atlanta:vars]
ntp_server=ntp.atlanta.example.com
proxy=proxy.atlanta.example.com

在 YAML 文件中

atlanta:
  hosts:
    host1:
    host2:
  vars:
    ntp_server: ntp.atlanta.example.com
    proxy: proxy.atlanta.example.com

组变量是在一次性应用于多个主机的变量的便捷方式。但是,在执行之前,Ansible 始终将变量(包括清单变量)展平到主机级别。如果一个主机是多个组的成员,Ansible 将读取所有这些组中的变量值。如果为不同组中的同一个变量分配了不同的值,Ansible 将根据内部 合并规则 选择使用哪个值。

继承变量值:组的组变量

您可以将变量应用于父组(嵌套组或组的组),以及子组。语法相同:INI 格式为 :vars,YAML 格式为 vars:

在 INI 文件中

[atlanta]
host1
host2

[raleigh]
host2
host3

[southeast:children]
atlanta
raleigh

[southeast:vars]
some_server=foo.southeast.example.com
halon_system_timeout=30
self_destruct_countdown=60
escape_pods=2

[usa:children]
southeast
northeast
southwest
northwest

在 YAML 文件中

usa:
  children:
    southeast:
      children:
        atlanta:
          hosts:
            host1:
            host2:
        raleigh:
          hosts:
            host2:
            host3:
      vars:
        some_server: foo.southeast.example.com
        halon_system_timeout: 30
        self_destruct_countdown: 60
        escape_pods: 2
    northeast:
    northwest:
    southwest:

子组的变量将具有比父组的变量更高的优先级(覆盖)。

组织主机和组变量

尽管您可以在主清单文件中存储变量,但存储单独的主机和组变量文件可能有助于您更轻松地组织变量值。您还可以使用主机和组变量文件中的列表和哈希数据,而您不能在主清单文件中这样做。

主机和组变量文件必须使用 YAML 语法。有效的文件扩展名包括“ .yml”、“ .yaml”、“ .json”或无文件扩展名。如果您不熟悉 YAML,请参阅 YAML 语法

Ansible 通过搜索相对于清单文件或剧本文件的路径来加载主机和组变量文件。如果您的清单文件位于 /etc/ansible/hosts 中,其中包含一个名为“foosball”的主机,该主机属于两个组“raleigh”和“webservers”,则该主机将使用以下位置的 YAML 文件中的变量。

/etc/ansible/group_vars/raleigh # can optionally end in '.yml', '.yaml', or '.json'
/etc/ansible/group_vars/webservers
/etc/ansible/host_vars/foosball

例如,如果您按数据中心对清单中的主机进行分组,并且每个数据中心都使用自己的 NTP 服务器和数据库服务器,则可以创建一个名为 /etc/ansible/group_vars/raleigh 的文件来存储 raleigh 组的变量。

---
ntp_server: acme.example.org
database_server: storage.example.org

您还可以创建以您的组或主机命名的目录。Ansible 将按字典顺序读取这些目录中的所有文件。使用“raleigh”组的示例

/etc/ansible/group_vars/raleigh/db_settings
/etc/ansible/group_vars/raleigh/cluster_settings

“raleigh”组中的所有主机都可以使用这些文件中定义的变量。当单个文件变得太大时,或者当您想要在某些组变量上使用 Ansible Vault 时,这非常有用。

对于 ansible-playbook,您还可以将 group_vars/host_vars/ 目录添加到您的剧本目录中。其他 Ansible 命令(例如,ansibleansible-console 等)只会查找清单目录中的 group_vars/host_vars/。如果您希望其他命令从剧本目录加载组和主机变量,则必须在命令行上提供 --playbook-dir 选项。如果您同时从剧本目录和清单目录加载清单文件,则剧本目录中的变量将覆盖在清单目录中设置的变量。

将您的清单文件和变量保存在 Git 仓库(或其他版本控制系统)中,是跟踪清单和主机变量更改的绝佳方法。

变量如何合并

默认情况下,在运行剧本之前,变量会被合并/扁平化到特定主机。这使 Ansible 专注于主机和任务,因此组在清单和主机匹配之外不会继续存在。默认情况下,Ansible 会覆盖变量,包括为组和/或主机定义的变量(参见 DEFAULT_HASH_BEHAVIOUR)。顺序/优先级为(从低到高)

  • all 组(因为它是一切其他组的“父组”)

  • 父组

  • 子组

  • 主机

默认情况下,Ansible 会按照 ASCII 顺序合并同一父/子级别的组,来自最后加载的组的变量会覆盖来自先前组的变量。例如,a_group 将与 b_group 合并,并且 b_group 中匹配的变量将覆盖 a_group 中的变量。

注意

Ansible 会合并来自不同来源的变量,并根据一组规则对某些变量应用优先级。例如,在清单中出现位置较高的变量可以覆盖在清单中出现位置较低的变量。有关更多信息,请参见 变量优先级:我应该在哪里放置变量?

您可以通过设置组变量 ansible_group_priority 来更改此行为,以更改同一级别组的合并顺序(在解决父/子顺序之后)。数字越大,合并的越晚,优先级越高。如果未设置,此变量默认为 1。例如

a_group:
  vars:
    testvar: a
    ansible_group_priority: 10
b_group:
  vars:
    testvar: b

在此示例中,如果两个组具有相同的优先级,则结果通常为 testvar == b,但由于我们赋予 a_group 更高的优先级,因此结果将为 testvar == a

注意

ansible_group_priority 只能在清单源中设置,而不能在 group_vars/ 中设置,因为该变量用于加载 group_vars。

管理清单变量加载顺序

使用多个清单源时,请记住,任何变量冲突都将根据 变量如何合并变量优先级:我应该在哪里放置变量?中描述的规则解决。您可以控制清单源中变量的合并顺序以获取所需的变量值。

在命令行传递多个清单源时,Ansible 会按照传递这些参数的顺序合并变量。如果暂存清单中的 [all:vars] 定义了 myvar = 1,而生产清单定义了 myvar = 2,则

  • 传递 -i staging -i production 以使用 myvar = 2 运行剧本。

  • 传递 -i production -i staging 以使用 myvar = 1 运行剧本。

将多个清单源放在一个目录中时,Ansible 会根据文件名按照 ASCII 顺序合并它们。您可以通过向文件添加前缀来控制加载顺序

inventory/
  01-openstack.yml          # configure inventory plugin to get hosts from Openstack cloud
  02-dynamic-inventory.py   # add additional hosts with dynamic inventory script
  03-static-inventory       # add static hosts
  group_vars/
    all.yml                 # assign variables to all hosts

如果 01-openstack.yml 为组 all 定义了 myvar = 102-dynamic-inventory.py 定义了 myvar = 2,而 03-static-inventory 定义了 myvar = 3,则剧本将使用 myvar = 3 运行。

有关清单插件和动态清单脚本的更多详细信息,请参见 清单插件使用动态清单

连接到主机:行为清单参数

如上所述,设置以下变量可以控制 Ansible 如何与远程主机交互。

主机连接

注意

Ansible 不会公开通道以允许用户与 ssh 进程通信,以便在使用 ssh 连接插件(默认值)时手动接受密码来解密 ssh 密钥。强烈建议使用 ssh-agent

ansible_connection

到主机的连接类型。这可以是任何 Ansible 连接插件的名称。SSH 协议类型为 sshparamiko。默认值为 ssh

所有连接的通用设置

ansible_host

要连接到的主机的名称,如果与您希望赋予它的别名不同。如果您使用委托,请勿将其设置为依赖于 inventory_hostname

ansible_port

连接端口号,如果不是默认端口(ssh 为 22)

ansible_user

连接到主机时使用的用户名

ansible_password

用于对主机进行身份验证的密码(切勿以纯文本形式存储此变量;始终使用保管库。参见 安全地显示保管库变量

SSH 连接的特定设置

ansible_ssh_private_key_file

SSH 使用的私钥文件。在使用多个密钥且不想使用 SSH 代理时很有用。

ansible_ssh_common_args

此设置始终附加到 sftpscpssh 的默认命令行。用于为特定主机(或组)配置 ProxyCommand

ansible_sftp_extra_args

此设置始终附加到默认的 sftp 命令行。

ansible_scp_extra_args

此设置始终附加到默认的 scp 命令行。

ansible_ssh_extra_args

此设置始终附加到默认的 ssh 命令行。

ansible_ssh_pipelining

确定是否使用 SSH 管道。这可以覆盖 ansible.cfg 中的 pipelining 设置。

ansible_ssh_executable(在版本 2.2 中添加)

此设置会覆盖默认行为,即使用系统 ssh。这可以覆盖 ansible.cfgssh_connection 中的 ssh_executable 设置。

权限提升(有关更多详细信息,请参见 Ansible 权限提升

ansible_become

等效于 ansible_sudoansible_su,允许强制权限提升

ansible_become_method

允许设置权限提升方法

ansible_become_user

等效于 ansible_sudo_useransible_su_user,允许您设置通过权限提升成为的用户

ansible_become_password

等效于 ansible_sudo_passwordansible_su_password,允许您设置权限提升密码(切勿以纯文本形式存储此变量;始终使用保管库。参见 安全地显示保管库变量

ansible_become_exe

等效于 ansible_sudo_exeansible_su_exe,允许您设置所选提升方法的可执行文件

ansible_become_flags

等同于 ansible_sudo_flagsansible_su_flags,允许您设置传递给所选提权方法的标志。这也可以在 ansible.cfg 文件中的 privilege_escalation 下的 become_flags 选项中全局设置。

远程主机环境参数

ansible_shell_type

目标系统的 shell 类型。除非您已将 ansible_shell_executable 设置为非 Bourne (sh) 兼容的 shell,否则不应使用此设置。默认情况下,命令使用 sh 样式语法进行格式化。将其设置为 cshfish 将导致在目标系统上执行的命令遵循这些 shell 的语法。

ansible_python_interpreter

目标主机的 Python 路径。这对于有多个 Python 或 Python 不位于 /usr/bin/python(例如 *BSD)的系统,或者 /usr/bin/python 不是 2.X 系列 Python 的系统很有用。我们不使用 /usr/bin/env 机制,因为这需要远程用户的路径设置正确,并且还假定 python 可执行文件名为 python,而可执行文件可能名为 python2.6 之类。

ansible_*_interpreter

适用于 ruby 或 perl 等任何内容,工作方式与 ansible_python_interpreter 相同。这将替换将在该主机上运行的模块的 shebang。

版本 2.1 中新增。

ansible_shell_executable

这将设置 Ansible 控制节点将在目标机器上使用的 shell,覆盖 ansible.cfg 中的 executable,默认值为 /bin/sh。只有在无法使用 /bin/sh 时(换句话说,如果目标机器上未安装 /bin/sh 或无法从 sudo 运行)才应更改此值。

来自 Ansible-INI 主机文件的示例

some_host         ansible_port=2222     ansible_user=manager
aws_host          ansible_ssh_private_key_file=/home/example/.ssh/aws.pem
freebsd_host      ansible_python_interpreter=/usr/local/bin/python
ruby_module_host  ansible_ruby_interpreter=/usr/bin/ruby.1.9.3

非 SSH 连接类型

如上一节所述,Ansible 通过 SSH 执行 playbook,但它并不局限于这种连接类型。使用特定于主机的参数 ansible_connection=<connector>,可以更改连接类型。有关可用插件和示例的完整列表,请参阅 插件列表

清单设置示例

另请参阅 Ansible 设置示例,其中显示了清单以及 playbook 和其他 Ansible 工件。

示例:每个环境一个清单

如果您需要管理多个环境,有时明智的做法是每个清单中仅定义单个环境的主机。这样,例如,当您想要更新一些“暂存”服务器时,就很难意外地更改“测试”环境内节点的状态。

对于上面提到的示例,您可以拥有一个 inventory_test 文件

[dbservers]
db01.test.example.com
db02.test.example.com

[appservers]
app01.test.example.com
app02.test.example.com
app03.test.example.com

该文件仅包含属于“测试”环境的主机。在名为 inventory_staging 的另一个文件中定义“暂存”机器

[dbservers]
db01.staging.example.com
db02.staging.example.com

[appservers]
app01.staging.example.com
app02.staging.example.com
app03.staging.example.com

要将名为 site.yml 的 playbook 应用于测试环境中的所有应用程序服务器,请使用以下命令

ansible-playbook -i inventory_test -l appservers site.yml

示例:按功能分组

在上一节中,您已经看到了使用组来聚类具有相同功能的主机的示例。例如,这允许您在 playbook 或角色中定义仅影响数据库服务器的防火墙规则

- hosts: dbservers
  tasks:
  - name: Allow access from 10.0.0.1
    ansible.builtin.iptables:
      chain: INPUT
      jump: ACCEPT
      source: 10.0.0.1

示例:按位置分组

其他任务可能侧重于特定主机所在的位置。假设 db01.test.example.comapp01.test.example.com 位于 DC1,而 db02.test.example.com 位于 DC2

[dc1]
db01.test.example.com
app01.test.example.com

[dc2]
db02.test.example.com

在实践中,您甚至可能最终混合所有这些设置,因为您可能需要在某一天更新特定数据中心中的所有节点,而在另一天更新所有应用程序服务器,无论它们位于何处。

另请参阅

清单插件

从动态或静态源提取清单

使用动态清单

从动态源(例如云提供商)提取清单

Ad hoc 命令简介

基本命令示例

使用 playbook

学习 Ansible 的配置、部署和编排语言。

沟通

有问题?需要帮助?想分享您的想法?请访问 Ansible 沟通指南