Bahubali Shetti

使用 Elastic 可观测性日志和 OpenAI 洞察 Kubernetes 错误

这篇博文提供了一个示例,说明如何使用 Elasticsearch 中的 ChatGPT(通过 Elasticsearch 使用 OpenAI API)分析错误消息。

阅读时间:25分钟
Gain insights into Kubernetes errors with Elastic Observability logs and OpenAI

正如我们在之前的博文中所展示的,Elastic® 提供了一种方法来摄取和管理来自 Kubernetes 集群 和在其上运行的 应用程序 的遥测数据。Elastic 提供开箱即用的仪表板,有助于跟踪指标、 日志管理和分析APM 功能(也支持 原生 OpenTelemetry),以及使用 AIOps 功能机器学习 (ML) 分析所有内容的能力。虽然您可以使用预先存在的 Elastic 中的 ML 模型开箱即用的 AIOps 功能 或您自己的 ML 模型,但仍然需要更深入地挖掘问题的根本原因。

Elastic 有助于减少运营工作,以支持更高效的运营,但用户仍然需要一种方法来调查和了解从问题原因到特定错误消息含义的所有内容。作为一名运维人员,如果您以前从未遇到过某个特定错误,或者它不属于某个运行手册的一部分,您可能会访问 Google 并开始搜索相关信息。

OpenAI 的 ChatGPT 正在成为一种有趣的生成式 AI 工具,它有助于使用其背后的模型提供更多信息。如果您可以在生产或开发环境中使用 OpenAI 获取更深入的见解(甚至简单的语义)来分析错误,那将会怎么样?您可以轻松地将 Elastic 与 OpenAI 的 API 关联起来以实现此目的。

Kubernetes 是大多数部署(本地或云服务提供商)中的支柱,需要大量的专业知识——即使这种专业知识只是用于管理像 GKE、EKS 或 AKS 这样的服务。

在本博文中,我将介绍如何使用 Elastic 的 Watcher 功能将 Elastic 连接到 OpenAI,并要求它提供有关 Elastic 从 Kubernetes 集群摄取的错误日志的更多信息。更具体地说,我们将使用 Azure 的 OpenAI 服务。Azure OpenAI 是微软和 OpenAI 之间的合作关系,因此 OpenAI 中的相同模型也可在 Microsoft 版本中使用。

虽然本博文介绍了一个具体的示例,但它可以修改为适用于 Elastic 在日志中接收的其他类型的错误。无论是来自 AWS、应用程序、数据库等,本博文中描述的配置和脚本都可以轻松修改。

先决条件和配置

如果您打算遵循本博文,以下是一些我们用于设置配置的组件和详细信息

  • 确保您在 Elastic Cloud 上拥有一个帐户,并已部署一个堆栈(请参阅此处的说明)。
  • 我们使用了一个 GCP GKE Kubernetes 集群,但您可以使用您选择的任何 Kubernetes 集群服务(本地或基于云)。
  • 我们还在运行 OpenTelemetry 演示的某个版本。有关将 Elastic 与 OpenTelemetry 演示一起使用的说明,请参阅 此处
  • 我们还有一个 Azure 帐户和 已配置的 Azure OpenAI 服务。您需要从 Azure 获取相应的令牌,以及从 Azure 的 OpenAI 服务获取正确的 URL 终结点。
  • 我们将使用 Elastic 的开发工具(具体来说是控制台)加载并运行脚本,该脚本是 Elastic Watcher
  • 我们还将添加一个新的索引来存储来自 OpenAI 查询的结果。

这是我们将在本博文中设置的配置

在逐步完成设置的过程中,我们还将提供使用 OpenAI 与 Azure OpenAI 服务的替代设置。

设置所有内容

在接下来的几个步骤中,我将逐步介绍

  • 在 Elastic Cloud 上获取帐户并设置您的 K8S 集群和应用程序
  • 获取 Azure OpenAI 授权(使用 OpenAI 的替代选项)
  • 识别 Kubernetes 错误日志
  • 使用正确的脚本配置 Watcher
  • 比较 Azure OpenAI/OpenAI 与 ChatGPT UI 的输出

