Elasticsearch Rally ベンチマーク入門: バージョン 8.x と 9.x の性能比較

BLOG

Elasticsearchのパフォーマンスを正確に把握したいと思ったことはありませんか?

Elasticsearchのアップグレードを前に、「この変更でパフォーマンスが低下(リグレッション)したらどうしよう」と不安になったことはありませんか? あるいは、新しいCPUやRAM、ディスクを選定する際に「どちらが我々のワークロードに本当に最適なのか」をデータで証明する必要に迫られたり、インデックス速度向上のためにパイプラインを調整したものの「その効果がどれほどだったか」を正確に示せなかったり。さらには、苦労して達成した性能向上を、説得力のあるレポートやプレゼン資料にまとめるのに苦労した経験は? もし、このような「性能に関する悩み」に一つでも心当たりがあるなら、「Rally」こそがあなたのためのツールです。本記事では、Elasticsearch Rally を初めて使用した経験を基に、Elasticsearch 8.11.0 と 9.0.0 の性能を比較するプロセスを詳しく書きます。

注意: これは私にとって初めてのRally使用体験であり、いくつかの失敗や学びがありました。この記事では、実際に遭遇した問題と、それらをどのように解決したかを正直に共有します。完璧なベンチマークガイドではなく、学習過程の記録として読んでいただければ幸いです。

  1. プロジェクト概要
    1. 目的
    2. テストデータセット
    3. テスト環境
    4. 実施内容と制限事項
  2. Rally の基礎知識
    1. Race(レース)とは
    2. Track(トラック)とは
    3. Challenge(チャレンジ)とは
    4. Clients(クライアント)の理解
  3. 環境構築
    1. ステップ1: AWS EC2インスタンスの起動
    2. ステップ2: システム依存関係のインストール
    3. ステップ3: Javaのインストール
      1. Java 17のインストール
      2. Java 21のインストール(ES 9.x用 – 後で気づいた必須要件)
    4. ステップ4: Rallyのインストール
    5. ステップ5: インストールの検証
  4. カスタムトラックの作成
    1. ディレクトリ構造
    2. ステップ1: インデックス設定とマッピングの作成
    3. ステップ2: 合成ログデータの生成
    4. ステップ3: トラック定義の作成
    5. 重要: Jinja2エスケープ
  5. インジェストパイプラインの構築
    1. パイプライン定義
    2. 重要: インラインvs外部パイプライン定義
    3. パイプライン適用の確認
  6. ベンチマークの実行
    1. テストフェーズ: 1Kサブセットでの検証
    2. 本番ベンチマーク
      1. 重要: 公平な比較の確保について
      2. Elasticsearch 8.11.0でのベンチマーク(Java 17使用 – 私の最初の試み)
      3. Elasticsearch 9.0.0でのベンチマーク(Java 21が必要 – ここで学習)
      4. 今回実行したチャレンジ
      5. 将来実行予定のチャレンジ
    3. レース出力の理解
    4. 結果の比較
  7. トラブルシューティング
    1. 問題1: パイプラインが適用されない
    2. 問題2: Jinja2テンプレートエラー
    3. 問題3: ウォームアップ警告
    4. 問題4: uncompressed-bytes検証エラー
    5. 問題5: Elasticsearch 9.0.0が起動しない
    6. 問題6: パイプラインパラメータの欠落
    7. デバッグのヒント
  8. ベストプラクティス
    1. 1. Javaバージョン管理
    2. 2. ウォームアップとイテレーション設定
    3. 3. トラックの整理
    4. 4. リソースモニタリング
    5. 5. データ管理
    6. 6. 結果管理
    7. 7. ベンチマークにタグを付ける
  9. 結果と分析
    1. 分析すべき主要メトリクス
      1. 1. スループット
      2. 2. レイテンシパーセンタイル
      3. 3. インジェストパイプラインメトリクス
      4. 4. マージ時間
      5. 5. エラー率
    2. サンプル比較出力
    3. 学んだこと
  10. 今後の展開

プロジェクト概要

目的

このプロジェクトの主な目標は以下の通りです:

  1. Elasticsearch Rally の使い方を理解する(最優先事項)
  2. カスタムログデータを使用したインジェストパイプラインのベンチマークを実施する
  3. Elasticsearch 8.11.0 と 9.0.0 の性能を比較する

テストデータセット

  • データ量: 100,000 件の合成ログデータ
  • テストサブセット: 1,000 件(検証用)
  • 形式: JSONL(JSON Lines)
  • 内容: タイムスタンプ、クライアントIP、レスポンスコード、OS情報などを含むウェブアクセスログ

テスト環境

  • プラットフォーム: AWS EC2
  • OS: Ubuntu 24.04 LTS
  • インスタンスタイプ: t3.medium(最小推奨)
  • Java:
    • Java 17(最初のES 8.x テスト用)
    • Java 21(ES 9.x テスト用 – 必須要件)
  • Python: 3.12.x
  • Rally: 2.x

