Skip to content
Published on

ELKスタック基盤ログ収集·分析パイプライン: Elasticsearch·Fluentd·Kibanaプロダクション構築と最適化

Authors
  • Name
    Twitter
ELK Stack Log Pipeline

はじめに

本番環境においてログは、障害検知、デバッグ、セキュリティ監査、パフォーマンス分析の核心データです。システム規模が大きくなるほど、分散した数十〜数百台のサーバーで発生するログを中央集中的に収集・分析する体制が不可欠となります。ELKスタック(Elasticsearch + Logstash + Kibana)はこうしたログパイプラインの事実上の標準として定着しており、LogstashをFluentdに置き換えたEFKスタックもKubernetes環境で広く採用されています。

この記事では、ELK/EFKスタックの各コンポーネントアーキテクチャ、Elasticsearchクラスタ設計とシャード戦略、ILM(Index Lifecycle Management)設定、FluentdとFluent Bitの比較と設定、Kibanaダッシュボード構成、パフォーマンスチューニング、そしてプロダクション環境で頻繁に発生する障害事例と復旧手順まで全過程を解説します。

ELK vs EFKスタック比較

ELKスタックとEFKスタックの核心的な違いはログコレクタにあります。

項目ELK (Logstash)EFK (Fluentd)
開発言語Java (JRuby)Ruby + C
メモリ使用量約500MB〜1GB約40〜100MB
プラグイン数200+1,000+
設定形式独自DSLタグベースルーティング
Kubernetes親和性普通非常に高い(CNCF卒業)
バッファリングメモリ/ディスクメモリ/ファイル
データパーシングGrokパターン正規表現 + パーサープラグイン
適した環境複雑な変換ロジッククラウドネイティブ、K8s

Fluentd vs Fluent Bit比較

FluentdとFluent Bitは同じエコシステムに属していますが、用途が異なります。

項目FluentdFluent Bit
開発言語Ruby + CC
メモリ使用量約40MB+約450KB
プラグインエコシステム1,000+コアプラグインのみ
役割中央集約/変換エッジ収集/転送
適した環境サーバー、アグリゲーターIoT、サイドカー、DaemonSet
処理性能普通非常に高い(10〜40倍)

プロダクション推奨構成は Fluent Bit(DaemonSet)+ Fluentd(Aggregator)+ Elasticsearch の組み合わせです。Fluent Bitが各ノードでログを軽量に収集し、Fluentdが中央で変換とルーティングを担当します。

Elasticsearchクラスタアーキテクチャ

ノード役割の分離

プロダクションElasticsearchクラスタではノード役割の分離が核心です。

# elasticsearch-master.yml
cluster.name: prod-logs
node.name: master-01
node.roles: [ master ]
network.host: 0.0.0.0
discovery.seed_hosts:
  - master-01
  - master-02
  - master-03
cluster.initial_master_nodes:
  - master-01
  - master-02
  - master-03

# JVM heap settings (jvm.options)
-Xms4g
-Xmx4g
# elasticsearch-data-hot.yml
cluster.name: prod-logs
node.name: data-hot-01
node.roles: [ data_hot, data_content ]
node.attr.data: hot
network.host: 0.0.0.0
discovery.seed_hosts:
  - master-01
  - master-02
  - master-03

# JVM heap settings
-Xms16g
-Xmx16g
# elasticsearch-data-warm.yml
cluster.name: prod-logs
node.name: data-warm-01
node.roles: [ data_warm ]
node.attr.data: warm
network.host: 0.0.0.0
discovery.seed_hosts:
  - master-01
  - master-02
  - master-03

# JVM heap settings
-Xms8g
-Xmx8g

各ノード役割の推奨スペックは以下の通りです。

ノード役割CPUメモリストレージ台数
Master4 vCPU8GB50GB SSD3台(奇数)
Data Hot8〜16 vCPU32〜64GBNVMe SSD3台以上
Data Warm4〜8 vCPU16〜32GBHDD/SSD2台以上
Data Cold2〜4 vCPU8〜16GBHDD1台以上
Coordinating4〜8 vCPU16GB50GB SSD2台
Ingest4〜8 vCPU16GB50GB SSD2台

シャードとレプリカ戦略

