使用 Logstash 丰富你的 Elasticsearch 文档

作者:来自 Elastic David Pilato

我们在上一篇文章中看到,我们可以使用摄取管道中的 Elasticsearch Enrich Processor 在 Elasticsearch® 中进行数据丰富。 但有时,你需要执行更复杂的任务,或者你的数据源不是 Elasticsearch,而是另一个源。 或者,你可能希望存储在 Elasticsearch 和第三方系统中,在这种情况下,将管道的执行转移到 Logstash® 很有意义。

使用 Elasticsearch 丰富 Elasticsearch 数据

使用 Logstash,使用类似于以下的管道,这非常容易:

ini 复制代码
1.  input {
2.    # Read all documents from Elasticsearch
3.    elasticsearch {
4.      hosts => ["${ELASTICSEARCH_URL}"]
5.      user => "elastic"
6.      password => "${ELASTIC_PASSWORD}"
7.      index => "kibana_sample_data_logs"
8.      docinfo => true
9.      ecs_compatibility => "disabled"
10.    }
11.  }

13.  filter {
14.    # Enrich every document with Elasticsearch
15.    elasticsearch {
16.      hosts => ["${ELASTICSEARCH_URL}"]
17.      user => "elastic"
18.      password => "${ELASTIC_PASSWORD}"
19.      index => "vip"
20.      query => "ip:%{[clientip]}"
21.      sort => "ip:desc"
22.      fields => {
23.        "[name]" => "[name]"
24.        "[vip]" => "[vip]"
25.      }
26.    }
27.    mutate { 
28.      remove_field => ["@version", "@timestamp"] 
29.    }
30.  }

32.  output {
33.    if [name] {
34.      # Write all modified documents to Elasticsearch
35.      elasticsearch {
36.        manage_template => false
37.        hosts => ["${ELASTICSEARCH_URL}"]
38.        user => "elastic"
39.        password => "${ELASTIC_PASSWORD}"
40.        index => "%{[@metadata][_index]}"
41.        document_id => "%{[@metadata][_id]}"
42.      }
43.    }
44.  }

总共,我们有 14074 个事件需要解析。 虽然不是很多,但对于这个演示来说已经足够了。 这是一个示例事件:

swift 复制代码
1.  {
2.    "agent": "Mozilla/5.0 (X11; Linux i686) AppleWebKit/534.24 (KHTML, like Gecko) Chrome/11.0.696.50 Safari/534.24",
3.    "bytes": 1831,
4.    "clientip": "30.156.16.164",
5.    "extension": "",
6.    "geo": {
7.      "srcdest": "US:IN",
8.      "src": "US",
9.      "dest": "IN",
10.      "coordinates": {
11.        "lat": 55.53741389,
12.        "lon": -132.3975144
13.      }
14.    },
15.    "host": "elastic-elastic-elastic.org",
16.    "index": "kibana_sample_data_logs",
17.    "ip": "30.156.16.163",
18.    "machine": {
19.      "ram": 9663676416,
20.      "os": "win xp"
21.    },
22.    "memory": 73240,
23.    "message": "30.156.16.163 - - [2018-09-01T12:43:49.756Z] \"GET /wp-login.php HTTP/1.1\" 404 1831 \"-\" \"Mozilla/5.0 (X11; Linux i686) AppleWebKit/534.24 (KHTML, like Gecko) Chrome/11.0.696.50 Safari/534.24\"",
24.    "phpmemory": 73240,
25.    "referer": "http://www.elastic-elastic-elastic.com/success/timothy-l-kopra",
26.    "request": "/wp-login.php",
27.    "response": 404,
28.    "tags": [
29.      "success",
30.      "info"
31.    ],
32.    "timestamp": "2023-03-18T12:43:49.756Z",
33.    "url": "https://elastic-elastic-elastic.org/wp-login.php",
34.    "utc_time": "2023-03-18T12:43:49.756Z",
35.    "event": {
36.      "dataset": "sample_web_logs"
37.    }
38.  }

正如我们在上一篇文章中看到的,vip 索引包含有关我们客户的信息:

json 复制代码
1.  { 
2.    "ip" : "30.156.16.164", 
3.    "vip": true, 
4.    "name": "David P" 
5.  }

我们可以通过以下方式运行管道:

ruby 复制代码
1.  docker run \
2.    --name=logstash \
3.    --rm -it \
4.    -v $(pwd)/logstash-config/pipeline/:/usr/share/logstash/pipeline/ \
5.    -e XPACK_MONITORING_ENABLED=false \
6.    -e ELASTICSEARCH_URL="$ELASTICSEARCH_URL" \
7.    -e ELASTIC_PASSWORD="$ELASTIC_PASSWORD" \
8.    docker.elastic.co/logstash/logstash:8.12.0

丰富的文档现在看起来像这样:

swift 复制代码
1.  {
2.    "agent": "Mozilla/5.0 (X11; Linux i686) AppleWebKit/534.24 (KHTML, like Gecko) Chrome/11.0.696.50 Safari/534.24",
3.    "bytes": 1831,
4.    "clientip": "30.156.16.164",
5.    "extension": "",
6.    "geo": {
7.      "srcdest": "US:IN",
8.      "src": "US",
9.      "dest": "IN",
10.      "coordinates": {
11.        "lat": 55.53741389,
12.        "lon": -132.3975144
13.      }
14.    },
15.    "host": "elastic-elastic-elastic.org",
16.    "index": "kibana_sample_data_logs",
17.    "ip": "30.156.16.163",
18.    "machine": {
19.      "ram": 9663676416,
20.      "os": "win xp"
21.    },
22.    "memory": 73240,
23.    "message": "30.156.16.163 - - [2018-09-01T12:43:49.756Z] \"GET /wp-login.php HTTP/1.1\" 404 1831 \"-\" \"Mozilla/5.0 (X11; Linux i686) AppleWebKit/534.24 (KHTML, like Gecko) Chrome/11.0.696.50 Safari/534.24\"",
24.    "phpmemory": 73240,
25.    "referer": "http://www.elastic-elastic-elastic.com/success/timothy-l-kopra",
26.    "request": "/wp-login.php",
27.    "response": 404,
28.    "tags": [
29.      "success",
30.      "info"
31.    ],
32.    "timestamp": "2023-03-18T12:43:49.756Z",
33.    "url": "https://elastic-elastic-elastic.org/wp-login.php",
34.    "utc_time": "2023-03-18T12:43:49.756Z",
35.    "event": {
36.      "dataset": "sample_web_logs"
37.    },
38.    "vip": true,
39.    "name": "David P"
40.  }

实际上很简单,但有一个问题:速度很慢。 通过网络进行查找,尽管 Elasticsearch 速度极快,但仍然会减慢整个管道的速度。

使用静态 JDBC 过滤器

我最近在 ParisJUG 遇到了 Laurent,他来自令人惊叹的 Elastic Consulting 团队,我们讨论了这个问题。 他告诉我,他的一位客户必须面对这个问题。 他建议改用 Logstash 中的 Elasticsearch 缓存。

问题是:Logstash 中没有这样的过滤器缓存插件。 他找到了一种非常聪明的方法来解决该问题,即利用静态 JDBC 过滤器插件Elasticsearch JDBC 驱动程序

请注意,这需要拥有白金许可证(或试用版)。

添加 Elasticsearch JDBC 驱动程序

我们首先需要将 JDBC 驱动程序添加到 Logstash 实例中。

bash 复制代码
1.  mdir -p logstash-config/lib
2.  wget https://artifacts.elastic.co/maven/org/elasticsearch/plugin/x-pack-sql-jdbc/8.12.0/x-pack-sql-jdbc-8.12.0.jar
3.  mv x-pack-sql-jdbc-8.12.0.jar logstash-config/lib

我们只需要与 Logstash docker 实例共享此目录:

