Ansible Runner 简介

Runner 旨在作为自动化和工具的一部分,这些自动化和工具需要调用 Ansible 并使用其结果。大多数 Ansible 命令行的参数化在 Runner 命令行上也可用,但 Runner 也可以依赖于映射到目录结构的输入接口,例如,可以在 源代码树 中看到。

本文档中的后续部分将介绍该层次结构的配置和布局。这不是与 Runner 本身交互的唯一方法。Python 模块接口允许以多种形式将这些详细信息作为直接模块参数提供,而命令行接口允许将其直接作为参数提供,模仿 ansible-playbook 的行为。拥有目录结构确实允许从其他地方收集输入并准备供 Runner 使用,然后工具可以继续并在运行后检查结果。

这在 Ansible AWX 使用 Runner 的方式中得到了最好的体现,其中大部分内容来自数据库(以及其他内容管理组件),但在启动 Ansible 任务时最终需要在一个地方集中起来。

Runner 输入目录层次结构

此目录包含所有必要的输入。以下是 演示目录 的视图,显示了活动的配置。

请注意,并非所有内容都是必需的。如果未提供,将使用默认值或省略值。

.
├── env
│   ├── envvars
│   ├── extravars
│   ├── passwords
│   ├── cmdline
│   ├── settings
│   └── ssh_key
├── inventory
│   └── hosts
└── project
    ├── test.yml
    └── roles
        └── testrole
            ├── defaults
            ├── handlers
            ├── meta
            ├── README.md
            ├── tasks
            ├── tests
            └── vars

env 目录

env 目录包含设置和敏感文件,这些文件会影响 Ansible 进程调用的某些方面,例如,可以在 演示 env 目录 中找到。每个文件也可以由命名管道表示,从而提供额外的安全层。这些文件的格式和预期略有不同,具体取决于它们表示的内容。

env/envvars

注意

有关示例,请参见 演示 envvars

Ansible Runner 将继承启动 shell 的环境。此文件(可以是 json 或 yaml 格式)表示将在运行时添加到环境的环境变量。

---
TESTVAR: exampleval

env/extravars

注意

有关示例,请参见 演示 extravars

Ansible Runner 收集此处提供的额外变量,并将其提供给 Ansible 进程本身。此文件可以是 json 或 yaml 格式。

---
ansible_connection: local
test: val

env/passwords

注意

有关示例,请参见 演示 passwords

警告

我们预计此接口将在未来发生变化/简化,但将保证向后兼容性。目标是让 Runner 的用户不必担心 Ansible 本身发出的某些提示的格式。特别是,保管库密码需要变得更加灵活。

Ansible 本身被设置为将密码发送到某些提示,这些提示可以被请求(例如,-k 用于提示连接密码)。同样,可以通过 vars_prompt 以及 Ansible 保管库 发出提示。

为了使 Runner 能用正确的密码进行响应,它需要能够匹配提示并提供正确的密码。目前支持通过提供一个 yaml 或 json 格式的文件来实现,该文件包含一个正则表达式和一个要发出的值,例如

---
"^SSH password:\\s*?$": "some_password"
"^BECOME password.*:\\s*?$": "become_password"

env/cmdline

警告

当前 Ansible Runner 不会验证使用此方法传递的命令行参数,因此由剧本编写者提供一组有效的选项。通过此方法提供的命令行选项的优先级低于 Ansible Runner 设置的选项。例如,这不会覆盖 inventorylimit 值。

Ansible Runner 收集此处提供的命令行选项作为字符串,并将其提供给 Ansible 进程本身。此文件应包含要添加的参数,例如

--tags one,two --skip-tags three -u ansible --become

env/ssh_key

注意

目前只能通过此机制提供单个 ssh 密钥,但这将 很快发生变化

此文件应包含用于连接到主机(s)的 ssh 私钥。Runner 检测到何时提供了私钥,并将对 Ansible 的调用包装在 ssh-agent 中。