実施内容と制限事項

今回実施したのは以下の通りです:

完了したこと:

  • Elasticsearch 8.11.0 でのデフォルトチャレンジ(4クライアント)実行
  • Elasticsearch 9.0.0 の環境構築とセットアップ
  • インジェストパイプラインの作成と検証
  • Rally の基本的な使い方の理解

今後実施予定:

  • 高並行チャレンジ(8クライアント)
  • ランプアップチャレンジ(段階的負荷増加)
  • 公平な比較のためのES 8.11.0 + Java 21でのテスト再実行
  • 詳細な性能比較レポートの作成

学習上の妥協点: 最初にES 8.11.0をJava 17でテストした後、ES 9.0.0にはJava 21が必要であることを知りました。理想的にはES 8.11.0もJava 21で再テストすべきですが、今回のプロジェクトの主目的は「Rallyの仕組みを理解すること」だったため、再テストは将来の作業として残しました。


Rally の基礎知識

Rallyを使い始める前に、その核となる概念を理解しましょう。

Race(レース)とは

Race は、1回の完全なベンチマーク実行を表します。これは始めから終わりまでの一連の性能テストです。

Raceには以下が含まれます:

  • Elasticsearchのプロビジョニング(ダウンロード、インストール、起動)
  • インデックスの作成
  • データのインジェスト
  • 操作の実行
  • メトリクスの収集
  • クラスタのシャットダウン

各Raceには結果追跡のための一意の識別子が割り当てられます↓:

[INFO] Race id is [18d32ed3-c159-4c2a-a507-7687bd834edb]

Track(トラック)とは

Track は、完全なベンチマークシナリオを定義するJSONファイルです。これはRallyがテストすべき内容の設計図です。

Trackには以下が含まれます:

  1. インデックス定義 – マッピングと設定
  2. データソース(Corpora) – テストデータの場所とサイズ
  3. インジェストパイプライン – データ変換の定義
  4. 操作(Operations) – 実行する個別のタスク
  5. チャレンジ(Challenges) – 異なる設定のテストシナリオ

Track構造の詳細については、公式Rallyドキュメントを参照してください。

Challenge(チャレンジ)とは

Challenge は、同じTrack内で異なるテストシナリオを定義します。複数のChallengeを使用することで、別々のTrackを作成せずに様々な条件下でテストできます。

チャレンジの例:

{
  "challenges": [
    {
      "name": "default",
      "description": "標準的な4クライアントベンチマーク",
      "default": true
    },
    {
      "name": "high-concurrency",
      "description": "8クライアントでのストレステスト"
    },
    {
      "name": "ramp-up",
      "description": "1から8クライアントへの段階的負荷増加"
    }
  ]
}

今回はdefaultチャレンジ(4クライアント)のみを実行しました。高並行チャレンジとランプアップチャレンジは定義しましたが、Rallyの基本を理解することに集中するため、実行は将来に延期しました。

Clients(クライアント)の理解

clientsパラメータはRallyの重要な設定オプションの一つですが、誤解されやすいです。

Client = 同時実行スレッド

Rallyにおける「クライアント」は、Elasticsearchに同時にリクエストを送信する単一のスレッドを表します。

{
  "operation": "bulk-index",
  "clients": 4  // 4つのスレッドが同時に実行
}

動作の仕組み:

クライアント 1: [リクエスト] → [待機] → [リクエスト] → [待機] → ...

クライアント 2: [リクエスト] → [待機] → [リクエスト] → [待機] → ...

クライアント 3: [リクエスト] → [待機] → [リクエスト] → [待機] → ...

クライアント 4: [リクエスト] → [待機] → [リクエスト] → [待機] → ...

スループットへの影響:

クライアント数期待スループット説明
1~500 docs/s単一スレッドは待機時間を無駄にする
2~900 docs/sほぼ2倍に増加
4~1,600 docs/s標準的な設定
8~2,800 docs/s高負荷(収穫逓減)

よくある誤解:

  • ❌ clients: 4 = 4台の別々のマシン
  • ✅ clients: 4 = 1台のマシン上の4つのスレッド

推奨設定:

環境クライアント数目的
2コアCPU2-4基本的な性能テスト
4コア以上のCPU4-8標準的なベンチマーク
高性能環境8-16ストレステスト

私の選択: Rally初心者として、標準的な4クライアント設定から始めました。これにより、基本的な概念を理解しながら、過度に複雑でない結果を得ることができました。

環境構築