ruby 复制代码
1.  time docker run \
2.    --name=logstash \
3.    --rm -it \
4.    -v $(pwd)/logstash-config/pipeline/:/usr/share/logstash/pipeline/ \
5.    -v $(pwd)/logstash-config/lib/:/tmp/lib/ \
6.    -e XPACK_MONITORING_ENABLED=false \
7.    -e ELASTICSEARCH_URL="$ELASTICSEARCH_URL" \
8.    -e ELASTIC_PASSWORD="$ELASTIC_PASSWORD" \
9.    docker.elastic.co/logstash/logstash:8.12.0

更新管道

input 部分不变。 但现在,我们要在内存中创建一个名为 vip 的临时表(为了保持一致性)。 该表结构是使用 local_db_objects 参数定义的:

css 复制代码
1.  jdbc_static {
2.    local_db_objects => [ {3.      name => "vip"4.      index_columns => ["ip"]
5.      columns => [6.        ["name", "VARCHAR(255)"],
7.        ["vip", "BOOLEAN"],
8.        ["ip", "VARCHAR(64)"]
9.      ]
10.    } ]
11.  }

当 jdbc_static 启动时,我们要首先从 Elasticsearch vip索引中读取所有数据集。 这是在 loaders 选项中完成的:

ini 复制代码
1.  jdbc_static {
2.    loaders => [ {
3.      query => "select name, vip, ip from vip"
4.      local_table => "vip"
5.    } ]
6.    jdbc_user => "elastic"
7.    jdbc_password => "${ELASTIC_PASSWORD}"
8.    jdbc_driver_class => "org.elasticsearch.xpack.sql.jdbc.EsDriver"
9.    jdbc_driver_library => "/tmp/lib/x-pack-sql-jdbc-8.12.0.jar"
10.    jdbc_connection_string => "jdbc:es://${ELASTICSEARCH_URL}"
11.  }

每次我们需要进行查找时,我们都希望使用以下语句来执行它:

sql 复制代码
SELECT name, vip FROM vip WHERE ip = "THE_IP"

这可以使用 local_lookups 参数定义:

dart 复制代码
1.  jdbc_static {
2.    local_lookups => [ {
3.      query => "SELECT name, vip FROM vip WHERE ip = :ip"
4.      parameters => { "ip" => "clientip" }
5.      target => "vip"
6.    } ]
7.  }

如果没有找到数据,我们可以使用 default_hash 选项提供默认值:

dart 复制代码
1.  jdbc_static {
2.    local_lookups => [ {
3.      query => "SELECT name, vip FROM vip WHERE ip = :ip"
4.      parameters => { "ip" => "clientip" }
5.      target => "vip" 
6.      default_hash => {
7.        name => nil
8.        vip => false
9.      }
10.    } ]
11.  }

最后,这将在事件中生成 vip.name 和 vip.vip 字段。

我们现在可以定义我们想要对这些临时字段执行的操作:

ini 复制代码
1.  jdbc_static {
2.    add_field => { name => "%{[vip][0][name]}" }
3.    add_field => { vip => "%{[vip][0][vip]}" }
4.    remove_field => ["vip"]
5.  }

这给出了以下过滤器:

dart 复制代码
1.  filter {
2.    # Enrich every document with Elasticsearch via static JDBC
3.    jdbc_static {
4.      loaders => [ {
5.        query => "select name, vip, ip from vip"
6.        local_table => "vip"
7.      } ]
8.      local_db_objects => [ {
9.        name => "vip"
10.        index_columns => ["ip"]
11.        columns => [
12.          ["name", "VARCHAR(255)"],
13.          ["vip", "BOOLEAN"],
14.          ["ip", "VARCHAR(64)"]
15.        ]
16.      } ]
17.      local_lookups => [ {
18.        query => "SELECT name, vip FROM vip WHERE ip = :ip"
19.        parameters => { "ip" => "clientip" }
20.        target => "vip" 
21.        default_hash => {
22.          name => nil
23.          vip => false
24.        }
25.      } ]
26.      add_field => { name => "%{[vip][0][name]}" }
27.      add_field => { vip => "%{[vip][0][vip]}" }
28.      remove_field => ["vip"]
29.      jdbc_user => "elastic"
30.      jdbc_password => "${ELASTIC_PASSWORD}"
31.      jdbc_driver_class => "org.elasticsearch.xpack.sql.jdbc.EsDriver"
32.      jdbc_driver_library => "/tmp/lib/x-pack-sql-jdbc-8.12.0.jar"
33.      jdbc_connection_string => "jdbc:es://${ELASTICSEARCH_URL}"
34.    }
35.    mutate { 
36.      remove_field => ["@version", "@timestamp"] 
37.    }
38.  }