env/settings - Runner 自身设置

settings 文件与此部分中提供的其他文件略有不同,因为其内容旨在直接控制 Runner

  • idle_timeout: 600 如果在这么多秒内未检测到来自 ansible 的输出,则将终止执行。

  • job_timeout: 3600 允许作业运行的最长时间,超过此时间,将终止执行。

  • pexpect_timeout: 10 内部 pexpect 命令在继续之前等待阻塞输入的秒数。

  • pexpect_use_poll: True 使用 poll() 函数与子进程通信,而不是 select()。当值为 False 时使用 select()select() 具有已知的限制,即仅使用最多 1024 个文件描述符。

  • suppress_output_file: False 允许 ansible 的输出不会流式传输到工件目录内的 stdoutstderr 文件。

  • suppress_ansible_output: False 允许 ansible 的输出不会打印到屏幕上。

  • fact_cache: 'fact_cache' 相对于 artifacts 的目录,其中将存储 jsonfile 事实缓存。默认为 fact_cache。如果 fact_cache_type 不同于 jsonfile,则忽略此项。

  • fact_cache_type: 'jsonfile' 要使用的事实缓存类型。默认为 jsonfile

Runner 的进程隔离设置

进程隔离设置旨在控制 Runner 的进程隔离功能。

  • process_isolation: False 启用限制剧本运行可以访问的文件系统上的目录。

  • process_isolation_executable: bwrap 将用于提供文件系统隔离的可执行文件的路径。

  • process_isolation_path: /tmp 隔离的剧本运行将用于暂存的路径。

  • process_isolation_hide_paths: None 系统上应从剧本运行中隐藏的路径或路径列表。

  • process_isolation_show_paths: None 系统上应公开给剧本运行的路径或路径列表。

  • process_isolation_ro_paths: None 系统上应以只读方式公开给剧本运行的路径或路径列表。

这些设置指示 **Runner** 在容器环境中执行 **Ansible** 任务。有关构建执行环境的信息,请参阅 ansible-builder

要使用执行环境执行 **Runner**

ansible-runner run --container-image my-execution-environment:latest --process-isolation -p playbook.yml .

有关其他与容器相关的选项,请参阅 ansible-runner -h

清单

私有数据目录下的 **Runner** inventory 位置与直接提供给 ansible 本身的清单具有相同的期望。它可以是单个文件或脚本,也可以是包含静态清单文件或脚本的目录。此清单在调用时会自动加载并提供给 **Ansible**,并且可以在命令行或通过 ANSIBLE_INVENTORY 环境变量进一步覆盖以直接指定主机。为清单位置提供绝对路径是最佳实践,因为相对路径相对于 当前工作目录 解释,默认为 项目 目录。

项目

**Runner** project 目录是包含剧本和这些剧本可以直接使用的角色的剧本根目录。这也是在启动 **Ansible** 进程时将设置为 当前工作目录 的目录。

模块

**Runner** 能够使用 Ansible 特设模式直接执行模块。

角色

**Runner** 能够直接执行 角色,而无需首先需要剧本来引用它们。此目录保存用于此目的的角色。在幕后,**Runner** 将生成一个剧本并调用 角色

Runner 工件目录层次结构

此目录将包含 **Runner** 调用结果,这些结果分组在 identifier 目录下。此标识符可以直接提供给 **Runner**,如果未提供,则会生成一个标识符作为 UUID。这就是目录结构从顶层开始的样子

.
├── artifacts
│   └── identifier
├── env
├── inventory
├── profiling_data
├── project
└── roles

工件目录本身包含一个特定的结构,该结构提供了来自 Ansible/Runner 正在运行或先前运行调用的许多额外详细信息