ステップ1: AWS EC2インスタンスの起動

  1. AWS EC2コンソールに移動
  2. 「インスタンスを起動」をクリック
  3. 設定:
    • 名前: rally-benchmark-server
    • AMI: Ubuntu Server 24.04 LTS
    • インスタンスタイプ: t3.medium以上
    • ストレージ: 最低20GB
    • セキュリティグループ: SSH(ポート22)を許可
  4. 起動して接続:
ssh -i /path/to/your-key.pem ubuntu@<ec2-public-ip>

ステップ2: システム依存関係のインストール

# システムパッケージの更新
sudo apt update && sudo apt upgrade -y

# Pythonと開発ツールのインストール
sudo apt install -y python3-pip python3-venv python3-dev build-essential git curl

# Pythonバージョンの確認(3.9以上である必要があります)
python3 --version

ステップ3: Javaのインストール

Javaバージョンに関する重要な注意事項:

これは私が最初に犯した間違いです。

  1. 最初にJava 17のみをインストール
  2. ES 8.11.0でベンチマークを実行(Java 17使用)
  3. ES 9.0.0を試したときに、Java 21が必要であることを発見
  4. Java 21をインストール
  5. ES 9.0.0でベンチマークを実行(Java 21使用)

理想的なアプローチ: 公平な比較のために、両方のバージョンにJava 21を使用すべきでした。しかし、今回のプロジェクトの主な目標はRallyを理解することだったため、ES 8.11.0のテストをJava 21で再実行しませんでした。これは将来の作業として残しました。

Java 17のインストール

sudo apt install -y openjdk-17-jdk

# JAVA_HOMEの設定
export JAVA_HOME=/usr/lib/jvm/java-17-openjdk-amd64
echo 'export JAVA_HOME=/usr/lib/jvm/java-17-openjdk-amd64' >> ~/.bashrc

# 確認
java -version

Java 21のインストール(ES 9.x用 – 後で気づいた必須要件)

sudo apt install -y openjdk-21-jdk

# JAVA21_HOMEの設定
export JAVA21_HOME=/usr/lib/jvm/java-21-openjdk-amd64
echo 'export JAVA21_HOME=/usr/lib/jvm/java-21-openjdk-amd64' >> ~/.bashrc

# 変更を適用
source ~/.bashrc

# 確認
ls -la /usr/lib/jvm/java-21-openjdk-amd64

ステップ4: Rallyのインストール

# ワークスペースディレクトリの作成
mkdir -p ~/rally-workspace
cd ~/rally-workspace

# Python仮想環境の作成
python3 -m venv rally-venv

# 仮想環境の有効化
source rally-venv/bin/activate

# Rallyのインストール
pip install --upgrade pip
pip install esrally

# インストールの確認
esrally --version

期待される出力:

esrally 2.x.x (Python 3.12.x)

ステップ5: インストールの検証

# 組み込みトラックで簡単なテストを実行
esrally race --distribution-version=8.11.0 --track=geonames --test-mode

これにより、Elasticsearch 8.11.0がダウンロードされ、小規模なテストが実行され、すべてが正常に動作しているか確認されます。

詳細なインストール手順については、Rallyインストールガイドを参照してください。


カスタムトラックの作成

ディレクトリ構造

整理されたトラックディレクトリを作成します:

cd ~/rally-workspace
mkdir -p log_ingest/corpora
cd log_ingest

構造は以下のようになります:

log_ingest/
├── track.json                 # メインのトラック定義
├── index-settings-mapping.json # インデックス設定
└── corpora/
    ├── logs.jsonl            # フルデータセット(100K docs)
    └── logs-1k.jsonl         # テストサブセット(1K docs)

ステップ1: インデックス設定とマッピングの作成

{
  "settings": {
    "index": {
      "number_of_shards": 1,
      "number_of_replicas": 0
    }
  },
  "mappings": {
    "properties": {
      "@timestamp": {
        "type": "date"
      },
      "clientip": {
        "type": "ip"
      },
      "response": {
        "type": "integer"
      },
      "request": {
        "type": "text"
      },
      "machine": {
        "properties": {
          "os": {
            "type": "keyword"
          },
          "ram": {
            "type": "long"
          }
        }
      },
      "ingest_time": {
        "type": "date"
      }
    }
  }
}

重要なポイント:

  • number_of_shards: 1 – ベンチマークを簡素化
  • number_of_replicas: 0 – テストのためのより高速なインジェスト
  • フィールドタイプはデータ構造と一致

ステップ2: 合成ログデータの生成

次に、ベンチマークに使用する合成ログデータを準備します。今回は、このために簡単なPythonスクリプトを利用しました。

スクリプトは、Rallyのtrack(競技種目)が読み込むための元データとなるログファイルを生成します。実行すると、以下のような2種類のデータセットが出力されるようにしました。

  • corpora/logs.jsonl – 100,000ドキュメント(フルデータセット)
  • corpora/logs-1k.jsonl – 1,000ドキュメント(テスト用)

データ形式の例:

{"@timestamp":"2025-10-16T10:00:00Z","clientip":"192.168.1.100","response":200,"request":"GET /api/users HTTP/1.1","machine":{"os":"linux","ram":8589934592},"referer":"https://example.com/login"}

ステップ3: トラック定義の作成

track.jsonを作成  :

{
  "version": 2,
  "description": "ES 8.x vs 9.x のログインジェストパイプラインベンチマーク",
  "indices": [
    {
      "name": "logs",
      "body": "index-settings-mapping.json"
    }
  ],
  "corpora": [
    {
      "name": "logcorpus",
      "documents": [
        {
          "source-file": "corpora/logs.jsonl",
          "document-count": 100000
        }
      ]
    }
  ],
  "operations": [
    {
      "name": "delete-index",
      "operation-type": "delete-index"
    },
    {
      "name": "create-index",
      "operation-type": "create-index"
    },
    {
      "name": "create-log-pipeline",
      "operation-type": "put-pipeline",
      "id": "log-pipe",
      "body": {
        "description": "ログ処理パイプライン",
        "processors": [
          {
            "set": {
              "field": "ingest_time",
              "value": "{{'{{'}}_ingest.timestamp{{'}}'}}"
            }
          },
          {
            "uppercase": {
              "field": "machine.os"
            }
          },
          {
            "convert": {
              "field": "response",
              "type": "integer"
            }
          },
          {
            "remove": {
              "field": "referer"
            }
          }
        ]
      }
    },
    {
      "name": "bulk-index-with-pipeline",
      "operation-type": "bulk",
      "bulk-size": 1000,
      "pipeline": "log-pipe"
    }
  ],
  "challenges": [
    {
      "name": "default",
      "description": "4クライアントでの標準ベンチマーク",
      "default": true,
      "schedule": [
        {
          "operation": "delete-index"
        },
        {
          "operation": "create-index"
        },
        {
          "operation": "create-log-pipeline"
        },
        {
          "operation": "bulk-index-with-pipeline",
          "warmup-iterations": 10,
          "iterations": 100,
          "clients": 4
        }
      ]
    },
    {
      "name": "high-concurrency",
      "description": "8クライアントでの高負荷テスト",
      "schedule": [
        {
          "operation": "delete-index"
        },
        {
          "operation": "create-index"
        },
        {
          "operation": "create-log-pipeline"
        },
        {
          "operation": "bulk-index-with-pipeline",
          "warmup-iterations": 10,
          "iterations": 100,
          "clients": 8
        }
      ]
    },
    {
      "name": "ramp-up",
      "description": "1から8クライアントへの段階的負荷増加",
      "schedule": [
        {
          "operation": "delete-index"
        },
        {
          "operation": "create-index"
        },
        {
          "operation": "create-log-pipeline"
        },
        {
          "operation": "bulk-index-with-pipeline",
          "warmup-iterations": 5,
          "iterations": 50,
          "clients": 1,
          "target-throughput": 500
        },
        {
          "operation": "bulk-index-with-pipeline",
          "warmup-iterations": 5,
          "iterations": 50,
          "clients": 2,
          "target-throughput": 1000
        },
        {
          "operation": "bulk-index-with-pipeline",
          "warmup-iterations": 5,
          "iterations": 50,
          "clients": 4,
          "target-throughput": 2000
        },
        {
          "operation": "bulk-index-with-pipeline",
          "warmup-iterations": 5,
          "iterations": 50,
          "clients": 8
        }
      ]
    }
  ]
}

トラックの主要コンポーネントの説明:

  1. Corpora: データファイルを指定
    • トラックディレクトリからの相対パスを使用
    • document-countは正確である必要があります
    • uncompressed-bytesは省略(Rallyが計算)
  2. Operations: 個別のタスクを定義
    • delete-index: 各実行でクリーンスレート
    • create-index: マッピングを適用
    • create-log-pipeline: 変換を定義
    • bulk-index-with-pipeline: ベンチマークを実行
  3. Challenges: 異なるテストシナリオ
    • default: 標準的な4クライアントテスト(今回実施)
    • high-concurrency: 8クライアントでのストレステスト(将来実施)
    • ramp-up: スケーラビリティテスト(将来実施)

重要: Jinja2エスケープ

これは私が遭遇した最初の技術的な問題でした!

RallyはJinja2テンプレートを使用するため、Elasticsearchのテンプレート構文{{…}}をエスケープする必要があります:

// ❌ 間違い - Rallyがこれを解釈しようとします
"value": "{{_ingest.timestamp}}"

// ✅ 正しい - Jinja2のためにエスケープ
"value": "{{'{{'}}_ingest.timestamp{{'}}'}}"

これにより、最終的なJSONがElasticsearchが処理するための{{_ingest.timestamp}}を含むことが保証されます。

