此博客文章是系列文章的一部分
简介
在这篇文章中,我们将继续我们的系列,重点介绍如何使用 Fluent Bit 将日志数据发送到 ClickHouse Cloud,特别是 Kubernetes 日志。随着 ClickHouse 越来越受欢迎地成为接收日志的后端,而 Kubernetes 几乎成为容器编排和软件部署的通用标准,Fluent Bit 提供了一种简单且开箱即用的方式来连接这些技术。我们将演示如何部署 FluentBit 以进行 Kubernetes 日志收集,以及关于 ClickHouse 中日志数据模式设计的一些简单建议。
环境
- AWS Kubernetes Service v1.23.8
- Fluent Bit v1.9.9
对于 ClickHouse,我们建议尝试我们的无服务器 ClickHouse Cloud,它提供慷慨的免费试用,足以完成这篇博文。或者,所有说明应与 22.6 以上的自管理版本兼容。
Kubernetes 日志
在我们的上一篇文章中,我们将原始 Nginx 日志发送到 ClickHouse。这里我们将看看更高级的 Kubernetes 日志用例。Fluent Bit 的好处之一是,除了在摄取期间将日志解析为众所周知的格式外,我们还可以使用对操作员和从业人员在搜索特定问题时有用的上下文来丰富日志。
例如,对于 Kubernetes,Fluent Bit 可以与 Kubernetes API 通信,并使用命名空间、pod 和其他重要信息来丰富每条日志消息。下图展示了这一点。
修改 Helm Chart
为了部署在 Kubernetes 之上,我们将从开源存储库 https://github.com/fluent/helm-charts 获取 Fluent Bit Helm chart。
wget https://github.com/fluent/helm-charts/releases/download/fluent-bit-0.20.9/fluent-bit-0.20.9.tgz
tar -xzvf fluent-bit-0.20.9.tgz
cd fluent-bit
我们将修改 values.yaml 文件,位于 fluent-bit
文件夹的根目录中,在两个关键位置。这些更改将用于制定我们的 Fluent Bit 配置。Helm chart 本身将在每个负责日志收集的节点上部署一个 daemonset。
在 filters
键下,我们将添加与我们之前的文章相同的 nest filter,以将所有字段移动到 log
列下。
虽然 JSON 类型非常适合日志的动态部分,但我们目前无法使用它创建的列作为主键。主键是加速 ClickHouse 中查询性能的关键组成部分,并且应大致匹配我们最有可能应用过滤器的列。为了举例说明,我们稍后将创建一个表,其主键位于 host
、pod_name
和 timestamp
字段上 - 按主机、pod 和时间戳过滤日志似乎是 Kubernetes 日志作为第一遍的合理常用模式。显然,这可能因您的用例和典型的诊断路径而异,我们鼓励用户阅读关于优化和配置主键的 此处。要将这些列用作主键,我们必须将它们从 JSON log
字段移动到消息的根目录。为了实现这一点,我们使用 lua filter。目前这是必要的,因为 nest filter 的 lift 功能不是选择性的,并且 modify filter 很遗憾 不支持嵌套字段。为了实现这一点,我们在 values.yaml 文件中设置 luaScripts
键
luaScripts:
functions.lua: |
function set_fields(tag, timestamp, record)
record['host'] = record['log']['kubernetes']['host']
record['log']['kubernetes']['host'] = nil
record['pod_name'] = record['log']['kubernetes']['pod_name']
record['log']['kubernetes']['pod_name'] = nil
return 2, timestamp, record
end
因此,我们的过滤器配置变为
## https://docs.fluentbit.io/manual/pipeline/filters
filters: |
[FILTER]
Name kubernetes
Match kube.*
Merge_Log On
Keep_Log Off
K8S-Logging.Parser On
K8S-Logging.Exclude On
[FILTER]
Name nest
Match *
Operation nest
Wildcard *
Nest_under log
[FILTER]
Name lua
Match *
script /fluent-bit/scripts/functions.lua
call set_fields
在 Output 部分,我们将用 ClickHouse HTTP 输出替换默认的 Elasticsearch 配置。请务必将 host
、port
和 http_passwd
参数替换为您的 ClickHouse Cloud 设置。提醒一下,用户可以从 ClickHouse Cloud 服务的连接设置中访问 HTTP 设置。
注意:我们使用了单独的表 kube
来存储数据,而不是我们之前的文章中使用的原始 jsonlogs
表。我们在下面创建它。
## https://docs.fluentbit.io/manual/pipeline/outputs
outputs: |
[OUTPUT]
name http
tls on
match *
host <YOUR CLICKHOUSE CLOUD HOST>
port 8443
URI /?query=INSERT+INTO+fluentbit.kube+FORMAT+JSONEachRow
format json_stream
json_date_key timestamp
json_date_format epoch
http_user default
http_passwd <YOUR PASSWORD>
完整示例配置的副本可以在此处找到。
创建表
为了准备日志,我们需要在 ClickHouse 中创建表。
如果您尚未在本系列的上一篇文章中创建数据库
CREATE DATABASE fluentbit
创建数据库后,我们需要通过实验性标志 allow_experimental_object_type
启用 JSON 对象类型,或者在 ClickHouse Cloud 中打开支持案例
SET allow_experimental_object_type = 1
设置完成后,我们可以使用提供的结构创建表。请注意我们如何通过 ORDER BY 子句指定我们的主键。显式声明消息根目录上的 host
和 pod_name
列,而不是依赖 ClickHouse 将它们动态推断为 JSON 列中简单的 String
,这使我们能够更严格地定义它们的类型 - 对于两者,我们都使用 LowCardinality(String),由于减少了 IO,从而提高了它们的压缩率和查询性能。我们创建常用的 log
列,其中将包含消息中的任何其他字段。
CREATE TABLE fluentbit.kube ( timestamp DateTime, log JSON, host LowCardinality(String), pod_name LowCardinality(String) ) Engine = MergeTree ORDER BY tuple(host, pod_name, timestamp)
创建完成后,我们可以部署 Fluent Bit 以发送我们的 Kubernetes 日志。
应用 Helm Chart
我们现在可以使用 fluent-bit
目录中的以下命令部署 helm chart
helm install . --generate-name
NAME: chart-1666796050
LAST DEPLOYED: Wed Oct 26 15:54:11 2022
NAMESPACE: default
STATUS: deployed
REVISION: 1
要确认安装成功,请列出默认命名空间中的 pod。请注意,您的命名空间和响应在生产环境中可能会有所不同。
kubectl get pods
NAME READY STATUS RESTARTS AGE
chart-1666796050-fluent-bit-bczgc 1/1 Running 0 65s
chart-1666796050-fluent-bit-mw27h 1/1 Running 0 65s
几分钟后,我们应该开始看到日志开始流入 ClickHouse。从 clickhouse-client 中,我们执行一个简单的 SELECT。请注意,需要 FORMAT
选项才能以 JSON 格式返回行,并且我们专注于可以从中提取主机和 pod_name 的日志消息。
SET output_format_json_named_tuples_as_objects = 1 SELECT * FROM fluentbit.kube LIMIT 10 FORMAT JSONEachRow
clickhouse-cloud :) SELECT * FROM fluentbit.kube WHERE host != '' AND pod_name != '' LIMIT 2 FORMAT JSONEachRow SELECT * FROM fluentbit.kube WHERE (host != '') AND (pod_name != '') LIMIT 1 FORMAT JSONEachRow
{
"timestamp": "2022-10-26 15:13:41",
"log": {
"kubernetes": {
"annotations": {
"checksum/config": "9787019d9ab49da594ab2636487dd89fbe22cc819fa100b97534277015b9a22d",
"checksum/luascripts": "84ee9e1eee2352af076ebec7a96ff7bcfd6476d4da3aa09c7c02c3b2902a768f",
"kubernetes.io/psp": "eks.privileged"
},
"container_hash": "",
"container_image": "cr.fluentbit.io/fluent/fluent-bit:1.9.9",
"container_name": "fluent-bit",
"docker_id": "80c28b724a3c18e5847c887d5a1d5f7df5bf1335b0b49e50e82fd6a43d4f7131",
"labels": {
"app.kubernetes.io/instance": "chart-1666797215",
"app.kubernetes.io/name": "fluent-bit",
"controller-revision-hash": "96df88b78",
"k8s-app": "",
"pod-template-generation": "1"
},
"namespace_name": "default",
"pod_id": "388d2e57-1239-45cc-9b63-3a69b96050ac"
},
"log": "[2022/10/26 15:13:41] [error] [output:http:http.0] qm5u2tm7n9.us-east-2.aws.clickhouse.cloud:8443, HTTP status=404\n",
"stream": "stderr",
"time": "2022-10-26T15:13:41.445772997Z"
},
"host": "ip-192-168-88-154.us-east-2.compute.internal",
"pod_name": "chart-1666797215-fluent-bit-blz9c"
}
我们现在可以像分析 Nginx 访问日志一样分析这些日志,例如,问题“每个 Kubernetes 命名空间发送了多少日志?”可以使用简单的 GROUP BY 回答
clickhouse-cloud :) SELECT count(), namespace FROM fluentbit.kube GROUP BY log.kubernetes.namespace_name AS namespace ┌─count()─┬─namespace───┐ │ 12 │ │ │ 2680 │ default │ │ 14 │ kube-system │ └─────────┴─────────────┘ 3 rows in set. Elapsed: 0.006 sec. Processed 2.71 thousand rows, 43.27 KB (453.55 thousand rows/s., 7.25 MB/s.)
可视化 Kubernetes 数据
在我们的早期文章中,我们使用了 Grafana 的 Explore 功能来可视化我们的日志。虽然这对探索性分析很有用,但用户通常更喜欢仪表板日志并将这些日志链接到其他来源,例如跟踪和指标。使用上述数据集,我们可以构建一个非常简单的仪表板来可视化 Kubernetes 集群的状态。此仪表板可以从 此处下载,并按照如下所示 导入到 Grafana - 请注意仪表板 ID 17284
。只读版本也发布在 此处。请随意扩展和丰富您的数据,并分享您的结果!请注意,为了本仪表板的目的,我们将主机等同于 Kubernetes 节点。有关使用 ClickHouse 官方 Grafana 插件的更多详细信息,请参阅 此处。
最佳实践
ClickHouse 中新的 JSON 设置的初始好处是我们不必指定嵌套 JSON 的模式;一切都开箱即用。日志中出现的任何新字段都会自动在 ClickHouse 中创建新列。我们鼓励用户仅将此功能用于动态列。对于预期在每条消息上并且类型已知的字段,我们鼓励用户将这些字段从 JSON 字段提升到根目录并显式定义其类型。此外,尽可能删除未使用的字段以最大限度地减少存储并避免列爆炸。因此,您的 lua 脚本在任何生产环境中可能会长得多。除了允许更显式的类型,例如,更低精度的整数,以获得更好的性能外,用户还可以利用编解码器进行压缩。
注意:JSON 对象类型是实验性的,并且正在改进。我们关于此功能的建议正在不断发展,因此在以后的版本中可能会发生变化。
最后,默认情况下,Fluent Bit Helm chart 配置了一个 要刷新的批次,每 1 秒刷新一次。ClickHouse 更喜欢 至少 1000 条记录的批次。您可能希望根据 Kubernetes 集群生成的日志量调整此刷新时间,以 避免常见问题。
结论
在这篇博客中,我们设置了 Fluent Bit 以将 Kubernetes 环境中的日志路由到 ClickHouse 以进行快速分析。我们已经介绍了如何使用新的实验性 JSON 类型来帮助存储日志数据,并提供了一些关于其用法的简单最佳实践。在未来的博客文章中,我们将演示大规模存储日志数据并构建端到端的 Observability 解决方案。
如果您对最新技术充满热情,并且对开源充满热情,我们目前正在为我们的 集成团队招聘,并期待您的来信。