# Set shard configuration via index template
curl -X PUT "localhost:9200/_index_template/logs-template" \
  -H 'Content-Type: application/json' \
  -d '{
  "index_patterns": ["logs-*"],
  "template": {
    "settings": {
      "number_of_shards": 3,
      "number_of_replicas": 1,
      "refresh_interval": "30s",
      "codec": "best_compression",
      "routing.allocation.require.data": "hot"
    },
    "mappings": {
      "dynamic": "strict",
      "properties": {
        "@timestamp": { "type": "date" },
        "level": { "type": "keyword" },
        "service": { "type": "keyword" },
        "message": { "type": "text" },
        "trace_id": { "type": "keyword" },
        "host": { "type": "keyword" }
      }
    }
  }
}'

シャード設計の核心原則は以下の通りです。

  • シャードサイズ: 30〜50GBを目標に設定します。10GB未満の小さなシャードはオーバーヘッドが大きく、50GBを超えるとリカバリ時間が長くなります
  • シャード数: ノードあたりのシャード数をヒープ1GBあたり20個以下に維持します
  • レプリカ: プロダクションでは最低1つのレプリカを設定して可用性を保証します
  • refresh_interval: ログデータはリアルタイム性が低いため30秒〜60秒に増やします

Index Lifecycle Management (ILM) 設定

ILMはインデックスの作成から削除まで自動化する核心機能です。

{
  "policy": {
    "phases": {
      "hot": {
        "min_age": "0ms",
        "actions": {
          "rollover": {
            "max_primary_shard_size": "50gb",
            "max_age": "1d"
          },
          "set_priority": {
            "priority": 100
          }
        }
      },
      "warm": {
        "min_age": "3d",
        "actions": {
          "shrink": {
            "number_of_shards": 1
          },
          "forcemerge": {
            "max_num_segments": 1
          },
          "allocate": {
            "require": {
              "data": "warm"
            }
          },
          "set_priority": {
            "priority": 50
          }
        }
      },
      "cold": {
        "min_age": "30d",
        "actions": {
          "allocate": {
            "require": {
              "data": "cold"
            }
          },
          "set_priority": {
            "priority": 0
          }
        }
      },
      "delete": {
        "min_age": "90d",
        "actions": {
          "delete": {}
        }
      }
    }
  }
}
# Create ILM policy
curl -X PUT "localhost:9200/_ilm/policy/logs-lifecycle" \
  -H 'Content-Type: application/json' \
  -d @ilm-policy.json

# Attach ILM policy to index template
curl -X PUT "localhost:9200/_index_template/logs-template" \
  -H 'Content-Type: application/json' \
  -d '{
  "index_patterns": ["logs-*"],
  "template": {
    "settings": {
      "index.lifecycle.name": "logs-lifecycle",
      "index.lifecycle.rollover_alias": "logs"
    }
  }
}'

# Create bootstrap index
curl -X PUT "localhost:9200/logs-000001" \
  -H 'Content-Type: application/json' \
  -d '{
  "aliases": {
    "logs": {
      "is_write_index": true
    }
  }
}'

ILM各Phaseの主要アクション

Phaseトリガー条件主要アクション目的
Hotインデックス作成時rollover, set_priorityアクティブな書き込み/読み取り
Warm3日経過shrink, forcemerge, allocate読み取り中心、ストレージ節約
Cold30日経過allocate, freezeまれな読み取り、コスト最小化
Delete90日経過deleteストレージ確保

Fluentd設定とパイプライン構成

Fluentd基本設定

<!-- /etc/fluentd/fluent.conf -->
<system>
  log_level info
  workers 4
</system>

