Hyperledger Fabric Multi Host 対応(1)

Hyperledger Fabric のマルチホスト構成について、実装方法の検討とFabcar を使用した実装例を考えてみる。

## Hyperledger Fabric のマルチホスト構成の検討
Hyperledger Fabric のマルチホスト構成については、Docker swarm を使用する方法が、よく紹介されている。  
swarm を使用すると、Docker 内の名前解決などが解決できるため容易に構築ができる反面、Docker network を共有することになり、企業を跨いだ環境を考えた場合、セキュリティ上の問題が出る可能性があり、現実的ではない。

本ドキュメントでは、Hyperledger のサンプルである fabric-sample の fabcar をベースに swarm を使用せずにマルチホスト化する。 

## swarm を使用しない場合の課題
Docker swarm を使用しない場合は、ホスト側のIPを使用して通信することになる。そのため、以下のような課題がある。

### コンテナ名の名前解決

名前解決が必要な箇所は2つある。  
Docker内と、ホストOS での名前解決が必要。

#### Docker内の名前解決
Dockerコンテナ上では、Docker networkがコンテナ名で名前解決を行ってくれるが、Docker networkが分離された場合、名前解決を行うことができない。  
Extra-hosts を指定することで、コンテナ内のhostsの設定を行うことができ、静的に名前解決を行うことができる。
今回は、Extra-hosts で対応する。  

#### Host os上からの名前解決

javascript からのアクセスなど、ホストOSからのアクセスの場合は、ホストOK 上からのコンテナ名の名前解決が必要となる。fabric-sample は、同一ホスト上で実行することを想定しているので、名前解決は行わず localhost アクセスするで対応している。  
ccp-template.json (.yaml) で、javascript からの接続先などを取得しているが、以下のように、grpc の接続先を'localhost' としている。  

ccp-template.json の peer部分の抜粋

```
    "peers": {
        "peer0.org${ORG}.example.com": {
            "url": "grpcs://localhost:${P0PORT}",
            "tlsCACerts": {
                "pem": "${PEERPEM}"
            },
            "grpcOptions": {
                "ssl-target-name-override": "peer0.org${ORG}.example.com",
                "hostnameOverride": "peer0.org${ORG}.example.com"
            }
        },
```

マルチホストの場合は、localhost では他のホスト上のコンテナにもアクセスする必要があり、宛先を指定する必要がある。  
直接 IPアドレスを記載することも可能であるが、IPの変更への対応が困難になるので、以下のように コンテナ名とし、名前解決を行うようにする。  

```
    "peers": {
        "peer0.org${ORG}.example.com": {
            "url": "grpcs://peer0.org${ORG}.example.com:${P0PORT}",
            "tlsCACerts": {
                "pem": "${PEERPEM}"
            },
            "grpcOptions": {
                "ssl-target-name-override": "peer0.org${ORG}.example.com",
                "hostnameOverride": "peer0.org${ORG}.example.com"
            }
        },
```

コンテナ名の名前解決については、DNS を使用するか hosts に記載する方法があるが、今回は、以下のように hosts で対応する。

```
$ sudo vi /etc/hosts
```
以下のように、ノードを配置したホストのIPを設定する。
※ ordererと、org1 は、192.168.1.100 に、org2 を192.168.1.101 に配置した例

```
192.168.1.100   orderer.example.com
192.168.1.100   ca.org1.example.com
192.168.1.100   peer0.org1.example.com
192.168.1.100   peer1.org1.example.com
192.168.1.101   ca.org2.example.com
192.168.1.101   peer0.org2.example.com
192.168.1.101   peer1.org2.example.com
```

あと、javascript 内の 'gateway.connect' にも修正が必要。
現状は、以下のように設定されている。

```

        // Create a new gateway for connecting to our peer node.
        const gateway = new Gateway();
        await gateway.connect(ccpPath, { wallet, identity: 'user1', discovery: { enabled: true, asLocalhost: true } });
```

asLocalhost: true だと、他のホスト側の参照がlocalhost に置き換えられてしまい以下のようなエラーが出ます。

```
2020-02-09T08:45:59.979Z - error: [Remote.js]: Error: Failed to connect before the deadline URL:grpcs://localhost:10051
2020-02-09T08:45:59.981Z - warn: [DiscoveryEndorsementHandler]: _build_endorse_group_member >> G1:0 - endorsement failed - Error: Failed to connect before the deadline URL:grpcs://localhost:10051
2020-02-09T08:46:02.981Z - error: [Remote.js]: Error: Failed to connect before the deadline URL:grpcs://localhost:9051
2020-02-09T08:46:02.982Z - warn: [DiscoveryEndorsementHandler]: _build_endorse_group_member >> G1:0 - endorsement failed - Error: Failed to connect before the deadline URL:grpcs://localhost:9051
2020-02-09T08:46:02.983Z - error: [DiscoveryEndorsementHandler]: _endorse - endorsement failed::Error: Endorsement has failed
    at DiscoveryEndorsementHandler._endorse (/home/fukata/fabric-samples/fabcar/javascript/node_modules/fabric-client/lib/impl/DiscoveryEndorsementHandler.js:185:19)
    at <anonymous>
    at process._tickCallback (internal/process/next_tick.js:189:7)
Failed to submit transaction: Error: Endorsement has failed
```

そのため、以下のように、asLocalhost: false に修正する必要があります。

```
        // Create a new gateway for connecting to our peer node.
        const gateway = new Gateway();
        await gateway.connect(ccpPath, { wallet, identity: 'user1', discovery: { enabled: true, asLocalhost: false } });
```

### 各peerやordererの証明書ファイルの配布
fabric-sample では、cryptogen で生成した全コンテナの証明書を、ホスト上のフォルダに格納し各コンテナが参照するようになっている。  
マルチホストの場合は、各ホストで生成して公開鍵を他のホストに配るなどの対応が必要となる。  
今回は、1つのホスト上で作成した証明書を他のホストに配るようにする。