步骤 0:在 Elastic Cloud 上创建帐户

按照说明 在 Elastic Cloud 上开始使用

获得 Elastic Cloud 登录后,设置您的 Kubernetes 集群和应用程序。完整的逐步说明博文可在此处获得 此处。这还概述了如何在 Elastic 中查看 Kubernetes 集群指标以及如何使用仪表板监控它们。

步骤 1:Azure OpenAI 服务和授权

当您登录到 Azure 订阅并设置 Azure OpenAI 服务的实例时,您将能够在“管理密钥”下获取密钥。

您的 OpenAI 实例有两个密钥,但您只需要 KEY 1。

此外,您需要获取服务 URL。请参阅上面我们已将服务 URL 隐藏的图片,以了解在哪里获取 KEY 1 和 URL。

如果您不使用 Azure OpenAI 服务而使用标准 OpenAI 服务,则可以在以下位置获取密钥:

**https** ://platform.openai.com/account/api-keys

您需要创建一个密钥并保存它。获得密钥后,您可以转到步骤 2。

步骤 2:识别 Elastic 日志中的 Kubernetes 错误

当您的 Kubernetes 集群运行时,在您的集群上的 Elastic Agent 守护程序集中运行的 Elastic 的 Kubernetes 集成 将日志和指标发送到 Elastic。 遥测数据会被摄取、处理和索引。Kubernetes 日志存储在名为 .ds-logs-kubernetes.container_logs-default-*(* 代表日期)的索引中,并且还预加载了一个自动数据流 logs-kubernetes.container_logs。因此,虽然您可以使用一些开箱即用的仪表板来调查指标,但您也可以查看 Elastic Discover 中的所有日志。

虽然任何来自Kubernetes的错误都可能令人望而生畏,但更细致的问题出现在kube-system命名空间中运行的Pod产生的错误中。以konnectivity agent Pod为例,它本质上是在节点上运行的网络代理,用于帮助建立隧道,是Kubernetes中至关重要的组件。任何错误都可能导致集群出现连接问题,并引发一系列问题,因此了解和排查这些错误非常重要。

当我们过滤出konnectivity agent的错误日志时,可以看到大量的错误。

但不幸的是,我们仍然无法理解这些错误的含义。

这时,我们可以借助OpenAI来更好地理解问题。通常,您会将从Discover中获取的错误消息与问题一起粘贴到ChatGPT中(或在Google上搜索该消息)。

我们遇到但无法理解的一个特定错误是

E0510 02:51:47.138292       1 client.go:388] could not read stream err=rpc error: code = Unavailable desc = error reading from server: read tcp 10.120.0.8:46156->35.230.74.219:8132: read: connection timed out serverID=632d489f-9306-4851-b96b-9204b48f5587 agentID=e305f823-5b03-47d3-a898-70031d9f4768

OpenAI的输出如下

ChatGPT为我们提供了一套关于此rpc错误针对我们的konnectivity-agent出现的原因的不错思路。

那么,当这些错误发生时,我们如何才能自动获得此输出呢?

步骤3:使用正确的脚本配置Watcher

什么是Elastic Watcher? Watcher是Elasticsearch的一项功能,您可以使用它根据条件创建操作,这些条件会使用针对您数据的查询定期进行评估。Watcher对于分析关键任务和业务关键的流数据很有帮助。例如,您可以监视应用程序日志中导致较大操作问题的错误。

配置Watcher后,可以:

  1. 手动触发
  2. 定期运行
  3. 使用UI或脚本创建

在这种情况下,我们将使用脚本,因为我们可以轻松地修改它并在需要时运行它。

我们使用DevTools控制台输入脚本并进行测试。

脚本列在博客末尾的**附录**中。也可以从这里下载

脚本执行以下操作:

  1. 每五分钟连续运行一次。
  2. 它将搜索容器konnectivity-agent的错误日志。
  3. 它将获取第一个错误消息,对其进行转换(重新格式化和清理),并将结果放入变量first_hit中。
