在 Kubernetes 上用 Fluent Bit 收集 Nginx 日志到 Easysearch
本文基于 k3s + Easysearch 2.0.3 实测验证,从零开始搭建一套完整的日志收集方案。
什么是 Fluent Bit
Fluent Bit 是一个轻量级的日志收集和转发工具,用 C 语言写的,内存占用极低(通常只需要几十 MB)。它的工作很简单:从某个地方读日志(INPUT),可选地处理一下(FILTER),然后发到某个地方(OUTPUT)。
1 | INPUT → FILTER → OUTPUT |
常见用法:
- 从文件读日志(
tail插件,类似tail -f) - 从容器 stdout 读日志
- 发送到 Elasticsearch / Easysearch / Kafka / S3 等
和 Fluentd 的区别:Fluent Bit 更轻量(C 语言 vs Ruby),适合作为 Agent 部署在每个节点或 Pod 里。Fluentd 功能更丰富,适合做日志聚合层。在 Kubernetes 场景下,Fluent Bit 是更常见的选择。
什么是 Easysearch
INFINI Easysearch 是兼容 Elasticsearch API 的搜索引擎。Fluent Bit 的 es(Elasticsearch)输出插件可以直接对接,不需要改配置。简单理解:Easysearch 是 Elasticsearch 的国产替代品。
为什么用 Sidecar 模式
本文用 Sidecar 模式部署 Fluent Bit:把它和 Nginx 放在同一个 Pod 里,共享日志目录。
另一种常见方式是 DaemonSet 模式:在每个节点上跑一个 Fluent Bit,收集该节点上所有 Pod 的 stdout 日志。DaemonSet 适合收集所有 Pod 的日志,Sidecar 适合收集特定应用的日志文件。
1 | Nginx Pod |
环境准备
- k3s 单节点(Ubuntu 24.04,30G 内存)
- Helm 3
- cert-manager(Easysearch 依赖)
安装 k3s
1 | curl -sfL https://get.k3s.io | sh - |
安装 Helm
1 | curl https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-3 | bash |
设置内核参数
Easysearch(基于 Lucene)需要较高的 mmap 限制:
1 | sudo sysctl -w vm.max_map_count=262144 |
第一步:部署 Easysearch
1.1 准备工具
更新helm仓库并且初始化cert-manager。
1 | helm repo add infinilabs https://helm.infinilabs.com |
1.2 创建命名空间和证书
1 | kubectl create namespace es |
1.3 创建密码 Secret
⚠️ 密码必须包含至少 2 类字符(大写/小写/数字/特殊字符),否则初始化会失败,Pod 直接 Exit Code 1 崩溃。
1 | kubectl create secret generic easysearch-secrets \ |
1.4 Helm 安装 Easysearch
使用厂家提供了helm Chart安装Easysearch,并且通过变量设置image的版本,截止目前,最新版本是2.0.3-2534。
1 | helm repo add infinilabs https://helm.infinilabs.com --force-update |
然后就是等 Pod Running:
1 | kubectl get pods -n es -w |
1.5 验证 Easysearch
1 | kubectl port-forward -n es pod/easysearch-0 9200:9200 & |
返回 JSON 集群信息即成功:
1 | { |
如果你的机器内存比较小(< 4G)或者使用其他 storageClassName 需要修改配置:
1 | helm install easysearch infinilabs/easysearch -n es \ |
第二步:部署 Nginx + Fluent Bit Sidecar
这个 YAML 包含三个 Kubernetes 资源(用 --- 分隔):
- ConfigMap:存放 Fluent Bit 的配置文件
fluent-bit.conf - Deployment:定义一个 Pod,里面跑两个容器(nginx + fluent-bit sidecar)
- Service:让集群内其他 Pod 可以通过
http://nginx:80访问 Nginx
创建 nginx-fluentbit.yaml
1 | cat << 'EOF' > nginx-fluentbit.yaml |
部署和删除
1 | # 部署 |
确认 Pod 里两个容器都 Running(READY 2/2):
1 | kubectl get pods |
YAML 逐字段解释
ConfigMap 部分(Fluent Bit 配置):
| 字段 | 值 | 说明 |
|---|---|---|
[SERVICE] Flush |
3 |
每 3 秒把缓冲区的日志刷到 OUTPUT |
Log_Level |
info |
Fluent Bit 自身的日志级别 |
Daemon |
off |
前台运行,容器里必须前台否则容器会退出 |
[INPUT] Name |
tail |
类似 tail -f,实时跟踪文件新增内容 |
Tag |
nginx.access |
给输入打标签,OUTPUT 用 Match 匹配 |
Path |
/var/log/nginx/access.log |
要读的日志文件路径 |
DB |
/var/log/flb_nginx.db |
SQLite 文件,记录读到哪一行了,重启不重复读 |
Mem_Buf_Limit |
5MB |
内存缓冲区上限,防止日志太多撑爆内存 |
Refresh_Interval |
5 |
每 5 秒检查文件是否有新内容 |
[OUTPUT] Name |
es |
Elasticsearch 兼容输出插件,Easysearch 直接可用 |
Match |
* |
匹配所有 Tag 的日志 |
Host |
easysearch.es.svc.cluster.local |
Easysearch 的 Service DNS(集群内访问) |
HTTP_User/Passwd |
admin/Admin123 |
Easysearch 认证信息 |
tls |
Off |
不用 HTTPS(本版本默认 HTTP) |
Logstash_Format |
On |
按日期创建索引,格式:nginx-logs-2026.03.11 |
Logstash_Prefix |
nginx-logs |
索引名前缀 |
Retry_Limit |
False |
发送失败无限重试 |
Suppress_Type_Name |
On |
兼容 ES 7.x+,不发送 _type |
⚠️ Fluent Bit 的 ini 格式不支持行内注释!
Flush 3 # 注释会把3 # 注释整个当成值,导致解析失败。
Deployment 部分(两个容器 + 共享卷):
| 字段 | 说明 |
|---|---|
containers[0]: nginx |
Nginx 容器,处理 HTTP 请求,日志写到 /var/log/nginx/access.log |
containers[1]: fluent-bit |
Sidecar 容器,读取 nginx 的日志文件发送到 Easysearch |
volumeMounts: nginx-logs |
两个容器都挂载这个卷,共享 /var/log/nginx 目录 |
readOnly: true |
Fluent Bit 只读挂载,最小权限原则 |
volumeMounts: config |
把 ConfigMap 挂载为 Fluent Bit 的配置文件目录 |
volumes: nginx-logs (emptyDir) |
临时卷,Pod 内容器共享,Pod 删除后数据丢失 |
volumes: config (configMap) |
引用上面的 ConfigMap |
1 | 同一个 Pod |
关键概念
为什么用 emptyDir?
emptyDir 是一个临时卷,在 Pod 创建时自动创建,Pod 删除时自动清理。它的作用是让同一个 Pod 里的多个容器共享文件。nginx 写日志到这个目录,fluent-bit 从这个目录读日志。
为什么 fluent-bit 挂载时加 readOnly: true?
Fluent Bit 只需要读日志文件,不需要写。加 readOnly 是最小权限原则,防止 Fluent Bit 意外修改日志文件。
Logstash_Format On 是什么意思?
开启后 Fluent Bit 会按日期自动创建索引,格式为 {Logstash_Prefix}-{日期},比如 nginx-logs-2026.03.11。这样每天的日志在不同索引里,方便按时间范围查询和清理旧数据。
DB /var/log/flb_nginx.db 是什么?
Fluent Bit 用这个 SQLite 数据库文件记录”读到文件的哪一行了”。如果 Fluent Bit 重启,它会从上次的位置继续读,不会重复发送已经处理过的日志。
第三步:产生日志并查询
3.1 产生访问日志
1 | kubectl run curl-test --rm -it --restart=Never --image=curlimages/curl -- \ |
3.2 查询 Easysearch
1 | kubectl port-forward -n es pod/easysearch-0 9200:9200 & |
返回结果:
1 | { |

3.3 从本地电脑访问(SSH 隧道)
Easysearch 自带了一个UI,但是跑在远程POD里,部署的Service也是Headless,所以没办法直接访问,我们的办法是先用kubectl port-forward把pod端口映射到服务器宿主机,然后再用 SSH 隧道把 9200 端口映射到本地:
1 | # 远程服务器上先跑 port-forward |
这样转发之后,我们也可以使用本机浏览器直接访问Easysearch自带的UI了。
踩坑记录
1. 密码复杂度(最常见的坑)
Easysearch 最新版本要求密码至少包含 2 类字符,不满足时 Pod 直接崩溃(Exit Code 1),日志来不及写,非常难排查。
1 | # ❌ 纯小写 |
2. vm.max_map_count
不设置的话 Easysearch 启动失败:
1 | sudo sysctl -w vm.max_map_count=262144 |
3. 小内存环境 OOMKilled
默认 JVM 堆 1G + 堆外内存,总共需要 2G+。小集群用:
1 | --set "javaOpts=-Xms256m -Xmx256m" |
4. StorageClass 不匹配(AWS 环境)
我的自建集群没有默认 StorageClass 是 gp2,需要指定:
1 | --set storageClassName=gp2 |
如果环境自带 local-path-provisioner,不需要指定。
5. Fluent Bit 配置不支持行内注释
Fluent Bit 的 ini 格式不支持行内注释,# 后面的内容会被当成值的一部分:
1 | # ❌ 错误:行内注释会被当成值 |
排查命令
1 | # Pod 状态 |
清理
1 | # 删除 Nginx + Fluent Bit |
在 Kubernetes 上用 Fluent Bit 收集 Nginx 日志到 Easysearch


