Elasticsearch 动态字段映射和 JSON 点符号
Elasticsearch Dynamic Field Mapping and JSON Dot Notation
我正在尝试将日志从 Kubernetes 集群写入 Elasticsearch 索引。 Fluent-bit 用于读取 stdout,它使用元数据(包括 pod 标签)丰富了日志。一个简化的示例日志对象是
{
"log": "This is a log message.",
"kubernetes": {
"labels": {
"app": "application-1"
}
}
}
问题是部署到集群的一些其他应用程序具有以下格式的标签:
{
"log": "This is another log message.",
"kubernetes": {
"labels": {
"app.kubernetes.io/name": "application-2"
}
}
}
这些应用程序是通过 Helm 图表安装的,较新的应用程序遵循 here. The naming convention for labels and selectors was updated in Dec 2018, seen here 中规定的标签和选择器约定,并非所有图表都已更新以反映这一点。
这样做的最终结果是,根据哪种类型的标签格式首先使其成为弹性索引,尝试发送另一种类型的标签格式将抛出映射异常。如果我创建一个新的空索引并首先发送命名空间标签,尝试记录简单的 app
标签将抛出此异常:
object mapping for [kubernetes.labels.app] tried to parse field [kubernetes.labels.app] as object, but found a concrete value
相反的情况,第二次发布命名空间标签,导致此异常:
Could not dynamically add mapping for field [kubernetes.labels.app.kubernetes.io/name]. Existing mapping for [kubernetes.labels.app] must be of type object but found [text].
我怀疑正在发生的事情是 Elasticsearch 将字段名称中的句点视为 JSON 点符号,并试图将其充实为一个对象。我能够找到 this PR from 2015 which explicitly disallows periods in field names however it seems to have been reversed in 2016 with this PR. There is also this multi-year thread 从 2015-2017 讨论这个问题,但我找不到任何最近涉及最新版本的东西。
我目前的想法是对我们使用的 Helm 图表进行标准化,让所有标签都使用相同的约定。这似乎是解决潜在问题的创可贴,但我觉得我在 Elasticsearch 和动态字段映射的配置中遗漏了一些明显的东西。
如有任何帮助,我们将不胜感激。
虽然我个人从未遇到过完全相同的问题,但当我索引一些测试数据并随后更改应该被索引的文档结构时(尤其是 "unflattening" 数据结构时,我遇到了类似的问题).
您对错误消息的解释是正确的。当您第一次索引文档时
{
"log": "This is another log message.",
"kubernetes": {
"labels": {
"app.kubernetes.io/name": "application-2"
}
}
}
由于动态映射,Elasticsearch 会将 app 识别为 object/structure。
然后当您尝试索引文档时
{
"log": "This is a log message.",
"kubernetes": {
"labels": {
"app": "application-1"
}
}
}
先前动态创建的映射将字段 app 定义为具有子字段的对象,但 elasticsearch 遇到具体值,即 "application-1".
我建议您设置一个索引模板来定义正确的映射。对于 'outdated' 日志记录版本,我建议通过 elasticsearch 摄取管道或使用例如Logstash 以正确格式获取文档。
希望对您有所帮助。
我选择使用带有 rename
选项的 Logstash mutate 过滤器,如下所述:
最终结果看起来像这样:
filter {
mutate {
'[kubernetes][labels][app]' => '[kubernetes][labels][app.kubernetes.io/name]'
'[kubernetes][labels][chart]' => '[kubernetes][labels][helm.sh/chart]'
}
}
我正在尝试将日志从 Kubernetes 集群写入 Elasticsearch 索引。 Fluent-bit 用于读取 stdout,它使用元数据(包括 pod 标签)丰富了日志。一个简化的示例日志对象是
{
"log": "This is a log message.",
"kubernetes": {
"labels": {
"app": "application-1"
}
}
}
问题是部署到集群的一些其他应用程序具有以下格式的标签:
{
"log": "This is another log message.",
"kubernetes": {
"labels": {
"app.kubernetes.io/name": "application-2"
}
}
}
这些应用程序是通过 Helm 图表安装的,较新的应用程序遵循 here. The naming convention for labels and selectors was updated in Dec 2018, seen here 中规定的标签和选择器约定,并非所有图表都已更新以反映这一点。
这样做的最终结果是,根据哪种类型的标签格式首先使其成为弹性索引,尝试发送另一种类型的标签格式将抛出映射异常。如果我创建一个新的空索引并首先发送命名空间标签,尝试记录简单的 app
标签将抛出此异常:
object mapping for [kubernetes.labels.app] tried to parse field [kubernetes.labels.app] as object, but found a concrete value
相反的情况,第二次发布命名空间标签,导致此异常:
Could not dynamically add mapping for field [kubernetes.labels.app.kubernetes.io/name]. Existing mapping for [kubernetes.labels.app] must be of type object but found [text].
我怀疑正在发生的事情是 Elasticsearch 将字段名称中的句点视为 JSON 点符号,并试图将其充实为一个对象。我能够找到 this PR from 2015 which explicitly disallows periods in field names however it seems to have been reversed in 2016 with this PR. There is also this multi-year thread 从 2015-2017 讨论这个问题,但我找不到任何最近涉及最新版本的东西。
我目前的想法是对我们使用的 Helm 图表进行标准化,让所有标签都使用相同的约定。这似乎是解决潜在问题的创可贴,但我觉得我在 Elasticsearch 和动态字段映射的配置中遗漏了一些明显的东西。
如有任何帮助,我们将不胜感激。
虽然我个人从未遇到过完全相同的问题,但当我索引一些测试数据并随后更改应该被索引的文档结构时(尤其是 "unflattening" 数据结构时,我遇到了类似的问题).
您对错误消息的解释是正确的。当您第一次索引文档时
{
"log": "This is another log message.",
"kubernetes": {
"labels": {
"app.kubernetes.io/name": "application-2"
}
}
}
由于动态映射,Elasticsearch 会将 app 识别为 object/structure。
然后当您尝试索引文档时
{
"log": "This is a log message.",
"kubernetes": {
"labels": {
"app": "application-1"
}
}
}
先前动态创建的映射将字段 app 定义为具有子字段的对象,但 elasticsearch 遇到具体值,即 "application-1".
我建议您设置一个索引模板来定义正确的映射。对于 'outdated' 日志记录版本,我建议通过 elasticsearch 摄取管道或使用例如Logstash 以正确格式获取文档。
希望对您有所帮助。
我选择使用带有 rename
选项的 Logstash mutate 过滤器,如下所述:
最终结果看起来像这样:
filter {
mutate {
'[kubernetes][labels][app]' => '[kubernetes][labels][app.kubernetes.io/name]'
'[kubernetes][labels][chart]' => '[kubernetes][labels][helm.sh/chart]'
}
}