Ansible Runner 简介

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

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

这在 Ansible AWX 使用 Runner 的方式中最为明显,其中大多数内容来自数据库(以及其他内容管理组件),但最终需要在启动 Ansible 任务时集中到一个地方。

Runner 输入目录层次结构

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

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

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

The env 目录

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

env/envvars

注意

例如,请参阅 demo envvars

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

---
TESTVAR: exampleval

env/extravars

注意

例如,请参阅 demo extravars

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

---
ansible_connection: local
test: val

env/passwords

注意

例如,请参阅 demo passwords

警告

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

Ansible 本身被设置为向某些提示发出密码,这些提示可以被请求(例如 -k 用于提示输入连接密码)。同样,提示可以通过 vars_promptAnsible Vault 发出。

为了让 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 检测到提供私钥时,将在 ssh-agent 中包装对 Ansible 的调用。

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 环境变量进一步覆盖,以直接指定主机。为清单位置提供绝对路径是最佳实践,因为相对路径相对于 current working directory 解释,默认情况下为 project 目录。

项目

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

模块

**Runner** 能够使用 Ansible 的临时模式直接执行模块。

角色

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

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** 返回的标准输出,这非常有用,因为它包含比纯标准输出更多的详细信息和状态。它在为事件分配顺序方面做了很多工作,并将它们以 JSON 格式存储在 job_events 工件目录下。它还比普通的 **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 计数数据。

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

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