基于 Docker 搭建开发环境(二):EFK 日志套件

基于 Docker 搭建开发环境(二):EFK 日志套件

基于 Docker 搭建开发环境系列:

在上一篇文章 基于 Docker 搭建开发环境(一):数据库+监控 中,介绍了一下如何使用 Docker 搭建起 MySQL + NACOS + Prometheus + Grafana 集成数据库、注册中心+配置管理、监控的开发环境。这篇文章来介绍一下如何在原来的基础上接入 Elasticsearch + Fluentd + Kibana 套件,并且将 NACOS 的日志接入到 Elasticsearch 里。

Elasticsearch

由于 Elasticsearch 8+ 的版本修改了安全策略,不允许 Kibana 使用超级管理员 elastic 连接 Elasticsearch,这里选用 7.x 版本做演示。

还有一点需要提醒,在设置 Elasticsearch 的超级管理员 elastic 的账户密码时,如果密码是全部的阿拉伯数字,那么需要用双引号或者单引号括起来。

在测试中,还遇到一个磁盘过载导致的只读问题。解决方式如下:

  1. curl -X GET "localhost:9200/_cat/allocation?v&pretty" 查看磁盘使用情况

  2. 解除只读状态

    $ curl -X PUT "localhost:9200/test/_settings" -H 'Content-Type: application/json' -d'
    {
      "index.blocks.read_only_allow_delete": null
    }
    '
  3. 调整磁盘使用阈值

    $ curl -X PUT "localhost:9200/_cluster/settings" -H 'Content-Type: application/json' -d'
    {
      "persistent": {
        "cluster.routing.allocation.disk.watermark.flood_stage": "97%", (1)
        "cluster.routing.allocation.disk.watermark.high": "90%", (2)
        "cluster.routing.allocation.disk.watermark.low": "85%",  (3)
        "cluster.info.update.interval": "1m" (4)
      }
    }
    '
    1当磁盘使用超过这个百分比时,索引会被设置为只读。
    2当磁盘使用超过这个百分比时,Elasticsearch 会尝试将分片迁移到磁盘使用较少的节点。
    3当磁盘使用超过这个百分比时,会触发分片的重新分配。
    4更新磁盘使用信息的间隔时间。

Kibana

配置好 Elasticsearch 的网址、账户、密码就可以启动访问了。

在网上见到别人把 Kibana 整的很精美的监控图表,不知道是怎么搞的?回头再研究研究。

Fluentd

安装 Fluentd,通过 Fluentd 把 NACOS 和业务应用的日志传输到 Elasticsearch。目前先把 NACOS 的日志接入到 Elasticsearch。

这里需要一个配置文件,用于知名日志的来源及去向:

./docker/config/fluentd/fluent.conf
# @author D瓜哥 · https://www.diguage.com

<source>
  @type monitor_agent
  port 9880  # HTTP 监控端口,默认为 9880
</source>

<source> (1)
  @type tail
  path "/var/log/nacos/nacos.log"    # NACOS 的日志路径
  pos_file "/fluentd/log/nacos.pos"  # 记录日志读取进度
  tag "nacos.*"
  <parse>
    @type "none" # 如果 NACOS 日志是纯文本格式,可以选择 `none` 或其他解析方式
  </parse>
</source>

<match nacos.**> (2)
  @type elasticsearch
  host "elasticsearch"    # Elasticsearch 的服务名
  port 9200
  logstash_format true    # 格式化为 logstash 格式
  logstash_prefix "nacos" # 索引前缀
  include_tag_key true
  flush_interval 5s
  <buffer>
    flush_interval 5s
  </buffer>
</match>
1读取日志
2日志发送目的地

接入成功后,curl http://localhost:9200/nacos*/_search?pretty 验证 NACOS 日志接入到 Elasticsearch。

下面重点介绍一下遇到的几个问题:

插件缺失导致的启动失败

由于需求将日志输送到 Elasticsearch,这就需要 fluent-plugin-elasticsearch 插件。容器中,默认是没有安装的,启动时就会导致失败。

两种解决办法:

  1. 使用上篇问 基于 Docker 搭建开发环境(一):数据库+监控 中 MySQL 的类似办法,基于官方镜像,在里面装上插件,再打包一个新镜像。

  2. docker-compose.yml 中通过 command 指令,运行 Fluentd 时安装 fluent-plugin-elasticsearch 插件。

为了简单起见,直接选择第二种方案。安装命令是: command: ["sh", "-c", "gem install fluent-plugin-elasticsearch --no-document && fluentd -c /fluentd/etc/fluent.conf"]

