DockerによるOpenSearchの構築・活用ガイド:全文検索とログ分析

Database tutorial - IT technology blog
Database tutorial - IT technology blog

SQLのLIKE句が限界を迎えるとき

エンジニアであれば誰しも、SELECT * FROM products WHERE name LIKE '%keyword%' のようなクエリを書いた経験があるでしょう。テーブルのデータが数千件程度であれば、データベースは瞬時に応答を返してくれます。

しかし、レコード数が1,000万件に達した状況を想像してみてください。LIKE '%...%' クエリはフルテーブルスキャン(Full Table Scan)を引き起こし、CPUリソースを枯渇させ、レスポンスタイムは20ミリ秒から3〜5秒へと跳ね上がります。さらにプロダクトオーナーから、「スペルミスに対応した曖昧検索(Fuzzy Search)やインクリメンタルサーチ(サジェスト機能)、デバッグ用に1日50GBのログ集約を実現してほしい」といった要求が重なると、従来のRDBMSだけで対応するのはもはや不可能です。

MySQLやPostgreSQLはACIDトランザクション(決済や注文管理)に極めて優れており、MongoDBは柔軟なドキュメント構造に適しています。しかし、高度な全文検索(Full-Text Search)や数百万行に及ぶリアルタイムなログ分析という課題に対しては、専用のツールが必要です。その最も有力な選択肢がOpenSearchです。

コアコンセプト:OpenSearchはどのように動作するのか?

わかりやすく例えると、リレーショナルデータベースは1,000ページの分厚い本のようなものです。特定の単語を見つけ出すには、1ページずつめくって探さなければなりません。これに対し、OpenSearchは巻末の「索引(Inverted Index / 転置インデックス)」のように機能します。「Docker」という単語は15ページと88ページにあり、「Cluster」は200ページにある、という対応表があらかじめ用意されているのです。この転置インデックスの仕組みにより、ドキュメント数が数千万件に及ぶ場合でも、OpenSearchは10〜30ミリ秒程度で検索結果を返せます。

Elasticsearchのライセンス変更という転換点

多くのエンジニアにとって、ELKスタック(Elasticsearch、Logstash、Kibana)はおなじみのものでした。しかし2021年初頭、Elastic社は従来のオープンソースライセンス(Apache 2.0)からSSPLへの移行を決定しました。これはクラウドプロバイダーがサービスをマネージド提供して商用展開することを厳しく制限するデュアルライセンスです。

コミュニティのオープン性を守るため、AWSをはじめRed HatやSAPなどの企業が、ElasticsearchおよびKibanaの最終オープンソースブランチ(バージョン7.10.2)をフォークしました。こうして開発が引き継がれたのが、OpenSearchとOpenSearch Dashboardsです。完全無料のApache 2.0ライセンスであり、ElasticsearchのREST APIとほぼ100%の互換性を維持しています。

押さえておくべき2つのコアコンポーネント

  • OpenSearch: データの保存、分散処理、および検索アルゴリズムの実行を担うエンジン(Apache Luceneベース)。
  • OpenSearch Dashboards: WebベースのGUIツール。curlでAPIを直接叩くことなく、グラフの描画、ログ検索、クラスタ状態の監視が行えます。

実践:Docker ComposeでOpenSearch環境を構築する

Docker Composeを利用すれば、ホスト環境を汚すことなく、わずか2分でクリーンな検証環境を用意できます。今回は検証用として、1ノードのOpenSearch(手順を簡略化するため内部セキュリティプラグインを無効化)と1ノードのOpenSearch Dashboardsでクラスタを構成します。

ステップ1: docker-compose.ymlの作成

作業ディレクトリを作成し、設定ファイルを開きます:

mkdir opensearch-lab && cd opensearch-lab
nano docker-compose.yml

ファイルに以下の設定を貼り付けます:

version: '3.8'

