1. 为什么你的K8s需要EFK日志系统刚上手Kubernetes那会儿我最头疼的就是看日志。Pod今天跑在这个节点明天可能就漂移到另一个节点去了。一开始只有三五个Pod用kubectl logs还能应付后来服务一多几十上百个Pod散落在各个角落想定位一个半夜发生的错误简直就像大海捞针。你需要在不同节点间反复切换手动拼接分散的日志片段效率极低而且很容易遗漏关键信息。这种时候一个集中式的日志收集系统就不是“锦上添花”而是“雪中送炭”了。在云原生生态里日志收集方案有很多最出名的莫过于ELK和EFK。ELK 大家听得更多它包含 Elasticsearch、Logstash 和 Kibana其中 Logstash 负责数据收集和加工。但 Logstash 是 JVM 系的资源消耗相对较大。而EFK栈用Fluentd替换了 Logstash。Fluentd 是用 C 和 Ruby 写的内存占用更小性能也不错最关键的是它被设计为 Kubernetes 的“一等公民”对容器和 K8s 元数据的支持天生就好所以也成了 K8s 官方文档里推荐的日志收集方案。简单来说EFK 在 K8s 里的分工非常明确Fluentd像个勤快的快递员跑在每个节点上负责收集节点上所有容器的日志Elasticsearch是个超级仓库负责存储和索引这些海量日志数据Kibana则是一个漂亮的展示前台让你能通过网页轻松地搜索、分析和可视化日志。搭建好这套系统后你就能在一个统一的界面里按服务、按时间、按关键词快速检索所有日志再也不用为找日志而焦头烂额了。这篇文章我就带你从零开始手把手在 K8s 集群里搭建一套完整的 EFK 系统。我会把每一步的操作、背后的原理还有我踩过的那些“坑”都讲清楚。只要你有一点 K8s 和 Docker 的基础跟着做下来一定能搭建成功。我用的环境是 CentOS 8.4 和 K8s 1.21但核心步骤和思路在其他版本上也是相通的。2. 动手之前规划与准备工作搭建生产可用的系统最忌讳的就是“走一步看一步”。在敲下第一条命令之前我们需要先做好规划和资源准备这能帮你避开很多后期的麻烦。2.1 集群节点与资源规划首先看资源。Elasticsearch 是个“内存大户”尤其是在数据量大的时候。如果让它和你的业务应用混部在同一个节点上很容易发生资源竞争导致业务应用或 ES 集群不稳定。所以我的第一个强烈建议是将 Elasticsearch 集群与业务节点隔离。理想情况下你应该至少准备三台独立的物理机或虚拟机来部署 Elasticsearch组成一个高可用的集群。每台机器的内存建议不低于 4GB如果日志量非常大8GB 或更多会更好。CPU 倒不是最关键的但至少保证 2 核。在我的测试环境里我单独准备了一个名为k8s-elasticsearch的节点配置了 8GB 内存专门用来跑 ES 和 Kibana。对于 Fluentd由于它需要以 DaemonSet 形式运行在每个节点上包括 Master 节点如果你想收集系统组件日志的话所以你需要确保每个节点都有一定的额外资源大约 100-200m CPU200-500Mi 内存可供分配。2.2 创建独立的命名空间为了逻辑清晰、便于管理我们最好为日志系统创建一个独立的命名空间Namespace。这就像在服务器上单独开一个文件夹来放所有日志相关的组件干净又整洁。kubectl create namespace kube-log创建完成后可以用kubectl get ns确认一下。之后我们所有的操作除非特别说明都会在这个kube-log命名空间下进行。2.3 为 Elasticsearch 准备持久化存储Elasticsearch 存储的是你的宝贵日志数据绝对不能丢。因此必须为它配置持久化存储。在 K8s 里我们通常用PersistentVolume (PV)和PersistentVolumeClaim (PVC)来管理存储。为了让存储的供给自动化我们会用到StorageClass和Provisioner。简单理解StorageClass 定义了存储的“类型”比如高速 SSD、普通 HDDProvisioner 则是负责根据这个类型动态创建 PV 的插件。这里我选择用 NFS 作为后端存储因为它配置相对简单适合实验环境。生产环境可以考虑 Ceph、云厂商的块存储等更可靠的方案。第一步在准备存放数据的节点我这里是k8s-elasticsearch上安装和配置 NFS 服务端# 安装 nfs 工具包 yum install -y nfs-utils # 启动并设置开机自启 systemctl start nfs-server systemctl enable nfs-server # 创建共享目录 mkdir -p /data/eslog # 编辑 exports 文件设置共享 vim /etc/exports # 在文件末尾添加一行允许所有节点访问生产环境应限制IP /data/eslog *(rw,no_root_squash,sync) # 使配置生效 exportfs -arv # 重启 nfs 服务 systemctl restart nfs-server第二步在 K8s 集群中部署 NFS Client Provisioner。这个 Provisioner 会 watch PVC 的创建并自动在指定的 NFS 服务器上创建对应的目录然后生成 PV。首先我们需要创建一个 ServiceAccount 并授予它必要的权限RBAC# serviceaccount.yaml apiVersion: v1 kind: ServiceAccount metadata: name: nfs-provisioner namespace: kube-logkubectl apply -f serviceaccount.yaml接着是权限配置内容稍长主要是让这个 ServiceAccount 有权限管理 PV、PVC 等资源# rbac.yaml apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole metadata: name: nfs-provisioner-runner rules: - apiGroups: [] resources: [persistentvolumes] verbs: [get, list, watch, create, delete] - apiGroups: [] resources: [persistentvolumeclaims] verbs: [get, list, watch, update] - apiGroups: [storage.k8s.io] resources: [storageclasses] verbs: [get, list, watch] - apiGroups: [] resources: [events] verbs: [create, update, patch] - apiGroups: [] resources: [services, endpoints] verbs: [get] --- apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRoleBinding metadata: name: run-nfs-provisioner subjects: - kind: ServiceAccount name: nfs-provisioner namespace: kube-log roleRef: kind: ClusterRole name: nfs-provisioner-runner apiGroup: rbac.authorization.k8s.io --- apiVersion: rbac.authorization.k8s.io/v1 kind: Role metadata: name: leader-locking-nfs-provisioner namespace: kube-log rules: - apiGroups: [] resources: [endpoints] verbs: [get, list, watch, create, update, patch] --- apiVersion: rbac.authorization.k8s.io/v1 kind: RoleBinding metadata: name: leader-locking-nfs-provisioner namespace: kube-log subjects: - kind: ServiceAccount name: nfs-provisioner namespace: kube-log roleRef: kind: Role name: leader-locking-nfs-provisioner apiGroup: rbac.authorization.k8s.iokubectl apply -f rbac.yaml权限搞定后就可以部署 Provisioner 的 Pod 了。注意这个 Pod 需要运行在拥有 NFS 目录的节点上我们通过nodeName直接指定# deployment-nfs-provisioner.yaml apiVersion: apps/v1 kind: Deployment metadata: name: nfs-provisioner namespace: kube-log spec: replicas: 1 selector: matchLabels: app: nfs-provisioner strategy: type: Recreate template: metadata: labels: app: nfs-provisioner spec: # 指定部署到 NFS 服务器所在的节点 nodeName: k8s-elasticsearch serviceAccountName: nfs-provisioner containers: - name: nfs-provisioner image: registry.cn-hangzhou.aliyuncs.com/open-ali/nfs-client-provisioner:latest imagePullPolicy: IfNotPresent volumeMounts: - name: nfs-client-root mountPath: /persistentvolumes env: - name: PROVISIONER_NAME value: es-log/nfs # 这个名字很重要后面创建 StorageClass 要用 - name: NFS_SERVER value: 172.16.66.169 # 你的 NFS 服务器 IP - name: NFS_PATH value: /data/eslog # NFS 共享目录路径 volumes: - name: nfs-client-root nfs: server: 172.16.66.169 path: /data/eslogkubectl apply -f deployment-nfs-provisioner.yaml最后创建 StorageClass这样以后申请存储就方便了# storageclass.yaml apiVersion: storage.k8s.io/v1 kind: StorageClass metadata: name: es-nfs-storage provisioner: es-log/nfs # 必须与上面 PROVISIONER_NAME 一致 reclaimPolicy: Retain # 删除PVC时PV和数据保留。也可用Delete但生产环境慎用 mountOptions: - hard - nfsvers4.1kubectl apply -f storageclass.yaml运行kubectl get storageclass你应该能看到名为es-nfs-storage的存储类。至此存储的“高速公路”就修好了随时可以给 Elasticsearch 分配“仓库”。3. 部署有状态的“大脑”Elasticsearch 集群Elasticsearch 集群的部署是整个 EFK 栈的核心也是最容易出问题的一环。我们将使用StatefulSet来部署它。StatefulSet 非常适合有状态应用它能保证 Pod 拥有唯一且稳定的网络标识符如es-cluster-0,es-cluster-1和持久化存储这对于 ES 集群节点间的发现和数据分片至关重要。3.1 理解无头服务Headless Service在部署 StatefulSet 之前需要先创建一个特殊的 Service叫做Headless Service。普通的 Service 会提供一个虚拟 IP 来做负载均衡但 Headless Service 没有 Cluster IP。它的作用是直接返回后端所有 Pod 的 DNS A 记录。这样ES 集群内的各个节点就能通过固定的 DNS 名称如es-cluster-0.elasticsearch.kube-log.svc.cluster.local直接互相通信这是 ES 集群组建和节点发现的基础。# service-es-headless.yaml apiVersion: v1 kind: Service metadata: name: elasticsearch namespace: kube-log labels: app: elasticsearch spec: clusterIP: None # 这就是“无头”的奥秘 selector: app: elasticsearch ports: - port: 9200 name: rest # REST API 端口给 Kibana 和 Fluentd 用 - port: 9300 name: inter-node # 节点间通信端口kubectl apply -f service-es-headless.yaml应用后kubectl get svc -n kube-log会看到elasticsearch服务的 CLUSTER-IP 是None。3.2 编写 Elasticsearch StatefulSet 配置这是最关键的配置文件我会逐段解释。请特别注意其中的环境变量和初始化容器Init Containers它们解决了 ES 在容器中运行时的几个典型问题。# statefulset-es.yaml apiVersion: apps/v1 kind: StatefulSet metadata: name: es-cluster namespace: kube-log spec: serviceName: elasticsearch # 必须指向前面创建的 Headless Service replicas: 3 # 生产环境建议至少3个节点以实现高可用 selector: matchLabels: app: elasticsearch template: metadata: labels: app: elasticsearch spec: # 指定部署到我们预留的节点避免干扰业务 nodeName: k8s-elasticsearch # 或者使用 nodeSelector更灵活 # nodeSelector: # dedicated: es-node containers: - name: elasticsearch image: docker.elastic.co/elasticsearch/elasticsearch:7.17.9 # 建议使用较新稳定版 imagePullPolicy: IfNotPresent resources: limits: cpu: 1 memory: 2Gi requests: cpu: 500m memory: 1Gi ports: - containerPort: 9200 name: rest protocol: TCP - containerPort: 9300 name: inter-node protocol: TCP volumeMounts: - name: data mountPath: /usr/share/elasticsearch/data env: - name: cluster.name value: k8s-logs - name: node.name valueFrom: fieldRef: fieldPath: metadata.name # 自动用 Pod 名称如 es-cluster-0作为节点名 - name: discovery.seed_hosts value: es-cluster-0.elasticsearch,es-cluster-1.elasticsearch,es-cluster-2.elasticsearch - name: cluster.initial_master_nodes value: es-cluster-0,es-cluster-1,es-cluster-2 - name: ES_JAVA_OPTS value: -Xms1g -Xmx1g # JVM 堆内存建议设为相同值且不超过物理内存一半 - name: bootstrap.memory_lock value: true # 锁定内存防止交换swap提升性能 - name: network.host value: 0.0.0.0 securityContext: privileged: true # 需要特权模式来执行内存锁定等操作 capabilities: add: - IPC_LOCK # 初始化容器在主容器运行前执行 initContainers: - name: fix-permissions image: busybox:1.28 command: [sh, -c, chown -R 1000:1000 /usr/share/elasticsearch/data] securityContext: privileged: true volumeMounts: - name: data mountPath: /usr/share/elasticsearch/data - name: increase-vm-max-map image: busybox:1.28 command: [sysctl, -w, vm.max_map_count262144] securityContext: privileged: true - name: increase-fd-ulimit image: busybox:1.28 command: [sh, -c, ulimit -n 65536] securityContext: privileged: true # 持久化存储声明模板每个 Pod 都会自动创建一个 PVC volumeClaimTemplates: - metadata: name: data spec: accessModes: [ ReadWriteOnce ] storageClassName: es-nfs-storage # 使用我们之前创建的 StorageClass resources: requests: storage: 10Gi关键点解析discovery.seed_hosts和cluster.initial_master_nodes这是 ES 集群组建的关键。discovery.seed_hosts提供了集群节点发现的初始主机列表。因为我们用了 Headless Service所以可以用es-cluster-序号.服务名这种稳定的 DNS 来指代每个 Pod。cluster.initial_master_nodes则指定了在集群首次启动时有资格被选为主节点的节点列表。初始化容器Init Containersfix-permissions容器内 ES 默认以 UID 1000 的用户运行但宿主机挂载的目录可能属于 root。这个容器提前把数据目录的权限改好。increase-vm-max-mapLinux 系统默认的vm.max_map_count值较小ES 需要大量内存映射文件这个容器调大该值。increase-fd-ulimit提高进程可打开的文件描述符数量上限。volumeClaimTemplates这是 StatefulSet 的精华。它会为每个 Podes-cluster-0,es-cluster-1,es-cluster-2动态创建一个独立的 PVC并绑定到一个 PV。这样每个 ES 节点都有自己的专属存储数据不会混在一起。应用这个配置kubectl apply -f statefulset-es.yaml3.3 你可能遇到的“坑”与解决方案部署后用kubectl get pods -n kube-log -w观察 Pod 状态。如果一切顺利你会看到三个 Pod 依次变成Running。但实践中我遇到过两个典型问题问题一镜像拉取失败。特别是docker.elastic.co的镜像可能因为网络问题拉不下来。解决办法是手动拉取并重命名# 在部署节点上操作 docker pull elasticsearch:7.17.9 docker tag elasticsearch:7.17.9 docker.elastic.co/elasticsearch/elasticsearch:7.17.9 # 对于 Kibana 镜像同理 docker pull kibana:7.17.9 docker tag kibana:7.17.9 docker.elastic.co/kibana/kibana:7.17.9问题二Pod 卡在Init:0/3或CreateContainerConfigError。这很可能是因为 K8s 版本尤其是 1.20的一个 API 变更导致的。在 CentOS/RHEL 8 等系统上需要修改 kube-apiserver 的配置。登录到你的 K8s Master 节点编辑文件vim /etc/kubernetes/manifests/kube-apiserver.yaml在spec.containers.command部分找到以- --feature-gates开头的行在其末尾添加,RemoveSelfLinkfalse。如果原来没有这个参数就新增一行- --feature-gatesRemoveSelfLinkfalse。保存后kubelet 会自动重启 apiserver Pod。等待几分钟再查看 ES Pod 状态应该就能正常启动了。当三个 Pod 都Running后可以验证一下集群健康状态# 任意进入一个 ES Pod 执行命令 kubectl exec -it es-cluster-0 -n kube-log -- curl http://localhost:9200/_cluster/health?pretty你会看到返回的 JSON 中status字段是green或yellow单节点集群是 yellow。green表示所有主分片和副本分片都正常。4. 部署数据展示“前台”KibanaElasticsearch 虽然强大但它的接口是 JSON对人不友好。Kibana 就是它的图形化界面让我们能用浏览器轻松搜索和查看日志。Kibana 的部署相对简单我们用一个普通的 Deployment 和 Service 即可。为了方便从集群外部访问这里将 Service 类型设为NodePort。生产环境可以考虑使用Ingress配合域名和 HTTPS。# deployment-kibana.yaml apiVersion: v1 kind: Service metadata: name: kibana namespace: kube-log labels: app: kibana spec: type: NodePort ports: - port: 5601 targetPort: 5601 nodePort: 30601 # 可以指定一个端口范围如 30000-32767 selector: app: kibana --- apiVersion: apps/v1 kind: Deployment metadata: name: kibana namespace: kube-log labels: app: kibana spec: replicas: 1 selector: matchLabels: app: kibana template: metadata: labels: app: kibana spec: nodeName: k8s-elasticsearch # 同样部署在 ES 节点 containers: - name: kibana image: docker.elastic.co/kibana/kibana:7.17.9 # 版本务必与 ES 一致 imagePullPolicy: IfNotPresent resources: limits: cpu: 500m memory: 1Gi requests: cpu: 100m memory: 512Mi env: - name: ELASTICSEARCH_HOSTS value: [http://elasticsearch:9200] # 指向 ES 的 Headless Service ports: - containerPort: 5601注意ELASTICSEARCH_HOSTS这个环境变量是关键。Kibana 通过它找到 Elasticsearch。这里我们用的是 Service 的名称elasticsearchK8s 的 DNS 会自动将其解析为 Service 的 Cluster IP对于 Headless Service则解析为后端 Pod 的 IP。因为它们在同一个命名空间kube-log下所以直接写服务名即可。应用配置kubectl apply -f deployment-kibana.yaml等待 Pod 变成Running状态后你就可以通过http://任意节点IP:30601来访问 Kibana 了。第一次打开可能会稍慢因为它正在连接 Elasticsearch。看到 Kibana 的欢迎界面就成功了一大半5. 部署日志“搬运工”Fluentd DaemonSet最后也是最灵动的一环——Fluentd。我们需要它在集群的每个节点上运行一个副本收集该节点上所有容器的日志。K8s 的DaemonSet控制器就是干这个的确保所有或指定节点上都运行一个 Pod 副本。5.1 Fluentd 如何收集日志在 K8s 中默认情况下容器的标准输出stdout和标准错误stderr会被 Docker 或 Containerd 捕获并写入到节点文件系统的 JSON 文件中通常位于/var/log/containers/目录下。同时容器的日志驱动信息在/var/lib/docker/containers/Docker 运行时。Fluentd 会以只读方式挂载这些宿主机的目录然后像tail -f一样实时跟踪这些日志文件的变化。5.2 配置 Fluentd DaemonSet同样我们需要先给 Fluentd 分配权限因为它需要读取 Pod 信息、Namespace 信息等。# rbac-fluentd.yaml apiVersion: v1 kind: ServiceAccount metadata: name: fluentd namespace: kube-log --- apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole metadata: name: fluentd rules: - apiGroups: - resources: - pods - namespaces verbs: - get - list - watch --- apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRoleBinding metadata: name: fluentd roleRef: kind: ClusterRole name: fluentd apiGroup: rbac.authorization.k8s.io subjects: - kind: ServiceAccount name: fluentd namespace: kube-logkubectl apply -f rbac-fluentd.yaml接下来是 DaemonSet 的核心配置。我选择了一个社区维护的、已经集成了 Elasticsearch 输出插件的 Fluentd 镜像省去了自己配置插件依赖的麻烦。# daemonset-fluentd.yaml apiVersion: apps/v1 kind: DaemonSet metadata: name: fluentd namespace: kube-log labels: app: fluentd spec: selector: matchLabels: app: fluentd template: metadata: labels: app: fluentd spec: serviceAccountName: fluentd # 容忍度允许 Pod 被调度到带有污点的 Master 节点如果你想收集 Master 节点日志 tolerations: - key: node-role.kubernetes.io/master effect: NoSchedule containers: - name: fluentd image: fluent/fluentd-kubernetes-daemonset:v1.16.1-debian-elasticsearch8-1.0 imagePullPolicy: IfNotPresent env: - name: FLUENT_ELASTICSEARCH_HOST value: elasticsearch.kube-log.svc.cluster.local # ES 服务的全限定域名 - name: FLUENT_ELASTICSEARCH_PORT value: 9200 - name: FLUENT_ELASTICSEARCH_SCHEME value: http - name: FLUENT_ELASTICSEARCH_USER # 如果 ES 有认证需要配置 value: - name: FLUENT_ELASTICSEARCH_PASSWORD value: - name: FLUENT_ELASTICSEARCH_SSL_VERIFY value: false # 测试环境可关闭 SSL 验证 - name: FLUENTD_SYSTEMD_CONF value: disable # 禁用 systemd 日志收集我们只关心容器日志 resources: limits: memory: 500Mi requests: cpu: 100m memory: 200Mi volumeMounts: - name: varlog mountPath: /var/log - name: varlibdockercontainers mountPath: /var/lib/docker/containers readOnly: true - name: fluentd-config mountPath: /fluentd/etc terminationGracePeriodSeconds: 30 volumes: - name: varlog hostPath: path: /var/log - name: varlibdockercontainers hostPath: path: /var/lib/docker/containers - name: fluentd-config configMap: name: fluentd-config这里我引入了一个ConfigMap来提供更灵活的 Fluentd 配置文件。默认镜像的配置可能不适合所有场景比如你想过滤掉某些日志或者修改日志格式。# configmap-fluentd.yaml apiVersion: v1 kind: ConfigMap metadata: name: fluentd-config namespace: kube-log data: fluent.conf: | # 从容器日志文件读取 source type tail id in_tail_container_logs path /var/log/containers/*.log pos_file /var/log/fluentd-containers.log.pos tag kubernetes.* read_from_head true parse type json time_format %Y-%m-%dT%H:%M:%S.%NZ /parse /source # 添加 Kubernetes 元数据如 Pod 名称、Namespace、标签等 filter kubernetes.** type kubernetes_metadata /filter # 匹配所有标签输出到 Elasticsearch match ** type elasticsearch id out_es host #{ENV[FLUENT_ELASTICSEARCH_HOST]} port #{ENV[FLUENT_ELASTICSEARCH_PORT]} scheme #{ENV[FLUENT_ELASTICSEARCH_SCHEME]} logstash_format true # 非常重要会生成按日滚动的索引如 logstash-2024.05.27 logstash_prefix fluentd logstash_dateformat %Y.%m.%d include_tag_key true type_name _doc buffer type file path /var/log/fluentd-buffers/kubernetes.system.buffer flush_mode interval retry_type exponential_backoff flush_thread_count 2 flush_interval 5s retry_forever true retry_max_interval 30 chunk_limit_size 2M queue_limit_length 8 overflow_action block /buffer /match# 依次应用 ConfigMap 和 DaemonSet kubectl apply -f configmap-fluentd.yaml kubectl apply -f daemonset-fluentd.yaml应用后使用kubectl get pods -n kube-log -l appfluentd -o wide查看应该会在每个节点包括 Master如果你配置了容忍度上都运行着一个 Fluentd Pod。6. 验收成果在 Kibana 中查看日志所有组件都运行起来后让我们验收一下成果。打开浏览器访问http://你的节点IP:30601。第一次进入 Kibana需要配置一个索引模式Index Pattern。因为 Fluentd 配置了logstash_format true所以数据会写入到名为fluentd-YYYY.MM.DD的索引中。点击左侧菜单的Management-Stack Management。在Kibana部分点击Index Patterns。点击Create index pattern。在索引模式名称中输入fluentd-*然后点击Next step。在Time field下拉框中选择timestamp。这是 Fluentd 自动添加的日志时间戳字段。点击Create index pattern。创建成功后点击左侧菜单的Analytics-Discover。在左上角选择你刚创建的fluentd-*索引模式。稍等片刻可能需要等 Fluentd 发送第一批数据你就能看到从集群中收集上来的日志流了。你可以尝试进行一些搜索比如在搜索框输入kubernetes.pod_name:websvr1来过滤特定 Pod 的日志或者用level:error来查找错误日志。Kibana 强大的查询语法和可视化功能能让你以前所未有的效率来管理和分析日志。7. 进阶调优与问题排查指南一套能跑起来的系统只是开始要让它稳定、高效地运行还需要一些调优并知道如何排查问题。7.1 Elasticsearch 性能与稳定性调优JVM 堆内存ES_JAVA_OPTS中的-Xms和-Xmx务必设置成一样避免堆内存调整开销。总量不要超过物理内存的 50%并且绝对不要超过 32GBJVM 指针压缩的极限。禁用 Swap在生产环境一定要在宿主机上禁用 Swap。可以在 K8s 层面为 Pod 设置spec.containers[].resources.limits.memory并配合bootstrap.memory_lock: true但最根本的还是关掉宿主机的 Swap。存储优化如果使用本地 SSD性能会远好于网络存储。对于 NFS确保网络带宽和延迟达标。考虑使用local类型的 PV但需要做好节点故障的数据备份。分片与副本默认索引有 1 个主分片和 1 个副本。对于日志类数据可以考虑增加主分片数如 3-5个以利用多节点并行写入和查询。副本数至少为 1 以保证高可用。7.2 Fluentd 配置优化缓冲区Buffer上面配置中使用了文件缓冲区。这比内存缓冲区更可靠进程重启数据不丢。chunk_limit_size和flush_interval决定了数据批处理的频率和大小可以根据日志量调整。增大它们可以减少 ES 的写入压力但会增加延迟。过滤与解析如果日志格式不是 JSON需要在parse块中使用type regexp或type grok来解析。可以使用filter插件来添加自定义字段、删除敏感信息如密码或根据条件丢弃无关日志如健康检查日志这能极大节省 ES 的存储和计算资源。多输出Multi Output除了 ES你还可以配置将日志同时输出到其他目的地比如 S3 做长期归档或者 Slack 做关键错误报警。7.3 常见问题排查思路Kibana 里看不到日志检查 Fluentd Pod 日志kubectl logs -f fluentd-pod-name -n kube-log。看是否有连接 ES 失败、解析错误等报错。检查 ES 集群健康状态kubectl exec -it es-cluster-0 -n kube-log -- curl -s http://localhost:9200/_cluster/health?pretty。检查 ES 中是否存在索引kubectl exec -it es-cluster-0 -n kube-log -- curl -s http://localhost:9200/_cat/indices?v。看看有没有fluentd-开头的索引。进入一个业务 Pod 的容器手动输出一些日志如echo test log $(date)然后观察 Fluentd 日志和 ES 索引变化。Fluentd Pod 不断重启最常见的原因是内存不足。检查 DaemonSet 的资源限制limits.memory适当调大。查看 Pod 描述信息kubectl describe pod fluentd-pod-name -n kube-log关注Events部分。检查节点磁盘空间缓冲区文件可能写满了。Elasticsearch 节点变红/黄红通常意味着主分片丢失数据可能已损坏。黄意味着副本分片未分配。首先检查_cluster/health和_cat/indices?v确认状态。检查 PVC/PV 是否正常绑定NFS 服务器是否可访问。检查节点资源CPU、内存、磁盘是否充足。这套 EFK 栈部署下来基本上能满足中小规模 K8s 集群的日志收集需求。它就像给你的集群装上了“全局监控摄像头”和“智能日志搜索引擎”无论是开发调试还是线上运维效率都会提升好几个档次。当然随着业务规模增长你可能需要考虑更高级的特性比如日志的冷热分离、基于日志的告警、更细粒度的权限控制等这些都可以在现有的 EFK 基础上逐步演进。