Elasticsearch 设置

Zabbix 可以将历史数据存储在 Elasticsearch 中,以替代关系数据库。

本指南介绍 受支持版本 的 Elasticsearch 设置。 如果您的 Elasticsearch 版本低于支持的最低版本,Zabbix 服务器将无法启动。 如果版本高于支持的最高版本,则只有在将 AllowUnsupportedDBVersions 参数设置为 1 时,Zabbix 服务器才会启动。

Elasticsearch 可以存储以下值类型:

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

重要说明

  • Elasticsearch 需要 libcurl。 有关详细信息,请参阅要求。
  • housekeeper 不会从 Elasticsearch 中删除数据。 要控制数据的保留时长,请参阅 Index Lifecycle Management。
  • Zabbix 不会在 Elasticsearch 中计算或存储趋势数据。 如需保留较早的数据,请考虑延长历史数据保留期。
  • 使用 Elasticsearch 时,从数据库检索值的范围查询会受到数据存储期时间戳的限制。
  • Elasticsearch 不支持 Zabbix proxy。
  • 从 Elasticsearch 8.0 开始,默认启用安全功能:Elasticsearch 要求进行身份验证,并通过 HTTPS 使用其 HTTP API,证书由自动生成的 CA 签名。 本页中的示例使用不带身份验证的 http://localhost:9200。 如果启用了安全功能,请使用 https:// URL,并在请求中传递 CA 证书和凭据(例如,sudo curl --cacert /etc/elasticsearch/certs/http_ca.crt -u elastic https://localhost:9200;CA 证书文件只能由 root 和 elasticsearch 组读取)。 要将 Zabbix 服务器和 Zabbix 前端连接到此类集群,请参阅连接到安全集群。

如果尚未安装 Elasticsearch,请在继续操作前参阅官方安装指南。

配置 Elasticsearch

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

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

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

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

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

要为 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 服务器配置中 value_types 选项列出的每种类型发送请求。

所有值类型的映射都可以在 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 值在存储前需要进行额外处理。 JSON 值类型的索引会将每个值存储在以下两个字段之一中:

  • value_parsed—JSON 对象,以扁平化字段的形式存储,因此可以按键和值进行搜索;
  • value_raw—所有其他 JSON 值(数组、字符串、数字、布尔值和 null),以字符串形式存储。

摄取管道会将每个值解析为 JSON,并将其存储在正确的字段中。 无效的 JSON 值不会发送到 Elasticsearch,因为接收这些值的监控项会变为不受支持。

首先,创建摄取管道:

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
         }
      },
      {
         "script": {
            "source": "if (ctx.value_parsed == null || !(ctx.value_parsed instanceof Map)) { ctx.value_raw = ctx.value; ctx.remove(\"value_parsed\"); }"
         }
      }
   ]
}'

Elasticsearch 将返回确认信息,表明摄取管道已创建:

{"acknowledged": true}

然后,通过向 Elasticsearch 实例发送以下请求(URL 中包含 /json)来为 JSON 值类型创建索引。 default_pipeline 设置会使 Elasticsearch 使用 json 摄取管道处理写入此索引的每个值。 此设置是必需的,因为 Zabbix 服务器在将历史数据存储到单个索引时不会指定摄取管道。

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

Elasticsearch 无法为长度超过 32766 字节的 keyword 和 flattened 值创建索引。 使用 "ignore_above": 8191 后,长度超过 8191 个字符的字符串不会被索引(也无法搜索),但仍会被完整存储并返回给 Zabbix。 如果没有此限制,Elasticsearch 会拒绝此类值,并且这些值会丢失。

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

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

要启用此功能,您需要:

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

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

  • 在 Elasticsearch 实例的 URL 中使用 _index_template/text_template。
  • 在 "index_patterns" 字段中使用 "text-*",以匹配基于日期的索引名称。
  • 在 "template" 字段中指定索引设置,以及 text 值类型的映射(请参阅 Zabbix 源代码仓库中的映射)。
