Elasticsearch 设置

Zabbix 可以将历史数据存储在 Elasticsearch 中,作为关系型数据库的替代方案。

Elasticsearch 的支持目前仍处于实验阶段。

本指南介绍 受支持版本 的 Elasticsearch 配置。 如果您使用的是其他版本,某些功能可能无法按预期工作。

Elasticsearch 可以存储以下值类型:

监控项值类型 数据库表 Elasticsearch 类型
数值(无符号) history_uint uint
数值(浮点) history dbl
字符 history_str str
日志 history_log log
文本 history_text text
二进制 history_bin 不受 Zabbix 支持
JSON history_json json

Elasticsearch 不接受 JSON 数组。 JSON 值必须是单个对象,或一组对象。

重要说明

  • Elasticsearch 需要 libcurl。详情请参见 requirements
  • housekeeper 不会删除 Elasticsearch 中的数据。 如需控制数据保留时长,请参见 Index Lifecycle Management
  • Zabbix 不会 在 Elasticsearch 中计算或存储趋势数据。 请考虑延长 history storage period 以保留更早的数据。
  • 使用 Elasticsearch 时,从数据库检索值的范围查询会受到数据存储周期时间戳的限制。
  • Zabbix proxy 不支持 Elasticsearch。

如果尚未安装 Elasticsearch,请在继续之前参考 official installation guide

配置 Elasticsearch

要将历史数据存储到 Elasticsearch 中,您需要:

  • 为要存储的每种值类型创建一个 索引——这是 Elasticsearch 存储数据的位置,类似于关系型数据库中的表。
  • 为每个索引定义一个 映射——它定义了数据的结构,类似于表结构。
  • 设置一个 ingest pipeline,以便在存储前处理值(JSON 值和基于日期的索引必须使用)。

Elasticsearch 可以将数据存储在每种值类型对应的单个索引中,也可以分布在多个基于日期的索引中。
下面将介绍这两种方法。

将历史数据存储在单个索引中

在这种方法中,给定值类型的所有历史数据都会写入单个索引(例如 uinttext)。

要为 Numeric (unsigned) 值类型创建索引,请向您的 Elasticsearch 实例发送以下请求(URL 中使用 /uint):

curl -X PUT \
 http://localhost:9200/uint \
 -H 'content-type:application/json' \
 -d '{
     "settings": {
      "index": {
         "number_of_replicas": 1,
         "number_of_shards": 5
      }
   },
   "mappings": {
      "properties": {
         "itemid": { "type": "long" },
         "clock": { "format": "epoch_second", "type": "date" },
         "value": { "type": "long" }
      }
   }
}'

Elasticsearch 将返回确认信息,表明索引已创建:

{"acknowledged": true, "shards_acknowledged": true, "index": "uint"}

对于您希望存储在 Elasticsearch 中的每种其他值类型,都必须发送类似的请求。

所有值类型的映射均可在 Zabbix 源代码仓库 中找到。

例如,要为 Text 值类型创建索引:

curl -X PUT \
 http://localhost:9200/text \
 -H 'content-type:application/json' \
 -d '{
   "settings": {
      "index": {
         "number_of_replicas": 1,
         "number_of_shards": 5
      }
   },
   "mappings": {
      "properties": {
         "itemid": { "type": "long" },
         "clock": { "format": "epoch_second", "type": "date" },
         "value": {
            "fields": {
               "analyzed": { "index": true, "type": "text", "analyzer": "standard" }
            },
            "index": false,
            "type": "text"
         }
      }
   }
}'
JSON 值类型

与其他值类型不同,JSON 值在存储前需要额外处理。

下面的索引对已解析值和原始值使用了单独的字段,因此需要使用 ingest pipeline 将每个值解析为 JSON 并存储到正确的字段中。

要为 JSON 值类型创建索引,请向您的 Elasticsearch 实例发送以下请求(URL 中包含 /json)。

