Hatoholのnログ蓄積・検索機能n2014/12版

: author

須藤功平

: institution

株式会社クリアコード

: date

2014/12/09

: allotted-time

60m

: theme

clear-code

内容

* ログ蓄積・検索機能の概要
  * ただし2014年12月時点での情報
* 詳細はWikiを参照
  * (('note:https://github.com/project-hatohol/hatohol/wiki/Log-archive'))
  * (('note:https://github.com/project-hatohol/hatohol/wiki/Log-search'))

目的

* 現状を共有すること

蓄積:解決したい問題

* 大量のログを蓄積したい
  * 目的:ルールを満たすため\n
    (('note:例:一定期間の過去ログ蓄積が義務'))
  * 目的:問題発生時期の特定\n
    (('note:問題発覚時にいつから問題が起こっていたかを確認'))

蓄積:解決方法の問題

* ZabbixでやるならRDBMSへ蓄積
* RDBMSへ蓄積:オーバースペック
  * リソース消費が割にあわない\n
    * めったに参照しない
    * 処理量が多い
  * レプリケーション必須
    * 失いたくないデータだから

蓄積:解決方針

* ファイルに保存
  * オーバーヘッドが少ない
  * 扱いやすい(コピー・圧縮)
* Fluentdと連携
  * データ配送システム

蓄積:構成

# image
# src = images/log-archive-system.svg
# relative_width = 100

スライドプロパティー

: enable-title-on-image

false

蓄積:ポイント(1)

* 監視対象は生ログを収集・転送
  * 直接蓄積ノードに送らない
* ルーティングノードの導入
  * 目的:構成変更耐性の強化
  * 変更:蓄積ノードの増減
  * 全監視対象を変更するより楽

蓄積:ポイント(2)

* 蓄積ノードでのソート
  * 時間順にログが届くとは限らない
  * 一定時間バッファリング
  * バッファ内をソート後に書き込み
  * ↑用プラグインは新規開発\n
    (('note:(fluent-plugin-sort)'))

蓄積:タグ設計

* 監視対象
  * (({raw.${type}.log.${host_name}}))
  * 例:(({raw.messages.log.node1}))
* 蓄積ノード
  * (({raw....}))→ソート→(({sorted.raw....}))
  * →蓄積

蓄積:設定:監視対象(1)

<source>
  type tail
  path /var/log/messages
  pos_file /var/log/td-agent/messages.pos
  tag "raw.messages.log.#{Socket.gethostname}"
  format none
</source>

蓄積:設定:監視対象(2)

<match raw.*.log.**>
  type copy
  <store>
    type secure_forward
    shared_key fluentd-secret
    self_hostname "#{Socket.gethostname}"
    # バッファー設定は省略
    <server>
      host router1.example.com
    </server> # ←ルーティングノードの数だけ書く
  </store>
</match>

蓄積:設定:ルーター(1)

# 受信
<source>
  type secure_forward
  shared_key fluentd-secret
  self_hostname "#{Socket.gethostname}"
  cert_auto_generate yes
</source>

蓄積:設定:ルーター(2)

<match raw.*.log.**> # 転送
  type forest
  subtype secure_forward
  <template> # 共通設定
    shared_key fluentd-secret
    self_hostname "#{Socket.gethostname}"
    # バッファー設定は省略
  </template>
  # 個別ケースは後述
  <case **> # デフォルト
    <server>
      host archiver2.example.com
    </server>
  </case>
</match>

蓄積:設定:ルーター(3)

# 個別設定
<case raw.*.log.node1.example.com>
  <server>
    host archiver1.example.com
  </server>
</case>
<case raw.*.log.node2.example.com>
  <server>
    host archiver2.example.com
  </server>
</case>

蓄積:設定:蓄積(1)

# 受信
<source>
  type secure_forward
  shared_key fluentd-secret
  self_hostname "#{Socket.gethostname}"
  cert_auto_generate yes
</source>

蓄積:設定:蓄積(2)

# ソート
<match raw.**>
  type sort
  add_tag_prefix sorted.
  buffer_type file
  buffer_path /var/spool/td-agent/buffer/sort
  flush_interval 60
