グラフ データベースは、データ エンティティとその間の関係をモデリングすることで、分析情報を得るために役立ちます。JanusGraph は、大量のデータを扱うことができるグラフ データベースです。このチュートリアルでは、オーケストレーション プラットフォームとして Google Kubernetes Engine を使用し、ストレージ バックエンドとして Bigtable を使用して、Google Cloud で JanusGraph を実行する方法について説明します。このチュートリアルは、ストレージ バックエンドとしてマネージド データベースを使用し、Google Cloud で JanusGraph のグラフ データベースを実行することを検討しているシステム アーキテクト、データベース管理者、DevOps プロフェッショナルを対象としています。また、Google Kubernetes Engine(GKE)、Kubernetes Pod、Helmグラフ、Bigtable、Elasticsearch に関する知識があることを前提としています。 Apache TinkerPop グラフ コンピューティング フレームワークと Gremlin グラフ走査マシンと言語に関する知識は必要ありませんが、このチュートリアルで説明する例を超えた Janusgraph を使用する場合には、これらの知識が必要になります。
概要
グラフ用語では、エンティティは「節点(node)」または「点(vertex)」と呼ばれ、関係は「辺(edge)」と呼ばれます。JanusGraph では、頂点と辺のどちらでも、プロパティを介して使用できるようになる追加の関連データを使用できます。
上の図はプロパティ グラフの例です。
グラフ データベースは、さまざまなドメインとアクティビティをモデル化するのに役立ちます。
- ソーシャル ネットワーク
- 金融取引(不正行為の分析用)
- 物理的ネットワークまたは仮想システム ネットワーク
グラフ データベースを作成する際は、数百万、場合によっては数十億の頂点とエッジを作成することがあります。Bigtable をストレージ層の基礎として JanusGraph を使用する場合は、高速クエリ(グラフ走査と呼ばれる)を実行し、サイズとスループットに応じてストレージ層を個別にスケーリングできます。JanusGraph はまた、プラグイン可能なインデックス バックエンドを使用して、頂点とエッジのプロパティに対する全文インデックスを提供します。このチュートリアルでは、スケーラブルな JanusGraph インフラストラクチャを GKE にデプロイします。StatefulSet の Pod で実行されるインデックス バックエンドとして Elasticsearch を使用し、ストレージ バックエンドとして Bigtable を使用します。完了すると、グラフデータ内に存在する関係を走査できます。次の図は、これらの要素がどのように組み合わされているかを示しています。
図は、Elasticsearch と Bigtable を使用した GKE への JanusGraph のデプロイを示しています。
Bigtable での JanusGraph データ
グラフデータは、隣接リストとして JanusGraph によって保存されます。各行は、頂点、隣接する頂点(エッジ)、頂点とエッジに関するプロパティ メタデータを表します。行キーは頂点の一意の識別子です。頂点と別の頂点の関係や関係をさらに定義するプロパティは、エッジ列またはエッジ プロパティ列として保存されます。列修飾子と列値の両方に、Bigtable のベスト プラクティスに沿って、エッジを定義するデータが保存されます。列修飾子と列値の両方を使用してプロパティを定義するため、各頂点プロパティは別々の列として保存されます。
次の図は、このストレージ構造を示しています。
この図は、小さなグラフ フラグメントの論理ストレージ構造と、2 つの頂点行の論理の詳細を示しています。この図では、2 つのサンプル行が 2 つの頂点を表しています。最初の頂点は単一の頂点プロパティでラベル付けされ、2 つの別個のエッジによって他の 2 つの頂点に関連付けられています。2 つ目の頂点は、2 つのプロパティと 1 つのエッジを含む列を保持しています。
次の図は、頂点エッジの論理データモデルで、エッジまたはエッジのプロパティ列の列修飾子と値の詳細を示しています。
隣接する頂点ごとに、そのエッジに関するメタデータが列に格納されます。列修飾子には、エッジの関係とエッジ方向に関するメタデータ、隣接する頂点へのポインタが含まれます。列の値には、エッジラベルと追加のエッジ プロパティが含まれています。走査はどちらの方向にも可能であるため、エッジはエッジの関係の両端に 1 つずつ、合計 2 回保存されます。双方向のエッジ ストレージでは走査パフォーマンスが大幅に向上しますが、追加のストレージ容量の冗長性と非アトミック エッジ ミューテーションによるトレードオフがあります。
次の図は、頂点プロパティ列の論理データモデルを示しています。
上の図は、エッジ列の列修飾子と値の詳細を示しています。
各頂点プロパティは、個別の列として保存されます。列修飾子はプロパティキーの一意の識別子です。列の値には、プロパティの識別子とプロパティの値の両方が含まれます。
JanusGraph は Bigtable の行と列の修飾子の辞書順に依存し、クエリのパフォーマンスを向上させます。
目標
- Bigtable インスタンスを作成する。
- GKE クラスタを作成する。
- Helm をインストールする。
- Helm チャートを使用して JanusGraph と Elasticsearch をデプロイする。
- Gremlin コンソールを使用して JanusGraph に接続する。
- サンプルデータを読み込んでクエリする。
費用
このドキュメントでは、Google Cloud の次の課金対象のコンポーネントを使用します。
- Google Kubernetes Engine (GKE)
- Compute Engine VMs are provisioned by GKE
- Bigtable
料金計算ツールを使うと、予想使用量に基づいて費用の見積もりを生成できます。
このドキュメントに記載されているタスクの完了後、作成したリソースを削除すると、それ以上の請求は発生しません。詳細については、クリーンアップをご覧ください。
前提条件
- Sign in to your Google Cloud account. If you're new to Google Cloud, create an account to evaluate how our products perform in real-world scenarios. New customers also get $300 in free credits to run, test, and deploy workloads.
-
In the Google Cloud console, on the project selector page, select or create a Google Cloud project.
-
Make sure that billing is enabled for your Google Cloud project.
-
Enable the Bigtable, Compute Engine, and GKE APIs.
-
In the Google Cloud console, on the project selector page, select or create a Google Cloud project.
-
Make sure that billing is enabled for your Google Cloud project.
-
Enable the Bigtable, Compute Engine, and GKE APIs.
環境を準備する
このチュートリアルでは、Cloud Shell を使用してコマンドを入力します。Cloud Shell では Google Cloud コンソールのコマンドラインにアクセスできます。また、Google Cloud で開発を行うために必要な Google Cloud CLI やその他のツールも含まれています。Cloud Shell は、Google Cloud コンソールの下部にウィンドウとして表示されます。初期化が完了するまでに数分かかることもありますが、ウィンドウはすぐに表示されます。
-
In the Google Cloud console, activate Cloud Shell.
Cloud Shell で、Bigtable クラスタとGKEクラスタを作成する Compute Engine ゾーンの環境変数と、GKEクラスタの名前、ノードタイプ、バージョンを設定します。
export PROJECT_ID=PROJECT_ID export GCP_ZONE=REGION export GKE_CLUSTER_NAME=GKE_CLUSTER_NAME export GKE_NODE_TYPE=n1-standard-4 export GKE_VERSION=1.20
以下を置き換えます。
PROJECT_ID
はプロジェクト IDに置き換えます。REGION
は、Bigtable クラスタと GKE クラスタが作成されるゾーンに置き換えます。GKE_CLUSTER_NAME
を GKE クラスタの名前に置き換えます。
コマンドは次の例のようになります。
export PROJECT_ID=bt-janusgraph-project-id export GCP_ZONE=us-central1-f export GKE_CLUSTER_NAME=janusgraph-gke export GKE_NODE_TYPE=n1-standard-4 export GKE_VERSION=1.20
JanusGraph がデプロイされる GKE クラスタを作成します。
gcloud container clusters create ${GKE_CLUSTER_NAME} \ --zone=${GCP_ZONE} \ --cluster-version=${GKE_VERSION} \ --machine-type ${GKE_NODE_TYPE} \ --scopes "https://www.googleapis.com/auth/cloud-platform"
Bigtable インスタンスを作成する
JanusGraph ストレージのバックエンドとして、このチュートリアルではニーズに合わせてすぐにスケーリングできる Bigtable を使用します。このチュートリアルでは、経済的で十分な単一ノード クラスタを使用します。小規模なクラスタでプロジェクトを開始し、本番環境データを使用する準備ができたら、規模を大きくしたクラスタに移行してください。作業に適したクラスタサイズを選ぶ際は、Bigtable のドキュメントに記載されているパフォーマンスとスケーリングに関する詳細な説明をご覧ください。
Cloud Shell で、Bigtable インスタンス識別子の環境変数を設定します。
export BIGTABLE_INSTANCE_ID=BIGTABLE_INSTANCE_ID
BIGTABLE_INSTANCE_ID
は、Bigtable インスタンスの ID に置き換えます。Bigtable インスタンスを作成します。
gcloud bigtable instances create ${BIGTABLE_INSTANCE_ID} \ --cluster-config=id=${BIGTABLE_INSTANCE_ID}-${GCP_ZONE},zone=${GCP_ZONE},nodes=1 \ --display-name=${BIGTABLE_INSTANCE_ID}-${GCP_ZONE}
Helm をインストールして構成する
Helm を使用して、アプリケーションを Kubernetes クラスタにデプロイします。このチュートリアルでは、Helm を使用して GKE クラスタに JanusGraph サービスと Elasticsearch サービスの両方をデプロイします。
Cloud Shell で Helm をインストールします。
curl -fsSL -o get_helm.sh \ https://raw.githubusercontent.com/helm/helm/master/scripts/get-helm-3 chmod 700 get_helm.sh DESIRED_VERSION=v3.5.0 ./get_helm.sh
elastic
チャート リポジトリを追加して、JanusGraph チャートのデプロイ時に Elasticsearch チャートの依存関係を検出できるようにします。helm repo add elastic https://helm.elastic.co
このチャート リポジトリは、Elasticsearch の作成者である Elastic がホストしています。
Helm を使用して JanusGraph と Elasticsearch をインストールする
このセクションでは、Helm チャートを使用して、JanusGraph と Elasticsearch を Kubernetes クラスタにデプロイします。
Helm チャートは GitHub から pull されます。Helm チャート リポジトリに含まれる Deployment では、内部アプリケーション ロードバランサを起動する Service の背後で一連の 3 つの JanusGraph Pod がデプロイされます。Pod が実行されているときに、起動と liveness プローブが HTTP リクエストを作成し、各 Pod の JanusGraph サーバーでヘルスチェックを実行します。さらに、Elastic から提供された、StatefulSet に 3 つの Elasticsearch Pod をデプロイした依存関係グラフも掲載されています。
Cloud Shell で、Helm 名と JanusGraph 名の環境変数を設定します。
export HELM_REPO=bigtable-janusgraph-helm export JANUSGRAPH_VERSION=0.5.3 export HELM_CHART_RELEASE_VERSION=1 export HELM_CHART_RELEASE_TAG=${JANUSGRAPH_VERSION}-${HELM_CHART_RELEASE_VERSION} export HELM_CHART_RELEASE_TAG_HASH=f8b271a4854d4a553dd5e9ba014d077fb098d9ab export HELM_CHART_NAME=janusgraph-bigtable
GitHub から Helm チャートを pull します。
git clone https://github.com/GoogleCloudPlatform/${HELM_REPO} \ --branch ${HELM_CHART_RELEASE_TAG}
Helm チャートのディレクトリに移動します。
cd ${HELM_REPO}
セキュリティ上の理由から、commit ハッシュを使用して検証します。
HEAD_COMMIT_HASH=$(git rev-parse --verify HEAD) if [ _${HEAD_COMMIT_HASH} == _${HELM_CHART_RELEASE_TAG_HASH} ] then echo "Commit hash verified" fi
出力が次のように表示されない場合は、クローニングされたタグの整合性が検証されていないため、処理を続行しないでください。
Commit hash verified
チャートの依存関係を更新します。
helm dep update
親ディレクトリに移動します。
cd ..
Helm エンティティと JanusGraph エンティティの名前の環境変数を設定します。
export HELM_RELEASE_NAME=janusgraph-bigtable-elastic export ELASTICSEARCH_CLUSTER_NAME=${HELM_RELEASE_NAME}-elasticsearch export BIGTABLE_JANUSGRAPH_TABLE=janusgraph-table
values.yaml
ファイルを作成します。このファイルに、JanusGraph チャートのデプロイ時に使用する構成プロパティを指定します。cat > values.yaml << EOF image: repository: docker.io/janusgraph/janusgraph tag: 0.5.3 pullPolicy: IfNotPresent replicaCount: 3 service: type: LoadBalancer port: 8182 serviceAnnotations: networking.gke.io/load-balancer-type: "Internal" elasticsearch: deploy: true clusterName: ${ELASTICSEARCH_CLUSTER_NAME} properties: storage.backend: hbase storage.directory: null storage.hbase.ext.google.bigtable.instance.id: ${BIGTABLE_INSTANCE_ID} storage.hbase.ext.google.bigtable.project.id: ${PROJECT_ID} storage.hbase.ext.hbase.client.connection.impl: com.google.cloud.bigtable.hbase2_x.BigtableConnection storage.hbase.short-cf-names: true storage.hbase.table: ${BIGTABLE_JANUSGRAPH_TABLE} index.search.backend: elasticsearch index.search.hostname: ${ELASTICSEARCH_CLUSTER_NAME}-master index.search.directory: null index.search.elasticsearch.health-request-timeout: 90s cache.db-cache: true cache.db-cache-clean-wait: 20 cache.db-cache-time: 180000 cache.db-cache-size: 0.5 cluster.max-partitions: 1024 graph.replace-instance-if-exists: true persistence: enabled: false debugLevel: INFO EOF
作成した
values.yaml
ファイルを使用して、JanusGraph に Helm チャートをデプロイします。helm upgrade --install \ --wait \ --timeout 600s \ ${HELM_RELEASE_NAME} \ ./${HELM_REPO} \ -f values.yaml
インストール プロセスは、すべてのリソースが準備できるまで待ってから実行されます。このプロセスには数分かかることがあります。
JanusGraph のデプロイを確認する
Helm のインストール プロセスが完了すると、最初の注意事項を説明する NOTES
セクションが表示されます。NOTES
セクションの手順で、JanusGraph 環境が機能していることを確認します。
Cloud Shell で、GKE にデプロイされた Helm チャート コンポーネントを確認します。
JanusGraph のデプロイを確認します。
kubectl get deployments
デプロイに成功すると、出力は次のようになります。
NAME READY UP-TO-DATE AVAILABLE AGE janusgraph-bigtable-elastic 3/3 3 3 3m28s
Elasticsearch StatefulSet を確認します。
kubectl get statefulsets
すべてが問題なく動作している場合、出力は次のようになります。
NAME READY AGE janusgraph-bigtable-elastic-elasticsearch-master 3/3 4m13s
環境変数に、JanusGraph Gremlin サーバーを実行している Kubernetes Pod の名前を設定します。Gremlin サーバーを実行している Pod の
app
ラベルは、Chart.yaml
ファイルで定義された Helm チャート名から作成されます。export APP_LABEL_FROM_CHART_NAME=${HELM_CHART_NAME} export POD_NAME=$(kubectl get pods \ --namespace default \ -l "app=${APP_LABEL_FROM_CHART_NAME}, \ release=${HELM_RELEASE_NAME}" \ -o jsonpath="{.items[0].metadata.name}")
Pod に接続して Gremlin コンソールを実行します。これは REPL(読み取り、評価、出力のループ)シェルです。コンテナの名前は、
Chart.yaml
の Helm チャート名から取得されます。export GREMLIN_CONTAINER=${HELM_CHART_NAME} kubectl exec \ -c ${GREMLIN_CONTAINER} \ -it $POD_NAME \ -- /opt/janusgraph/bin/gremlin.sh
Gremlin コンソールで、Apache TinkerPop サーバーに接続します。
セッションを開始します。
:remote connect tinkerpop.server conf/remote.yaml session
出力は次のようになります。
==>Configured localhost/127.0.0.1:8182-[b08972f2-a2aa-4312-8018-bcd11bc9812c]
サーバーに接続する
:remote console
出力は次のようになります。
==>All scripts will now be sent to Gremlin Server - [localhost/127.0.0.1:8182]-[b08972f2-a2aa-4312-8018-bcd11bc9812c] - type ':remote console' to return to local mode>
Gremlin コンソールで、グラフ インスタンスを表す
graph
変数を調べて、Gremlin サーバーが正しく実行されていることを確認します。graph
出力は、JanusGraph サーバーが HBase 互換のデータベース(この場合は Bigtable)をストレージ バックエンドとして実行していることを示しています。
==>standardjanusgraph[hbase:[127.0.0.1]]
Gremlin で 2 つの頂点を作成します。
v1 = graph.addVertex(label, 'hello') v2 = graph.addVertex(label, 'world')
コンソール出力が次のようになった場合は、2 つの頂点が追加されていることを示しています。
==>v[4344] ==>v[4152]
2 つの頂点を接続するエッジを作成します。
v1.addEdge('followedBy', v2)
コンソール出力が次のようになった場合は、2 つの頂点の間にエッジが追加されたことを示しています。
==>e[17j-3co-4fmd-oe054][4344-followedBy->4152]
トランザクションを commit します。
graph.tx().commit()
コンソールの出力が
null
の場合は、オペレーションが commit されたことを示します。==>null
次の図は、コマンドによって作成されたグラフを示しています。
hello
というラベルが付いた頂点は、followedBy
というラベルの付いた有向エッジによってworld
という頂点に接続しています。Gremlin クエリを発行して、
hello
というラベルの付いた頂点からfollowedBy
というラベルの付いたエッジに続く頂点のラベルを調べます。g.V().has(label, 'hello').out('followedBy').label()
クエリの構文については、次のセクションで説明します。クエリの出力として
world
という単語が表示されます。==>world
サンプル データセットを読み込んでクエリを実行する
JanusGraph をデプロイし、Gremlin を使用して JanusGraph に接続できたため、独自のデータの読み込みとクエリを開始できます。このプロセスがどのようなものであるかを確認するため、JanusGraph にバンドルされているサンプル データセットを読み込みます。読み込むのは、ローマの神話の神々とそれらの神の場所のプロパティを表す Graph of the Gods です。
Gremlin で、前に作成したグラフを読み込みます。
GraphOfTheGodsFactory.load(graph)
出力は次のとおりです。
==>null
Jupiter のすべての兄弟を見つけるグラフ走査クエリを発行します。
g.V().has('name', 'jupiter').out('brother').values('name')
次の表に、クエリで走査する手順を示します。
走査ステップ 説明 g.V()
最初に点の集合を調べます。 has('name', 'jupiter')
プロパティ name
の値がjupiter
となっている点を探します。out('brother')
そこから、 brother
というラベルを持つすべての辺に沿って移動します。values('name')
この辺につながる点の name
プロパティを取得します。==>neptune ==>pluto
この Graph of the Gods データセットに発行可能な走査クエリをよりよく理解できるよう、JanusGraph のドキュメントで他のサンプルクエリを試してみてください。
Bigtable にデータが保存されていることを確認する
JanusGraph クラスタにサンプルデータを作成できたため、Bigtable がストレージ バックエンドとして使用されたことを確認できます。
Gremlin コンソールを閉じます。
:q
Cloud Shell で、データが Bigtable の
janusgraph
テーブルに永続化されていることを確認します。cbt -project=${PROJECT_ID} \ -instance=${BIGTABLE_INSTANCE_ID} \ count ${BIGTABLE_JANUSGRAPH_TABLE}
出力は次のようになります。
2021/03/02 02:32:19 -creds flag unset, will use gcloud credential 101
出力の値
101
はjanusgraph table
内の行数を表しますが、これは実際には異なる場合があります。
Elasticsearch で検索インデックスの作成を確認する
Cloud Shell で、Elasticsearch Pod のインデックスと名前の変数を設定します。
export ELASTICSEARCH_POD_ORDINAL=0 export ELASTICSEARCH_POD_NAME_ROOT=${ELASTICSEARCH_CLUSTER_NAME}-master export ELASTICSEARCH_POD=${ELASTICSEARCH_POD_NAME_ROOT}-0
Elasticsearch Pod の名前は、Elasticsearch Helm の依存関係によって定義されます。Pod 名は、作成した
values.yaml
ファイルで指定されているクラスタ名、master
という単語、ゼロ インデックスの序数で構成され、すべてハイフンで区切られています。このステップでは、ゼロ(0)で表される最初の Pod を選択します。Elasticsearch Aliases REST API を使用して、インデックスを検査します。
kubectl exec \ -c elasticsearch \ -it ${ELASTICSEARCH_POD} \ -- \ curl -XGET "127.0.0.1:9200/_aliases?pretty=true";
出力には、JanusGraph で作成された、頂点とエッジのプロパティを使用した効率的なルックアップを可能にする 2 つのインデックス
janusgraph_vertices
とjanusgraph_edges
が表示されます。{ "janusgraph_vertices" : { "aliases" : { "janusgraph" : { } } }, "janusgraph_edges" : { "aliases" : { "janusgraph" : { } } } }
いずれかのインデックスの値のクエリを実行するには、Elasticsearch Search REST API を使用します。
kubectl exec \ -c elasticsearch \ -it ${ELASTICSEARCH_POD} \ -- \ curl -XGET "127.0.0.1:9200/janusgraph_edges/_search?pretty=true&q=*";
検索結果は、JanusGraph によって作成されたインデックスにエントリがあることを示しています。次のような出力が表示されます。これは、
janusgraph_edges
インデックスにエントリがあることを示しています。{ "took" : 94, "timed_out" : false, "_shards" : { "total" : 1, "successful" : 1, "skipped" : 0, "failed" : 0 }, "hits" : { "total" : { "value" : 6, "relation" : "eq" }, "max_score" : 1.0, "hits" : [ { "_index" : "janusgraph_edges", "_type" : "_doc", "_id" : "6bvp-5ovc-b2t-2yko", "_score" : 1.0, "_source" : { "reason" : "loves waves" } }, { …
プロジェクトを削除する
このチュートリアルで使用したリソースについて、Google Cloud アカウントに課金されないようにするには、リソースを含むプロジェクトを削除するか、プロジェクトを維持して個々のリソースを削除します。
- In the Google Cloud console, go to the Manage resources page.
- In the project list, select the project that you want to delete, and then click Delete.
- In the dialog, type the project ID, and then click Shut down to delete the project.
次のステップ
- JanusGraph とグラフ データベースの詳細を確認する。
- Apache TinkerPop グラフ コンピューティング フレームワークについて確認し、Gremlin グラフ走査言語について学習する。
- JanusGraph が Bigtable にデータを保存する方法について詳細を確認する。
- JanusGraph アプリケーションのサンプルをデプロイして、グラフのユースケースをさらに深く学習する。