curl -X PUT \
 http://localhost:9200/json \
 -H 'content-type:application/json' \
 -d '{
   "settings": {
      "number_of_shards": 5,
      "number_of_replicas": 1
   },
   "mappings": {
      "dynamic": false,
      "properties": {
         "itemid": { "type": "long" },
         "clock": { "type": "date", "format": "epoch_second" },
         "ns": { "type": "long" },
         "value_parsed": { "type": "flattened" },
         "value_raw": { "type": "keyword", "ignore_above": 1000000 }
    }
  }
}'

然后,创建 ingest pipeline:

curl -X PUT \
 http://localhost:9200/_ingest/pipeline/json \
 -H 'content-type:application/json' \
 -d '{
   "processors": [
      {
         "json": {
            "field": "value",
            "target_field": "value_parsed",
            "ignore_failure": true
         }
      },
      {
         "set": {
            "if": "ctx.value_parsed == null",
            "field": "value_raw",
            "value": "{{{ value }}}"
         }
      }
   ],
   "on_failure": [
      {
         "set": {
            "field": "value_raw",
            "value": "{{{ value }}}"
         }
      }
   ]
}'

Elasticsearch 将返回一条确认信息,表明 ingest pipeline 已创建:

{"acknowledged": true}

将历史数据存储在按日期划分的索引中

Elasticsearch 不必将所有历史数据都写入单个索引(例如 uint),而是可以将这些数据分布到多个按日期划分的索引中(例如 uint-2026-01-01uint-2026-01-02)。 这样可以更轻松地随时间管理数据量和保留策略。

要启用此功能,您需要:

  • 为要存储的每种值类型创建一个 索引模板——这会告诉 Elasticsearch 在自动创建新的按日期划分的索引时应应用哪种映射。
  • 为每种值类型创建一个 ingest pipeline——它会处理每个传入的值,并将其路由到正确的按日期划分的索引。
索引模板

要为 text 索引创建模板,请发送包含以下详细信息的请求:

  • 在 Elasticsearch 实例的 URL 中使用 _template/text_template
  • "index_patterns" 字段中使用 "text*" 以匹配索引名称。
  • text 值类型使用映射(请参见 Zabbix 源代码仓库 中的映射)。
curl -X PUT \
 http://localhost:9200/_template/text_template \
 -H 'content-type:application/json' \
 -d '{
   "index_patterns": [ "text*" ],
   "settings": {
      "index": {
         "number_of_replicas": 1,
         "number_of_shards": 5
      }
   },
   "mappings": {
      "properties": {
         "itemid": { "type": "long" },
         "clock": { "format": "epoch_second", "type": "date" },
         "value": {
            "fields": {
               "analyzed": { "index": true, "type": "text", "analyzer": "standard" }
            },
            "index": false,
            "type": "text"
         }
      }
   }
}'

json 索引的模板:

curl -X PUT \
 http://localhost:9200/_template/json_template \
 -H 'content-type:application/json' \
 -d '{
   "index_patterns": [ "json*" ],
   "settings": {
      "number_of_shards": 5,
      "number_of_replicas": 1
   },
   "mappings": {
      "dynamic": false,
      "properties": {
         "itemid": { "type": "long" },
         "clock": { "type": "date", "format": "epoch_second" },
         "ns": { "type": "long" },
         "value_parsed": { "type": "flattened" },
         "value_raw": { "type": "keyword", "ignore_above": 1000000 }
      }
   }
}'
Ingest pipeline

要为 text 索引创建 ingest pipeline:

  • 在 Elasticsearch 实例的 URL 中使用 _ingest/pipeline/text-pipeline
  • 包含一个 date_index_name 处理器,根据时间戳将每个值路由到正确的基于日期的索引。
curl -X PUT \
 http://localhost:9200/_ingest/pipeline/text-pipeline \
 -H 'content-type:application/json' \
 -d '{
   "description": "daily text index naming",
   "processors": [
      {
         "date_index_name": {
            "field": "clock",
            "date_formats": ["UNIX"],
            "index_name_prefix": "text-",
            "date_rounding": "d"
         }
      }
   ]
}'