curl -X PUT \
 http://localhost:9200/_index_template/text_template \
 -H 'content-type:application/json' \
 -d '{
   "index_patterns": [ "text-*" ],
   "template": {
      "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 索引的模板(有关 value_parsed 和 value_raw 字段的详细信息,请参阅 JSON 值类型):

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

请使用如上所示的可组合索引模板(_index_template)。

旧版索引模板(_template)已弃用,并且 Elasticsearch 会拒绝索引模式与其内置模板重叠的旧版模板(例如,为 log 索引使用 log* 模式的旧版模板)。

Ingest 管道

要为 text 索引创建 ingest 管道:

  • 在 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"
         }
      }
   ]
}'

为要存储的每种值类型创建一个管道,并相应地修改管道名称、描述和 "index_name_prefix" 值。 管道名称必须由索引名称和 -pipeline 后缀组成(例如 uint-pipeline),因为 Zabbix 服务器在向 Elasticsearch 发送值时会使用此名称。

对于 json 索引,管道还需要先解析 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 参数。

例如,将 Character、Log、Text 和 JSON 类型的值存储在 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

如果已启用 Elasticsearch 安全功能,请指定带有凭据的 https:// URL(请参阅连接到安全集群)。

完成更改后,重启 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 安全功能,请指定与服务器配置中相同的带凭据的 https:// URL(请参阅连接到安全集群)。

连接到受保护的集群

如果启用了 Elasticsearch 安全功能(Elasticsearch 8.0 起默认启用),Zabbix 服务器和 Zabbix 前端必须信任 Elasticsearch HTTP 层的 CA 证书,并使用用户名和密码进行身份验证。

  1. 为 Zabbix 创建一个角色和一个用户。 以下角色允许 Zabbix 获取 Elasticsearch 版本(monitor 集群权限)、创建索引、写入和读取历史数据,以及在清除监控项历史数据时删除历史数据:
sudo curl -X PUT \
 --cacert /etc/elasticsearch/certs/http_ca.crt -u elastic \
 https://localhost:9200/_security/role/zabbix_history \
 -H 'content-type:application/json' \
 -d '{
   "cluster": [ "monitor" ],
   "indices": [
      {
         "names": [ "uint", "uint-*", "dbl", "dbl-*", "str", "str-*", "log", "log-*", "text", "text-*", "json", "json-*" ],
         "privileges": [ "create_index", "index", "read", "delete" ]
      }
   ]
}'
sudo curl -X PUT \
 --cacert /etc/elasticsearch/certs/http_ca.crt -u elastic \
 https://localhost:9200/_security/user/zabbix \
 -H 'content-type:application/json' \
 -d '{
   "password": "<password>",
   "roles": [ "zabbix_history" ]
}'
  1. 将 Elasticsearch CA 证书添加到 Zabbix 服务器和 Zabbix 前端主机操作系统的受信任证书中。 如果 Zabbix 与 Elasticsearch 运行在不同的主机上,请先将 CA 证书(/etc/elasticsearch/certs/http_ca.crt)复制到相应主机。 例如,在 Debian/Ubuntu 上:
sudo cp /etc/elasticsearch/certs/http_ca.crt /usr/local/share/ca-certificates/elasticsearch-http-ca.crt
sudo update-ca-certificates

URL 中的主机名必须与 Elasticsearch HTTP 证书匹配。 自动生成的证书对 localhost、主机名以及 Elasticsearch 主机的 IP 地址有效。

  1. 在 Zabbix 服务器配置文件和前端配置文件中,将凭据指定在 URL 中。 如果密码包含特殊字符,请对其进行百分号编码(例如,将 @ 编码为 %40,将 : 编码为 %3A,将 , 编码为 %2C)。