</match>

蓄積:設定:蓄積(3)

# タグの並び替え(次の処理をしやすくするためだけ)
# sorted.raw.${type}.log.${host_name}
# ↓
# archive.raw.log.${host_name}.${type}
<match sorted.raw.**>
  type record_reformer
  enable_ruby false

  tag archive.${tag_parts[1]}.${tag_parts[3]}.${tag_suffix[4]}.${tag_parts[2]}
</match>

蓄積:設定:蓄積(4)

<match archive.raw.log.**> # ←蓄積
  type forest
  remove_prefix archive.raw.log
  # ↓タグの区切りでディレクトリーを掘る
  escape_tag_separator /
  subtype file
  <template>
    path /var/log/archive/${escaped_tag}
    compress gz # ← 圧縮
    format single_value
    append true
    flush_interval 60
  </template>
</match>

蓄積:Hatohol連携

なし

蓄積:Hatohol連携案(1)

* 蓄積ノードへリンク
  * ログは単なるファイル
  * HTTPで公開することは簡単
* 課題
  * アクセス権限はどうする?
  * Hatoholが認可サーバーになる?
  * HatoholをLDAP対応させる?

蓄積:Hatohol連携案(2)

* ログ検索システムへのロードUI
  * 検索→ログ検索システム(('note:(後述)'))へ\n
    ロードが必要
  * ロードは(({fluent-cat}))で可能
* 課題
  * 蓄積ノードでのコマンド実行が必要
  * ホスト管理機能との連携で可能?

検索:解決したい問題

* ログの確認が面倒
  * 対象ホスト数が多い\n
    (('note:(Hatoholは大規模システム用のソフトウェア)'))
    * 個別にログインするのが面倒
  * 対象ログ数が多い
    * 該当ログを探すのが面倒

検索:解決方法

* 全文検索システムの利用
* 要件
  * 最新のログをすぐに検索できる
  * 十分高速
  * 検索対象は最新数週間から数ヶ月

検索:全文検索システム

* Groongaを採用
  * 即時検索可能・十分高速
  * 追加中も検索性能が落ちない\n
    (('note:(ログは常に追加される)'))
  * nginxのモジュールとしても使える
    * nginxの機能・ノウハウを使える\n
      (('note:(HTTPSや認証周りなど)'))
    * 運用が容易

検索:構成

# image
# src = images/log-search-system.svg
# relative_width = 100

スライドプロパティー

: enable-title-on-image

false

検索:ポイント

* 監視対象は生ログを収集・転送
  * パースしない
  * ログ蓄積と同じ設定→共通化可能
* ログパースノードの導入
  * 監視対象の負荷を下げる

検索:構成:共通化

# image
# src = images/log-archive-and-search-system.svg
# relative_width = 100

スライドプロパティー

: enable-title-on-image

false

検索:タグ設計(1)

* 監視対象
  * (({raw.${type}.log.${host_name}}))
  * 例:(({raw.messages.log.node1}))
* ログパースノード
  * (({raw.${type}....}))→パース→
  * (({${type}....}))→フォーマット→(({log}))

検索:タグ設計(2)

* 検索ノード
  * (({log}))→Groonga

検索:設定:監視対象(1)

<source>
  type tail
  path /var/log/messages
  pos_file /var/log/td-agent/messages.pos
  tag "raw.messages.log.#{Socket.gethostname}"
  format none
</source>

検索:設定:監視対象(2)

<match raw.*.log.**>
  type copy
  <store>
    # 蓄積用設定
  </store>
  <store>
    type secure_forward
    # 蓄積用と同じなので設定は省略
    <server>
      host parser1.example.com
    </server> # ←ログパースノードの数だけ書く
  </store>
</match>

検索:設定:パース(1)

# 受信
<source>
  type secure_forward
  shared_key fluentd-secret
  self_hostname "#{Socket.gethostname}"
  cert_auto_generate yes
</source>

検索:設定:パース(2)

# パース
<match raw.*.log.**>
  type forest
  subtype parser
  <template>
    key_name message
  </template>
  <case raw.messages.log.**>
    remove_prefix raw
    format syslog # ← messages型はsyslogとしてパース
  </case>