对于 json 索引,该 pipeline 还需要先解析 JSON 值,然后再将其路由到正确的索引:

curl -X PUT \
 http://localhost:9200/_ingest/pipeline/json-pipeline \
 -H 'content-type:application/json' \
 -d '{
   "description": "daily json index naming"
   "processors": [
      {
         "json": {
            "field": "value",
            "target_field": "value_parsed",
            "ignore_failure": true
         }
      },
      {
         "script": {
            "source": "if (ctx.value_parsed == null || !(ctx.value_parsed instanceof Map)) { ctx.value_raw = ctx.value; ctx.remove(\"value_parsed\"); }"
         }
      },
      {
         "date_index_name": {
            "field": "clock",
            "date_formats": [ "UNIX" ],
            "index_name_prefix": "json-",
            "date_rounding": "d"
         }
      }
   ]
}'

配置 Zabbix 服务器

在 Zabbix 服务器配置文件 (zabbix_server.conf) 中,设置 HistoryProvider 参数。

例如,要将 CharacterLogTextJSON 类型的值存储到 Elasticsearch 中(同时将 Numeric 值保留在数据库中):

HistoryProvider=elasticsearch;value_types="str,log,text,json",url=http://localhost:9200

如果你使用基于日期的索引,请在该参数中添加 date_index=1

HistoryProvider=elasticsearch;value_types="str,log,text,json",url=http://localhost:9200,date_index=1

完成更改后,重启 Zabbix 服务器:

systemctl restart zabbix-server

配置 Zabbix 前端

在你的 Zabbix 前端配置文件(zabbix.conf.php)中,将 $HISTORY_PROVIDERS 变量设置为与服务器配置一致:

$HISTORY_PROVIDERS[] = [
  'types' => ['str','log','text','json'],
  'provider' => 'elasticsearch',
  'url' => 'http://localhost:9200'
];

故障排查

以下步骤可能有助于你排查 Elasticsearch 配置中的问题:

  1. 检查 Elasticsearch 或 Zabbix 服务器日志中是否有错误。

  2. 要识别慢查询,请在 Zabbix 服务器配置参数 HistoryProvider 中使用 log_slow_queries 选项。

  3. 确认 Elasticsearch 允许来自 Zabbix 服务器和 Zabbix 前端的访问。

  4. 确认已启用 auto_create_index

curl -X GET \
 "http://localhost:9200/_cluster/settings?include_defaults=true&filter_path=**.auto_create_index"

# {"defaults": {"action": {"auto_create_index": "false"} } }

要启用它,请发送以下请求:

curl -X PUT \
 http://localhost:9200/_cluster/settings \
 -H 'content-type:application/json' \
 -d '{
   "persistent": {
      "action.auto_create_index": "true"
   }
}'

# {"acknowledged": true, "persistent": {"action": {"auto_create_index": "true"} }, "transient": {} }
  1. 通过向各自的 URL 发送 GET 请求,确认映射、模板和 ingest pipeline 是否正确:
curl -X GET http://localhost:9200/json
curl -X GET http://localhost:9200/_template/json*
curl -X GET http://localhost:9200/_ingest/pipeline/json*

你可以将收到的响应与 Elasticsearch API 文档中的预期响应进行比较。

  1. 检查是否有任何 shards 处于失败状态;重启 Elasticsearch 可能会解决此问题。

  2. 查询 Elasticsearch,查看 Zabbix 收集的数据是否已存储,例如:

curl 'http://localhost:9200/json/_search' \
 -H 'Content-Type: application/json' \
 -d '{
   "query": {
     "term": {
       "itemid": 42269
     }
   }
 }'
  1. 如果你需要重置 Elasticsearch 配置并重新开始,可以删除所有索引、模板和 ingest pipeline:
curl -X DELETE "http://localhost:9200/_all"
curl -X DELETE "http://localhost:9200/_template/*"
curl -X DELETE "http://localhost:9200/_ingest/pipeline/*"