NEMの初期バージョンのノード(NIS1: NEM Infrastructure Server)の構築方法

NEMの次期バージョンSymbolが、とうとうメインネットローンチされました。(長かった...ホント長かった...)そちらの記事もいずれ書きたいのですが、NEMの初期バージョンは今後もSymbolとは別のブロックチェーンとして稼働し続けていくので、自身の備忘録のためにも、Google Cloud Ploatform上にNEMの初期バージョンのノード(=NIS1と呼ばれる... NEM Infrastructure Serverの略称)を構築した方法をこの記事にまとめておこうと思います。
何か間違いや、より良い方法等あれば、ぜひ教えて頂けると助かります。よろしくお願いします。

# 概要

1. ファイアーウォールルールを作成
1. GCEでVMを作成しSSHでログイン
1. Javaのインストール
1. NIS1の本体をダウンロードして解凍
1. ノードの起動スクリプトを修正
1. ノードを起動
1. 起動後の確認
1. SSL化

# 手順

## 1. ファイアーウォールルールを作成

NIS1では、httpでは7890番ポート、WebSocketでは7778番ポートを使用するようです。また、httpsでは7891番ポート、WebSocket(SSL)では7779番ポートを使用するのが慣例のようです。以下の例の通り、ファイアーウォールルールを作成しておきます。名前やターゲットタグは自由ですが、ターゲットタグは後ほどVMの設定で使用します。

1. 左上のナビゲーションメニュー(三本線アイコン)をクリック
1. 「VPCネットワーク」をクリック(下の方にスクロールすると出てくる)
1. 「ファイアウォール」をクリック
1. 「ファイアウォールルールを作成」をクリックして以下2個のファイアーウォールルールを「作成」する

### ファイアウォールルール1個目: http用
 - 名前: `nem-http-allow`
 - ターゲットタグ: `nem-http-allow`
 - ソースIPの範囲: `0.0.0.0/0`
 - プロトコルとポート: 指定したプロトコルとポートのラジオボタンにチェックを入れ、「tcp」の左横のチェックボックスにチェックを入れ、その右横のインプットボックスに`7890, 7778`
 - 「作成」をクリック

### ファイアウォールルール2個目: https用

 - 名前: `nem-https-allow`
 - ターゲットタグ: `nem-https-allow`
 - ソースIPの範囲: `0.0.0.0/0`
 - プロトコルとポート: 指定したプロトコルとポートのラジオボタンにチェックを入れ、「tcp」の左横のチェックボックスにチェックを入れ、その右横のインプットボックスに`7891, 7779`
 - 「作成」をクリック

## 2. GCEでVMを作成

事前に調べたり噂を聞いたりした結果、それなりに重い処理が走りそうな印象だったため、最初はそれなりにスペック高めの設定にしてみました。
初期設定時の同期が終わって状態が安定したら、コストも考えてスペックを絞ることを検討したほうが良いと思います。
VMの設定例は以下の通りです。

### VMの設定例

- 名前: nem-mainnet-1
- リージョン: asia-northeast1(東京)
- ゾーン: asia-northeast1-b
- マシンの構成
    - シリーズ: E2
    - マシンタイプ: カスタム
    - コア数: 2 vCPU
    - メモリ: 16 GB (適当に確保しすぎな感がありますが一応...)
- ブートディスク
    - 「変更」をクリック
    - オペレーティングシステム: Ubuntu
    - バージョン: Ubuntu 20.04 LTS
    - ブートディスクの種類: 標準の永続ディスク
    - サイズ: 400 GB (適当に確保しすぎな感...以下略)
    - 「選択」をクリックしてブートディスクの設定を保存
- ファイアウォール
    - 「HTTPトラフィックを許可する」チェックON
    - 「HTTPSトラフィックを許可する」チェックON
- 管理、セキュリティ、ディスク、ネットワーク、単一手ナンシーをクリックし、ネットワーキングタブを選択
    - ネットワークタグ: `nem-http-allow`と`nem-https-allow`を設定
    - ネットワークインターフェース: 編集アイコンをクリックして設定
    - 「外部IP」を「エフェメラル」から「IPアドレスを作成」を選択し、ダイアログで適当な名前(例えばnem-mainnet-1)を入力し「予約」をクリック
    - 「完了」をクリックしてネットワークインターフェースの設定を保存
- 「作成」をクリックするとVMが作成される

### VMにSSHログイン

VM作成後、しばらくすると、VMの起動が完了し、VMの一覧表の「接続」という列の「SSH」というセレクトリストがクリックできるようになるのでクリックするとブラウザでVMにSSHログインした画面が開きます。以降の操作は主にその画面内で行います。

## 3. Javaのインストール

NIS1の実行には Java 8 が必要なのでインストールします。ついでに他の更新もやっておきましょう。以下のコマンドを実行します。

### 更新

```
sudo apt update
```

### Java 8 (正確にはOpenJDK8)のインストール

```
sudo apt install -y openjdk-8-jdk
```

### Java 8 インストールの確認

以下コマンドを実行し、

```
java -version
```

以下のような結果が得られたならインストール成功です。

