K8S Filebeat DaemonSet 节点覆盖不全导致 Kafka 日志量偏少

记录一次测试环境 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