学んだ教訓: このエスケープを最初は理解していなかったため、「_ingest is undefined」エラーが発生しました。Rallyのドキュメントを読むことで解決しました。


インジェストパイプラインの構築

パイプライン定義

私のパイプラインは4つの変換を実行します:

  1. インジェストタイムスタンプの追加
{
  "set": {
    "field": "ingest_time",
    "value": "{{'{{'}}_ingest.timestamp{{'}}'}}"
  }
}
  1. OSフィールドの大文字変換
{
  "uppercase": {
    "field": "machine.os"
  }
}
  1. レスポンスを整数に変換
{
  "convert": {
    "field": "response",
    "type": "integer"
  }
}
  1. refererフィールドの削除
{
  "remove": {
    "field": "referer"
  }
}

重要: インラインvs外部パイプライン定義

方法1: 外部ファイル(動作しない場合があります)

"ingest_pipelines": [
  {
    "name": "log-pipe",
    "body": "ingest-pipeline.json"
  }
]

方法2: インライン定義(推奨)

"operations": [
  {
    "name": "create-log-pipeline",
    "operation-type": "put-pipeline",
    "id": "log-pipe",
    "body": {
      "processors": [...]
    }
  }
]

インライン定義の方が信頼性が高いことがわかりました。スケジュールにパイプライン作成を含めてください:

"schedule": [
  {"operation": "create-index"},
  {"operation": "create-log-pipeline"},  // 明示的な作成
  {"operation": "bulk-index-with-pipeline"}
]

私は最初は外部ファイル参照を使用しようとしましたが、パイプラインが適用されませんでした(カウント0)。インライン定義に切り替えることで問題が解決しました。

パイプライン適用の確認

ベンチマーク実行後、パイプラインが適用されたかを確認:

# Rally出力でパイプラインメトリクスを確認
grep "Ingest Pipeline" ~/.rally/logs/rally.log

# --preserve-installを使用した場合、Elasticsearchに直接クエリ
curl -X GET "localhost:PORT/_ingest/pipeline/log-pipe?pretty"
curl -X GET "localhost:PORT/logs/_search?size=1&pretty"

インジェストパイプラインの詳細については、Elasticsearchインジェストパイプラインドキュメントを参照してください。


ベンチマークの実行

テストフェーズ: 1Kサブセットでの検証

まず、小さなデータセットでテスト:

# Rally環境を有効化
source ~/rally-workspace/rally-venv/bin/activate

# 1Kサブセットでテスト
esrally race \
  --track-path=~/rally-workspace/log_ingest \
  --distribution-version=8.11.0 \
  --test-mode

–test-modeフラグ:

  • 最小限のイテレーションを実行
  • 最大1000ドキュメントのみを処理
  • 設定エラーを迅速に特定

本番ベンチマーク

重要: 公平な比較の確保について

これは私の学習プロセスで理解した重要なポイントです。

❌ 不公平な比較(私が最初に行ったこと):

ES 8.11.0 (Java 17) vs ES 9.0.0 (Java 21)
→ 2つの変数が変化: ESバージョンとJavaバージョン

✅ 公平な比較(次回行うべきこと):

ES 8.11.0 (Java 21) vs ES 9.0.0 (Java 21)
→ ESバージョンのみが変化

Elasticsearch 8.11.0でのベンチマーク(Java 17使用 – 私の最初の試み)

# Java 17が設定されていることを確認
export JAVA_HOME=/usr/lib/jvm/java-17-openjdk-amd64
source ~/.bashrc

# 確認
java -version  # Java 17であることを確認

# デフォルトチャレンジを実行(4クライアント)
esrally race \
  --track-path=~/rally-workspace/log_ingest \
  --distribution-version=8.11.0 \
  --user-tag="version:8.11.0,java:17,challenge:default"

実行結果: これは正常に動作し、Rallyの動作を理解するのに役立ちました。

Elasticsearch 9.0.0でのベンチマーク(Java 21が必要 – ここで学習)

ES 9.0.0を実行しようとしたとき、Java 21が必要であることを発見しました:

# 最初の試み(失敗)
esrally race \
  --track-path=~/rally-workspace/log_ingest \
  --distribution-version=9.0.0

# エラー:
# [ERROR] Cannot race. Daemon startup failed with exit code [1]

問題の診断:

# ログを確認
tail -100 ~/.rally/logs/rally.log

# "Java 21 or higher required" のようなメッセージを発見

解決策: Java 21のインストール

# Java 21をインストール
sudo apt install -y openjdk-21-jdk

# JAVA21_HOMEを設定
export JAVA21_HOME=/usr/lib/jvm/java-21-openjdk-amd64
echo 'export JAVA21_HOME=/usr/lib/jvm/java-21-openjdk-amd64' >> ~/.bashrc
source ~/.bashrc

# 確認
echo $JAVA21_HOME
ls -la $JAVA21_HOME

# 再試行
esrally race \
  --track-path=~/rally-workspace/log_ingest \
  --distribution-version=9.0.0 \
  --user-tag="version:9.0.0,java:21,challenge:default"

実行結果: Java 21をインストール後、ES 9.0.0が正常に動作しました。

今回実行したチャレンジ

今回はdefaultチャレンジ(4クライアント)のみを実行しました:

# デフォルトチャレンジの実行
esrally race \
  --track-path=~/rally-workspace/log_ingest \
  --distribution-version=8.11.0 \
  --user-tag="version:8.11.0,java:17"

将来実行予定のチャレンジ

# 高並行チャレンジ(8クライアント)
esrally race \
  --track-path=~/rally-workspace/log_ingest \
  --distribution-version=8.11.0 \
  --challenge=high-concurrency \
  --user-tag="version:8.11.0,java:21,challenge:highcon"

# ランプアップチャレンジ(段階的負荷増加)
esrally race \
  --track-path=~/rally-workspace/log_ingest \
  --distribution-version=8.11.0 \
  --challenge=ramp-up \
  --user-tag="version:8.11.0,java:21,challenge:rampup"

レース出力の理解

実行中、Rallyは進行状況を表示します:

[INFO] Racing on track [log_ingest], challenge [default]
[INFO] Preparing track [log_ingest] for race
[INFO] Downloading Elasticsearch 8.11.0...
[INFO] Starting Elasticsearch cluster
[INFO] Racing (please be patient)...

Running delete-index                    [100% done]
Running create-index                    [100% done]
Running create-log-pipeline             [100% done]
Running bulk-index-with-pipeline        [100% done]

完了後、Rallyは詳細なメトリクスを提供します:

------------------------------------------------------
    Final Results
------------------------------------------------------

|                   Metric |         Task |     Value |   Unit |
|--------------------------|--------------|-----------|--------|
|           Total indexing | bulk-index   |      2.50 |    min |
|            Total merge   | bulk-index   |      0.12 |    min |
|           Throughput     | bulk-index   |   1,650.5 | docs/s |
|       Service time (p50) | bulk-index   |     15.23 |     ms |
|       Service time (p99) | bulk-index   |     42.18 |     ms |
| Total Ingest Pipeline    | bulk-index   |   100,000 |  count |
| Total Ingest Pipeline    | bulk-index   |      1.85 |      s |

結果の比較

# すべてのレースをリスト表示

# すべてのレースをリスト表示
esrally list races

# 2つの特定のレースを比較
esrally compare \
  --baseline=<race-id-es8> \
  --contender=<race-id-es9>

Rallyは各メトリクスの改善率または劣化率をパーセンテージで表示します。

注意: 私のケースでは、Java 17とJava 21を使用したため、この比較は完全に公平ではありません。これは将来改善する必要があります。

結果の解釈の詳細については、Rallyメトリクスドキュメントを参照してください。


トラブルシューティング

これらは私が実際に遭遇した問題と、それらをどのように解決したかです。

問題1: パイプラインが適用されない

症状:

Total Ingest Pipeline count: 0
Total Ingest Pipeline time: 0 s

これは最初のベンチマーク実行で起こりました。パイプラインが定義されているのに、ドキュメントに適用されていませんでした。

解決策:

  1. operations内でインラインパイプライン定義を使用
  2. スケジュールにパイプライン作成を明示的に含める
  3. bulk操作にpipelineパラメータが設定されていることを確認:
{
  "operation": "bulk-index-with-pipeline",
  "operation-type": "bulk",
  "bulk-size": 1000,
  "pipeline": "log-pipe"  // パイプラインIDと一致する必要があります
}

問題2: Jinja2テンプレートエラー

症状:

jinja2.exceptions.UndefinedError: '_ingest' is undefined

パイプラインで{{_ingest.timestamp}}を使用したときにこのエラーが発生しました。

解決策:

Elasticsearchテンプレート構文をエスケープ:

"value": "{{'{{'}}_ingest.timestamp{{'}}'}}"

学んだ教訓: RallyがJinja2テンプレートエンジンを使用していることを知りませんでした。ドキュメントを読んでこれを学びました。

問題3: ウォームアップ警告

症状:

[WARNING] No throughput metrics available for [bulk-index-with-pipeline].
Likely cause: The benchmark ended already during warmup.

解決策:

時間ベースではなくイテレーションベースのウォームアップを使用:

// ❌ 小さなデータセットでの問題
{
  "warmup-time-period": 60,
  "clients": 4
}

// ✅ 解決策
{
  "warmup-iterations": 10,
  "iterations": 100,
  "clients": 4
}

問題4: uncompressed-bytes検証エラー

症状:

jsonschema.exceptions.ValidationError: 0 is less than the minimum of 1
Path: ['corpora', 0, 'documents', 0, 'uncompressed-bytes']

解決策:

フィールドを省略するだけです:

// ❌ 間違い
{
  "source-file": "corpora/logs.jsonl",
  "document-count": 100000,
  "uncompressed-bytes": 0
}

// ✅ 正しい
{
  "source-file": "corpora/logs.jsonl",
  "document-count": 100000
}

Rallyがサイズを自動的に計算します。

問題5: Elasticsearch 9.0.0が起動しない

症状:

[ERROR] Cannot race. Daemon startup failed with exit code [1]

原因: ES 9.xにはJava 21以上が必要

診断:

# Javaバージョンを確認
java -version

# Rallyログを確認
tail -100 ~/.rally/logs/rally.log

解決策:

# Java 21をインストール
sudo apt install -y openjdk-21-jdk

# 環境変数を設定
export JAVA21_HOME=/usr/lib/jvm/java-21-openjdk-amd64
echo 'export JAVA21_HOME=/usr/lib/jvm/java-21-openjdk-amd64' >> ~/.bashrc
source ~/.bashrc

# 再試行
esrally race --track-path=~/log_ingest --distribution-version=9.0.0

学んだ教訓: 常に各Elasticsearchバージョンの最小Java要件を確認してください。

問題6: パイプラインパラメータの欠落

症状:

Parameter source for operation 'put-pipeline' did not provide 
the mandatory parameter 'id' or 'body'

解決策:

idとbodyの両方を含める:

{
  "name": "create-log-pipeline",
  "operation-type": "put-pipeline",
  "id": "log-pipe",          // 必須
  "body": {                   // 必須
    "processors": [...]
  }
}

デバッグのヒント

  1. Rallyログを確認:
tail -f ~/.rally/logs/rally.log
  1. Elasticsearchログを確認:
# レースディレクトリを検索
ls -lt ~/.rally/benchmarks/races/

# ESログを表示
cat ~/.rally/benchmarks/races/<race-id>/rally-node-0/logs/server/rally-benchmark.log
  1. クラスタを検査するために–preserve-installを使用:
esrally race \
  --track-path=~/log_ingest \
  --distribution-version=8.11.0 \
  --preserve-install

# クラスタは手動検査のために実行され続けます

ベストプラクティス

これらは私の学習経験から得たベストプラクティスです。

1. Javaバージョン管理

公平な比較のために常に同じJavaバージョンを使用:

# 両方のバージョンをインストール
sudo apt install -y openjdk-17-jdk openjdk-21-jdk

# 環境変数を設定
export JAVA_HOME=/usr/lib/jvm/java-21-openjdk-amd64
export JAVA17_HOME=/usr/lib/jvm/java-17-openjdk-amd64
export JAVA21_HOME=/usr/lib/jvm/java-21-openjdk-amd64

# 永続化のために.bashrcに追加
echo 'export JAVA_HOME=/usr/lib/jvm/java-21-openjdk-amd64' >> ~/.bashrc
echo 'export JAVA17_HOME=/usr/lib/jvm/java-17-openjdk-amd64' >> ~/.bashrc
echo 'export JAVA21_HOME=/usr/lib/jvm/java-21-openjdk-amd64' >> ~/.bashrc

推奨される比較戦略:

  • ES 8.11.0 + Java 21 vs ES 9.0.0 + Java 21(主な比較)
  • ES 8.11.0 + Java 17(Javaアップグレード影響のためのオプションベースライン)

2. ウォームアップとイテレーション設定

ガイドライン:

データセットサイズウォームアップ戦略推奨
< 10K docsイテレーションベース総イテレーションの10-20%
10K-1M docsイテレーションベース最低10イテレーション
> 1M docs時間またはイテレーション60秒または100イテレーション

例:

{
  "warmup-iterations": 10,
  "iterations": 100,
  "clients": 4
}

3. トラックの整理

推奨されるディレクトリ構造:

rally-workspace/
├── rally-venv/              # Python仮想環境
├── tracks/
│   ├── log_ingest/
│   │   ├── track.json
│   │   ├── index-settings-mapping.json
│   │   └── corpora/
│   │       ├── logs.jsonl
│   │       └── logs-1k.jsonl
│   ├── search_performance/
│   └── bulk_indexing/
├── scripts/
│   ├── synthetic-log-generator.py
│   └── verify_pipeline.sh
└── results/
    └── comparison_reports/

4. リソースモニタリング

ベンチマーク中にEC2リソースを監視:

# CPUとメモリ
htop

# ディスク容量
df -h

# ネットワーク
iftop

# Rallyプロセス
ps aux | grep esrally

# Elasticsearchプロセス
ps aux | grep elasticsearch

5. データ管理

テストサブセットの作成:

# 最初の1000行
head -n 1000 logs.jsonl > logs-1k.jsonl

# ランダムな1000行
shuf -n 1000 logs.jsonl > logs-1k-random.jsonl

# ドキュメント数を確認
wc -l logs.jsonl
wc -l logs-1k.jsonl

ファイル形式要件:

  • フォーマット: JSONL(1行1ドキュメント)
  • エンコーディング: UTF-8
  • 行末: LF(Unix形式)
  • 末尾のカンマやブラケットなし

6. 結果管理

結果を保存して整理:

# resultsディレクトリを作成
mkdir -p ~/rally-workspace/results

# すべてのレースをリスト表示
esrally list races > results/all_races.txt

# 特定のレース詳細をエクスポート
esrally list races --race-id=<race-id> > results/race_<version>.txt

# 比較して保存
esrally compare \
  --baseline=<race-id-8> \
  --contender=<race-id-9> \
  > results/comparison_8_vs_9.txt

7. ベンチマークにタグを付ける

結果を整理するために–user-tagを使用:

esrally race \
  --track-path=~/log_ingest \
  --distribution-version=8.11.0 \
  --user-tag="env:test,purpose:learning,java:17,challenge:default"

これにより、結果のフィルタリングと比較がはるかに簡単になります。

結果と分析

分析すべき主要メトリクス

Rallyは包括的なメトリクスを提供します。以下の主要な指標に焦点を当てます:

1. スループット

測定内容: 1秒あたりに処理されるドキュメント数

スループット = 総ドキュメント数 / 総時間

目標値:

  • 良好: > 1,000 docs/s(このワークロードの場合)
  • 優秀: > 2,000 docs/s
  • 卓越: > 5,000 docs/s

2. レイテンシパーセンタイル

測定内容: レスポンス時間の分布

  • p50(中央値): 50%のリクエストがこの時間内に完了
  • p95: 95%のリクエストがこの時間内に完了
  • p99: 99%のリクエストがこの時間内に完了
  • p100(最大): 最悪ケースのレイテンシ

分析例:

Service time p50: 15 ms   (典型的なリクエスト)
Service time p95: 35 ms   (まだ良好)
Service time p99: 120 ms  (スパイクに注意)
Service time p100: 500 ms (外れ値が存在)

3. インジェストパイプラインメトリクス

パイプラインテストに重要:

Total Ingest Pipeline count: 100,000  # ドキュメント数と一致する必要があります
Total Ingest Pipeline time: 1.85 s    # パイプラインで費やした時間

カウントが0の場合、パイプラインが適用されていません!

4. マージ時間

測定内容: Luceneセグメントのマージに費やした時間

Total merge time: 0.12 min

低い方が良いです。高いマージ時間は書き込み圧力を示します。

5. エラー率

測定内容: 失敗した操作

Error rate: 0.00%

成功したベンチマークでは常に0%であるべきです。

サンプル比較出力

esrally compare –baseline=<es8-race> –contender=<es9-race>

出力例:

ベースライン(ES 8.11.0)とコンテンダー(ES 9.0.0)の比較

|                     Metric |   Baseline |  Contender |    Diff |   Unit |
|----------------------------|------------|------------|---------|--------|
|             Total indexing |       2.50 |       2.35 |  -0.15  |    min |
|                 Throughput |    1650.50 |    1750.25 | +99.75  | docs/s |
|          Service time (p50)|      15.23 |      14.10 |  -1.13  |     ms |
|          Service time (p99)|      42.18 |      38.50 |  -3.68  |     ms |
|    Ingest Pipeline time    |       1.85 |       1.72 |  -0.13  |      s |

解釈:

  • ✅ ES 9.0.0は約6%のスループット向上を示す
  • ✅ すべてのパーセンタイルでレイテンシが減少
  • ✅ パイプライン処理が約7%高速化

注意: 私のケースでは、異なるJavaバージョン(17 vs 21)を使用したため、この比較は完全に公平ではありません。改善がES自体によるものなのか、Javaアップグレードによるものなのかを区別できません。

学んだこと

今回の初めてのRally体験から学んだこと:

  1. セットアップが重要: Java要件などの基本を正しく理解することで、後で多くの時間を節約できます
  2. 小さく始める: 1Kドキュメントから始めることで、設定エラーを早期に発見できました
  3. メトリクスを理解する: 各メトリクスが何を意味するかを理解するのに時間がかかりました
  4. 一度に一つのこと: デフォルトチャレンジのみに焦点を当てることで、圧倒されずに学習できました

今後の展開

今回の試みは、Rallyの使い方自体を把握することが主な目的でした 。この主要な目標は達成されましたが 、高並行チャレンジ やランプアップチャレンジ など、いくつかの高度なテストも残っています 。今後は、もっとリアルワールドのユースケースで実行をしてみたいと考えています。