```
openjdk version "1.8.0_252"
OpenJDK Runtime Environment (build 1.8.0_252-8u252-b09-1ubuntu1-b09)
OpenJDK 64-Bit Server VM (build 25.252-b09, mixed mode)
```

## 4. NIS1の本体をダウンロードして解凍

LinuxでNIS1を実行するためのツールはtgzファイルに圧縮されたものが以下URLより配布されているので、ダウンロードして解凍して使用します。
2021/11/24時点の最新バージョン: https://bob.nem.ninja/nis-0.6.100.tgz
他バージョンや関連するデータのリンクが表示されたページ: https://bob.nem.ninja/

### ダウンロード

以下コマンドでnis1.tgzというファイル名としてダウンロードできます。nis1.tgzのファイル名は拡張子さえ.tgzになっているならなんでもOKですが、次の解凍のコマンドではファイル名の箇所を適切に読み替えてください。

```
curl -o nis1.tgz https://bob.nem.ninja/nis-0.6.100.tgz
```

### 解凍

(ダウンロード時にnis1.tgzというファイル名でダウンロードしていた場合、)以下コマンドで解凍できます。解凍すると、packageというフォルダができているでしょう。

```
tar -xzvf nis1.tgz
```

## 5. ノードの起動スクリプトを修正

以下コマンドでpackageフォルダに移動し、

```
cd package
```

以下コマンドでnix.runNis.shファイル(ノード起動のシェルスクリプト)をエディタで開いて修正します。ここではエディタとしてnanoを使う例を示しますが、エディタはお好みのものをお使いください。

```
nano nix.runNis.sh
```

最低限、変更すべき点は、以下の通りです。

### メモリ使用量の最大値を緩和

デフォルトの設定値では、メモリが足りず、正常に起動できないようです。今回はメモリは16GBとかなりの余力を持ってVMを作成しているので、思い切ってNIS1のプロセスに14GBまでメモリを使用できるような設定として試してみます。(おそらくもう少し控えめな最大メモリ割り当てでも動くと思います。)

### 起動コマンドの先頭に`nohup`を追加、起動コマンドの末尾に`&`を追加

ノード起動のシェルスクリプトをデフォルトで実行すると、NIS1がフォアグランドで実行されてしまいます。これではログオフするとNIS1が強制的に終了となってしまいます。起動コマンドの先頭に`nohup`を追加、起動コマンドの末尾に`&`を追加することで、バックグラウンドで、ログインしていなくても実行され続けるようにすることができるようです。

変更前のnix.runNis.sh

```
#!/bin/bash

cd nis
java -Xms512M -Xmx1G -cp ".:./*:../libs/*" org.nem.deploy.CommonStarter
cd -

```

変更後のnix.runNis.sh

```
#!/bin/bash

cd nis
nohup java -Xms512M -Xmx14G -cp ".:./*:../libs/*" org.nem.deploy.CommonStarter &
cd -
```

エディタとしてnanoを使用した場合、上書き保存するには、まず「Ctrl」 + 「X」で、次に「Y」、ファイル名表示されたら「Enter」という手順になります。

次に、必須ではないのですが、IPアドレスではなく、ドメイン名を設定しておくと良いでしょう。

### ドメイン名の設定

ドメインレジストラ側の設定は、各環境に応じて適宜設定が必要だと思います。サーバーのIPアドレスをAレコードとして登録するという形がシンプルでよくある方法だと思います。この手順は詳細の説明は省略します。

そして、ドメイン名(ここでは`nem-main-1.next-web-technology.com`とします)を、package/nis/config.properties ファイル内の以下の箇所に設定します。(config.propertiesファイルを直接編集するのはどうやら良くないようで、config-user.propertiesファイルを作成して、そこに各ユーザー毎の設定を追記するのが適切だそうです。おそらく、バージョンアップの内容がconfig.properties内にある場合もあり、前バージョンのconfig.propertiesの内容を無条件にコピペして上書きして、不適切な設定でノードを起動してしまうような事態を避けたいということだと思います。)

```sh
nem.host = nem-main-1.next-web-technology.com
```

## 6. ノードを起動

以上でノード起動の準備が整ったので、以下コマンドでスクリプトファイルに実行権限を付与して、ノードを起動します。

```
sudo chmod +x nix.runNis.sh
./nix.runNis.sh
```

## 7. 起動後の確認

バックグランドで動作するように設定しているので、画面には起動後も特段メッセージ等は表示されないため、外部からブロック高さやノード情報等を取得するAPIを叩いて、適切な値が返ってくるか確認しておきましょう。IPアドレスは自分の環境に読み替えてください。

### ブロック高さ

http://nem-main-1.next-web-technology.com:7890/chain/height

- レスポンスの例

```
{"height":44967}
```

起動直後であれば、まだ同期が終わっておらず、最新のブロック高さよりもずいぶん低いところまでしか同期が済んでいないことがわかるでしょう。この数字が、少しずつ増えていき、いずれは最新のブロック高さに追いつくとノードの構築は一旦区切りといったところでしょうか。

