catapult-service-bootstrap のノードを別々のサーバに立ち上げてネットワークを構築する

おおまかこちらの受け売りですが、物理的に別のサーバにノードを立てて、
現実に近いようなネットワーク環境を立ち上げてみました。
なるべく少ない手間でできたと思うので、実証実験などをやる際には、ぜひ試してみてください。

* [catapult\-service\-bootstrapにpeer\-nodeを追加する その１ サービス追加 \- Qiita](https://qiita.com/planethouki/items/b80fe29157d80a63122c)
* [catapult\-service\-bootstrapにpeer\-nodeを追加する その２ サーバー編 \- Qiita](https://qiita.com/planethouki/items/4c73a6b924b92529bd32)

記事中のIPアドレス等はご自身の環境等に読み替えてください。


# 環境

* Alibaba Cloud ECS
  * 今回スペックは不問。動けば良しということで。
  * AWSやAzureや他VPSサービスでも同じような方法で構築できるでしょう
* [tech\-bureau/catapult\-service\-bootstrap](https://github.com/tech-bureau/catapult-service-bootstrap)
  * 執筆時点では`2348d31`(Bison版)に(チューニングではない)修正をしたフォークを使用
  * [44uk/catapult\-service\-bootstrap at prevent\-overwriting\-configs](https://github.com/44uk/catapult-service-bootstrap/tree/prevent-overwriting-configs)


# ノードネットワークを作成

`catapult-service-bootstrap`は`docker-compose up`するだけで、3つのノードが立ち上がり、
すぐに`catapult`環境を試せる便利な構成になっているものです。

通常では実行したマシン上で動いている`docker`内の同一ネットワークで動作するようになっていますが、
今回はその３つのノードをそれぞれ別のサーバインスタンス上で動かし、相互に通信しているような状態を目指します。


## インスタンスからイメージを作成

それぞれ別のリージョンでインスタンスを作りますが、ひとつづつ立ち上げるのは面倒なので、
まずは東京リージョンでインスタンスを立ち上げて、セットアップます。

まずはベースになるインスタンスを作ります。
なお、手抜きで`root`ユーザのまま進めていくことをお許しください。
(作業中に不正侵入されないように、雑なパスワード認証じゃなく、ちゃんと公開鍵認証にしときましょう)


### インスタンスを作成

雰囲気でポチポチクリックしてインスタンスを作成してください。
日本リージョンのインスタンスで作業を始めるのがレスポンスも良くやりやすいです。
(ドイツ、インドのインスタンスは距離があるためターミナルのレスポンスが遅い)

「従量課金」を選択し、イメージは`Ubuntu 18.04`で
「インスタンスタイプ」はとりあえず`ecs.sn2ne.large`にしました。
ログインはキーペアを作って、公開鍵認証でやりました。
パブリックIPを割り当てて、インターネットからアクセスできるようにします。


### セキュリティグループ

- 22
- 3000
- 7900
- 8000

ポートはこの4つを開けておきます。
実際はノードの役割によっては不要なポートも空いてしまいますが、
あとで定義を他のリージョンにコピーして手間を省くためです。

![スクリーンショット 2019-01-21 3.26.58.png](https://qiita-image-store.s3.amazonaws.com/0/2353/6bee194c-9960-2f1e-118b-964129ada9df.png)


### 必要なツールをインストール

インスタンスにログインしたら、ツールをインストールしていきます。

* git
* docker
* docker-compose

https://docs.docker.com/install/linux/docker-ce/ubuntu/
ここを丸パクリで`docker`と`docker-compose`とついでに`git`もインストールします。

```bash
#!/bin/bash
apt update -y && apt install -y \
    apt-transport-https \
    ca-certificates \
    curl \
    gnupg2 \
    git \ # ついでにインストール
    software-properties-common
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add -
apt-key fingerprint 0EBFCD88
add-apt-repository \
   "deb [arch=amd64] https://download.docker.com/linux/ubuntu \
   $(lsb_release -cs) \
   stable"

# install docker
apt update -y && apt install docker-ce -y

# install docker-compose
curl -L "https://github.com/docker/compose/releases/download/1.23.2/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
sudo chmod +x /usr/local/bin/docker-compose
```

ついでなので、ここで`catapult-service-bootstrap`をクローンしてしまいましょう。

```bash
git clone --depth 1 -b prevent-overwriting-configs https://github.com/44uk/catapult-service-bootstrap.git
```

公式のものだと、生成した設定ファイルを書き換えても、
ノードの起動時に上書きされてしまう挙動をするので、
それだけ避ける修正をいれた私のブランチのものを使います。
変更箇所を確認してもらえれば、公式からクローンして修正してもいいです。
https://github.com/44uk/catapult-service-bootstrap/commit/7acb46225ce120f0e1a258ca288dbd1884f878c4


### インスタンスのイメージを作成

他の２つのサーバも中身は同じでいいので、ここでイメージ化してしまいます。
イメージ化が済んだら、別のリージョンにも複製し、そのイメージを使ってインスタンスを立ち上げます。
これでそれぞれのリージョンに同じ状態のインスタンスが立ち上がったことになります。

セキュリティグループやイメージは「クローン」や「イメージのコピー」というところで
別のリージョンへ同じ設定をコピーできるので、これを利用します。

![スクリーンショット 2019-01-21 3.35.12.png](https://qiita-image-store.s3.amazonaws.com/0/2353/7f34aa59-eb7e-ecc0-731e-c5856d62611c.png)

![スクリーンショット 2019-01-21 3.32.17.png](https://qiita-image-store.s3.amazonaws.com/0/2353/8d212765-7a1b-8524-aa1b-c7bace0e6f66.png)


### インスタンスをイメージから作成

イタリアにリージョンがあれば三国同盟が完成したのですが、
無かったので響きが近い「日独印」としました。
インドは非同盟主義だって…？ええぃこまけぇことは(ry
おおまか地球上での物理的な配置も均等な距離になるかと思われます。

ドイツリージョンとインドリージョンに切り替えて、インスタンスを作成してください。
作成途中でカスタムイメージからコピーしてきたイメージやセキュリティグループを使用します。
「イメージのパスワードを使用」を選択すると同じ鍵でログインできるので楽です。

![スクリーンショット 2019-01-21 3.37.42.png](https://qiita-image-store.s3.amazonaws.com/0/2353/77337f50-4b0c-3b4c-015a-9fc614507aeb.png)


## 設定を生成・編集

まず、日本サーバにログインして、設定ファイルを生成します。

```bash
docker-compose up generate-raw-addresses store-addresses generate-configs
```

`catapult-service-bootstrap/build`の中に、初期アドレスや初期設定ファイルが生成されました。
これを一度ローカルにダウンロードしてきます。

```bash
scp -Cri ~/aliyun.pem root@47.74.49.8:~/catapult-service-bootstrap/build ~/Desktop/
```

### peerのホスト名を書き換える

- `api-node-0/userconfig/resources/peers-api.json`
- `api-node-0/userconfig/resources/peers-p2p.json`

それぞれ`host`の部分を担当するサーバのIPアドレスに書き換えます。
`peer-node-0/`,`peer-node-1/`
これらのディレクトリにも入っていますが、いま編集したものと同じでいいので
書き換えたら、それで上書きコピペしてしまってよいです。


### ハーベスターの設定

このままだと１台しかハーベスト(ブロック生成)しないため、エクスプローラで見たときに
動作がわかりにくいので、ハーベスタを設定します。

```catapult-config/peer-node-1/userconfig/resources/config-harvesting.properties

harvestKey = <秘密鍵>
```

ここでの秘密鍵には

```address.yaml
nemesis_addresses_harvesting:
```

の２つ目の秘密鍵を設定します。


```catapult-config/peer-node-1/userconfig/resources/config-node.properties
# p2p extensions
extension.eventsource = true
extension.harvesting = true # 追記する
extension.syncsource = true
```

`extension.harvesting = true` を追記します。


### 編集したファイルをサーバに転送する

上記の修正を行ったファイルを各サーバへ転送します。

```bash
scp -Cri ~/aliyun.pem ~/Desktop/build root@47.74.49.8:~/catapult-service-bootstrap/
scp -Cri ~/aliyun.pem ~/Desktop/build root@47.254.177.14:~/catapult-service-bootstrap/
scp -Cri ~/aliyun.pem ~/Desktop/build root@149.129.139.135:~/catapult-service-bootstrap/
```


### `7900`ポートを晒す

```docker-compose.yml(またはdocker-compose-with-explorer.yml)
ports:
- "7900:7900"
```

インターネット越しにノードが通信できるように`7900`ポートを晒します。
これは立ち上げるサービスのところだけに記述すればいいです。
各サーバにログインして、書き換えてください。


# サーバを起動

いよいよサーバを起動していきます。


## API・エクスプローラサーバを起動

```bash
docker-compose -f docker-compose-with-explorer.yml up rest-gateway api-node-0 block-explorer
```

起動したら、それぞれアクセスできるか見てみましょう。

- http://47.74.49.8:3000/block/1
- http://47.74.49.8:8000/

```bash
api-node-0_1 | 2019-01-20 15:44:24.295387 0x00007fe894b1d700: <error> (ionet::PacketSo
cket.cpp@456) failed when connecting to '149.129.139.5': Connection refused (cancelled? false)
api-node-0_1 | 2019-01-20 15:44:24.436240 0x00007fe89431c700: <error> (ionet::PacketSocket.cpp@456) failed when connecting to '47.254.177.14': Connection refused (cancelled? false)
```

まだ`peer`サーバが立ち上がっていないので、ログにはこのようなエラーが出てきているはずです。
指定したIPアドレスへアクセスしようとしているかどうかを確認しておきましょう。


## Peerサーバを起動

では他の2台でもノードを立ち上げてみましょう。

```bash
docker-compose up peer-node-0
```

```bash
docker-compose up peer-node-1
```

これで、しばらくするとノード同士が接続されて、ブロック同期が始まり、ブロック生成が始まります。

ログにも同期成功が現れてきます。

```
api-node-0_1 | 2019-01-20 18:17:12.678942 0x00007fab116b5700: <info> (chain::RemoteApiForwarder.h@65) completed 'synchronizer task' (peer-node-1 @ 149.129.139.135:7900) with result Success
```

エクスプローラで確認してみてください。

![スクリーンショット 2019-01-21 3.18.34.png](https://qiita-image-store.s3.amazonaws.com/0/2353/25072afa-7432-a168-bbdd-b14ca90ec1b8.png)

ハーベストのばらつきが偶然確認できないようであれば、
片肺運転にしてみてください。

これでインターネット越しにネットワークが立ち上がりました。
日独印三国同盟の完成です。


## サービス化

systemdでサービス化するためのコマンドメモ。
パスは適宜置き換えてください。

```
# APIサーバ用
cat << __EOD__ > /etc/systemd/system/catapult.service
[Unit]
Description = Catapult Server
Requires=docker.service
[Service]
WorkingDirectory=/root/catapult-service-bootstrap/
User=root
Type=simple
ExecStart=/usr/local/bin/docker-compose -f docker-compose-with-explorer.yml up rest-gateway api-node-0 block-explorer
ExecStop=/usr/local/bin/docker-compose -f docker-compose-with-explorer.yml stop
Restart=always
RestartSec=60s
[Install]
WantedBy=multi-user.target
__EOD__

# Peer0用
cat << __EOD__ > /etc/systemd/system/catapult.service
[Unit]
Description = Catapult Server
Requires=docker.service
[Service]
WorkingDirectory=/root/catapult-service-bootstrap/
User=root
Type=simple
ExecStart=/usr/local/bin/docker-compose up peer-node-0
ExecStop=/usr/local/bin/docker-compose stop
Restart=always
RestartSec=60s
[Install]
WantedBy=multi-user.target
__EOD__

# Peer1用
cat << __EOD__ > /etc/systemd/system/catapult.service
[Unit]
Description = Catapult Server
Requires=docker.service
[Service]
WorkingDirectory=/root/catapult-service-bootstrap/
User=root
Type=simple
ExecStart=/usr/local/bin/docker-compose up peer-node-1
ExecStop=/usr/local/bin/docker-compose stop
Restart=always
RestartSec=60s
[Install]
WantedBy=multi-user.target
__EOD__

各サーバでサービスの有効化や動作確認など
systemctl enable catapult
systemctl start catapult
journalctl -fu catapult
```


# ＊

ちょっとざっくり書いたので後で加筆修正するかもしれません。