从Cold Storage中预热日志文件的步骤

94 阅读10分钟

日志在一个应用程序的生命周期中扮演着几个角色。它们帮助调试问题--有些给我们分析数据(如访问日志)--并帮助提供一个应用程序在特定时刻的状态概览。这需要不同的访问和可视化模式,而且,由于每种结果所涉及的不同行为,我们往往最终使用不同的工具。比如说。

  • 对于实时跟踪日志,我们使用Loki
  • 对于复杂的可视化,我们更喜欢Elasticsearch,以及
  • 归档日志,我们更喜欢对象存储(如Amazon S3)。

然而,在有些情况下,我们需要回顾过去,并且只将日志归档为gzipped文本文件。当然,一定有更好的方法来运行分析。那么解决方案是什么呢:在这些工具之间传输日志!在这篇博客中,我们将讨论一个简单的方案,如何将日志从S3桶转移到Elasticsearch。

这里描述的功能将包含在即将发布的One Eye 0.4.0版本中。

从哪里开始? 🔗︎

最明显的解决方案是编写小型脚本来获取、转换和摄取日志。然而,这些脚本很少可以重复使用,而且需要作者付出相当大的努力。

尝试编写脚本 🔗︎

假设我们在S3桶中已经有了一堆压缩的日志。我们可以使用aws-cli 来浏览和下载这些档案。在我们得到原始日志之后,我们可以用一个简单的curl 命令将它们摄入Elasticsearch。让我们来看看这在实践中是如何运作的吧!

列出一个桶中的特定键 🔗︎

aws s3 ls s3://my-bucket --recursive | grep 'terms'

从S3取回日志 🔗︎

aws s3 cp s3://my-bucket/my-key - | gzip -d > raw.logs

向elasticsearch发送批量索引请求 🔗︎