"script": "return ['first_hit': ctx.payload.first.hits.hits.0._source.message.replace('\"', \"\")]"
  1. 错误消息将与查询一起发送到OpenAI。
What are the potential reasons for the following kubernetes error:
  { { ctx.payload.second.first_hit } }
  1. 如果搜索产生了错误,它将继续创建索引并将错误消息、pod.name(在我们的设置中为konnectivity-agent-6676d5695b-ccsmx)和OpenAI输出放入名为chatgpt_k8_analyzed的新索引中。

为了查看结果,我们针对新创建的索引创建了一个名为chatgpt_k8_analyzed的新数据视图。

在Discover中,数据视图上的输出为我们提供了错误分析。

对于脚本在五分钟间隔内看到的每个错误,它都会获得错误分析。我们也可以根据需要使用范围来分析特定时间段内的错误。只需相应地修改脚本即可。

步骤4. Azure OpenAI/OpenAI与ChatGPT UI的输出

如您在上面看到的,我们从Azure OpenAI API调用中获得的结果与在ChatGPT UI中测试查询的结果基本相同。这是因为我们配置API调用以运行与UI中选择的模型相同/类似的模型。

对于API调用,我们使用了以下参数:

"request": {
             "method" : "POST",
             "Url": "https://XXX.openai.azure.com/openai/deployments/pme-gpt-35-turbo/chat/completions?api-version=2023-03-15-preview",
             "headers": {"api-key" : "XXXXXXX",
                         "content-type" : "application/json"
                        },
             "body" : "{ \"messages\": [ { \"role\": \"system\", \"content\": \"You are a helpful assistant.\"}, { \"role\": \"user\", \"content\": \"What are the potential reasons for the following kubernetes error: {{ctx.payload.second.first_hit}}\"}], \"temperature\": 0.5, \"max_tokens\": 2048}" ,
              "connection_timeout": "60s",
               "read_timeout": "60s"
                            }

通过将role: system设置为“You are a helpful assistant”并使用gpt-35-turbo URL部分,我们实际上是将API设置为使用davinci模型,这与ChatGPT UI默认设置的模型相同。

此外,对于Azure OpenAI服务,您需要将URL设置为类似以下内容:

https://YOURSERVICENAME.openai.azure.com/openai/deployments/pme-gpt-35-turbo/chat/completions?api-version=2023-03-15-preview

