## 19.2 ELK/EFK 堆栈 ELK (Elasticsearch,Logstash,Kibana) 是目前业界最流行的开源日志解决方案。而在容器领域,由于 Fluentd 更加轻量级且对容器支持更好,EFK (Elasticsearch,Fluentd,Kibana) 组合也变得非常流行。 ### 19.2.1 方案架构 我们将采用以下架构: 1. **Docker Container**:容器将日志输出到标准输出 (stdout/stderr)。 2. **Fluentd**:作为 Docker 的 Logging Driver 或运行为守护容器,收集容器日志。 3. **Elasticsearch**:存储从 Fluentd 接收到的日志数据。 4. **Kibana**:从 Elasticsearch 读取数据并进行可视化展示。 ### 19.2.2 部署流程 我们将使用 Docker Compose 来一键部署整个日志堆栈。 #### 1. 编写 Compose 文件 1. 编写 `compose.yaml` (或 `docker-compose.yml`) 配置如下: ```yaml services: elasticsearch: image: docker.elastic.co/elasticsearch/elasticsearch:7.17.0 container_name: elasticsearch environment: - "discovery.type=single-node" - "ES_JAVA_OPTS=-Xms512m -Xmx512m" ports: - "9200:9200" volumes: - es_data:/usr/share/elasticsearch/data networks: - logging kibana: image: docker.elastic.co/kibana/kibana:7.17.0 container_name: kibana environment: - ELASTICSEARCH_HOSTS=http://elasticsearch:9200 ports: - "5601:5601" links: - elasticsearch networks: - logging fluentd: image: fluent/fluentd-kubernetes-daemonset:v1.14.3-debian-elasticsearch7-1.0 container_name: fluentd environment: - "FLUENT_ELASTICSEARCH_HOST=elasticsearch" - "FLUENT_ELASTICSEARCH_PORT=9200" - "FLUENT_ELASTICSEARCH_SCHEME=http" - "FLUENT_UID=0" ports: - "24224:24224" - "24224:24224/udp" links: - elasticsearch volumes: - ./fluentd/conf:/fluentd/etc networks: - logging volumes: es_data: networks: logging: ``` #### 2. 配置 Fluentd 创建 `fluentd/conf/fluent.conf`: ```ini @type forward port 24224 bind 0.0.0.0 @type copy @type elasticsearch host elasticsearch port 9200 logstash_format true logstash_prefix docker logstash_dateformat %Y%m%d include_tag_key true type_name access_log tag_key @log_name flush_interval 1s @type stdout ``` #### 3. 配置应用容器使用 fluentd 驱动 启动一个测试容器,指定日志驱动为 `fluentd`: ```bash docker run -d \ --log-driver=fluentd \ --log-opt fluentd-address=localhost:24224 \ --log-opt tag=nginx-test \ --name nginx-test \ nginx ``` **注意**:确保 `fluentd` 容器已经启动并监听在 `localhost:24224`。在生产环境中,如果你是在不同机器上,需要将 `localhost` 替换为运行 fluentd 的主机 IP。 #### 4. 在 Kibana 中查看日志 1. 访问 `http://localhost:5601`。 2. 进入 **Management**->**Kibana**->**Index Patterns**。 3. 创建新的 Index Pattern,输入 `docker-*` (我们在 fluent.conf 中配置的前缀)。 4. 选择 `@timestamp` 作为时间字段。 5. 去 **Discover** 页面,你就能看到 Nginx 容器的日志了。 #### Kibana 建索引模式常见坑 首次接入 EFK/ELK 时,“Elasticsearch 有数据但 Kibana 看不到”很常见,通常是 Kibana 配置或时间窗口问题: * **Index Pattern 不匹配**:确认 Kibana 的 Index Pattern 与实际索引前缀一致。可以先用 `_cat/indices` 查看真实索引名。 * **时间字段选择错误**:若索引里包含 `@timestamp`,一般选择它;如果选择了错误的字段,会导致 Discover 无法按时间筛选。 * **时间窗口/时区**:Discover 右上角的时间范围默认可能是最近 15 分钟,且时区可能影响显示。建议先把范围扩大到最近 24 小时再验证。 * **数据解析失败**:若日志是非结构化文本,仍可入库但字段不可用;生产环境建议输出 JSON 并在采集端解析。 #### 5. 验证日志是否写入 Elasticsearch (生产排错必备) 当你在 Kibana 看不到日志时,建议先跳过 UI,从存储端直接验证“日志是否入库”。 1. 查看索引是否创建: ```bash curl -s http://localhost:9200/_cat/indices?v ``` 如果 Fluentd 使用了 `logstash_format true` 且 `logstash_prefix docker`,通常会看到形如 `docker-YYYY.MM.DD` 的索引。 2. 查看最近一段时间的日志文档: ```bash curl -s -H 'Content-Type: application/json' \ http://localhost:9200/docker-*/_search \ -d '{"size":1,"sort":[{"@timestamp":"desc"}]}' ``` 如果 Elasticsearch 中已经有文档,但 Kibana 仍然为空,常见原因是: * Index Pattern 没匹配到索引 (例如写成了 `docker-*` 但实际索引前缀不同)。 * 时间字段没选对或时区不一致,导致 Discover 时间窗口内看不到数据。 ### 19.2.3 总结 通过 Docker 的日志驱动机制,结合 ELK/EFK 强大的收集和分析能力,我们可以轻松构建一个能够处理海量日志的监控平台,这对于排查生产问题至关重要。 ### 19.2.4 生产要点 在生产环境中,日志系统往往比监控系统更容易因为“容量与写入压力”出问题,建议特别关注: * **容量规划**:日志增长速度与磁盘占用直接相关。建议设置日志保留周期与索引生命周期策略 (ILM),避免 Elasticsearch 因磁盘水位触发只读或不可用。 * **资源配置**:Elasticsearch 对 JVM Heap 较敏感。除示例中的 `ES_JAVA_OPTS` 外,生产环境需要结合节点内存、分片规模、查询压力做评估。 * **链路可靠性**:采集端到存储端要考虑网络抖动、背压与重试策略;当 Elasticsearch 写入变慢时,采集端的缓冲与落盘策略决定了是否会丢日志。 * **日志格式**:推荐应用输出结构化日志 (JSON) 并包含关键字段 (如 `trace_id`、`request_id`、`service`、`env`),以便快速过滤与关联分析。 #### 索引与保留策略的落地建议 无论是 EFK 还是 ELK,生产上都需要回答两个问题: * 日志保留多久? * 保留期内的日志如何保证可查询、不过度占用存储? 建议按环境与业务重要性对日志分层,并制定不同的保留周期,例如: * **生产环境**:7~30 天 * **测试环境**:1~7 天 实现方式通常有两类: * **按天滚动索引**:如 `docker-YYYY.MM.DD`,再定期删除过期索引。 * **使用 ILM**:定义 Hot/Warm/Cold/删除阶段,按时间与容量自动滚动与回收。 对于中小规模集群,先把“按天滚动 + 过期删除”做扎实,往往就能解决 80% 的容量问题;当日志量上来、查询压力变大后,再逐步引入 ILM、分层存储与更精细的分片规划。 #### 最小可用的“过期索引清理”示例 如果你采用按天滚动索引 (例如 `docker-YYYY.MM.DD`),可以通过 Elasticsearch API 定期清理过期索引。 下面示例仅用于演示思路:获取所有 `docker-` 前缀索引并删除指定索引。生产环境建议基于日期计算、灰度验证与权限控制后再执行自动化清理。 1. 列出索引: ```bash curl -s http://localhost:9200/_cat/indices/docker-*?v ``` 2. 删除某个过期索引 (示例): ```bash curl -X DELETE http://localhost:9200/docker-2026.02.01 ``` 如果你希望更自动化的治理能力,可以进一步使用 ILM 为索引配置滚动与删除策略。