何らかの理由によりThe Graph 等のhosted-serviceを使わずに自分でgraph-node を建てる場合、グラフノードのスケールアウトも正しく考慮する必要があります。スケールアウトできる構成を組まないと、Web3クライアントからリクエストが504(Gateway Timeout)になってしまう事態が頻発します。本稿では簡単に実現できるグラフノードのスケールアウトの方法をご紹介します。
TL;DR
公式のdocker-compose.yml を以下のように書き換えます(解説は後述)。
version: '3'
services:
graph-node-index:
image: graphprotocol/graph-node
ports:
- '8020:8020'
depends_on:
- ipfs
- postgres
extra_hosts:
- host.docker.internal:host-gateway
environment:
postgres_host: postgres
postgres_user: graph-node
postgres_pass: let-me-in
postgres_db: graph-node
ipfs: 'ipfs:5001'
ethereum: 'mainnet:http://host.docker.internal:8545'
GRAPH_LOG: info
node_role: index-node
node_id: index-node
BLOCK_INGESTOR: index-node
graph-node-query:
image: graphprotocol/graph-node
ports:
- '8000:8000'
- '8001:8001'
depends_on:
- ipfs
- postgres
extra_hosts:
- host.docker.internal:host-gateway
environment:
postgres_host: postgres
postgres_user: graph-node
postgres_pass: let-me-in
postgres_db: graph-node
ipfs: 'ipfs:5001'
ethereum: 'mainnet:http://host.docker.internal:8545'
GRAPH_LOG: info
node_role: query-node
ipfs:
image: ipfs/go-ipfs:v0.4.23
ports:
- '5001:5001'
volumes:
- ./data/ipfs:/data/ipfs
postgres:
image: postgres
ports:
- '5432:5432'
command:
[
"postgres",
"-cshared_preload_libraries=pg_stat_statements",
"-cmax_connections=100"
]
environment:
POSTGRES_USER: graph-node
POSTGRES_PASSWORD: let-me-in
POSTGRES_DB: graph-node
volumes:
- ./data/postgres:/var/lib/postgresql/data
その後以下のようにノードを実行します
docker-compose up -d --scale graph-node-query=5
以下解説します
グラフノードのスケールアウト
グラフノードのスケールアウトができるようにdocker-composeの構成を変更しています。やっていることは主に2つ
- グラフノードをインデックスノード・クエリノードの2つに分ける
- Postgresqlの同時接続数を増やす
Postgresqlの同時接続数を増やす
まずは簡単な方から。起動時オプションを追加してPostgreSQLの同時接続数増やします。
@@ -34,7 +53,8 @@
command:
[
"postgres",
- "-cshared_preload_libraries=pg_stat_statements"
+ "-cshared_preload_libraries=pg_stat_statements",
+ "-cmax_connections=100"
]
environment:
POSTGRES_USER: graph-node
スケールアウトのためにグラフノードを増やすのでPostgreSQL側でも待ち受け接続数を増やす必要があります。ここでは適当に100にしています。
グラフノードをインデックスノード・クエリノードの2つに分ける
次にグラフノードを2つの役割分け、インデックス専用ノード・クエリー専用ノードに分けます。このうちクエリ専用ノードの数を増やすことでスケールアウトします。この役割分割をしないままスケールアウトとするとインデックスノード複数になりインデックスの仕事が競合します。別解としてnode_idをユニークにするというアプローチもありますが今回はこの方法を採用しませんでした、理由は後述。
インデックスノードとクエリーノードは、起動時のDISABLE_BLOCK_INGESTOR
環境変数で切り替えます。DISABLE_BLOCK_INGESTOR=false
であればクエリノードに、true
であればインデックスノードになります。これはちょっと複雑なのですがDockerfileの内部のstart
スクリプトでnode_role
により切り替えられていますので、こちらを利用して以下のように設定しています。
graph-node-index:
environment:
node_role: index-node
node_id: index-node
BLOCK_INGESTOR: index-node
graph-node-query:
environment:
node_role: query-node
これだけだとホスト側のポート設定が競合するので、ホスト側のポート競合を防ぐためインデックスノード(graph-node-index)はAPIポート(8020)のみ、クエリーノード(graph-node-query)はHTTP(8000)とWebsocket(8001)のみで待ち受けます。
graph-node-index:
image: graphprotocol/graph-node
ports:
# - '8000:8000'
# - '8001:8001'
- '8020:8020'
#- '8030:8030'
#- '8040:8040'
graph-node-query:
image: graphprotocol/graph-node
ports:
- '8000:8000'
- '8001:8001'
#- '8020:8020'
#- '8030:8030'
#- '8040:8040'
この状態で、クエリノードをスケールアウトしてやれば無事完了です。
docker-compose up -d --scale graph-node-query=5
お疲れさまでした。
(後述)なぜnode_idをユニークにすることで競合回避しなかったのか
私の最初のアプローチはnode_idをユニークにすることでした。しかしながら以下の2つの理由により方針を変更しました。
APIノードのエンドポイントの隠匿
知らない人に勝手にAPIにアクセスされるの機会を減らすため、一般のユーザがアクセスするクエリノードと完全に隔離したかった。
公式のDockerfileをそのまま使いたかった
node_idをユニークに変更するために$HOSTNAME
に設定するのが一番簡単そうですが、このためにstartスクリプトを修正する必要があり、つまり単にめんどくさかった。