[OpenSearch]冗長化について：ノード構成・コスト・Serverless比較まで

# はじめに

ある日、OpenSearchノードが沈黙し、環境もろとも地獄に沈んだ。
それを受けて、**「ノード1台は怖すぎる」** という常識的な判断に至り、ケチらず冗長構成（3台）へと移行することになりました。

本記事では、インスタンス選定、コスト試算、OpenSearch Serverlessの可能性まで含めて検討した内容をまとめます。
同じようにOpenSearchの可用性に頭を抱えている方々の参考になれば幸いです。たぶん。

:::note warn
「冗長化するんだからノードを2個にしたらいいんでしょ」って思っているそこのあなた！
私はあなたに伝えることがあるのでこの記事を読んでください。
:::


## 背景と目的
目的：単一ノード障害で停止→可用性を高めるために冗長構成を導入

手段：構成を1台から3台へ変更し、単一障害点を排除

## 分散システムとは何か（OpenSearch的に）

分散システムとは、複数の独立したコンピュータ（ノード）を協調させて、一つのシステムとして動かすアーキテクチャのことです。
OpenSearchのような検索エンジンは、取り扱うデータ量が多く・検索処理も重たいので、1台では限界がある。よって、複数台に処理を分散して高性能・高可用性を実現しています。

https://www.splunk.com/ja_jp/blog/it/distributed-systems.html

### OpenSearchでの役割分担

OpenSearchでは、以下のようにノード同士が役割を分担して動いています：

- マスターノード：クラスター全体の管理者。ノードの状態監視、シャード配置などを決定
- データノード：実際のデータを保持し、検索・インデックス処理を行う主力プレイヤー
- クライアントノード（コーディネータ）：リクエストを受け取り、裏で他のノードに処理を振る司令塔

※OpenSearchでは、すべてのノードがこの役割を兼任することもあれば、明確に分けて構成することも可能です。

## ノードとクラスターの違い、説明できますか？
どっちも聞いたことあるけど、違いがごっちゃになってる人が8割です。
つまり、あなたもたぶんそうです。（今のうちに知った顔しておきましょう）

|用語|	意味	|例えると|
|---|---|---|
|ノード (Node)	|OpenSearchを動かしている「1台のサーバー」。データを持ち、検索処理を担当。|	アルバイト1人|
|クラスター (Cluster)|	複数のノードで構成される、1つのOpenSearchシステム全体。ノードたちは協力し合ってデータを保持・検索。|	アルバイト軍団＋その店長（＝クラスターのコントローラ）|

## ※重要　quorum（クォーラム）について

ノード２台構成では、実質的な冗長化は成立せず、**最低３台** のノードが必要であるようです。

この内容は OpenSearch に限らず、その他の分散システムにも共通する
設計上の原則「quorum（クォーラム）」に基づいているようです。

詳しく知りたい方は以下の記事がとても分かりやすかったので参考にしてください！

https://qiita.com/momotaro98/items/493ce0557a9838c9fd04

# 本題：コスト・性能について

前置きが長くなりましたがここからはコストと性能についてまとめていきます。

## Opensearchの性能

OpenSearchの性能は、単に「検索が速いかどうか」では語れないようですが、ここではvCPUとRAMについてまとめます。

どちらも大事な指標ですがバランスが重要みたいです。

| 観点          | vCPU（仮想CPU）                                    | RAM（メモリ）                           |
| ----------- | ---------------------------------------------- | ---------------------------------- |
| **主な役割**    | 並列処理能力（クエリ/インデックス処理）                           | JVMヒープ＆OSキャッシュ                     |
| **重要性**     | クエリスループット・応答時間に影響                              | GCの頻度/速度、キャッシュヒット率に影響              |
| **推奨構成**    | データノードは最低4vCPU以上                               | RAMは16GiB以上推奨（ヒープ8GiB確保）           |
| **ボトルネック例** | - スレッドプールが枯渇してリクエスト詰まり<br>- クエリが特定ノードに偏ってCPU飽和 | - GC頻発でスローダウン<br>- キャッシュ効かずI/O依存に  |
| **増強の効果**   | - 同時リクエスト処理数の向上<br>- バックグラウンド処理の安定化            | - キャッシュヒット率改善<br>- GC効率化＆停止時間の短縮   |
| **やりすぎ注意点** | - スレッドプールが無意味に増えて逆に管理コスト↑                      | - ヒープ増やしすぎるとGC時間が逆に長くなる            |
| **診断ポイント**  | - スレッドキューの長さ<br>- CPU使用率が常に高いノードがあるか           | - JVMヒープ使用率/GC時間<br>- ディスクI/O増加の兆候 |
| **使い分け**    | - 書き込み/検索が高頻度なワークロード向き                         | - キャッシュ重視 or 安定運用向き（Warmノードなど）     |


> vCPU＝並列処理能力：クエリやインデックスをいかに同時にさばけるか
>RAM＝メモリキャッシュとJVMの余裕：繰り返しの高速化と安定稼働の生命線

:::note info
ノードが１から３になったら性能は3倍でしょ？
　→なりません。

 分散処理のスケーリングは、Amdahlの法則と並列分散処理のオーバーヘッドを考慮する費用があり、
 実際には **2.4倍程度** の性能になるくらいに見込んでください。