由于权限导致的安装失败

配置上述命令后,启动就遇到了该错误: ERROR: While executing gem …​ (Gem::FilePermissionError) You don’t have write permissions for the /usr/lib/ruby/gems/3.2.0 directory.

这是由于默认以 fluent 运行的,该用户对 /usr/lib/ruby/gems/3.2.0 没有写权限。所以,在安装插件时,就会报错。

解决方案: 通过在 docker-compose.yml 中增加 user: root 配置,使用 root 来运行容器。

健康检查

尝试了三种方案:

  1. 启用 monitor_agent 插件,监控 9880 端口。失败!

  2. 使用 nc 检查 Fluentd 监听的 24224 端口。失败!

  3. 最后通过使用 test: [ "CMD-SHELL", "pgrep fluentd || exit 1" ] 来查看 Fluentd 的进程方式。成功!

完整 docker-compose.yml

按照惯例,再把最新的完整 docker-compose.yml 文件展示一下:

# @author D瓜哥 · https://www.diguage.com
services:
  # mysql -h127.0.0.1 -uroot -p123456
  mysql:
    container_name: mysql
    build:
      context: .
      dockerfile: ./docker/images/mysql.dockerfile
    image: example/mysql:8.4
    environment:
      - TZ=Asia/Shanghai  # 设置时区为上海时间
    env_file:
      - ./docker/env/mysql.env
    volumes:
      - ./data/mysql:/var/lib/mysql
    ports:
      - "3306:3306"
    healthcheck:
      test: [ "CMD", "mysqladmin" ,"ping", "-h", "localhost" ]
      interval: 30s  # 每 30 秒检查一次
      timeout: 10s   # 请求超时时间为 10 秒
      retries: 5     # 如果检查失败,最多重试 5 次
      start_period: 60s  # 等待 60 秒后再开始进行 healthcheck

  # Nacos: http://127.0.0.1:8848/nacos/
  # http://localhost:8848/nacos/actuator/prometheus
  # http://localhost:8848/nacos/actuator/health
  nacos:
    image: nacos/nacos-server:${NACOS_VERSION:-latest}
    container_name: nacos
    environment:
      - TZ=Asia/Shanghai  # 设置时区为上海时间
    env_file:
      - ./docker/env/nacos.env
    volumes:
      - ./docker/config/nacos/application.properties:/home/nacos/conf/application.properties
      - nacos_log:/home/nacos/logs (1)
    ports:
      - "8848:8848"
      - "9848:9848"
    restart: on-failure
    healthcheck:
      test: [ "CMD", "curl", "-f", "http://localhost:8848/nacos/actuator/health" ]
      interval: 30s  # 每 30 秒检查一次
      timeout: 10s   # 请求超时时间为 10 秒
      retries: 5     # 如果检查失败,最多重试 5 次
      start_period: 60s  # 等待 60 秒后再开始进行 healthcheck
    depends_on:
      mysql:
        condition: service_healthy

  # Prometheus: http://localhost:9090/
  # http://localhost:9090/-/healthy
  prometheus:
    image: prom/prometheus:${PROMETHEUS_VERSION:-latest}
    container_name: prometheus
    environment:
      - TZ=Asia/Shanghai  # 设置时区为上海时间
    command:
      - --config.file=/etc/prometheus/prometheus.yml
    volumes:
      - ./docker/config/prometheus/prometheus.yml:/etc/prometheus/prometheus.yml:ro
    ports:
      - 9090:9090
    restart: on-failure
    healthcheck:
      test: [ "CMD-SHELL", "wget --spider -q http://localhost:9090/-/healthy || exit 1" ]
      interval: 30s  # 每 30 秒检查一次
      timeout: 10s   # 请求超时时间为 10 秒
      retries: 5     # 如果检查失败,最多重试 5 次
      start_period: 60s  # 等待 60 秒后再开始进行 healthcheck
    depends_on:
      - nacos

  # Grafana: http://localhost:3000/
  # admin/admin
  grafana:
    container_name: grafana
    image: grafana/grafana:${GRAFANA_VERSION:-latest}
    environment:
      - GF_SECURITY_ADMIN_USER=admin
      - GF_SECURITY_ADMIN_PASSWORD=admin
      - TZ=Asia/Shanghai  # 设置时区为上海时间
    volumes:
      - ./data/grafana:/var/lib/grafana  # 将主机目录映射到 Grafana 容器内的 /var/lib/grafana
    ports:
      - 3000:3000
    restart: on-failure
    healthcheck:
      test: [ "CMD", "curl", "-f", "http://localhost:3000/api/health" ]
      interval: 30s  # 每 30 秒检查一次
      timeout: 10s   # 请求超时时间为 10 秒
      retries: 5     # 如果检查失败,最多重试 5 次
      start_period: 60s  # 等待 60 秒后再开始进行 healthcheck
    depends_on:
      - prometheus

  # ElasticSearch http://localhost:9200/
  # http://localhost:9200/_cluster/health
  elasticsearch:
    image: docker.elastic.co/elasticsearch/elasticsearch:${ELASTICSEARCH_VERSION:-7.17.24}
    container_name: elasticsearch
    environment:
      - discovery.type=single-node
      - ELASTIC_PASSWORD='123456'  # 设置 elastic 用户的默认密码 (2)
      - TZ=Asia/Shanghai  # 设置时区为上海时间
    ulimits:
      memlock:
        soft: -1
        hard: -1
    volumes:
      - ./data/elasticsearch:/usr/share/elasticsearch/data
    ports:
      - "9200:9200"
      - "9300:9300"
    healthcheck:
      test: [ "CMD-SHELL", "curl -fsSL http://localhost:9200/_cluster/health || exit 1" ]
      interval: 30s  # 每 30 秒检查一次
      timeout: 10s   # 请求超时时间为 10 秒
      retries: 5     # 如果检查失败,最多重试 5 次
      start_period: 60s  # 等待 60 秒后再开始进行 healthcheck

  # Kibana http://localhost:5601
  # http://localhost:5601/api/status
  kibana:
    image: docker.elastic.co/kibana/kibana:${KIBANA_VERSION:-7.17.24}
    container_name: kibana
    environment:
      - ELASTICSEARCH_URL=http://elasticsearch:9200
      - ELASTICSEARCH_USERNAME=elastic
      - ELASTICSEARCH_PASSWORD='123456' (2)
      - TZ=Asia/Shanghai  # 设置时区为上海时间
    ports:
      - "5601:5601"
    restart: on-failure
    healthcheck:
      test: [ "CMD", "curl", "-f", "http://localhost:5601/api/status" ]
      interval: 30s  # 每 30 秒检查一次
      timeout: 10s   # 请求超时时间为 10 秒
      retries: 5     # 如果检查失败,最多重试 5 次
      start_period: 60s  # 等待 60 秒后再开始进行 healthcheck
    depends_on:
      - elasticsearch

  # Fluentd http://localhost:9880/api/plugins.json 插件的安装情况
  fluentd:
    image: fluentd:${FLUENTD_VERSION:-latest}
    container_name: fluentd
    user: root  # 使用 root 用户安装插件 (3)
    ports:
      - "24224:24224"
      - "9880:9880"  # 开启监控端口
    volumes:
      - ./docker/config/fluentd/fluent.conf:/fluentd/etc/fluent.conf  # 挂载 Fluentd 配置文件
      - ./data/fluentd:/fluentd/log  # 持久化 Fluentd 数据目录
      - nacos_log:/var/log/nacos  # 挂载 NACOS 日志目录 (1)
    environment:
      FLUENT_ELASTICSEARCH_HOST: elasticsearch
      FLUENT_ELASTICSEARCH_PORT: 9200
    # command: ["sh", "-c", "gem install fluent-plugin-elasticsearch --no-document && fluentd -c /fluentd/etc/fluent.conf"]
    command: [ "sh", "-c", "gem install fluent-plugin-elasticsearch --no-document && chown -R fluent /usr/lib/ruby/gems && fluentd -c /fluentd/etc/fluent.conf" ]
    healthcheck:
      test: [ "CMD-SHELL", "pgrep fluentd || exit 1" ]
      interval: 30s  # 每 30 秒检查一次
      timeout: 10s   # 请求超时时间为 10 秒
      retries: 5     # 如果检查失败,最多重试 5 次
      start_period: 60s  # 等待 60 秒后再开始进行 healthcheck
    depends_on:
      - elasticsearch

volumes:
  nacos_log:  (1)
1把 NACOS 的日志目录同时挂载到 Fluentd,以便读取并发送到 Elasticsearch 中。
2纯数字的密码要用引号括起来,并且和 Elasticsearch 中设置的密码相对应。
3使用 root 用户运行容器。

相关配置已经推送到 GitHub: diguage/develop-env: 基于 Docker 的开发环境,感兴趣欢迎围观。