此博文是系列文章的一部分
简介
在本篇文章中,我们将继续 我们的系列文章,重点介绍如何使用 Fluent Bit 将 Kubernetes 日志发送到 ClickHouse Cloud。随着 ClickHouse 成为越来越受欢迎的日志接收后端,以及 Kubernetes 几乎成为容器编排和软件部署的普遍标准,Fluent Bit 提供了一种简单且开箱即用的方式来连接这些技术。我们将演示如何在 Kubernetes 中部署 FluentBit 以收集日志,以及一些关于 ClickHouse 中日志数据模式设计的简单建议。
环境
- AWS Kubernetes 服务 v1.23.8
- Fluent Bit v1.9.9
对于 ClickHouse,我们建议您尝试我们的无服务器 ClickHouse Cloud,它提供慷慨的免费试用,足以满足本博文的学习需求。或者,所有说明都应与 22.6 版及更高版本的自托管版本兼容。
Kubernetes 日志
在我们 上一篇文章 中,我们向 ClickHouse 发送了原始的 Nginx 日志。在这里,我们将探讨更高级的 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 过滤器,以将所有字段移动到 log
列下。
虽然 JSON 类型非常适合日志的动态部分,但我们目前无法将它创建的列用作主键。 主键 是加速 ClickHouse 查询性能的关键组成部分,并且应该大致匹配我们最可能在其上应用过滤器的那些列。例如,我们稍后将创建具有 host
、pod_name
和 timestamp
字段主键的表,因为根据主机、Pod 和时间戳过滤日志似乎是 Kubernetes 日志的常见使用模式。显然,这可能因您的用例和典型诊断路径而异,我们鼓励用户阅读有关优化和配置主键的更多信息 此处。为了将这些列用作主键,我们必须将它们从 JSON log
字段移动到消息的根目录。为此,我们使用 lua 过滤器。这目前是必要的,因为 nest 过滤器的 lift 功能不具有选择性,并且 modify 过滤器 遗憾的是 不支持嵌套字段。为此,我们在 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
在 输出部分,我们将替换默认的 Elasticsearch 配置为 ClickHouse HTTP 输出。请确保将 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的探索功能来可视化日志。虽然这对于探索性分析很有用,但用户通常更喜欢将日志制作成仪表板,并将这些日志与其他来源(例如跟踪和指标)链接起来。使用上述数据集,我们可以构建一个非常简单的仪表板来可视化Kubernetes集群的状态。此仪表板可以从这里下载,并导入到Grafana中,如下所示 - 请注意仪表板ID 17284
。一个只读版本也发布在这里。请随意扩展和丰富它,并分享您的结果!请注意,出于此仪表板的目的,我们将主机等同于Kubernetes节点。有关使用ClickHouse的官方Grafana插件的更多详细信息,请参见这里。
最佳实践
ClickHouse中新的JSON设置的初始优势在于我们不必指定嵌套JSON的模式;一切开箱即用。日志中出现的任何新字段都将在ClickHouse中自动为其创建新列。我们鼓励用户仅将此功能用于动态列。对于每个消息中都期望存在的字段,以及已知类型的字段,我们鼓励用户将这些字段从JSON字段提升到根并显式定义其类型。此外,尽可能删除未使用的字段以最大程度地减少存储,并避免列爆炸。因此,在任何生产环境中,您的Lua脚本都可能要长得多。除了允许更明确的类型化(例如,更低精度的整数)以获得更好的性能外,用户还可以利用编解码器进行压缩。
注意:JSON对象类型处于实验阶段,并且正在改进中。我们对该功能的建议正在不断发展,因此可能会在以后的版本中更改。
最后,默认情况下,Fluent Bit Helm图表配置刷新批处理每1秒。ClickHouse更喜欢至少1000条记录的批处理。您可能希望根据Kubernetes集群生成的日志量调整此刷新时间,以避免常见问题。
结论
在本博文中,我们设置了Fluent Bit将日志从Kubernetes环境路由到ClickHouse,以便进行快速分析。我们已经介绍了如何使用新的实验性JSON类型来帮助存储日志数据,并提供了一些关于其用法的简单最佳实践。在未来的博文中,我们将演示如何大规模存储日志数据以及构建端到端的可观察性解决方案。
如果您对最新技术充满热情,并且对开源充满热情,我们目前正在为我们的集成团队招聘,并期待您的加入。