curl -XPOST elasticsearch:port/my_index/_bulk -d 
{"index":{}}
{"stream":"stderr","logtag":"F","message":"I0730 09:31:05.661702       1 trace.go:116] Trace[736852026]: \"GuaranteedUpdate etcd3\" type:*v1.Endpoints ...}}
{"index":{}}
{"stream":"stderr","logtag":"F","message":"some other log ...}}

注意:Elasticsearch的批量请求需要一个以换行符分隔的JSON!

这种方法的唯一问题是,你需要从原始日志中创建一个Elasticsearch有效载荷。这听起来很简单,但要做到这一点,你需要添加元数据,如索引,等等。这仍然是一个可行的方法,但现在让我们跳过这部分。

优点。

缺点。

  • 过滤日志的潜力有限
  • 将日志转换为Elasticsearch的批量请求格式
  • 使其可重复使用需要相当大的努力

使用AWS Lambda 🔗︎

最后一个例子其实更像是一个临时性的解决方案。有更多标准化的方法来处理这个问题。其中一个更复杂的方法是使用AWS Lambda。我们不会深入挖掘这种解决方案,因为已经有大量关于它的优秀文章,你可以在这里找到。

优点。

缺点。

  • 限于亚马逊
  • 使其可重复使用是一个相当大的努力

用Athena改善过滤功能 🔗︎

你可能知道,有一个专门用于在Amazon S3桶上运行查询的服务,叫做Amazon Athena。Athena可以成为帮助过滤你的日志的一个伟大的工具:它允许你用一个简单的SQL查询来查询S3对象存储。

亚马逊也有一个独立的服务来处理ETL(提取转换负载)工作。这是我们所描述的问题的一般化版本。你可以在官方文档中阅读更多关于它的内容。

一目了然 🔗︎

正如你所看到的,有几种方法可以在云中完成这个工作。它们中的大多数都是随用随付的服务,深深地融入了云供应商的产品中。这意味着,在内部或不同的环境中使用它们,是一个真正的挑战。One Eye是一个商业产品,为Kubernetes监控提供开箱即用的解决方案,以你已经喜欢使用的开源软件为中心。根据我们的用户需求,我们确定了以下关键功能。

  • 处理向对象存储摄取日志和从对象存储重新加载日志的问题
  • 基本的时间和元数据过滤(集群、命名空间、吊舱、容器)。
  • 易于使用的用户界面
  • 自动完成从零到仪表盘的整个过程

让我们看一个例子 🔗︎

第一步,我们还没有触及,就是把日志保存到对象存储中。有一个定义明确的格式来摄取这些日志是极其重要的。原因是我们不希望随便使用任何索引来映射我们存储的日志,以及存储在哪里。在这种情况下,我们要完全依靠对象键路径来存储元数据。

Logging Operator的S3输出 🔗︎

One Eye部署了Logging Operator,它负责处理日志运输。

如果你对Logging Operator不熟悉,请阅读我们的介绍博文

不谈细节,Logging Operator配置了一个fluentd S3输出。你可以在插件中定义s3_object_key_format 作为一个参数。这种格式可以包含您日志中的任何元数据。我们的参考格式如下。

${clustername}/%Y/%m/%d/${$.kubernetes.namespace_name}/${$.kubernetes.pod_name}/${$.kubernetes.container_name}/%H:%M_%{index}.%{file_extension}

上面的例子翻译成这样的内容。

one-eye-cluster/2020/07/30/kube-system/kube-apiserver-kind-control-plane/kube-apiserver/09:00_0.gz

这种密钥格式仍然是人类可读的,但也提供了通过密钥过滤的机会,通过上面采用的术语。存储在块中的日志是经过gzip压缩的。块的大小取决于S3输出的timekey 设置,gzip格式支持流式数据处理。

One Eye Log Restoration 🔗︎

让我们来看看UI中的过滤功能,这比较容易理解。你首先要选择的是你要检索的日志的时间范围。

one-eye-ui-datetime

在接下来的步骤中,有几个选项。如果你已经知道你要加载日志的表达式,使用Advanced 选项,然后写出你的查询。如果你需要帮助建立你的表达式,我们建议你使用Simple 表达式生成器。

image.png

UI会根据你选择的时间范围,列出对象模式。这有助于缩小查询的范围。如果你不想缩小范围,使用any 选项。你也可以使用Go regexp来缩小你的搜索范围。

image.png

一旦你完成了你的查询表达式,你会看到一个摘要页,上面有你的选择。我们建议你审查两次,因为搜刮有许多对象的巨大范围可能需要大量的时间。

image.png

最后一步是提交工作。然后你会得到一个状态栏,告诉你搜刮你的归档日志的工作是如何进行的。

image.png

image.png

工作完成后,你可以导航到Kibana仪表盘来探索你的预热日志。

日志恢复的深度 🔗︎

这看起来很简单,不是吗?嗯,在引擎盖下,它有点儿复杂。让我们来看看日志恢复子系统的架构。

image.png

  • S3输出将日志保存到给定的S3桶中,并有一个预先定义的对象键,它以这样的方式结构数据,使日志恢复能够进行过滤。
  • 通过定义集群的唯一名称,可以将多个集群的日志保存到同一个S3桶中,这意味着跨集群的日志也可以从同一个桶中检索。然而,一个作业目前只限于一个集群。
  • One Eye UI 为启动/管理/定制日志恢复工作提供了一个开箱即用的解决方案。
  • One Eye Backend 将日志恢复工作作为Kubernetes工作启动,并轮询Prometheus端点以监控其进度。它还启动Elasticsearch和Kibana的目标实例。
  • Log-restoration job 将数据传输到新初始化的Elasticsearch实例,并公开Prometheus端点以进行刮擦。

筛选 🔗︎

你可以根据关键属性设置各种过滤器来定制你要恢复的日志子集。一个过滤器由多个criteria 。一个标准是一个过滤器表达式,包含命名空间、荚和容器。每个属性都表示为一个RE2正则表达式。空的过滤器和标准被解释为.*

Golang RE2 语法

修复工作的API表示。

{
  "cluster": "one-eye-cluster",
  "from": "2020-08-03T16:27:51.69398+00:00",
  "to": "2020-08-14T16:27:51.693982+00:00",
  "filters": [
    {
      "namespaces": [
        ".*"
      ],
      "pods": [
        ".*-kb-.*",
        "elastic-operator.*"
      ],
      "containers": [
        ""
      ]
    },
    {
      "namespaces": [],
      "pods": [],
      "containers": [
        "fluent.*"
      ]
    }
  ]
}

我们知道这乍一看有点复杂,这就是为什么我们在用户界面中加入了表达式生成器。通过API,你可以将工作自动化,也可以将其整合到CICD系统中。

Log-restoration job 🔗︎

我们设计这个系统是为了尽可能地利用Kubernetes的天然优势。恢复工作实际上是一个Kubernetes工作。该作业暴露了Prometheus指标,这些指标都符合One Eye生态系统的要求。日志恢复是一个独立的二进制文件,它的实现方式是使用一个模块链来处理日志。这种方式的代码是可重复使用的,容易维护,也容易扩展。 image.png

我们所做的,符合UNIX的理念:"做一件事,并把它做好",所以这项工作由链上的几个模块组成。这样一来,这些模块与数据相对独立,可以在多种情况下使用。

  • S3 downloader:从Amazon S3对象存储中下载所有对象,并将数据作为流转发。

  • gzip extractor:将流式数据从gzip中解压。

  • uniform logs:将有效载荷转换为通用的日志格式,这可以确保此后使用的模块将收到一个标准化的格式。

  • dedot filter:记录可能有限制的键,比如Elasticsearch如何解释点作为层次结构的分隔符;这个模块只是用下划线替换点。

  • NDJSON formatter:将日志行转换为NDJSON格式。(Elasticsearch批量输入需要)

  • Bulk buffer:缓冲模块,接收NDJSON行,只有在超过目标缓冲区大小(或缓冲区被刷新)时才转发数据。

  • elasticsearch bulker:调用Elasticsearch Bulk API端点。

  • Reader:模块链,从一组对象键中生成统一的日志格式。

  • Writer:将统一的日志传输到一个端点的模块链。

    让我们假设,你想创建一个Amazon S3 到一个Sumologic log-restoration job。在这种情况下,实现只需要在一个Writer 链中编写几个模块。然而,单个组件--如dedot filter --可以被重复使用。只有格式化器和传输器组件需要被实现。

  • meta stream:所有的模块都能够将元数据报告到一个流中,这个流在日志恢复过程中被处理。这个元数据包含日志、Prometheus指标和进度报告,但有可能扩展到包含更多内容。

该作业的第一个版本是一个单线程的应用程序,将数据从reader 转移到writer 组件。在未来的版本中,我们将添加一个异步传输层来处理多个readerswriters ,这将提高高流量性能。

下一步是什么? 🔗︎

第一个版本的日志修复只支持S3和Elasticsearch作为后端。我们在可行的地方使用了标准的Go绑定。

虽然日志修复组件是相当新的,但我们已经有了几个关于如何扩展它的想法。

  • 异步读写器链
  • 支持其他对象存储,如GCS、Azure Storage
  • 支持其他端点,如fluentd、Kafka等。