【番外編】RetinaでもElastic Stack連携がしたい！～ネットワーク監視情報の少しへそ曲がりな連携方法～

# はじめに
これまで、Microsoft社が開発するKubernetesネットワークオブザーバビリティツールのOSS、Retinaを実際に導入して動作を試しながら3回にわたってご紹介してきました。

* [KubernetesのネットワークオブザーバビリティプラットフォームRetinaを試してみた＜前編：構築編＞](https://qiita.com/melknzw/items/a8986b12fa124a2d5df0)
* [KubernetesのネットワークオブザーバビリティプラットフォームRetinaを試してみた＜中編：メトリクス使いこなし編＞](https://qiita.com/melknzw/items/15f5931b2fca0a5b5e39)
* [KubernetesのネットワークオブザーバビリティプラットフォームRetinaを試してみた＜後編：キャプチャ使いこなし編＞](https://qiita.com/melknzw/items/563cfcdcf5ff76917d6a)

今回は番外編として、少し特殊なRetinaとツールの連携方法をご紹介したいと思います。

Retinaは、Microsoft社が中心になって開発しているだけあって、やはりAzure推しになっているところが垣間見えます。例えば、[概要ページ](https://retina.sh/docs/Introduction/intro)で紹介されているテレメトリーデータの保存先にPrometheusの次にAzure Monitorを挙げている、[パケットキャプチャ](https://retina.sh/docs/Captures/overview/)の図で結果の保存先の筆頭がAzure Blob Storageになっているなどです。
もちろん実際には、プラットフォーム非依存をうたっている通り、キャプチャデータをAWSのS3にも連携できます。

そこで、本記事では、取得したデータをあえてAzureではないElastic Stackに送信することを試してみたいと思います。といっても、Retina側もElastic Stack側もデータ形式としてOpenMetrics形式に対応しているため、比較的容易に連携できます。
連携の概要としては、各ノードで稼働するRetinaのエージェントからMetricbeatでメトリクスを取得し、Elasticsearchに送信し、Kibanaで可視化します。

:::note
Elastic Stackは、Elasticsearch、Kibana、Beatsで構成される監視スタックです。詳細や構築手順は本記事では省略するため、他の技術記事等をご確認ください。
:::

# 事前準備
事前準備として、環境構成とOpenMetricsについてまとめておきます。

## 環境
これまでの記事と同様に、手元のVMで組んでいる検証用のオンプレKubernetesクラスターを使用します。Kubernetesのバージョンは`v1.31.6`です。

今回使用するRetinaのバージョンは`v0.0.30`です。
これまでの記事で使用したバージョンより新しいものを使用します。この間にロゴが新しくなったり、CiliumやHubbleとの連携が強化されたりと日々機能強化されています。
[過去の記事](https://qiita.com/melknzw/items/a8986b12fa124a2d5df0)と同様に構築し、[Advancedモード](https://qiita.com/melknzw/items/15f5931b2fca0a5b5e39#%E3%83%A1%E3%83%88%E3%83%AA%E3%82%AF%E3%82%B9%E3%81%AE%E3%83%A2%E3%83%BC%E3%83%89)のメトリクスを有効にしておきます。

Elastic Stackの各バージョンは以下の通りです。いずれも前述のKubernetesクラスター内で稼働します。
* Elasticsearch：`v8.17.5`
* Kibana：`v8.17.5`
* Metricbeat：`v8.17.5`

## OpenMetricsとは
メトリクスを収集する前に、OpenMetrics形式をおさらいしておきましょう。
名前としては、そこまで聞き慣れないかもしれませんが、Prometheusなどでメトリクスを収集する際に見かけるあの出力形式です。
CNCFのサンドボックスプロジェクトになっていて、メトリクスの出力形式のデファクトスタンダードになっています。

https://openmetrics.io/

厳密にはPrometheusの形式を改善したものであり、差異が少しあります。以下の記事が詳しいです。

https://sysdig.com/blog/openmetrics-is-prometheus-unbound/

実際の値の例を以下に示します。
以下の例は、`# HELP`で始まるメトリクスの概要を示す部分、`# TYPE`で始まるメトリクスの種類を示す部分、メトリクス名とメトリクス値を示す部分の3行で構成されています。
```
# HELP go_threads Number of OS threads created.
# TYPE go_threads gauge
go_threads 13
```
`TYPE`で指定できるメトリクスの種類には、ゲージ、カウンター、ヒストグラムなどがあります。
これらのフォーマットに従ってメトリクスをやり取りすることで、異なるツール間での連携が容易になるわけです。
詳しいフォーマットの定義は以下で公開されています。

https://github.com/prometheus/OpenMetrics/blob/main/specification/OpenMetrics.md

# 設定
実際にメトリクスの収集設定をしていきます。

ツール間の連携の概要を図にするとこんな感じです。図ではRetinaとMetricbeatは、一番上のノードにしか存在していませんが、実際にはDaemonSetとして各ノードにデプロイされていて、Metricbeatは同じノード内のRetinaからメトリクスを収集します。

![Retina連携図](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3830637/8f576740-00d8-4ff0-83aa-01ffd37bbbcc.png)

## Metricbeatの設定
Metricbeatは、Elastic StackのBeatsファミリーの一種でメトリクスの収集・転送を担当します。
本記事では、あらかじめDaemonSetとして、Kubernetesクラスターの各ノードにデプロイされていることを前提とします。

Retinaのエージェントは、`10093`番ポートの`/metrics`パスにメトリクスを公開します。
そこで、Metricbeatの設定ファイルには以下の内容を追記し、各ノードで稼働しているRetinaのエージェントからメトリクスを収集するように設定します。なお、収集した情報の送信先などの設定は掲載を省略しています。
```metricbeat.yml
metricbeat.modules:
  - module: openmetrics
    period: 10s
    hosts: ["${NODE_NAME}:10093"]
    metricsets: ['collector']
    metrics_path: /metrics
    enabled: true
    metrics_filters:
      include: ["networkobservability_*"]
    processors:
      - add_fields:
          target: ""
          fields:
            openmetrics.target: retina
```
設定内容について、2点補足しておきます。
* 同時に出力されているGoやエージェントのプロセスに関するメトリクスを収集対象から除外するために、`metrics_filters`で`networkobservability_`から始まるメトリクスのみを対象とするように指定。
* Retinaから取得したメトリクスであることが分かるように、Metricbeatの`add_fields`プロセッサを使用して`openmetrics.target: retina`のフィールドを追加。

:::note
Retinaが提供するメトリクスの名前には、プレフィックスとして`networkobservability_`が付きます。
:::

## Kibanaの設定
次に、Kibanaの設定をします。実際には、Metricbeatの設定を反映し、データの取得が始まってから実施するのが良いです。
Kibanaの操作にある程度慣れている方を想定して手順を簡単に説明します。

:::note warn
Grafanaとは違って公式でダッシュボードが用意されているわけではないため、自分で設定する必要があります。
:::

### データビューの作成
KibanaでMetricbeatなどのデータを扱うには、まずデータビューを作成する必要があります。
データビューとは、Elasticsearchのインデックスに対するクエリを簡単に実行するためのエイリアスのようなもので、以前はインデックスパターンと呼ばれていました。
ここでは、初めてMetricbeatのデータを扱う場合を想定して、データビュー作成手順を示しますが、既に利用中の場合はスキップしてください。

KibanaのDiscover画面の左上にあるデータビュー選択画面から作成します。インデックスパターンには`metricbeat-*`を指定し、適当な名前を付けて保存します。

![データビューの作成](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3830637/e662a707-f5df-4b8a-b93e-245708803ae4.png)

### データの確認
Discover画面で実際のデータを確認してみます。
Metricbeatで他にも監視対象がある場合は、画面上部の検索窓でフィルターの設定をするとよいです。今回の例だと、クエリとして`openmetrics.target :"retina"`を指定します。

![データの確認](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3830637/84f2d7e9-1d32-4ee8-b5eb-0870ac6aa149.png)

Retina側でのメトリクスの出力設定をAdvancedモードに設定していることもあって、結構な量のメトリクスが取得されています。
ここで示した例では、`networkobservability_adv_dns_request_count`のメトリクスが取得できていることを確認できました。

Kibana側で表示されている1レコードは、Retinaで出力されている1つのメトリクスに該当します。メトリクスがラベルで分類されている場合は、個々のラベルごとに1メトリクスとして取り込まれるため、さらに多くなります。
例えば、以下の例だとメトリクス名は全て`networkobservability_tcp_state`ですが、ラベル`state`が7種類あるため、7つのレコードとして別々に取り込まれます。各レコードにはメタデータも付与されるので少し冗長な気もしますが…。

```
# HELP networkobservability_tcp_state Number of active TCP connections by state
# TYPE networkobservability_tcp_state gauge
networkobservability_tcp_state{state="CLOSE_WAIT"} 1
networkobservability_tcp_state{state="ESTABLISHED"} 48
networkobservability_tcp_state{state="FIN_WAIT1"} 1
networkobservability_tcp_state{state="FIN_WAIT2"} 1
networkobservability_tcp_state{state="LISTEN"} 15
networkobservability_tcp_state{state="SYN_SENT"} 1
networkobservability_tcp_state{state="TIME_WAIT"} 134
```

## ダッシュボードの作成
次にKibanaでRetinaから取得したデータを基にダッシュボードを作成してみます。
KibanaのDashboards画面から新しいダッシュボードを作成します。

先に完成図を示しておきます。

![ダッシュボード完成](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3830637/87b1104f-b457-4352-bd34-30ba55a12849.png)


### コントロールの設置
ダッシュボード全体でノードごとのデータを切り替えて表示できると分析に便利なため、コントロールを設置します。不要な場合はスキップして構いません。
Kibanaの画面上部にある「Controls」→「Add control」をクリックします。Fieldで`agent.name`を選択し、保存します。

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3830637/124198d8-22fc-481e-b9f9-c3e056cadcb0.png" alt="コントロールの設置" width="70%">


### 棒グラフの作成
次に、TCPフラグカウント(`openmetrics.metrics.networkobservability_adv_tcpflags_count`)の時系列の変動を積み上げ棒グラフで可視化してみます。
Kibanaの画面上部にある「Create visualization」をクリックしてビジュアライゼーション作成画面を出します。

設定内容は以下の通りです。記載していない項目は初期値のままにします。
* Horizontal axis
  * Fuctions: Date histogram
  * Field: `@timestamp`
  * Drop partial intervals: 有効
* Vertical axis
  * Functions: Differences
  * Choose a sub-function: Sum
  * Field: `openmetrics.metrics.networkobservability_adv_tcpflags_count`
* Breakdown
  * Functions: Top values
  * Field: `openmetrics.labels.flag`
  * Number of values: 10

![stack_barchart.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3830637/c63fa6f4-3c76-4f8b-8cf2-c7f8fec52331.png)


### サンキーダイアグラムの作成
最後に、パケットがどこからどこへ送信されたかのフローを示すサンキーダイアグラムを作成したいと思います。ただ、この図はKibana上では組み込みパーツとして用意されておらず、カスタムでコードを書く必要があります。

:::note
Kibanaのプラグインで実現する方法もありますが、今回はKibanaが対応しているデータ視覚化記述言語のVegaを使用して実現したいと思います。
:::

実装にあたって、以下の記事及びその参照元を参考にさせていただきました。

https://qiita.com/j-yama/items/81f0ea6cf967b3bc7203

https://www.elastic.co/blog/sankey-visualization-with-vega-in-kibana

Kibanaの画面上部にある「Add panel」をクリックして、「Custom visualization」を選択します。
表示される編集画面の右側のパネルに、上記記事で述べられている通り、「Vega code」に掲載されているソースコードを貼り付けます。
その際に、今回使用するデータに合わせて3か所(インデックス、送信元、送信先)を書き換えます。
変更箇所を以下に示します。
```diff
{
  $schema: https://vega.github.io/schema/vega/v3.0.json
  data: [
    {
      // query ES based on the currently selected time range and filter string
      name: rawData
      url: {
        %context%: true
        %timefield%: @timestamp
-       index: logstash-*
+       index: metricbeat-*
        body: {
          size: 0
          aggs: {
            table: {
              composite: {
                size: 10000
                sources: [
                  {
                    stk1: {
-                     terms: {field: "geo.src"}
+                     terms: {field: "openmetrics.labels.source_ip"}
                    }
                  }
                  {
                    stk2: {
-                     terms: {field: "geo.dest"}
+                     terms: {field: "openmetrics.labels.destination_ip"}
                    }
...
```
実際の画面を以下に示します。どのIPアドレスからどのIPアドレスへとどれだけのフローが流れているか一目瞭然ですね！

![サンキーダイアグラム](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3830637/0640ca56-dd9d-46c7-b70f-8ad769b05d0a.png)

# おわりに
今回は、RetinaをあえてElastic Stackと連携させてデータを可視化してみました。
実際にやってみたところ、データの収集までは簡単でしたが、可視化の部分でダッシュボードを一から作成する必要があり、結構な手間だと感じました。やはり、公式が提供しているGrafanaのダッシュボードを活用するのが手っ取り早いです。

本記事で作成したダッシュボードは、Elastic Stackで可視化するとどんなイメージになるかというお試しなので、実用的なダッシュボードになるよう皆さんも改良してみてください。

Elastic Stackとの連携はこれくらいにして、今後もRetinaの進化をウォッチしていきたいと思います。