</match>

検索:設定:パース(3)

# フォーマット
<match *.log.*.**>
  type record_reformer
  enable_ruby false
  tag ${tag_parts[1]} # ←tagをlogだけにする
  <record> # メタデータをデータにする
    host ${tag_suffix[2]}
    type ${tag_parts[0]}
    timestamp ${time}
  </record>
</match>

検索:設定:パース(4)

# 転送
<match log>
  type secure_forward
  shared_key fluentd-secret
  self_hostname "#{Socket.gethostname}"
  # バッファー設定は省略
  <server>
    host search.example.com
  </server>
</match>

検索:設定:検索(1)

# 受信
<source>
  type secure_forward
  shared_key fluentd-secret
  self_hostname "#{Socket.gethostname}"
  cert_auto_generate yes
</source>

検索:設定:検索(2)

# 保存
<match log>
  type groonga
  store_table Logs
  # バッファー設定は省略
  protocol http
  host 127.0.0.1
  # スキーマ定義
</match>

検索:設定:検索(3)

# スキーマ定義
<match log>
  <table> # 全文検索用語彙表
    name Terms
    flags TABLE_PAT_KEY
    key_type ShortText
    default_tokenizer TokenBigram
    normalizer NormalizerAuto
  </table>
</match>

検索:設定:検索(4)

# スキーマ定義
<match log>
  <table> # ホスト名データを共有
    name Hosts
    flags TABLE_PAT_KEY
    key_type ShortText
    # normalizer NormalizerAuto
  </table>
</match>

検索:設定:検索(5)

# スキーマ定義
<match log>
  <table> # タイムスタンプ用語彙表
    name Timestamps
    flags TABLE_PAT_KEY
    key_type Time
  </table>
</match>

検索:設定:検索(6)

# スキーマ定義
<match log>
  <mapping> # ホスト名データを共有
    name host
    type Hosts
    <index> # 高速検索のための索引定義
      table Hosts
      name logs_index
    </index>
  </mapping>
</match>

検索:設定:検索(7)

# スキーマ定義
<match log>
  <mapping>
    name timestamp
    type Time
    <index> # 高速検索のための索引定義
      table Timestamps
      name logs_index
    </index>
  </mapping>
</mapping>

検索:設定:検索(7)

# スキーマ定義
<match log>
  <mapping>
    name message
    type Text
    <index> # 全文検索用索引定義
      table Terms
      name logs_message_index
    </index>
  </mapping>
</match>

検索:設定:Hatohol(1)

# image
# src = images/hatohol-log-search-systems-configuration-menu.png
# relative_height = 60

((‘tag:center’))検索UI管理画面に移動

検索:設定:Hatohol(2)

# image
# src = images/hatohol-log-search-systems-add-button.png
# relative_height = 60

((‘tag:center’))検索UIの追加フォームを出す

検索:設定:Hatohol(3)

# image
# src = images/hatohol-log-search-systems-add-form.png
# relative_height = 60

((‘tag:center’))検索UIの追加フォームを入力

検索:Hatohol連携

# image
# src = images/hatohol-log-search-form.png
# relative_width = 100

((‘tag:center’))ダッシュボードに検索フォーム

検索:Hatohol連携案(1)

* イベントページにリンク
  * 障害情報から検索できる
  * 時間情報から検索できる
* 課題
  * ログ以外の情報が多い
  * ↑検索可能ではない情報が多い

検索:Hatohol連携案(2)

* ログ以外も検索可能にする
  * 例:イベントの説明
  * 例:Redmineのコメント
* 課題
  * 検索結果の見せ方はどうなる?
  * 見つかった後の動線は?
  * Hatoholに戻る?Redmineに行く?

検索:Hatohol連携案(3)

* 検索キーワードを事前登録
  * クリックで検索できて楽
* 課題
  * 誰が登録するのか
  * 既存ログから拾ってこれると\n
    よさそう?

検索:課題

* ユーザー毎に権限設定できない
  * ログ検索UIのURLをサーバーではなく\n
    UIに保存しているため
* 検索UIからHatoholに戻って\n
  これない
  * Redmine連携も同じ?