本来であれば、この同期にかかる時間を短くするために、ある程度直近までのブロックチェーンのDB情報を別途ダウンロードして展開しておくという方法が可能なので、どこかでその方法も追記したいと思っています。

### ノード情報

http://nem-main-1.next-web-technology.com:7890/node/info

- レスポンスの例

```json
{
	"metaData": {
		"features": 1,
		"application": null,
		"networkId": 104,
		"version": "0.6.100",
		"platform": "Ubuntu (11.0.14.1) on Linux"
	},
	"endpoint": {
		"protocol": "http",
		"port": 7890,
		"host": "nem-main-1.next-web-technology.com"
	},
	"identity": {
		"name": "next-web-technology-main-1",
		"public-key": "68bd26a4305f9a248a01cf7cc432970f3848b085fba3b91881b61cdc902ff83e"
	}
}
```

その他にノード情報のAPI等も叩いておくと、自身の設定への確信が深まるでしょう。networkIdが104はMAIN_NETを示していて、NIS1とJavaのバージョンが示されていたり、APIのプロトコルやポートやホスト名が示されているのがわかると思います。

## 8. SSL化

近年のWeb開発やアプリ開発においてはSSL化されていないAPIエンドポイントはかなり使いにくくなってきており、NIS1のノードが提供するAPIも可能であればSSL化されていると開発者が使用しやすくなってより望ましいと思います。しかし、デフォルトではそのような機能は埋め込まれていないため、Let's Encryptで証明書を取得し、stunnelで該当ポートへのリバースプロキシを設定することで実現する方法を記載しておきます。

### pipをインストール

```sh
sudo apt install python3-pip
```

### Let's Encryptをインストール

```sh
sudo pip3 install letsencrypt
```

### Let's Encryptを実行

```sh
sudo letsencrypt certonly --standalone -d nem-main-1.next-web-technology.com
```

### stunnelをインストール

```sh
sudo apt-get install -y stunnel4
```

### stunnelの設定ファイルでポートや証明書のパス等を設定する

`/etc/stunnel/stunnel.conf`を以下の内容で生成します。ドメインの箇所は各自の環境に適宜置き換えてください。

```
[nis]
accept = 0.0.0.0:7891
connect = 7890
cert = /etc/letsencrypt/live/nem-main-1.next-web-technology.com/fullchain.pem
key = /etc/letsencrypt/live/nem-main-1.next-web-technology.com/privkey.pem

[websocket]
accept = 0.0.0.0:7779
connect = 7778
cert = /etc/letsencrypt/live/nem-main-1.next-web-technology.com/fullchain.pem
key = /etc/letsencrypt/live/nem-main-1.next-web-technology.com/privkey.pem
```

### stunnelを再起動

```sh
sudo systemctl restart stunnel4
```

ここまで正常に実行できると、以下のようにhttpsから始まるURLでアクセスできるようになっていると思います。httpsの場合、ポート番号がhttpのポート番号に+1した番号となることにご注意ください。

https://nem-main-1.next-web-technology.com:7891/chain/height
https://nem-main-1.next-web-technology.com:7891/node/info

### 3か月に1回証明書更新

Let's Encryptは3か月で証明書が切れるので、3か月に1回、`certbot renew`コマンドを実行する等して証明書の更新を行う必要があります。

certbotのインストール

```sh
sudo apt install -y certbot
```

`certbot renew`コマンドの実行は、cron等で自動化するのが便利だと思います。

# 最後に

実はシェルスクリプトを実行してJavaを動かしているだけなのか！と最初に驚いた印象があります。それゆえ、説明するまでもない...という部分が多く、かっちりとした手順のような情報があまり見つけられない印象を個人的には感じています。

はまりどころとしては、メモリの最大値の修正箇所や、デフォルトだとフォアグランド実行になってしまう点や、そもそもNIS1本体プログラムの置き場所はどこ？といった部分でしょうか。それらのポイントがもしこの記事で解決できたらとてもうれしいです。

なお、記事に間違いや、望ましくない点等あれば、ぜひご指摘頂けると幸いです。

最後に、NIS1のノードは次期バージョンSymbolのノードと異なり、ノードを建てるインセンティブは正直乏しいです。しかし、NIS1自体はこれからも次期バージョンSymbolとはまた違った形で生き続けるブロックチェーンであってくれるといいな...と思います。そのためには、SSL化されたノードが安定して多数稼動してくれていることが大切だと思います。この記事がその一助になれば幸いです。

# 参考情報

以下の情報を参考にさせて頂きました。先駆者の皆様、ありがとうございます。

- NEM公式ブログのNIS1ノード構築: https://blog.nem.io/ubuntu-installation-guide-standalone/
- NEM公式ブログのNIS1ノードSSL化: https://blog.nem.io/https-nis-node/
- テストネットのNIS1ノード構築: https://blog.44uk.net/2018/02/12/up-nem-testnet-node/
- メインネットのNIS1ノード構築: https://mizunashi-rin.hatenablog.jp/entry/2017/02/15/002342
- NIS1ノードSSL化: https://blog.44uk.net/2017/10/31/nis-with-https-by-dehydrated/