はじめに
ネットワーク構成図は、目的によって見せたい情報が大きく変わります。
物理構成を説明したいときもあれば、IPの経路だけを整理したいとき、アプリケーション間の通信関係だけを示したいときもあります。
そのため、それぞれで別々の構成図を描くことが負担になります。
そこで「データは一か所に集約し、見せ方だけを切り替える」ことを考えました。
この考えを試す手段として、グラフDB使ってネットワーク構成を表現できないかグラフDBの使い方を勉強しつつ試してみました。
やりたいこと
今回はサンプルとして、Client → Switch → Router → WebServer という構成のネットワークで、以下のような見え方を実現できるか試してみました。
- 物理レイヤ: 物理的な接続が全て見える
- IPレイヤ: IPルーティング経路のみ見える
- アプリケーションレイヤ: HTTPエンドポイント間の経路のみ見える
このように、同じデータから、レイヤごとに異なる情報を取り出せるかを試してみました。
実装
環境構築
グラフDBについて、今回はNeo4jを利用します。(下記に従いDockerで構築しました)
docker compose up -d
これで http://localhost:7474 にアクセスすれば、Neo4j Browserが使えます。
今回は、この Browser 上でデータ登録と可視化を行います。
データの登録
ノードは共通、エッジをレイヤごとに分けることを意識して登録していきます。
以下のステップ通りにデータを登録していきます。
ステップ1: ノードの作成
4つの機器を Device ノードとして登録します。ノードには共通プロパティ(name, type)と、必要に応じてIP情報を追加します。
CREATE (client:Device {name: "Client", type: "client", ip: "192.168.1.100"});
CREATE (switch:Device {name: "Switch", type: "switch"});
CREATE (router:Device {name: "Router", type: "router", ip: "10.0.0.1"});
CREATE (web:Device {name: "WebServer", type: "webserver", ip: "10.0.0.10"});
Cypherの記法に関する補足:
-
client:Device:clientは変数名、Deviceはラベル(ノードの種類)。どちらも任意の名前を付与可能 -
{name: "Client", ...}:{}で囲まれた部分はプロパティ(属性情報)。ノードに持たせたい情報を定義
命名規則の補足:Neo4jでは以下の命名規則が推奨されています(公式ドキュメント参照):
-
Node labels:
PascalCase(各単語の先頭が大文字、スペースやアンダースコアなし)例::Device,:VehicleOwner -
Relationship types:
UPPER_CASE(全て大文字、単語間はアンダースコア)例::CONNECTS,:OWNS_VEHICLE
ステップ2: 物理レイヤのエッジ(リレーション)を作成
物理レイヤでは、実際にケーブルで接続されている関係を定義します。
MATCH (client:Device {name: "Client"}), (switch:Device {name: "Switch"})
CREATE (client)-[:CONNECTS {layer: "physical", port: "eth0", cable: "CAT6"}]->(switch);
MATCH (switch:Device {name: "Switch"}), (router:Device {name: "Router"})
CREATE (switch)-[:CONNECTS {layer: "physical", port: "eth1", cable: "CAT6"}]->(router);
MATCH (router:Device {name: "Router"}), (web:Device {name: "WebServer"})
CREATE (router)-[:CONNECTS {layer: "physical", port: "eth2", cable: "CAT6"}]->(web);
Cypherの記法に関する補足:
-
MATCH: ラベルが Device のノードの中からname プロパティが "Client" にマッチしたものを変数(client,switchなど)に -
CREATE (client)-[:CONNECTS {...}]->(switch): ノード間の関係を作成-
CONNECTSはリレーションシップの種類(Relationship Type)で、任意の名前を付与 -
->はグラフ上の関係の向きを表す(物理的な通信方向を意味するわけではない) - プロパティ
{}を持たせることが可能
-
layer: "physical" を付与し、物理レイヤ特有の情報(ポート番号、ケーブル種別)をプロパティとして持たせます。
ステップ3: IPレイヤのエッジ(リレーション)を作成
IPレイヤでは、IPパケットのルーティング観点で接続を定義します。IPレイヤなので、L2のSwitchは記載しません。
MATCH (client:Device {name: "Client"}), (router:Device {name: "Router"})
CREATE (client)-[:CONNECTS {layer: "ip", src_ip: "192.168.1.100", dst_ip: "10.0.0.10"}]->(router);
MATCH (router:Device {name: "Router"}), (web:Device {name: "WebServer"})
CREATE (router)-[:CONNECTS {layer: "ip", src_ip: "192.168.1.100", dst_ip: "10.0.0.10"}]->(web);
layer: "ip"` という情報と、IPレイヤ特有の情報(送信元IP、宛先IP)を持たせます。
ステップ4: アプリケーションレイヤのエッジ(リレーション)を作成
アプリケーションレイヤでは、エンドポイント間の通信のみを定義します。
MATCH (client:Device {name: "Client"}), (web:Device {name: "WebServer"})
CREATE (client)-[:CONNECTS {layer: "application", protocol: "HTTP", endpoint: "/index.html"}]->(web);
layer: "application" という情報と、アプリケーションレイヤ特有の情報(プロトコル、エンドポイント)を持たせます。
データ構造のまとめ
- ノードは全レイヤ共通
- エッジ(リレーション)はレイヤごとに分離
- レイヤの違いは、
layerプロパティで区別
これにより、「同じノードセットから、レイヤに応じて異なるエッジ(リレーション)を取り出す」ことができます。
レイヤ別可視化クエリ
各レイヤを可視化するCypherクエリを紹介します。(あとでNeo4j Browserの設定や表示内容を説明します)
物理レイヤ
MATCH path = (a:Device)-[r:CONNECTS {layer: "physical"}]->(b:Device)
RETURN path
Cypherの記法に関する補足:
-
MATCH path = ...: 条件に合うパターンを検索し、結果を変数pathに格納 -
(a:Device):Deviceノードを変数aに格納 -
-[r:CONNECTS {layer: "physical"}]->: リレーションがCONNECTSで、layerプロパティが"physical"のものを変数rに格納 -
RETURN path: マッチしたパス全体(ノードa、エッジr、ノードb)を返します
つまり、「物理レイヤで繋がっている全てのノードペア」を取得するクエリとなります。
IPレイヤ
MATCH path = (a:Device)-[r:CONNECTS {layer: "ip"}]->(b:Device)
RETURN path
アプリケーションレイヤ
MATCH path = (a:Device)-[r:CONNECTS {layer: "application"}]->(b:Device)
RETURN path
layer プロパティでフィルタリングするだけで、各レイヤのグラフが取得できます。
Neo4j Browserの設定
Neo4j Browserでは、デフォルト設定のままだと指定していないリレーションまで表示されるので、オフにします。
- Neo4j Browserの左下にある歯車アイコン(Settings)をクリック
- 「Graph Visualization」セクションを探す
- 「Connect result nodes」のチェックを外す
この設定変更により、クエリで指定したリレーションのみが表示されます。
結果
実際にNeo4j Browserでクエリを実行してみた結果がこちらです。 期待通り、レイヤごとに異なる可視化ができました。
物理レイヤ
IPレイヤ
アプリケーションレイヤ
まとめ
グラフDBでは、「同じノードセットに対して、複数の異なる関係性を定義する」ことができるため、このようなレイヤ別可視化を実現できることがわかりました。
一方で、実際に使っていくには、以下のような改善点が残ります。
- データ登録の簡易化
- 可視化にあたってのUI改善
それでも、「構成図を描く」という作業を改善できる可能性は十分に感じられました。



