dockerの勉強がてら、nginxコンテナ+nisコンテナを立ててみました。
目次
構成
ディレクトリ構成
dockerの構成としてはこんな感じです。nisコンテナの前段に、リバースプロキシとしてnginxコンテナを立てます。
├ docker-compose.yml
├ nginx/
│ ├ Dockerfile
│ └ nis.conf
└ nis/
└ Dockerfile
リバースプロキシを置くメリットとしては
- nisよりアクセスログが見やすい
- リクエストを制御しやすい
→アクセス許可/拒否、リダイレクト、キャッシュ、SSL化
みたいなことが挙げられ、nis単体で運用するよりも何かとリクエストを管理しやすくなります。
例えばこないだPoSの脆弱性が見つかったとかで、加工したリクエストを他のノードに投げてダウンさせる攻撃が話題になってましたが、IPとポートを野晒しにしてるNIS相手ならそんな手の込んだことをしなくてもF5アタックで普通に狙い撃ちできてしまいますよね。
自分のノードが狙われると困っちゃうので、あらかじめリバースプロキシなりWAFなりを置いてアプリケーションを守れるようにしとくのが懸命です。
nginxコンテナ
Dockerfile
1 2 3 4 5 6 7 8 9 |
From nginx:1.15.8-alpine ADD nis.conf /etc/nginx/conf.d/ RUN set -xe; \ rm /etc/nginx/conf.d/default.conf; \ ln -sf /dev/stdout /var/log/nginx/nis.access.log; \ ln -sf /dev/stderr /var/log/nginx/nis.error.log CMD ["nginx", "-g", "daemon off;"] |
nginxのalpineイメージをベースに、nis用の設定ファイルとしてnis.confを追加して起動しています。
nis.conf
1 2 3 4 5 6 7 8 9 10 11 12 |
server { listen 80; server_name _; charset UTF-8; access_log /var/log/nginx/nis.access.log main; error_log /var/log/nginx/nis.error.log; location / { proxy_pass http://nis:7890/; } } |
80ポートでリッスンしてnisコンテナの7890ポートにプロキシします。必要に応じてここに色々追記してアクセス制御していきますが、今回はとりあえずまっさらです。
nisコンテナ
Dockerfile
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 |
FROM openjdk:8-jdk-alpine3.8 ENV NIS_VERSION 0.6.96 ENV NIS_URL="http://bob.nem.ninja/nis-${NIS_VERSION}.tgz" ENV NIS_XMS="4G" ENV NIS_XMX="4G" ARG PRIVATE_KEY ARG BOOT_NAME # Install NIS RUN set -xe; \ apk add --no-cache --virtual .fetch-deps \ tar \ wget \ ; \ \ mkdir -p /opt/nem/nis/data; \ \ wget -O nis.tgz "$NIS_URL"; \ tar xzf nis.tgz -C /opt; \ chmod -R g-w /opt/package; \ rm nis.tgz; \ \ apk del .fetch-deps # Init NIS WORKDIR /opt/package RUN set -xe; \ apk add --no-cache \ bash \ ; \ sed -i \ -e "s/Xms512M/Xms${NIS_XMS}/" \ -e "s/Xmx1G/Xmx${NIS_XMX}/" \ nix.runNis.sh; \ \ sed -i \ -e "s/^nem.folder = \%h\/nem/nem\.folder = \/opt\/nem/" \ -e "s/^#nis.bootKey.*/nis\.BootKey = $PRIVATE_KEY/" \ -e "s/^#nis.bootName.*/nis\.BootName = $BOOT_NAME/" \ nis/config.properties EXPOSE 7890 CMD ["./nix.runNis.sh"] |
ベースはopenjdkのalpineイメージです。javaのバージョン何が良いのか分からなかったんですが、適当に8にしたら動きました。
やってることはNISをダウンロードしてきて起動してるだけです。メモリは初期値の1GBだと厳しいので4GBに変えてます。あとはnisのログやDBが置かれるパスを/opt/nemに変更しているのと、ローカルハーベストも行うので自分のアドレスの秘密鍵とノードの名前を環境変数で渡してあげます。環境変数はdocker-compose.ymlで定義します。
docker-compose
docker-compose.yml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
version: "3" services: nginx: container_name: "nginx" build: context: ./nginx ports: - "80:80" links: - nis nis: container_name: "nis" volumes: - nis-db:/opt/nem build: context: ./nis args: PRIVATE_KEY: "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX" BOOT_NAME: "MyNisName" volumes: nis-db: driver_opts: type: none device: /Users/MyName/docker/data/nis o: bind |
ホストの80ポートをnginxコンテナの80ポートにbindしています。nisコンテナのほうでは、秘密鍵とノード名の環境変数を定義すると共に、コンテナ上の/opt/nemをホスト(ここではmacです)の/Users/MyName/docker/data/nisにマウントしています。これにより、コンテナを再起動した場合でもデータが保持されるため、次回起動時の同期処理を高速化することができます。
NISの稼働に必要なリソース
nginxのほうはただプロキシしてるだけなので、よほどアクセスが来ない限りはほとんどリソースを使いません。問題はnisコンテナの方です。CPUコア数こそ1で充分ですが、同期が進めば進むほどメモリを食うので4GBだとなかなか苦しく、8GBあれば安定する感じです。(2018年2月現在の使用状況です)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
[/opt/package] # top Mem: 8034836K used, 130172K free, 784K shrd, 112432K buff, 3082136K cached CPU: 54% usr 0% sys 0% nic 45% idle 0% io 0% irq 0% sirq Load average: 1.09 1.11 1.05 3/490 52 PID PPID USER STAT VSZ %VSZ CPU %CPU COMMAND 6 1 root S 5770m 70% 1 55% java -Xms4G -Xmx4G -cp .:./*:../libs/* org.nem.deploy.CommonStarter 1 0 root S 6244 0% 1 0% {nix.runNis.sh} /bin/bash ./nix.runNis.sh 47 0 root S 1588 0% 0 0% /bin/sh 52 47 root R 1524 0% 0 0% top [/opt/package] # [/opt/package] # [/opt/package] # [/opt/package] # free -m total used free shared buffers cached Mem: 7973 7846 127 0 109 3009 -/+ buffers/cache: 4726 3246 Swap: 1023 6 1017 |
同期の時短チャレンジ
フルノードはチェーン上の全てのデータを保持しています。そのため、データの整合性を保つために他のノードと逐次同期しているわけですが、初回起動時は1ブロック目から最新ブロックまで全データの同期処理が走るのでかなり時間がかかります。2019年2月現在でチェーンのブロック高は200万ちょい、最新ブロックまでの同期待ちで1日半くらいかかりました。流石にこれは時間かかりすぎだろってことで、一度最新化したDBのダンプを使ってリストアするようにしましたが、それでも同期が終わるまでに1時間半くらいかかりました。
おそらくnisの同期は「チェーンのダウンロード」と「データのベリファイ」の2つの処理をやってるのかと思います。リストアすればダウンロードの大半を省略できるので時短はできるものの、ベリファイを1ブロック目から順に流していく必要があるので、どうしても時間はかかってしまうのではないかと推測します。trust but verifyってやつですね。
せっかくコンテナにしたので、コマンド一発でサクッとnisを使えるようにできたら何かと捗るかと思ったんですが、生憎そういう今風の使い方はできないようですね。データを持たせないことを正義とするコンテナと、データを可視化することを正義とするブロックチェーンがアンマッチなのは分かってるんですが、チェーンが進めば進むほど時間が延びるのもなぁって感じでモヤモヤします。
ともあれベリファイに時間がかかるのは仕方がないので、ひとまずはダウンロードの時間を時短できれば良しとします。Dockerfile的には、最初は都度ダンプを落としてきてリストアする形式で書いてたんですが、結局それらにも時間がかかるので、最終的にはボリュームをホストにマウントする形で落ち着きました。この場合でも、コンテナを再起動した場合にベリファイは走りますが、過去に取得したブロックまでのダウンロードは省略できます。
まとめ
少し課題が残るところではありますが、とりあえず当初の目標であったnisのコンテナ化はできました。ちょっとテコ入れすればAWS Fargateとかでも動くと思うので、今度はそっち方面使って冗長化とかしてみようかと思います。
このページにたどり着く方はNISの建て方を調べている方だと思われますが、わざわざサーバを用意しなくともお手元のmacやlinuxのリソースが余っているならdocker入れて docker-compose up -d してもらうだけですぐにフルノードが立てられるはずです。どうやらNEMはまだまだノードが足りないと噂されているようなので、気が向いたら試してみてください。
なお余談ではありますが、僕が探した限りでは、何を根拠にNEMのノードが足りないと言われているのか、その数字的な背景は見当たりませんでした。そもそもチェーン運用に必要とされるノード数は何台で、その台数はどういう計算から導かれたのか、みたいなやつです。
何となく「有名な人がそう言ってるからそうなんだろう」が独り歩きしてる感が否めませんが、技術者としてはアンチパターンなこの風潮も、エンジニアの少ないコミュニティではよく見られる光景だったりします。ブロックチェーンだけでなく人間の方もtrust but verifyを心がけるようにしたいですね。
こんな記事も読まれてます
![]() |
20代30代の客先常駐ITエンジニアが最速で年収を上げる方法 この記事の所要時間: 約 14分56秒
この記事の所要時間: 約 14分56秒 もしあなたが以下の3つ全てに該当するエンジニアであれば、今すぐにでも転職をオススメします。 客先常駐型のエンジニアとして働いている 案件の途中で契約を切られるこ … |