:::

## コストの計算

料金の計算は以下のサイトを用いました。

https://aws.amazon.com/jp/opensearch-service/pricing/

デプロイしたいリージョンを選択するとインスタンスごとにスペックと1時間あたりにかかる費用が表示されます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3818832/9bf0a174-5c5a-48e8-a213-27c251d636d0.png)

ここに記載されている値に720(24 * 30)時間をかけてあげると1か月にかかる大まかな費用が分かります。

> ノードを複数デプロイするのであれば　`×ノード数`　をしていただく必要があります

以下に例を示します

| 構成 | 合計vCPU | 合計RAM | 特徴 | 月額コスト | 備考 |
|------|----------|----------|------|--------------|------|
| **m7g.medium ×3** | 3 | 12 GiB | Graviton3系 / 高効率・低コスト | 約 **28,188.0円** | RAM少なめだが安定運用向き（$0.087/hr ×3） |
| **t3.medium ×3** | 6 | 12 GiB | バースト型 / 安価 | 約 **36,288.0円** | クレジット枯渇の可能性あり（$0.112/hr ×3） |

## OpenSearch Serverlessについて

### 概要

Serverlessではインデックス作成、検索、ストレージはそれぞれ分離され、インデックスはOCU（OpenSearch Compute Unit）、ストレージはS3で管理されます。OCUはワークロードに合わせてオートスケーリングするため細かい調整などをする必要はありません。ストレージについても事前のプロビジョニングが不要となります。

### 構成

以下の2つのリソースが必要となります。

- OpenSearch Compute Unit (OCU) - Indexing
- OpenSearch Compute Unit (OCU) - Search and Query

冗長構成を取る場合レプリカを作成することになります。

2024年6月よりOpenSearch ServerlessのOCUの単位が1から0.5になったようなので
冗長構成を取る場合最低でも 0.5 * 4 = 2 [OCU]必要になります

### 課金方式

OpenSearch Serverless では、検索容量単位（OCU）・データストレージ・データ転送の3点に対して課金されます。
今回の冗長化ではOCUに課金される金額をまとめていきます。

最低でも「OCU」というユニットが常に動いており、サーバレスということでlambdaと同じ料金感覚だと勘違いしやすいため要注意です。

| 構成                              | 合計vCPU | 合計RAM  | 特徴                           | 月額コスト         | 備考                                               |
|-----------------------------------|----------|----------|--------------------------------|--------------------|----------------------------------------------------|
| 参考（m6g.xlarge ×1）             | 4        | 16 GiB   | 高性能・単一障害点あり          | 約 **31,500.0円**   | ーーー                                           |
| OpenSearch Serverless（2OCU:0.5*4）  | 12       | 24 GiB   | フルマネージド / 自動スケール化 | 約 **72,144.0円**   | $0.334/hr × 2 OCU × 720h × 150円換算で計算        |

スペックは単位が異なるためChatGPTに質問した結果であり、概算です。
フルマネージドであるため気にする必要はそこまでないと考えます。

### 参考
https://dev.classmethod.jp/articles/amazon-opensearch-serverless-price-down/


## 用語集

- クレジット枯渇

バースト型インスタンス（例：t3）は通常は低性能で、一定時間高性能を使うための「CPUクレジット」を貯めて使います。
このクレジットが尽きると、CPU性能が制限され、処理が遅くなる状態を指します。

- バースト型インスタンス

通常は低リソースで動作し、必要時に一時的に性能（CPU）を上げられる仕組みです。
クレジットが枯渇すると、性能が低下するため、常時高負荷な用途には不向きです。

- t系 vs m系 インスタンスの違い

| 特徴         | **t系（バースト型）**                           | **m系（汎用型）**                        |
|--------------|----------------------------------------------|------------------------------------------|
| 主な用途     | 軽量処理・不定期負荷                         | 安定した中規模処理                      |
| CPU性能      | 通常は低性能、**必要時だけ高性能（クレジット制）** | 常に一定の性能（クレジットなし）       |
| コスト       | **安価**                                      | やや高め                                 |
| 適した場面   | テスト、管理ツール、低頻度なAPI              | アプリ本番、DB、Webサーバ               |
| 安定性       | クレジット枯渇で**性能低下リスクあり**         | 常時安定した性能                        |

簡単に言うと：
**t系は「軽い処理でコスパ重視」** 向け
**m系は「安定性能が必要な本番運用」** 向け

- OCU(OpenSearch Compute Unit)

OpenSearch Serverless における リソース利用量の単位
正確には異なるが、vCPUとRAMが合わさったような単位

## 最後に

「OpenSearch？1ノードで充分でしょ？」って言ってたやつ、今どうしてるかな。
この記事を読んだあなたは、そうならないための知識を手に入れました。
あとはケチらず、実行するだけです。

問題は「壊れること」ではなく、「壊れた時にどうなるか」です。
冗長化はその問いに対する、最もシンプルで最も高価な答えの一つです。
でもね、止まるよりマシ。

とGPTが言ってました。笑