Watcher 的工作原理

edit

Watcher 的工作原理

edit

您可以添加监视,以便在满足某些条件时自动执行操作。这些条件通常基于您已加载到监视中的数据,也称为监视负载。此负载可以从不同来源加载——从Elasticsearch、外部HTTP服务,甚至是两者的组合。

例如,您可以配置一个监视器,当日志数据中的搜索表明在过去5分钟内有太多503错误时,向系统管理员发送电子邮件。

本主题描述了手表的组成部分以及手表的工作原理。

观察定义

edit

一个监视器由一个触发器输入条件动作组成。动作定义了当条件满足时需要执行的操作。此外,您可以定义条件转换来处理和准备监视器负载,然后再执行动作。

Trigger
确定何时检查监视器。监视器必须有一个触发器。
Input
将数据加载到监视有效载荷中。如果没有指定输入,则加载一个空的有效载荷。
Condition
控制是否执行观察操作。如果没有指定条件,条件默认为always
Transform
处理监视负载以准备用于监视操作。您可以在监视级别定义转换,或定义特定于操作的转换。可选。
Actions
指定当观察条件满足时发生的情况。

例如,以下代码片段展示了一个创建或更新监视器请求,该请求定义了一个监视器,用于查找日志错误事件:

PUT _watcher/watch/log_errors
{
  "metadata" : { 
    "color" : "red"
  },
  "trigger" : { 
    "schedule" : {
      "interval" : "5m"
    }
  },
  "input" : { 
    "search" : {
      "request" : {
        "indices" : "log-events",
        "body" : {
          "size" : 0,
          "query" : { "match" : { "status" : "error" } }
        }
      }
    }
  },
  "condition" : { 
    "compare" : { "ctx.payload.hits.total" : { "gt" : 5 }}
  },
  "transform" : { 
    "search" : {
        "request" : {
          "indices" : "log-events",
          "body" : {
            "query" : { "match" : { "status" : "error" } }
          }
        }
    }
  },
  "actions" : { 
    "my_webhook" : {
      "webhook" : {
        "method" : "POST",
        "host" : "mylisteninghost",
        "port" : 9200,
        "path" : "/{{watch_id}}",
        "body" : "Encountered {{ctx.payload.hits.total}} errors"
      }
    },
    "email_administrator" : {
      "email" : {
        "to" : "sys.admino@host.domain",
        "subject" : "Encountered {{ctx.payload.hits.total}} errors",
        "body" : "Too many error in the system, see attached data",
        "attachments" : {
          "attached_data" : {
            "data" : {
              "format" : "json"
            }
          }
        },
        "priority" : "high"
      }
    }
  }
}

元数据 - 您可以为手表附加可选的静态元数据。

触发器 - 此计划触发器每5分钟执行一次观察。

输入 - 此输入搜索 log-events 索引中的错误,并将响应加载到监视负载中。

条件 - 此条件检查是否存在超过5个错误事件(搜索响应中的点击)。如果存在,则继续执行所有操作

转换 - 如果满足观察条件,此转换将通过使用默认搜索类型 query_then_fetch 搜索错误,将所有错误加载到观察负载中。所有观察操作都可以访问此负载。

操作 - 此监视器有两个操作。my_webhook 操作通知第三方系统有关问题。email_administrator 操作向系统管理员发送高优先级电子邮件。包含错误的监视器负载会附加到电子邮件中。

查看执行

edit

当你添加一个监视器时,Watcher会立即将其触发器注册到相应的触发引擎。具有schedule触发器的监视器会被注册到scheduler触发引擎。

调度器跟踪时间并根据它们的计划触发监视。 在每个包含.watches分片的节点上,一个绑定到监视器生命周期的调度器运行。 即使所有主分片和副本分片都被考虑在内,当一个监视被触发时,监视器也会确保每个监视只在其中一个分片上触发。 你添加的副本分片越多,监视的执行就越分散。 如果你添加或删除副本,所有监视都需要重新加载。 如果一个分片被重新定位,这个特定分片的主分片和所有副本分片都将重新加载。

因为监视器是在节点上执行的,而监视器分片位于这些节点上,所以你可以通过使用分片分配过滤来创建专用的监视器节点。

您可以配置节点使用专用的 node.attr.role: watcher 属性,然后像这样配置 .watches 索引:

PUT .watches/_settings
{
  "index.routing.allocation.include.role": "watcher"
}

当Watcher服务停止时,调度器也随之停止。触发引擎使用与执行监视器不同的线程池。

当一个监视器被触发时,Watcher 会将其排队等待执行。一个 watch_record 文档会被创建并添加到监视历史记录中,同时监视器的状态会被设置为 awaits_execution

当执行开始时,Watcher 为监视创建一个监视执行上下文。执行上下文为脚本和模板提供了访问监视元数据、有效载荷、监视 ID、执行时间和触发信息的权限。更多信息,请参阅 监视执行上下文

在执行过程中,Watcher:

  1. 将输入数据作为监视执行上下文中的有效负载加载。这使得数据在执行过程中的所有后续步骤中都可用。此步骤由监视的输入控制。
  2. 评估监视条件以确定是否继续处理监视。如果条件满足(评估为true),则处理进入下一步。如果不满足(评估为false),则监视的执行停止。
  3. 根据需要对监视有效负载应用转换。
  4. 在满足条件且监视未被限制的情况下执行监视操作。

当监视执行完成时,执行结果会记录为监视历史中的一个监视记录。监视记录包括执行时间和持续时间、监视条件是否满足,以及每个执行动作的状态。

以下图表展示了观察执行过程:

watch execution

观察确认与限流

edit

Watcher 支持基于时间和基于确认的节流机制。这使您能够防止对同一事件重复执行操作。

默认情况下,Watcher 使用基于时间的节流,节流周期为 5 秒。这意味着如果一个监视器每秒执行一次,即使条件总是满足,其操作最多每 5 秒执行一次。您可以在每个操作的基础上或在监视器级别配置节流周期。

基于确认的限流功能使您能够告诉 Watcher,只要其条件满足,就不再发送有关该监视的任何通知。一旦条件评估为 false,确认将被清除,Watcher 将恢复正常执行监视操作。

更多信息,请参阅确认和限流

监视活动状态

edit

默认情况下,当您添加一个监视时,它会立即设置为活动状态, 注册到相应的触发引擎,并根据其配置的触发器执行。

您还可以将监视器设置为非活动状态。非活动的监视器不会注册到触发引擎,并且永远不会被触发。

要在创建时将监视器设置为非活动状态,请将active参数设置为inactive。要停用现有监视器,请使用停用监视器 API。要重新激活非活动监视器,请使用激活监视器 API

您可以使用执行观察API 来强制执行一个即使在非活动状态下的观察。

停用监视器在多种情况下非常有用。例如,如果您有一个监视外部系统的监视器,并且需要对该系统进行维护,您可以停用该监视器,以防止它在维护窗口期间错误地报告可用性问题。

停用一个监视器也使您能够在将来使用它,而不必从系统中删除它。

脚本和模板

edit

在定义监视时,您可以使用脚本和模板。脚本和模板可以引用监视执行上下文中的元素,包括监视的有效负载。执行上下文定义了您可以在脚本中使用的变量和模板中的参数占位符。

Watcher 使用 Elasticsearch 脚本基础设施,支持 内联存储的 脚本和模板。 脚本和模板由 Elasticsearch 编译并缓存,以优化重复执行。还支持自动加载。有关更多信息,请参阅 脚本如何编写脚本

观察执行上下文

edit

以下代码片段展示了观察执行上下文的基本结构:

{
  "ctx" : {
    "metadata" : { ... }, 
    "payload" : { ... }, 
    "watch_id" : "<id>", 
    "execution_time" : "20150220T00:00:10Z", 
    "trigger" : { 
      "triggered_time" : "20150220T00:00:10Z",
      "scheduled_time" : "20150220T00:00:00Z"
    },
    "vars" : { ... } 
}

在监视定义中指定的任何静态元数据。

当前的监视负载。

执行的监视器的ID。

显示观察执行开始时间的标记。

关于触发事件的信息。对于schedule触发器,这包括triggered_time(观察何时被触发)和scheduled_time(观察被计划触发的时间)。

在执行过程中可以由不同构造设置和访问的动态变量。这些变量的作用域限定为单次执行(即它们不会持久化,不能在同一监视的不同执行之间使用)

使用脚本

edit

您可以使用脚本来定义条件转换。默认的脚本语言是 Painless

从5.0开始,Elasticsearch 随附了新的 Painless 脚本语言。 Painless 是为在 Elasticsearch 中使用而创建和设计的。 除了提供广泛的功能集外,它最大的特点是它被正确地沙盒化并且可以在系统中的任何地方(包括在 Watcher 中)安全使用,而无需启用动态脚本。

脚本可以引用监视执行上下文中的任何值或通过脚本参数显式传递的值。

例如,如果手表元数据包含一个color字段 (例如"metadata" : {"color": "red"}),你可以通过 ctx.metadata.color变量访问其值。如果你在条件或转换定义中传递了一个color参数 (例如"params" : {"color": "red"}),你可以通过color变量访问其值。

使用模板

edit

您可以使用模板来定义手表的动态内容。在执行时,模板从手表执行上下文中提取数据。例如,您可以使用模板来填充subject字段,该字段用于email操作,数据存储在手表的有效载荷中。模板还可以访问通过模板参数显式传递的值。

您可以使用Mustache脚本语言指定模板。

例如,以下代码片段展示了模板如何在发送的电子邮件中启用动态主题:

{
  "actions" : {
    "email_notification" : {
      "email" : {
        "subject" : "{{ctx.metadata.color}} alert"
      }
    }
  }
}
内联模板和脚本
edit

要定义一个内联模板或脚本,您只需直接在字段的值中指定它。例如,以下代码片段使用一个内联模板配置了email操作的主题,该模板引用了上下文元数据中的color值。

"actions" : {
  "email_notification" : {
     "email" : {
       "subject" : "{{ctx.metadata.color}} alert"
     }
   }
  }
}

对于一个脚本,您只需将内联脚本指定为 script 字段的值。例如:

"condition" : {
  "script" : "return true"
}

您还可以通过使用正式对象定义作为字段值来显式指定内联类型。例如:

"actions" : {
  "email_notification" : {
    "email" : {
      "subject" : {
         "source" : "{{ctx.metadata.color}} alert"
      }
    }
  }
}

脚本的正式对象定义为:

"condition" : {
  "script" : {
    "source": "return true"
  }
}
存储的模板和脚本
edit

如果你存储 你的模板和脚本,你可以通过id引用它们。

要引用存储的脚本或模板,您可以使用正式的对象定义并在id字段中指定其id。例如,以下代码片段引用了email_notification_subject模板:

{
  ...
  "actions" : {
    "email_notification" : {
      "email" : {
        "subject" : {
          "id" : "email_notification_subject",
          "params" : {
            "color" : "red"
          }
        }
      }
    }
  }
}