如果您使用OpenAI(而不是Azure OpenAI服务),则请求调用(针对https://api.openai.com/v1/completions)将如下所示:

"request": {
            "scheme": "https",
            "host": "api.openai.com",
            "port": 443,
            "method": "post",
            "path": "\/v1\/completions",
            "params": {},
            "headers": {
               "content-type": "application\/json",
               "authorization": "Bearer YOUR_ACCESS_TOKEN"
                        },
            "body": "{ \"model\": \"text-davinci-003\",  \"prompt\": \"What are the potential reasons for the following kubernetes error: {{ctx.payload.second.first_hit}}\",  \"temperature\": 1,  \"max_tokens\": 512,     \"top_p\": 1.0,      \"frequency_penalty\": 0.0,   \"presence_penalty\": 0.0 }",
            "connection_timeout_in_millis": 60000,
            "read_timeout_millis": 60000
          }

如果您有兴趣创建更多基于OpenAI的版本,您可以下载备用脚本并查看Elastic社区成员的另一篇博文

超越Kubernetes日志获取其他见解

现在脚本已启动并运行,您可以使用不同的内容进行修改:

  • 输入
  • 条件
  • 操作
  • 转换

在此处了解如何修改它一些修改示例可能包括:

  1. 查找来自应用程序组件(例如,cartService、frontEnd、来自OTel演示)、云服务提供商(例如,AWS/Azure/GCP日志)甚至来自Kafka、数据库等组件的错误日志。
  2. 将时间范围从连续运行更改为在特定范围内运行。
  3. 查找日志中的特定错误。
  4. 查询一次分析一组错误,而不是我们演示的单个错误。

修改方式多种多样,当然,您可以使用OpenAI而不是Azure OpenAI服务来运行此操作。

结论

希望您已经了解了Elastic Observability如何帮助您连接到OpenAI服务(如我们所示的Azure OpenAI,甚至OpenAI)以更好地分析错误日志消息,而不是必须运行多个Google搜索并寻找可能的见解。

以下是我们所涵盖内容的快速回顾:

  • 开发一个Elastic Watcher脚本,可用于查找并将Kubernetes错误发送到OpenAI,并将它们插入到新索引中。
  • 使用正确的授权和请求参数配置Azure OpenAI服务或OpenAI。

准备好开始了吗?注册Elastic Cloud并尝试我上面概述的功能和特性,以充分利用您的OpenTelemetry数据并获得最佳可见性。

附录

Watcher脚本

PUT _watcher/watch/chatgpt_analysis
{
    "trigger": {
      "schedule": {
        "interval": "5m"
      }
    },
    "input": {
      "chain": {
          "inputs": [
              {
                  "first": {
                      "search": {
                          "request": {
                              "search_type": "query_then_fetch",
                              "indices": [
                                "logs-kubernetes*"
                              ],
                              "rest_total_hits_as_int": true,
                              "body": {
                                "query": {
                                  "bool": {
                                    "must": [
                                      {
                                        "match": {
                                          "kubernetes.container.name": "konnectivity-agent"
                                        }
                                      },
                                      {
                                        "match" : {
                                          "message":"error"
                                        }
                                      }
                                    ]
                                  }
                                },
                                "size": "1"
                              }
                            }
                        }
                    }
                },
                {
                    "second": {
                        "transform": {
                            "script": "return ['first_hit': ctx.payload.first.hits.hits.0._source.message.replace('\"', \"\")]"
                        }
                    }
                },
                {
                    "third": {
                        "http": {
                            "request": {
                                "method" : "POST",
                                "url": "https://XXX.openai.azure.com/openai/deployments/pme-gpt-35-turbo/chat/completions?api-version=2023-03-15-preview",
                                "headers": {
                                    "api-key" : "XXX",
                                    "content-type" : "application/json"
                                },
                                "body" : "{ \"messages\": [ { \"role\": \"system\", \"content\": \"You are a helpful assistant.\"}, { \"role\": \"user\", \"content\": \"What are the potential reasons for the following kubernetes error: {{ctx.payload.second.first_hit}}\"}], \"temperature\": 0.5, \"max_tokens\": 2048}" ,
                                "connection_timeout": "60s",
                                "read_timeout": "60s"
                            }
                        }
                    }
                }
            ]
        }
    },
    "condition": {
      "compare": {
        "ctx.payload.first.hits.total": {
          "gt": 0
        }
      }
    },
    "actions": {
        "index_payload" : {
            "transform": {
                "script": {
                    "source": """
                        def payload = [:];
                        payload.timestamp = new Date();
                        payload.pod_name = ctx.payload.first.hits.hits[0]._source.kubernetes.pod.name;
                        payload.error_message = ctx.payload.second.first_hit;
                        payload.chatgpt_analysis = ctx.payload.third.choices[0].message.content;
                        return payload;
                    """
                }
            },
            "index" : {
                "index" : "chatgpt_k8s_analyzed"
            }
        }
    }
}

其他日志记录资源

日志的常见用例示例

在本博文中,我们可能使用了第三方生成式AI工具,这些工具由其各自的所有者拥有和运营。Elastic对第三方工具没有任何控制权,对它们的内容、操作或使用,以及因您使用此类工具而可能产生的任何损失或损害不承担任何责任。使用AI工具处理个人、敏感或机密信息时,请务必谨慎。您提交的任何数据都可能用于AI培训或其他用途。我们无法保证您提供的信息会得到安全或保密。在使用任何生成式AI工具之前,您应熟悉其隐私惯例和使用条款。

Elastic、Elasticsearch和相关标记是Elasticsearch N.V.在美国和其他国家/地区的商标、徽标或注册商标。所有其他公司和产品名称是其各自所有者的商标、徽标或注册商标。

经Microsoft许可使用Microsoft产品的屏幕截图。

分享此文章