记录一次测试环境 nginx-only Filebeat 采集 Ingress access.log 写入 Kafka 数量偏少的排查过程。核心问题不是 ConfigMap 内容不一致,而是 DaemonSet 调度范围与 Ingress Controller 实际运行节点不一致。
1. 背景
测试环境单独部署了一套 nginx-only Filebeat,用于采集 Ingress access.log,按域名与路径过滤后写入 Kafka topic:
yaml
output.kafka:
topic: 'sample_nginx_log_c'
Filebeat 的 ConfigMap 中按如下规则过滤:
- 域名:
sdk-test.example.com - 请求路径:
/sdk/ad/get、/sdk/ad/init、/sdk/ad/setting - 采集路径:
/data/nginx_logs/*/access.log、/alllogs/nginx_logs/*/access.log
研发反馈现象是:部分日志能采集到,部分日志采集不到,Kafka 中数量明显少于预期。
2. 初步判断
一开始怀疑点有几个:
- Filebeat ConfigMap 过滤规则是否写错,导致部分请求被
drop_event丢弃。 - Kafka 输出是否异常,例如 publish 失败、队列满、broker 不可达。
- Filebeat registry offset 是否导致历史日志未补采。
- DaemonSet 是否没有覆盖所有产生 Ingress access.log 的节点。
由于该 nginx-only Filebeat 的所有 Pod 使用同一个 ConfigMap,如果是 ConfigMap 内容本身错误,理论上所有节点都会表现一致;但实际是"部分有、部分没有",因此优先排查 DaemonSet 节点覆盖范围。
3. 关键排查过程
3.1 查看 nginx-only Filebeat 原始调度规则
导出的 DaemonSet 中发现原来配置了固定节点选择器:
yaml
nodeSelector:
app: log-node
当时 DaemonSet 状态为:
text
desiredNumberScheduled: 5
numberReady: 5
说明这套 nginx-only Filebeat 只跑在 5 个带 app=log-node 标签的节点上。
3.2 查看 app=log-node 节点
bash
kubectl --kubeconfig config_test get nodes -l app=log-node -o wide
结果只有 5 个节点:
text
10.0.0.11
10.0.0.12
10.0.0.13
10.0.0.14
10.0.0.15
3.3 查看 Ingress Controller 实际所在节点
bash
kubectl --kubeconfig config_test -n ingress-nginx get pod -o wide | grep ingress
Ingress Controller 实际分布在以下节点:
text
10.0.1.11
10.0.1.12
10.0.0.12
10.0.1.13
10.0.1.14
10.0.1.15
10.0.1.16
10.0.1.17
与 app=log-node 节点的交集只有:
text
10.0.0.12
这说明原 nginx-only Filebeat 只覆盖了 1 个 Ingress 节点,其他 Ingress 节点上的 access.log 没有被这套 Filebeat 采集到 Kafka topic sample_nginx_log_c。
3.4 对比默认 Filebeat
默认 Filebeat 的 DaemonSet 无 nodeSelector,状态如下:
text
Desired Number of Nodes Scheduled: 40
Current Number of Nodes Scheduled: 40
Pods Status: 40 Running
这解释了为什么 Kibana 中能看到 nginx 上报日志:默认 Filebeat 覆盖范围更广,可能已经采集了原始 nginx 日志或下游消费端日志;但 nginx-only Filebeat 写入 Kafka 的专用链路没有覆盖所有 Ingress 节点。
3.5 删除 nodeSelector 后验证覆盖范围
删除 nginx-only Filebeat DaemonSet 中的:
yaml
nodeSelector:
app: log-node
然后对比节点集合:
bash
# 默认 filebeat 所在节点
kubectl --kubeconfig config_test -n elk get pod -l app=sample-filebeat -o wide \
| awk 'NR>1{print $7}' | sort -u > /tmp/default-filebeat-nodes.txt
# nginx-only filebeat 所在节点
kubectl --kubeconfig config_test -n elk get pod -l app=sample-filebeat-nginx-only -o wide \
| awk 'NR>1{print $7}' | sort -u > /tmp/nginx-only-filebeat-nodes.txt
# ingress 所在节点
kubectl --kubeconfig config_test -n ingress-nginx get pod -o wide \
| grep ingress | awk '{print $7}' | sort -u > /tmp/ingress-nodes.txt
# 检查哪些 ingress 节点没有 nginx-only filebeat
comm -23 /tmp/ingress-nodes.txt /tmp/nginx-only-filebeat-nodes.txt
comm 没有输出,说明当前所有 Ingress 节点已经被 nginx-only Filebeat 覆盖。
4. Pending Pod 处理
删除 nodeSelector 后,DaemonSet 会尝试调度到更多节点,出现少量 Pending:
text
0/49 nodes are available:
1 Too many pods,
2 node(s) had taint {app: dedicated-api},
4 node(s) had taint {ci-android: android},
5 node(s) had taint {workload: ci-runner}
这些 Pending 节点不是 Ingress 节点,但会触发告警,因此需要收窄调度范围,排除不需要采集的专用节点和 Pod 数量已满的节点。
最终在 DaemonSet 中加入 nodeAffinity:
yaml
affinity:
nodeAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
nodeSelectorTerms:
- matchExpressions:
- key: app
operator: NotIn
values:
- dedicated-api
- key: ci-android
operator: NotIn
values:
- android
- key: workload
operator: NotIn
values:
- ci-runner
- key: kubernetes.io/hostname
operator: NotIn
values:
- 10.0.2.11
- 10.0.2.12
其中 10.0.2.12 的排查结果:
bash
kubectl --kubeconfig config_test describe node 10.0.2.12 \
| egrep -i "Taints:|Non-terminated Pods|pods:|Allocated resources"
返回:
text
Taints: <none>
pods: 10
pods: 10
Non-terminated Pods: (10 in total)
说明该节点不是 taint 问题,而是 Pod 数量达到节点上限,导致调度报 Too many pods。
5. 根因
根因是:测试环境 Ingress Controller 没有固定节点池或节点选择器,会在多个普通节点间漂移;但 nginx-only Filebeat DaemonSet 被限制在 app=log-node 节点上,导致多数 Ingress 节点没有对应 Filebeat Pod 采集 access.log。
具体表现:
- Ingress Controller 实际运行在 8 个节点。
- 原 nginx-only Filebeat 只运行在 5 个
app=log-node节点。 - 两者交集只有 1 个节点。
- 因此只有部分 Ingress access.log 被写入 Kafka topic
sample_nginx_log_c。
6. 修复方案
6.1 短期修复
将 nginx-only Filebeat DaemonSet 从固定 app=log-node 节点改为覆盖所有普通可调度业务节点:
- 删除旧的
nodeSelector: app=log-node - 增加
nodeAffinity排除不需要采集的专用节点 - 排除 Pod 数量已满且非 Ingress 的节点
应用配置:
bash
kubectl --kubeconfig config_test apply -f path/to/filebeat-nginx-only.yaml
验证:
bash
kubectl --kubeconfig config_test -n elk get ds sample-filebeat-nginx-only
kubectl --kubeconfig config_test -n elk get pod -l app=sample-filebeat-nginx-only -o wide
确认 Ingress 节点覆盖:
bash
comm -23 /tmp/ingress-nodes.txt /tmp/nginx-only-filebeat-nodes.txt
无输出即表示所有 Ingress 节点都已覆盖。
6.2 滚动更新说明
修改 DaemonSet 的 spec.template.spec.affinity 会触发滚动更新。该 DaemonSet 使用:
yaml
updateStrategy:
rollingUpdate:
maxUnavailable: 1
type: RollingUpdate
因此会看到 Pod 逐个删除并重建,这是正常行为。完成后应观察到:
text
Desired Number of Nodes Scheduled = Current Number of Nodes Scheduled
Number of Nodes Scheduled with Up-to-date Pods = Desired Number of Nodes Scheduled
Number of Nodes Scheduled with Available Pods = Desired Number of Nodes Scheduled
7. 后续建议
7.1 测试环境也应固定 Ingress 节点池
更合理的架构是为 Ingress Controller 设置明确的节点标签,例如:
yaml
nodeSelector:
app: ingress
必要时配套 taint/toleration:
yaml
tolerations:
- key: app
operator: Equal
value: ingress
effect: NoSchedule
这样 Ingress 节点、入口流量、日志落盘路径和采集 DaemonSet 的范围都更清晰。
7.2 Filebeat 应与 Ingress 节点范围保持一致
nginx-only Filebeat 只需要跑在产生 Ingress access.log 的节点上。最佳实践是:
- Ingress Controller 固定到入口节点池。
- nginx-only Filebeat 使用相同的节点选择规则。
- 默认 Filebeat 可以继续负责普通业务日志。
7.3 避免用过期业务标签绑定日志采集
本次旧配置使用 app=log-node,但 Ingress 实际已不只运行在这些节点。类似标签如果不是明确的"入口节点池标签",长期容易造成采集范围漂移。
8. 排查命令速查
bash
# 查看专用 Filebeat DaemonSet 状态
kubectl --kubeconfig config_test -n elk get ds sample-filebeat-nginx-only
# 查看专用 Filebeat Pod 分布
kubectl --kubeconfig config_test -n elk get pod -l app=sample-filebeat-nginx-only -o wide
# 查看 Ingress Controller 分布
kubectl --kubeconfig config_test -n ingress-nginx get pod -o wide | grep ingress
# 查看某标签节点
kubectl --kubeconfig config_test get nodes -l app=log-node -o wide
# 对比 Ingress 节点与 Filebeat 节点
comm -23 /tmp/ingress-nodes.txt /tmp/nginx-only-filebeat-nodes.txt
# 查看 Pending Pod 事件
kubectl --kubeconfig config_test -n elk describe pod <pending-pod-name>
# 查看节点污点与 Pod 数量上限
kubectl --kubeconfig config_test describe node <node-ip> \
| egrep -i "Taints:|Non-terminated Pods|pods:|Allocated resources"
# 查看滚动更新状态
kubectl --kubeconfig config_test -n elk rollout status ds/sample-filebeat-nginx-only