HistoryProvider=elasticsearch;value_types="str,log,text,json",url=https://zabbix:<password>@localhost:9200
$HISTORY_PROVIDERS[] = [
  'types' => ['str','log','text','json'],
  'provider' => 'elasticsearch',
  'url' => 'https://zabbix:<password>@localhost:9200'
];
  1. 重启 Zabbix 服务器和 PHP-FPM(如果 PHP 作为 Web 服务器模块运行,则重启 Web 服务器)。

故障排除

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

  1. 检查 Elasticsearch 或 Zabbix 服务器日志中是否存在错误。 例如,以下 Zabbix 服务器日志消息表示连接到安全集群时出现问题(请参阅连接到安全集群):
  • cannot query Elasticsearch: SSL certificate problem: self-signed certificate in certificate chain—Elasticsearch CA 证书不受信任;
  • cannot query Elasticsearch, HTTP error message: The requested URL returned error: 401—凭据缺失或不正确;
  • cannot query Elasticsearch, HTTP error message: The requested URL returned error: 403—用户没有所需的权限。

Zabbix 前端会显示类似的错误(例如,Elasticsearch error: missing authentication credentials for REST request)。

  1. 要识别运行缓慢的查询,请设置 Zabbix 服务器配置参数 LogSlowQueries。 如果从 Elasticsearch 读取历史数据所需的时间超过指定时间,Zabbix 服务器会记录 slow query: <N> sec, "<query>"。 在等待 Elasticsearch 响应时,Zabbix 服务器还会在每次经过指定时间后记录 waiting for Elasticsearch response <N>sec;如果 LogSlowQueries 设置为 0,则每 10 秒记录一次。

  2. 验证 Elasticsearch 是否允许 Zabbix 服务器和 Zabbix 前端访问。

  3. 验证 auto_create_index 是否已启用(默认已启用):

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

# {"persistent": {"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 请求,验证映射、模板和摄取管道是否正确:
curl -X GET "http://localhost:9200/json*"
curl -X GET "http://localhost:9200/_index_template/json*"
curl -X GET "http://localhost:9200/_ingest/pipeline/json*"

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

  1. 检查是否有任何分片处于失败状态;重启 Elasticsearch 可能会解决此问题。 在单节点集群中,包含副本("number_of_replicas": 1)的索引会显示黄色状态,因为副本无法分配到与主分片相同的节点上;这不会影响 Zabbix。 要避免此问题,请将 "number_of_replicas" 设置为 0。

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

curl "http://localhost:9200/json*/_search" \
 -H 'Content-Type: application/json' \
 -d '{
   "query": {
     "term": {
       "itemid": 42269
     }
   }
 }'
  1. 如果需要重置 Elasticsearch 设置并重新开始,请删除为 Zabbix 创建的索引、索引模板和摄取管道(这会删除存储在 Elasticsearch 中的所有历史数据)。 默认情况下,Elasticsearch 不允许通过通配符表达式删除索引,因此要删除基于日期的索引,请临时更改 action.destructive_requires_name 设置。 索引模板和摄取管道必须逐个删除;对于不存在的模板或管道,删除请求会返回错误,可以忽略该错误。
curl -X DELETE "http://localhost:9200/uint,dbl,str,log,text,json?ignore_unavailable=true"

curl -X PUT \
 http://localhost:9200/_cluster/settings \
 -H 'content-type:application/json' \
 -d '{"persistent": {"action.destructive_requires_name": false}}'
curl -X DELETE "http://localhost:9200/uint-*,dbl-*,str-*,log-*,text-*,json-*"
curl -X PUT \
 http://localhost:9200/_cluster/settings \
 -H 'content-type:application/json' \
 -d '{"persistent": {"action.destructive_requires_name": null}}'

for type in uint dbl str log text json; do
  curl -X DELETE "http://localhost:9200/_index_template/${type}_template"
  curl -X DELETE "http://localhost:9200/_ingest/pipeline/${type}-pipeline"
done
curl -X DELETE "http://localhost:9200/_ingest/pipeline/json"