
在现代 IT 运维与安全运营中,自动化和编排能力已经成为团队提升效率、缩短响应时间(MTTR)的关键。作为 Elastic 生态中强大的自动化工具,Elastic 工作流(Workflows) 能够帮助团队无缝编排安全响应、可观测性诊断以及日常的数据管理流程。
要编写一个高效、健壮的 Elastic 工作流,首先必须理解其底层的 YAML 定义结构。本文将带你深度剖析 Elastic 工作流的"解剖图",详细拆解其 11 个顶级字段,并解析工作流的完整执行生命周期。
1. Elastic 工作流的 "骨架":YAML 完整结构
一个标准的 Elastic 工作流定义由 11 个顶级字段构成,其中大部分是可选的。在深入细节之前,我们先通过一个模拟 "SLO 违约响应(SLO Breach Response)"的 YAML 完整示例来建立直观认知:
堆栈版本 9.5+ / Serverless 推荐结构(输入参数嵌套在触发器内)
yaml
`
1. name: slo-breach-response
2. description: 调查并缓解 SLO 违约。
3. enabled: true
4. tags:
5. - observability
6. - slo
8. version: "1"
10. triggers:
11. - type: manual
12. inputs:
13. - name: service_name
14. type: string
15. required: true
17. consts:
18. severity_threshold: 70
20. outputs:
21. - name: result
22. type: string
24. settings:
25. timeout: "5m"
26. concurrency:
27. strategy: drop
29. steps:
30. - name: investigate
31. type: elasticsearch.esql.query
32. with:
33. query: "..."
`AI写代码
💡 版本差异提示 :在 Elastic Stack 9.4 及更早版本中,
inputs(运行时输入参数)位于 YAML 的最顶层(与name、steps同级)。自 9.5+ 版本及 Serverless 环境 起,新创建的工作流推荐将inputs定义在具体的manual触发器内部,以便更紧密地绑定触发场景。不过,旧版的顶级inputs结构依然保持向下兼容。
2. 核心字段逐一拆解:11 大顶级字段
Elastic 工作流的每个字段都承载着特定的功能。以下是它们的详细参考和设计最佳实践:
① name(字符串 | 必填)------ 工作流的唯一身份标识
name 是工作流的编程标识符,会显示在 Kibana UI 中,并在 API 调用中作为标识。
- 最佳命名实践 :推荐使用
<领域>--<动词>-<名词>的命名规范。例如:security--triage-malware-alert(安全领域-分流恶意软件告警)observability--respond-to-slo-breach(可观测性-响应 SLO 违约)platform--rotate-service-account-keys(平台维护-轮换服务账号密钥)
- 注意:工作流名称在同一个 Kibana 空间(Space)内必须是唯一的。
② description(字符串 | 可选)------ 简短的副标题
用于在工作流列表中展示的简短说明。
- 设计建议 :尽量控制在一句话内,说明工作流做什么(业务价值),而不是 "如何做"(技术实现)。
- 示例 :
description: 调查并缓解生产服务中的 SLO 违约。
③ enabled(布尔值 | 可选)------ 工作流的 "紧急安全开关"
控制工作流是否处于激活状态,默认为 true。
- 妙用场景 :当你想暂停一个工作流又不想将其彻底删除时,可以将其设为
false。此时,定时触发器会停止触发,关联了告警的工作流将停止响应,手动运行则会返回明确的"工作流已禁用"错误。 - 提示 :在实验或重构工作流时,禁用比删除更安全,因为这不会破坏任何引用该工作流的告警规则。
④ tags(字符串数组 | 可选)------ 过滤与组织标签
用于在工作流列表中进行分类过滤的免费文本标签。
- 常用模式 :
- 按产品领域:
security、observability、search - 按环境 criticality:
prod、demo - 按受众/角色:
soc、oncall
- 按产品领域:
⑤ version(字符串 | 可选)------ 架构版本
声明工作流定义所遵循的 Schema 版本,默认值为 "1"。
- ⚠️ 踩坑点警告 :它的值必须是带双引号的字符串
"1",而不能是未加引号的数字1。未加引号的1会被解析为数值类型,从而导致 Schema 验证失败。
⑥ triggers(触发器数组 | 必填)------ 何时运行
定义启动工作流的一个或多个触发源。你可以在一个工作流中定义多个触发器。
-
示例:支持手动触发和每小时定时触发共存:
markdown` 1. triggers: 2. - type: manual 3. - type: scheduled 4. with: 5. every: "1h" `AI写代码
⑦ inputs(对象或输入参数数组 | 可选)------ 运行时参数
工作流在调用时期望接收的输入。用户在 Kibana 的"运行"弹窗中输入的数据,或 API 调用者在请求体中提供的值,都属于 inputs。
- 引用方式 :在工作流内部,使用双大括号的 Liquid 模板语法进行引用,例如:
{{ inputs.service_name }}。 - 参数支持类型 :
string(字符串)、number(数字)、boolean(布尔值)、choice(下拉选择,需配合options数组使用)和array(数组)。 - 高级技巧 :
default(默认值)不仅可以接受字面量,还可以接受 Liquid 表达式(例如自动生成当前时间戳:default: "{{ 'now' | date: '%Y-%m-%dT%H:%M:%SZ' }}")。注意:在 Elastic Stack 9.3-9.4 版本中,默认值仅支持字面量解析。
⑧ consts(对象 | 可选)------ 命名常量
用于定义那些在工作流中会被多次引用的固定值。
-
示例:
yaml` 1. consts: 2. watch_index: "security.watch.findings" 3. threshold: 0.85 `AI写代码 -
注意 :常量在工作流加载时只被评估一次。它们无法 访问
inputs、steps或event。如果你需要动态变量,请使用data.set步骤。
⑨ outputs(对象或输出参数数组 | 可选)------ 声明输出结构
描述工作流结束时产生的数据结构。
-
关键作用 :在 工作流嵌套/组合(Workflow Composition) 场景中,父工作流调用此工作流作为子工作流时,父工作流必须知道子工作流的输出结构。
-
示例:
markdown` 1. outputs: 2. - name: verdict 3. type: string 4. - name: severity 5. type: number `AI写代码
⑩ settings(对象 | 可选)------ 全局运行行为
这是一组控制工作流范围行为的 "杂货袋",支持配置超时时间、时区、并发控制、全局错误处理等。
-
示例(限制最大运行时间、并发排队策略以及失败重试):
yaml` 1. settings: 2. timeout: "10m" 3. timezone: "America/Los_Angeles" 4. concurrency: 5. key: "{{ event.alerts[0].host.name }}" 6. strategy: drop 7. max: 1 8. on-failure: 9. retry: 10. max-attempts: 2 11. delay: "10s" `AI写代码
⑪ steps(步骤数组 | 必填)------ 工作流的主体结构
工作流具体执行的有序步骤列表。每个步骤都包含唯一的 name(在当前工作流中唯一)、步骤类型 type 以及类型特有的 with 参数块。
-
示例:先从 Elasticsearch 查询,然后用 AI 进行总结:
markdown` 1. steps: 2. - name: fetch 3. type: elasticsearch.search 4. with: 5. index: logs-* 6. size: 100 7. - name: summarize 8. type: ai.summarize 9. connector-id: my-openai 10. with: 11. input: "{{ steps.fetch.output.hits.hits | map: '_source.message' | join: '\\n' }}" `AI写代码
3. 工作流执行生命周期与状态机
当工作流通过手动、定时或事件触发运行后,Elastic 引擎会管理其生命周期,在 Kibana 界面或 API 响应中,你会看到它流转于以下状态:
| 状态名称 (State) | 状态含义 (Meaning) |
|---|---|
queued |
处于并发限制队列中,等待空闲槽位(与 pending 不同)。 |
pending |
已调度并正在等待正式启动。 |
running |
至少有一个步骤正在执行中。 |
waiting |
工作流在 wait 步骤上暂停,等待计时器到期。 |
waiting_for_input |
工作流在 waitForInput 步骤上暂停,等待外部输入。 |
waiting_for_child |
工作流在等待通过嵌套执行的子工作流完成。 |
completed 🏁 |
终结状态。工作流全部步骤成功执行完毕。 |
failed 🏁 |
终结状态 。某一步骤失败且全局 on-failure 策略未能挽回。 |
cancelled 🏁 |
终结状态。运行被操作员手动取消,或由于并发策略而被终止。 |
timed_out 🏁 |
终结状态 。执行时间超过了 settings.timeout 的限制。 |
skipped 🏁 |
终结状态 。运行被丢弃。例如,由于并发 drop 策略,或队列积压超过限制。 |
4. 实战演示:一个完整且可运行的简单工作流示例
为了帮助你更直观地理解工作流的运转,我们来看一个完整、简单且可直接运行的实际示例。
该工作流的场景是:"当指定微服务出现 Error 级别日志时,自动检索该日志并通过 AI 总结其报错原因,最后发送 Slack 通知。" 1
完整工作流 YAML 定义
yaml
``
1. name: observability--alert-and-summarize-errors
2. description: 检索指定服务的错误日志并使用 AI 进行根因总结和 Slack 告警。
3. enabled: true
4. tags:
5. - observability
6. - alert
7. - ai
9. version: "1"
11. # 1. 触发器与运行时输入
12. triggers:
13. - type: manual
14. inputs:
15. - name: service_name
16. type: string
17. required: true
18. default: "order-service" # 默认查询订单服务
20. # 2. 内部重用常量
21. consts:
22. log_index: "logs-*"
23. slack_channel: "#ops-alerts"
25. # 3. 声明输出结果,便于在组合工作流中被父流程调用
26. outputs:
27. - name: summary_result
28. type: string
30. # 4. 全局行为配置
31. settings:
32. timeout: "3m" # 3分钟超时
33. on-failure:
34. retry:
35. max-attempts: 2
36. delay: "5s"
38. # 5. 执行步骤主体(有序串行)
39. steps:
40. # 步骤一:在 Elasticsearch 中检索近期的 Error 日志 [1]
41. - name: fetch_error_logs
42. type: elasticsearch.search
43. with:
44. index: "{{ consts.log_index }}"
45. query:
46. bool:
47. must:
48. - match:
49. service.name: "{{ inputs.service_name }}"
50. - match:
51. log.level: "error"
52. size: 5
54. # 步骤二:调用 AI 总结步骤一获取到的错误日志内容 [1]
55. - name: summarize_errors
56. type: ai.summarize
57. connector-id: my-openai-connector # 指向你在 Kibana 中配置好的 OpenAI 连接器 ID
58. with:
59. # 通过 Liquid 模板动态提取步骤一(fetch_error_logs)中命中数据的 _source.message,并用换行符拼接 [1]
60. input: "{{ steps.fetch_error_logs.output.hits.hits | map: '_source.message' | join: '\\n' }}"
62. # 步骤三:将 AI 总结结果发送至 Slack 运维频道 [1]
63. - name: post_to_slack
64. type: slack.postMessage
65. with:
66. channel: "{{ consts.slack_channel }}"
67. text: |
68. 🚨 **【服务异常告警】** 🚨
69. 服务名称:`{{ inputs.service_name }}`
71. **AI 异常诊断总结:**
72. {{ steps.summarize_errors.output }}
74. # 步骤四:设置最终输出,结束工作流 [1]
75. - name: return_output
76. type: data.set # 使用 data.set 来设置局部变量或最终输出
77. with:
78. summary_result: "{{ steps.summarize_errors.output }}"
``AI写代码收起代码块
💡 该工作流是如何运行的?
根据我们在第三部分介绍的生命周期,该工作流的执行过程如下 1:
- 触发 (Triggers & Inputs) : 你在 Kibana 界面中手动点击 "Run" 运行此工作流,或通过 Kibana 告警规则触发 1。由于设置了输入参数,你可以输入目标服务名称(例如
payment-service)。如果未提供,系统将自动使用默认值order-service1。 - 状态流转至
running(Running) :- 步骤一 (
fetch_error_logs) 启动 1。它利用常量consts.log_index(logs-*),在 Elasticsearch 中检索payment-service且日志级别为error的最新 5 条数据 1。 - 步骤二 (
summarize_errors) 启动 1。它读取步骤一的输出结果(steps.fetch_error_logs.output),利用 Liquid 的map过滤器将多条日志的message字段串联起来,作为 Prompt 发送给你配置的 OpenAI Connector,让 AI 总结出报错的底层原因 1。 - 步骤三 (
post_to_slack) 启动 1。它读取步骤二 AI 生成的文本,将其排版格式化,然后通过 Slack 渠道发送至运维频道#ops-alerts1。 - 步骤四 (
return_output) 启动 1。将 AI 的总结结果绑定至输出,以便其他父流程使用 1。
- 步骤一 (
- 完成并进入
completed终结状态 : 工作流顺利执行完毕,Kibana 执行历史中状态变为绿色 1。如果在执行过程中因为网络抖动导致 AI 或 Slack 调用失败,由于我们在settings中配置了on-failure,引擎会自动延迟 5 秒并最多重试 2 次,从而极大减少了偶发性失败 1。
5. 总结与开发建议
理解了 Elastic 工作流的解剖结构,你在构建自动化流程时就能更加得心应手。在编写自己的工作流时,不妨牢记以下几条"黄金准则":
- 优先做好命名规范 :遵循
<domain>--<verb>-<noun>,这能让你的工作流列表井井有条。 - 谨慎对待版本号 :记住
version: "1"的双引号不能省。 - 充分利用
consts与settings:把硬编码的阈值提取到常量中,并为生产工作流设置合理的超时(timeout)与重试(retry),能极大地增强自动化流程的稳健性。