Fragments of verbose memory

冗長な記憶の断片 - Web技術のメモをほぼ毎日更新(準備中)

May 3, 2022 - コメント - 日記

グラフノードのスケールアウト

English version

何らかの理由により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スクリプトを修正する必要があり、つまり単にめんどくさかった。