見出し画像

Docker上でWebサービス+Fluentd+Elasticsearchでアクセスログ管理


docker-compose.yml

KibanaからElasticsearchに接続する際は`kibana_system`を使う構成です。後述のスクリプトでパスワードを設定しています。
webappのloggingのtagの値でfluentdのフィルター等は構成します。後述の設定ファイルで定義します。

version: "3"
services:
  elasticsearch:
    image: docker.elastic.co/elasticsearch/elasticsearch:8.12.2
    container_name: analytics-demo-elasticsearch
    environment:
      - discovery.type=single-node
      - xpack.security.enabled=true
      - ELASTIC_PASSWORD=password
    ports:
      - 9200:9200
    volumes:
      - esdata:/usr/share/elasticsearch/data

  fluentd:
    build: ./fluentd
    container_name: analytics-demo-fluentd
    volumes:
      - ./fluentd/conf:/fluentd/etc
      - ./fluentd/log:/fluentd/log
    ports:
      - 24224:24224
      - 24224:24224/udp

  kibana:
    image: docker.elastic.co/kibana/kibana:8.12.2
    container_name: analytics-demo-kibana
    environment:
      - ELASTICSEARCH_URL=http://elasticsearch:9200
      - ELASTICSEARCH_USERNAME=kibana_system
      - ELASTICSEARCH_PASSWORD=password
    ports:
      - 5601:5601

  webapp:
    image: analytics-demo-app
    container_name: analytics-demo-app
    environment:
      - FLUENTD_HOST=fluentd
      - FLUENTD_PORT=24224
    ports:
      - 3000:3000
    logging:
      driver: fluentd
      options:
        fluentd-address: localhost:24224
        tag: webapp

volumes:
  esdata:

fluentd/Dockerfile

シンプルな構成は以下です。GeoIPを使う場合などはここで他のプラグインもインストールします。

FROM fluent/fluentd:v1.16-debian-2
USER root
RUN fluent-gem install fluent-plugin-elasticsearch --no-document
USER fluent

fluentd/conf/fluent.conf

以下はWebサービスのログ形式がApache2の形式を想定していますが、フィルターはWebサービスのログ形式に合わせる必要があります。
`@timestamp`の上書きを行わないとElasticsearchのドキュメントに反映されませんでした。

<system>
  log_level debug
  workers 2
</system>

<source>
  @type forward
  port 24224
  bind 0.0.0.0
</source>

<filter webapp>
  @type parser
  key_name log
  <parse>
    @type regexp
    expression /^(?<host>[^ ]*) [^ ]* (?<user>[^ ]*) \[(?<time>[^\]]*)\] "(?<method>\S+)(?: +(?<path>(?:[^\"]|\\.)*?)(?: +\S*)?)?" (?<code>[^ ]*) (?<size>[^ ]*)(?: "(?<referer>(?:[^\"]|\\.)*)" "(?<agent>(?:[^\"]|\\.)*)")?$/
    time_format %d/%b/%Y:%H:%M:%S %z
  </parse>
</filter>

<filter webapp>
  @type record_transformer
  enable_ruby
  <record>
    host_param "#{Socket.gethostname}"
    size ${record["size"] == "-" ? 0 : record["size"]}
    @timestamp ${time.strftime('%Y-%m-%dT%H:%M:%S%z')}
  </record>
  remove_keys log
</filter>

<match webapp>
  @type copy

  <store>
    @type elasticsearch
    host elasticsearch
    port 9200
    user elastic
    password password
    index_name fluentd-${tag}
    flush_interval 1s
  </store>

  <store>
    @type stdout
  </store>
</match>

ElasticsearchのIndex Template

ダッシュボードで可視化する際などに使える`keyword`を追加したりしています。
`text`だけではグラフを作成する際の軸のフィールドとして選択出来ません。

{
  "index_patterns": ["fluentd-*"],
  "template": {
    "settings": {
      "number_of_shards": 1,
      "number_of_replicas": 1
    },
    "mappings": {
      "properties": {
        "host": { "type": "ip" },
        "user": { "type": "keyword" },
        "method": { "type": "keyword" },
        "path": {
          "type": "text",
          "fields": {
            "keyword": { "type": "keyword", "ignore_above": 256 }
          }
        },
        "code": { "type": "integer" },
        "size": { "type": "integer" },
        "referer": {
          "type": "text",
          "fields": {
            "keyword": { "type": "keyword", "ignore_above": 256 }
          }
        },
        "agent": {
          "type": "text",
          "fields": {
            "keyword": { "type": "keyword", "ignore_above": 256 }
          }
        },
        "host_param": { "type": "keyword" },
        "@timestamp": { "type": "date" }
      }
    }
  }
}

起動スクリプト

`kibana_system`のパスワードも設定したく、各サービスが起動した状態で起動順序を制御したかったのでスクリプトを作成しました。
環境構築時に起動・停止を繰り返す場合は`docker-compose down -v`のように、`-v`を付けておくことで関連するリソース(docker volumeなど)も削除してくれます。

#!/bin/bash

# Docker Build
docker-compose build --no-cache

# Elasticsearchの起動
echo "Starting Elasticsearch..."
docker-compose up -d elasticsearch

# Elasticsearchが起動するのを待つ
echo "Waiting for Elasticsearch to be ready..."
until $(curl -s -f -u "elastic:password" -o /dev/null "http://localhost:9200/_cluster/health?wait_for_status=yellow&timeout=50s"); do
    printf '.'
    sleep 5
done
echo "Elasticsearch is ready."

# kibana_systemのパスワード設定
curl -s -u "elastic:password" -X POST "http://localhost:9200/_security/user/kibana_system/_password" -H "Content-Type: application/json" -d "{\"password\":\"password\"}";

# Templateの適用
curl -s -u "elastic:password" http://localhost:9200/_index_template/template_fluentd -H "Content-Type: application/json"  -d @elasticsearch/template/fluentd.json

# Kibanaの起動
echo "Starting Kibana..."
docker-compose up -d kibana

# Fluentdの起動
echo "Starting Fluentd..."
docker-compose up -d fluentd

# Fluentdが起動するのを待つ
echo "Waiting for Fluentd to be ready..."
until nc -z localhost 24224; do
    printf '.'
    sleep 1
done
sleep 5  # Fluentdの起動を待つために適切な待ち時間を設定する
echo "Fluentd is ready."

# Webappの起動
echo "Starting Webapp..."
docker-compose up -d webapp

echo "All services have been started."

この記事が気に入ったらサポートをしてみませんか?