<!-- Input: Application log collection -->
<source>
  @type tail
  path /var/log/app/*.log
  pos_file /var/log/fluentd/app.log.pos
  tag app.logs
  read_from_head true
  <parse>
    @type json
    time_key timestamp
    time_format %Y-%m-%dT%H:%M:%S.%NZ
  </parse>
</source>

<!-- Input: Kubernetes log collection -->
<source>
  @type tail
  path /var/log/containers/*.log
  pos_file /var/log/fluentd/containers.log.pos
  tag kubernetes.*
  <parse>
    @type cri
  </parse>
</source>

<!-- Filter: Add Kubernetes metadata -->
<filter kubernetes.**>
  @type kubernetes_metadata
  @id filter_kube_metadata
  skip_labels false
  skip_container_metadata false
</filter>

<!-- Filter: Remove unnecessary logs -->
<filter **>
  @type grep
  <exclude>
    key log
    pattern /healthcheck|readiness|liveness/
  </exclude>
</filter>

<!-- Filter: Tag based on log level -->
<filter app.logs>
  @type record_transformer
  enable_ruby true
  <record>
    hostname "#{Socket.gethostname}"
    environment "production"
  </record>
</filter>

<!-- Output: Send to Elasticsearch -->
<match **>
  @type elasticsearch
  host elasticsearch-coordinating
  port 9200
  logstash_format true
  logstash_prefix fluentd-logs
  logstash_dateformat %Y.%m.%d
  include_tag_key true
  tag_key @fluentd_tag

  <buffer tag, time>
    @type file
    path /var/log/fluentd/buffer
    timekey 1h
    timekey_wait 10m
    chunk_limit_size 64MB
    total_limit_size 8GB
    flush_mode interval
    flush_interval 30s
    flush_thread_count 4
    retry_max_interval 30
    retry_forever true
    overflow_action block
  </buffer>
</match>

Fluent Bit DaemonSet設定(Kubernetes)

# fluent-bit-configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: fluent-bit-config
  namespace: logging
data:
  fluent-bit.conf: |
    [SERVICE]
        Flush         5
        Log_Level     info
        Daemon        off
        Parsers_File  parsers.conf
        HTTP_Server   On
        HTTP_Listen   0.0.0.0
        HTTP_Port     2020
        storage.path  /var/log/flb-storage/
        storage.sync  normal
        storage.checksum off
        storage.backlog.mem_limit 5M

    [INPUT]
        Name              tail
        Tag               kube.*
        Path              /var/log/containers/*.log
        Parser            cri
        DB                /var/log/flb_kube.db
        Mem_Buf_Limit     5MB
        Skip_Long_Lines   On
        Refresh_Interval  10
        storage.type      filesystem

    [FILTER]
        Name                kubernetes
        Match               kube.*
        Kube_URL            https://kubernetes.default.svc:443
        Kube_CA_File        /var/run/secrets/kubernetes.io/serviceaccount/ca.crt
        Kube_Token_File     /var/run/secrets/kubernetes.io/serviceaccount/token
        Kube_Tag_Prefix     kube.var.log.containers.
        Merge_Log           On
        Keep_Log            Off
        K8S-Logging.Parser  On
        K8S-Logging.Exclude On

    [FILTER]
        Name    grep
        Match   *
        Exclude log healthcheck

    [OUTPUT]
        Name            forward
        Match           *
        Host            fluentd-aggregator.logging.svc.cluster.local
        Port            24224
        Retry_Limit     False

  parsers.conf: |
    [PARSER]
        Name        cri
        Format      regex
        Regex       ^(?<time>[^ ]+) (?<stream>stdout|stderr) (?<logtag>[^ ]*) (?<log>.*)$
        Time_Key    time
        Time_Format %Y-%m-%dT%H:%M:%S.%L%z
# fluent-bit-daemonset.yaml
apiVersion: apps/v1
kind: DaemonSet
metadata:
  name: fluent-bit
  namespace: logging
spec:
  selector:
    matchLabels:
      app: fluent-bit
  template:
    metadata:
      labels:
        app: fluent-bit
    spec:
      serviceAccountName: fluent-bit
      tolerations:
        - key: node-role.kubernetes.io/master
          effect: NoSchedule
      containers:
        - name: fluent-bit
          image: fluent/fluent-bit:3.2
          resources:
            requests:
              cpu: 100m
              memory: 128Mi
            limits:
              cpu: 500m
              memory: 256Mi
          volumeMounts:
            - name: varlog
              mountPath: /var/log
            - name: config
              mountPath: /fluent-bit/etc/
            - name: storage
              mountPath: /var/log/flb-storage/
      volumes:
        - name: varlog
          hostPath:
            path: /var/log
        - name: config
          configMap:
            name: fluent-bit-config
        - name: storage
          emptyDir: {}

Kibanaダッシュボード構成

インデックスパターン設定

Kibanaでまずインデックスパターンを作成する必要があります。

# Create index pattern via Kibana API
curl -X POST "localhost:5601/api/saved_objects/index-pattern" \
  -H 'kbn-xsrf: true' \
  -H 'Content-Type: application/json' \
  -d '{
  "attributes": {
    "title": "fluentd-logs-*",
    "timeFieldName": "@timestamp"
  }
}'

効果的なダッシュボード設計原則

Kibanaダッシュボードは目的に応じて設計する必要があります。

ダッシュボードタイプ含まれる可視化対象ユーザー
運用概要ログ量推移、エラー率グラフ、サービス別分布SRE/DevOps
エラー分析エラータイプ別分類、Topエラーメッセージ、スタックトレース開発者
セキュリティ監査認証失敗イベント、異常アクセスパターンセキュリティチーム
インフラモニタリングノード別ログ量、インデキシング速度、レイテンシプラットフォームチーム

主要な可視化コンポーネントは以下の通りです。

  • Lensチャート: 時系列ログ量、サービス別エラー率
  • TSVB (Time Series Visual Builder): 詳細な時系列分析
  • Data Table: Top Nエラーメッセージ、サービス別統計
  • Markdownウィジェット: ダッシュボード説明、ランブックリンク

パフォーマンスチューニングと最適化

Elasticsearchインデキシングパフォーマンス最適化

# Bulk indexing optimization settings
curl -X PUT "localhost:9200/logs-000001/_settings" \
  -H 'Content-Type: application/json' \
  -d '{
  "index": {
    "refresh_interval": "30s",
    "translog.durability": "async",
    "translog.sync_interval": "30s",
    "translog.flush_threshold_size": "1gb"
  }
}'

主要パフォーマンスチューニングパラメータ

パラメータデフォルト値推奨値説明
refresh_interval1s30s〜60sログデータはリアルタイム性が低いため増やす
translog.durabilityrequestasync非同期translogで書き込み性能向上
number_of_replicas10(初期ロード時)大量ロード時はレプリカを無効化
bulkサイズ-5〜15MB大きすぎるとメモリ圧迫、小さすぎるとオーバーヘッド
flush_thread_count14〜8Fluentd出力スレッド数

JVMヒープメモリ設定

# jvm.options settings
# Max 50% of total RAM, never more than 31GB (Compressed OOPs limit)
-Xms16g
-Xmx16g

# G1GC settings (default in Elasticsearch 8.x)
-XX:+UseG1GC
-XX:G1HeapRegionSize=16m
-XX:InitiatingHeapOccupancyPercent=30
-XX:+ParallelRefProcEnabled

# Enable GC logging
-Xlog:gc*,gc+age=trace,safepoint:file=logs/gc.log:utctime,pid,tags:filecount=32,filesize=64m

ストレージ最適化

# Force merge segments (on read-only indices)
curl -X POST "localhost:9200/logs-2026.03.01/_forcemerge?max_num_segments=1"

# Check index compression
curl -X GET "localhost:9200/_cat/indices/logs-*?v&h=index,store.size,pri.store.size,docs.count&s=index"

# Disk watermark settings
curl -X PUT "localhost:9200/_cluster/settings" \
  -H 'Content-Type: application/json' \
  -d '{
  "persistent": {
    "cluster.routing.allocation.disk.watermark.low": "85%",
    "cluster.routing.allocation.disk.watermark.high": "90%",
    "cluster.routing.allocation.disk.watermark.flood_stage": "95%"
  }
}'

運用時の注意事項

1. マッピング爆発の防止

ダイナミックマッピングが有効な状態でキー名が自由なJSONログをインデキシングすると、フィールド数が急増しクラスタが不安定になります。

# Set mapping field count limit
curl -X PUT "localhost:9200/_index_template/logs-template" \
  -H 'Content-Type: application/json' \
  -d '{
  "index_patterns": ["logs-*"],
  "template": {
    "settings": {
      "index.mapping.total_fields.limit": 1000
    },
    "mappings": {
      "dynamic": "strict"
    }
  }
}'

2. シャード過剰割り当ての注意

  • 1,000個以上の小規模シャードはマスターノードに深刻な負荷を与えます
  • インデックスあたりのシャード数を最小化し、ILMロールオーバーでサイズベースの分割を使用します
  • _cat/shards APIで定期的にモニタリングします

3. GCプレッシャーの管理

  • ヒープを31GB以下に維持してCompressed OOPsを活用します
  • Old GCが頻繁な場合、fielddataキャッシュサイズを制限します
  • circuit breaker設定でOOMを防止します
# Circuit breaker settings
curl -X PUT "localhost:9200/_cluster/settings" \
  -H 'Content-Type: application/json' \
  -d '{
  "persistent": {
    "indices.breaker.total.limit": "70%",
    "indices.breaker.fielddata.limit": "40%",
    "indices.breaker.request.limit": "40%"
  }
}'

4. セキュリティ設定

# elasticsearch.yml - Security settings
xpack.security.enabled: true
xpack.security.transport.ssl.enabled: true
xpack.security.transport.ssl.verification_mode: certificate
xpack.security.transport.ssl.keystore.path: /etc/elasticsearch/certs/elastic-certificates.p12
xpack.security.transport.ssl.truststore.path: /etc/elasticsearch/certs/elastic-certificates.p12

xpack.security.http.ssl.enabled: true
xpack.security.http.ssl.keystore.path: /etc/elasticsearch/certs/http.p12

障害事例と復旧手順

障害事例1: クラスタステータスRED

症状: プライマリシャードが割り当てられず、データ損失の可能性

# Check unassigned shards
curl -X GET "localhost:9200/_cat/shards?v&h=index,shard,prirep,state,unassigned.reason&s=state"

# Diagnose unassignment cause
curl -X GET "localhost:9200/_cluster/allocation/explain?pretty"

# Manual shard allocation (when disk space is insufficient)
curl -X POST "localhost:9200/_cluster/reroute" \
  -H 'Content-Type: application/json' \
  -d '{
  "commands": [
    {
      "allocate_stale_primary": {
        "index": "logs-2026.03.10",
        "shard": 0,
        "node": "data-hot-02",
        "accept_data_loss": true
      }
    }
  ]
}'

障害事例2: インデキシング遅延(Bulk Rejection)

症状: Fluentdログに 429 Too Many Requests エラーが大量発生

# Check thread pool status
curl -X GET "localhost:9200/_cat/thread_pool/write?v&h=node_name,active,rejected,queue,completed"

# Adjust bulk queue size
curl -X PUT "localhost:9200/_cluster/settings" \
  -H 'Content-Type: application/json' \
  -d '{
  "persistent": {
    "thread_pool.write.queue_size": 1000
  }
}'

復旧手順:

  1. Fluentdバッファ状態確認(ディスクバッファ残量)
  2. Elasticsearchノード追加またはバルクキューサイズ増加
  3. refresh_intervalを一時的に60秒に増加
  4. 必要に応じてレプリカを0に減らしインデキシング負荷を軽減
  5. 安定化後、レプリカを元の値に復元

障害事例3: ディスクウォーターマーク超過

症状: インデックスがread-onlyモードに切り替わる

# Release read-only (after securing disk space)
curl -X PUT "localhost:9200/_all/_settings" \
  -H 'Content-Type: application/json' \
  -d '{
  "index.blocks.read_only_allow_delete": null
}'

# Manually delete old indices
curl -X DELETE "localhost:9200/logs-2026.01.*"

# Check disk usage
curl -X GET "localhost:9200/_cat/allocation?v"

モニタリング構成

ELKスタック自体をモニタリングするための別途モニタリングクラスタの構成が推奨されます。

# metricbeat.yml - Elasticsearch monitoring
metricbeat.modules:
  - module: elasticsearch
    xpack.enabled: true
    period: 10s
    hosts:
      - 'https://es-node-01:9200'
      - 'https://es-node-02:9200'
    username: 'monitoring_user'
    password: 'secure_password'
    ssl.certificate_authorities:
      - /etc/metricbeat/certs/ca.crt

  - module: kibana
    xpack.enabled: true
    period: 10s
    hosts:
      - 'https://kibana:5601'

output.elasticsearch:
  hosts:
    - 'https://monitoring-es:9200'
  username: 'metricbeat_writer'
  password: 'secure_password'

核心モニタリング指標は以下の通りです。

指標しきい値対処
クラスタステータスRED即座の対応が必要
JVMヒープ使用率85%以上ノード追加またはヒープ調整
インデキシング速度急激な変動ソース確認
検索レイテンシ5秒以上シャード/クエリ最適化
ディスク使用率85%以上ILMポリシー点検
未割当シャード数0超過割り当て原因の診断
Fluentdバッファサイズしきい値超過出力ボトルネック確認

まとめ

ELK/EFKスタックは成熟したエコシステムと豊富な機能を備えたログパイプラインソリューションです。しかしプロダクションで安定的に運用するには、Elasticsearchクラスタアーキテクチャ設計、シャード戦略、ILMポリシー、Fluentdバッファリング戦略、そしてモニタリング体制まで総合的に考慮する必要があります。

核心ポイントは以下の通りです。

  • ノード役割の分離: Master、Data Hot/Warm/Cold、Coordinatingノードを分離して障害分離とパフォーマンス最適化を実現する
  • ILM活用: Hot-Warm-Cold-Delete段階の自動遷移でストレージコストを最適化する
  • Fluent Bit + Fluentdの組み合わせ: エッジでの軽量収集と中央での変換・ルーティングを分離する
  • マッピング管理: strictマッピングとフィールド数制限でマッピング爆発を防止する
  • モニタリング: 別途モニタリングクラスタでELKスタック自体を監視する

規模が小さい環境では単一ノードや小規模クラスタから始め、データ量の増加に応じてHot-Warm-ColdアーキテクチャとILMを段階的に導入することを推奨します。

参考資料