.
├── artifacts
│   └── 37f639a3-1f4f-4acb-abee-ea1898013a25
│       ├── fact_cache
│       │   └── localhost
│       ├── job_events
│       │   ├── 1-34437b34-addd-45ae-819a-4d8c9711e191.json
│       │   ├── 2-8c164553-8573-b1e0-76e1-000000000006.json
│       │   ├── 3-8c164553-8573-b1e0-76e1-00000000000d.json
│       │   ├── 4-f16be0cd-99e1-4568-a599-546ab80b2799.json
│       │   ├── 5-8c164553-8573-b1e0-76e1-000000000008.json
│       │   ├── 6-981fd563-ec25-45cb-84f6-e9dc4e6449cb.json
│       │   └── 7-01c7090a-e202-4fb4-9ac7-079965729c86.json
│       ├── rc
│       ├── status
│       └── stdout

**rc** 文件包含 **Ansible** 进程的实际返回代码。

**status** 文件包含三个适合显示的状态之一

**stdout** 文件包含此时显示的实际标准输出。

Runner 工件作业事件(主机和剧本事件)

**Runner** 收集作为 **Ansible** 运行的一部分发出的单个任务和剧本事件。如果您不想处理或读取从 **Ansible** 返回的标准输出,这非常有用,因为它包含比普通标准输出更多的详细信息和状态。它承担了一些将顺序分配给事件的繁重工作,并将它们存储在 job_events 工件目录下的 json 格式中。它还比普通的 **Ansible** 回调插件更进一步,因为它会将与事件关联的 stdout 与原始事件数据一起存储(以及标准输出行号)。它还为没有相应主机事件数据的标准输出生成虚拟事件

{
  "uuid": "8c164553-8573-b1e0-76e1-000000000008",
  "parent_uuid": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
  "counter": 5,
  "stdout": "\r\nTASK [debug] *******************************************************************",
  "start_line": 5,
  "end_line": 7,
  "event": "playbook_on_task_start",
  "event_data": {
    "playbook": "test.yml",
    "playbook_uuid": "34437b34-addd-45ae-819a-4d8c9711e191",
    "play": "all",
    "play_uuid": "8c164553-8573-b1e0-76e1-000000000006",
    "play_pattern": "all",
    "task": "debug",
    "task_uuid": "8c164553-8573-b1e0-76e1-000000000008",
    "task_action": "debug",
    "task_path": "\/home\/mjones\/ansible\/ansible-runner\/demo\/project\/test.yml:3",
    "task_args": "msg=Test!",
    "name": "debug",
    "is_conditional": false,
    "pid": 10640
  },
  "pid": 10640,
  "created": "2018-06-07T14:54:58.410605"
}

如果剧本运行完成而没有被终止,则最后一个事件将始终是 stats 事件

{
  "uuid": "01c7090a-e202-4fb4-9ac7-079965729c86",
  "counter": 7,
  "stdout": "\r\nPLAY RECAP *********************************************************************\r\n\u001b[0;32mlocalhost,\u001b[0m                 : \u001b[0;32mok=2   \u001b[0m changed=0    unreachable=0    failed=0   \r\n",
  "start_line": 10,
  "end_line": 14,
  "event": "playbook_on_stats",
  "event_data": {
    "playbook": "test.yml",
    "playbook_uuid": "34437b34-addd-45ae-819a-4d8c9711e191",
    "changed": {

    },
    "dark": {

    },
    "failures": {

    },
    "ok": {
      "localhost,": 2
    },
    "processed": {
      "localhost,": 1
    },
    "skipped": {

    },
    "artifact_data": {

    },
    "pid": 10640
  },
  "pid": 10640,
  "created": "2018-06-07T14:54:58.424603"
}

注意

**Runner 模块接口** 提供了这些事件的编程接口,允许获取最终状态并执行任务事件的主机过滤。

Runner 分析数据目录

如果为 **Runner** 启用了资源分析,则 profiling_data 目录将填充一组包含分析数据的文件