services:
  opensearch-node:
    image: opensearchproject/opensearch:2.12.0
    container_name: opensearch-node
    environment:
      - cluster.name=opensearch-cluster
      - node.name=opensearch-node
      - discovery.type=single-node
      - bootstrap.memory_lock=true
      - "OPENSEARCH_JAVA_OPTS=-Xms512m -Xmx512m"
      - DISABLE_SECURITY_PLUGIN=true
    ulimits:
      memlock:
        soft: -1
        hard: -1
      nofile:
        soft: 65536
        hard: 65536
    volumes:
      - opensearch-data:/usr/share/opensearch/data
    ports:
      - 9200:9200
      - 9600:9600
    networks:
      - opensearch-net

  opensearch-dashboards:
    image: opensearchproject/opensearch-dashboards:2.12.0
    container_name: opensearch-dashboards
    ports:
      - 5601:5601
    environment:
      - 'OPENSEARCH_HOSTS=["http://opensearch-node:9200"]'
      - DISABLE_SECURITY_DASHBOARDS_PLUGIN=true
    networks:
      - opensearch-net
    depends_on:
      - opensearch-node

volumes:
  opensearch-data:

networks:
  opensearch-net:

ステップ2: カーネル設定の調整と起動

コンテナ内で動作するLuceneプロセスは、膨大な数のメモリマップ領域(memory-mapped areas)を必要とします。Linuxサーバーでは、コンテナ起動前に仮想メモリのマップ上限値を引き上げておきます:

sudo sysctl -w vm.max_map_count=262144
docker compose up -d

ノードが起動を完了するまで20秒ほど待ちます。その後、以下のコマンドでクラスタの状態を確認します:

curl http://localhost:9200

コンソールに "tagline" : "The OpenSearch Project: https://opensearch.org/" という文字列やバージョン情報を含むJSONが返ってくれば、クラスタのデータ受け入れ準備は完了です。

ステップ3: インデックス作成と検索クエリのテスト

REST API経由でサンプル記事を2件インデックス登録してみましょう:

# 1. 最初のドキュメントを追加
curl -X POST "http://localhost:9200/articles/_doc/1" -H 'Content-Type: application/json' -d'{
  "title": "ゼロから始めるプログラミング学習ガイド",
  "author": "Nguyen Van A",
  "content": "プログラミング学習には忍耐が必要です。変数やループなどの基礎知識から始めましょう。",
  "tags": ["it", "coding", "beginner"]
}'

# 2. 2件目のドキュメントを追加
curl -X POST "http://localhost:9200/articles/_doc/2" -H 'Content-Type: application/json' -d'{
  "title": "大規模データベース運用のノウハウ",
  "author": "Tran Van B",
  "content": "SQLクエリの最適化とシステムログの分析により、サーバー障害を最小限に抑えられます。",
  "tags": ["database", "devops"]
}'

次に、実際に「データベース」または「ログ」というキーワードを含む記事を検索してみます:

curl -X GET "http://localhost:9200/articles/_search" -H 'Content-Type: application/json' -d'{
  "query": {
    "match": {
      "content": "データベース ログ"
    }
  }
}'

OpenSearchは単語の分割(トークナイズ)を自動で行い、BM25アルゴリズムに基づいて関連度スコア(score)を算出し、わずか数ミリ秒で2件目のドキュメントを先頭に返します。

ステップ4: OpenSearch Dashboardsを触ってみる

ブラウザを開き、http://localhost:5601 にアクセスすると、管理ダッシュボードが表示されます。主な機能は以下の通りです:

  • Dev Tools: シンタックス補完機能を備え、JSONクエリの作成や検証を直接実行できる環境。
  • Discover & Dashboards: 時系列に沿った数百万行のログフィルタリングや、500エラー発生率・レイテンシの推移を棒グラフやヒートマップで可視化可能。

まとめ

OpenSearchはMySQLやPostgreSQLを完全に置き換えるものではありません。トランザクションの厳密な管理や口座データのロックなどを目的として設計されたツールではないからです。実務におけるベストプラクティスは、RDBMSを信頼できる唯一の情報源(Single Source of Truth)として維持しつつ、検索対象データやログをOpenSearchに同期する構成です。

このアプローチを採用することで、データの整合性と堅牢性を確保しながら、商用ライセンスの制約を受けることなく圧倒的な検索パフォーマンスを手に入れることができます。

Share: