docker-composeを使って、プライベートでstandaloneなgethを立ち上げて永続化する方法【解決編】


失敗編からの続きです。

前提

こんな人には役に立つかもしれない

  • Ethreumを使ってガシガシ開発したいから、開発に使えるEthreumノードが必要。
  • どこかのVPSにノードを立ち上げたりInfura.ioのtestnetを使えばよいのだけど、立ち上げは手軽じゃないし、まだ開発中だからtestnetじゃなくてやプライベートでやりたい、そして、Wifiがなくても開発機内で完結したい、という欲求がある。
  • docker-composeを使いたい。
  • コンテナを終了してもチェーンDATAは残したい(永続化したい)。次にコンテナを立ち上げたときには次のブロックからマイニングが始まるようにしたい。

そんな需要があったので頑張ってやってみました。

当初戦略

  1. gethは公式のDockerイメージを使う。
  2. Dockerfile内でgethのinitやテストアカウントのimportをする。
  3. chain_dataはホスト内に保存されるようにマウントする。
  4. コンテナを立ち上げるときにプライベートネットのnetworkIdを引数で渡す

困ったこと

  • マウント無しでDockerfile, docker-compose.ymlを以下のようにするとすんなりとプライベートネットが立ち上がるが、永続化されていないので、一旦コンテナを削除すると、ジェネシスブロックからの作り直しになってしまう。
  • data_dirをマウントして立ち上げると、今度はnetworkIdが1(つまりmainnet)になってしまう。

マウントするとmainnetになってしまう原因

  • buildのイメージ内にプライベートネットのジェネシスブロックが生成されていても、ホストのディレクトリをマウントした時点で消えてしまう
  • そうするとコンテナ立ち上げのときにgethの引数でdatadirとnetworkIdを指定しても、ジェネシスブロックが存在しないので、デフォルトでmainnetとなるようになっている。

解決手順

マウント=>ジェネシスブロック生成(geth init)=>geth立ち上げ という順序にすることは不可能なので以下のような手順をふむようにした。

  1. まずは、ジェネシスブロック生成用のコンテナを立ち上げる(geth_initコンテナ)
  2. ホストの./geth_dataにgeth_initコンテナの/tmp(空)をマウントする
  3. geth_initコンテナに生成されたdatadirを/tmpに同期するとホストの./geth_dataにチェーンデータができる。
  4. geth_initコンテナを終了してもホストにはプライベートネットのチェーンデータが残る

  5. マイニング用のgethコンテナを先程の./geth_dataをマウントして立ち上げる。

  6. プライベートネットのnetworkIdとマウントされたディレクトリをdatadireとして引数を与えてれば、gethコンテナのgethはそこを参照して、プライベートネットが立ち上がる。

  7. DAG生成後、マイニングが始まり、続くブロックが生成される。

  8. gethコンテナを終了しても、生成されたブロックはホストの./geth_dataに残っている。

  9. 再びgethコンテナを立ち上げると、既存のチェーンデータが参照され、続きからマイニングされる。

コマンド

$ git clone https://github.com/imaichel/ethereum-docker.git
$ cd ethereum-docker/standalone/
$ docker build -f geth/Dockerfile_gethinit -t gethinit:1.0.0 geth
~略~
Successfully built f524701f5b3a
Successfully tagged gethinit:1.0.0
$ docker images
REPOSITORY           TAG                 IMAGE ID            CREATED              SIZE
gethinit             1.0.0               f524701f5b3a        About a minute ago   48.1MB

$ mkdir volume
$ docker run -d -v $PWD/volume:/tmp --name gethinit gethinit:1.0.0 --networkid 1234 --datadir=/root/.ethereum
$ docker exec -it gethinit /bin/bash
# rsync -av /root/.ethereum /tmp/
# exit
$ docker-compose up -d --build
$ docker-compose logs -f