.
├── profiling_data
│   ├── 0-34437b34-addd-45ae-819a-4d8c9711e191-cpu.json
│   ├── 0-34437b34-addd-45ae-819a-4d8c9711e191-memory.json
│   ├── 0-34437b34-addd-45ae-819a-4d8c9711e191-pids.json
│   ├── 1-8c164553-8573-b1e0-76e1-000000000006-cpu.json
│   ├── 1-8c164553-8573-b1e0-76e1-000000000006-memory.json
│   └── 1-8c164553-8573-b1e0-76e1-000000000006-pids.json

每个文件都采用 JSON 文本格式。文件的每一行都将以记录分隔符 (RS) 开头,后跟 JSON 字典,最后以换行符 (LF) 结尾。以下提供了资源文件可能是什么样子的示例。请注意,由于 RS 和 LF 是控制字符,因此它们实际上并未在下面打印

==> 0-525400c9-c704-29a6-4107-00000000000c-cpu.json <==
{"timestamp": 1568977988.6844425, "task_name": "Gathering Facts", "task_uuid": "525400c9-c704-29a6-4107-00000000000c", "value": 97.12799768097156}
{"timestamp": 1568977988.9394386, "task_name": "Gathering Facts", "task_uuid": "525400c9-c704-29a6-4107-00000000000c", "value": 94.17538298892688}
{"timestamp": 1568977989.1901696, "task_name": "Gathering Facts", "task_uuid": "525400c9-c704-29a6-4107-00000000000c", "value": 64.38272588006255}
{"timestamp": 1568977989.4594045, "task_name": "Gathering Facts", "task_uuid": "525400c9-c704-29a6-4107-00000000000c", "value": 83.77387744259856}

==> 0-525400c9-c704-29a6-4107-00000000000c-memory.json <==
{"timestamp": 1568977988.4281094, "task_name": "Gathering Facts", "task_uuid": "525400c9-c704-29a6-4107-00000000000c", "value": 36.21484375}
{"timestamp": 1568977988.6842303, "task_name": "Gathering Facts", "task_uuid": "525400c9-c704-29a6-4107-00000000000c", "value": 57.87109375}
{"timestamp": 1568977988.939303, "task_name": "Gathering Facts", "task_uuid": "525400c9-c704-29a6-4107-00000000000c", "value": 66.60546875}
{"timestamp": 1568977989.1900482, "task_name": "Gathering Facts", "task_uuid": "525400c9-c704-29a6-4107-00000000000c", "value": 71.4609375}
{"timestamp": 1568977989.4592078, "task_name": "Gathering Facts", "task_uuid": "525400c9-c704-29a6-4107-00000000000c", "value": 38.25390625}

==> 0-525400c9-c704-29a6-4107-00000000000c-pids.json <==
{"timestamp": 1568977988.4284189, "task_name": "Gathering Facts", "task_uuid": "525400c9-c704-29a6-4107-00000000000c", "value": 5}
{"timestamp": 1568977988.6845856, "task_name": "Gathering Facts", "task_uuid": "525400c9-c704-29a6-4107-00000000000c", "value": 6}
{"timestamp": 1568977988.939547, "task_name": "Gathering Facts", "task_uuid": "525400c9-c704-29a6-4107-00000000000c", "value": 8}
{"timestamp": 1568977989.1902773, "task_name": "Gathering Facts", "task_uuid": "525400c9-c704-29a6-4107-00000000000c", "value": 13}
{"timestamp": 1568977989.4593227, "task_name": "Gathering Facts", "task_uuid": "525400c9-c704-29a6-4107-00000000000c", "value": 6}
  • 资源分析数据按剧本任务分组。

  • 对于每个任务,将有三个文件,分别对应于 cpu、内存和 pid 计数数据。

  • 每个文件包含在剧本任务过程中收集的一组数据点。

  • 如果任务执行速度很快,并且给定指标的轮询率足够大,则可能在任务执行期间不会收集任何分析数据。如果是这种情况,则不会创建任何数据文件。