将修改后的文档写入Elasticsearch

在第一个管道中,我们测试事件中是否确实存在名称字段:

markdown 复制代码
1.  if [name] {
2.    # Index to Elasticsearch
3.  }

我们仍然可以使用类似的东西,但因为我们提供了默认值,以防在 Elasticsearch vip 索引中找不到 ip,所以现在它会在标签表中生成一个新的 _jdbcstaticdefaultsused 标签。

我们可以用它来知道我们是否发现了某些东西,如果是前者,则将我们的数据发送到 Elasticsearch:

ini 复制代码
1.  output {
2.    if "_jdbcstaticdefaultsused" not in [tags] {
3.      # Write all the modified documents to Elasticsearch
4.      elasticsearch {
5.        manage_template => false
6.        hosts => ["${ELASTICSEARCH_URL}"]
7.        user => "elastic"
8.        password => "${ELASTIC_PASSWORD}"
9.        index => "%{[@metadata][_index]}"
10.        document_id => "%{[@metadata][_id]}"
11.      }
12.    }
13.  }

更快吗?

因此,当我们在这个小数据集上运行测试时,我们可以看到,使用 Elasticsearch 过滤器方法,需要两分钟多一点的时间来丰富我们的数据集:

markdown 复制代码
1.  real    2m3.146s
2.  user    0m0.077s
3.  sys     0m0.042s

当使用 JDBC 静态过滤器方法运行管道时,现在只需不到一分钟:

markdown 复制代码
1.  real    0m48.575s
2.  user    0m0.064s
3.  sys     0m0.039s

正如我们所看到的,我们显着减少了该丰富管道的执行时间(增益约为 60%)。

如果你有一个可以轻松放入 Logstash JVM 内存的小型 Elasticsearch 索引,你可以尝试此策略(或类似的策略)。 如果你有数亿个文档,你仍然应该使用 Elasticsearch Filter Plugin

结论

在这篇文章中,我们了解了当我们需要在 Elasticsearch 中执行一些查找时,如何使用 JDBC 静态过滤器插件来加速数据丰富管道。 在下一篇文章中,我们将了解如何使用 Elastic Agent 在边缘进行类似的丰富。

本文中描述的任何特性或功能的发布和时间安排均由 Elastic 自行决定。 当前不可用的任何特性或功能可能无法按时交付或根本无法交付

更多阅读:

原文:Enrich your Elasticsearch documents with Logstash | Elastic Blog

相关推荐
Elastic 中国社区官方博客2 小时前
为 Elastic Cloud Serverless 和 Elasticsearch 引入统一的 API 密钥
大数据·运维·elasticsearch·搜索引擎·云原生·serverless
vastsmile5 小时前
(R)26.04.23 hermes agent执行本地命令超级慢的原因
开发语言·elasticsearch·r语言
OtIo TALL10 小时前
Java进阶(ElasticSearch的安装与使用)
java·elasticsearch·jenkins
Elastic 中国社区官方博客13 小时前
Jina embeddings v3 现已在 Gemini Enterprise Agent Platform Model Garden 上可用
大数据·人工智能·elasticsearch·搜索引擎·ai·全文检索·jina
前端若水14 小时前
Git 撤销与恢复完全指南(超级详细版)
大数据·git·elasticsearch
tonydf14 小时前
日志模块该如何设计
后端·elasticsearch
前端若水15 小时前
Git 可以做的所有操作(完整分类)
大数据·git·elasticsearch
Elasticsearch15 小时前
我们如何构建 Elasticsearch simdvec,使向量搜索成为世界上最快之一
elasticsearch
搬砖天才、15 小时前
es数据备份
大数据·elasticsearch·jenkins
aXin_ya15 小时前
微服务第六天 es继续了解
大数据·elasticsearch·搜索引擎