这是一篇简要翻译的文章,亲测有效,因为目前关于搭建多主机hyperledger fabric的文章除了像 深蓝居 (http://www.cnblogs.com/studyzy/tag/Fabric/) 大神使用官方的 e2e_cli 代码进行部署外,其他并没有见到别的什么方式。这篇博客获得了不少的✨,所以自己搭了一遍,当然做这个的直接看原链接就可以,就当留个笔记。原文地址链接: https://medium.com/@wahabjawed/hyperledger-fabric-on-multiple-hosts-a33b08ef24f
Hyperledger Fabric 1.0及到目前最新的1.2的版本与之前的0.6版本在架构上又很大的不同,进行了模块化的解耦及对节点角色的重新定义。0.6的使用过程相对简单,简要记录一下:
1. 启动网络:包括启动根节点,然后其他peer节点加入
2. 部署(deploy) chaincode:可以使用SDK,也可以直接使用postman之类的可以操作restful API的工具发送POST请求,deploy结束会返回一个ChainID的字符串。
3. 利用上一步返回的chainID,调用SDK或者使用postman实现Invoke,query,get_chainlist等操作。
1.0以后的版本中取消了restful的方式,只能使用SDK了!
Hyperledger Fabric
的运行是依赖于Docker
的,各组件运行在不同的容器中,为了使这些容器彼此通信,可以通过创建一个网络然后将容器加入进去,当运行官方示例fabric-samples
的First Network
时,主要是运行 docker-compose-cli.yaml
文件,从中可以看出,创建了一个byfn
网络,然后所有容器attach到这个网络中。
Hyperledger Fabric
给出了很好的关于单机运行的示例,即在同一主机上运行多个不同的docker
容器,通过不同端口来模拟多主机之间的通信,但在实际环境中,我们需要将服务运行在多主机的分布式系统中。
当需要在多主机上运行hyperledger fabric
时,容器之间不能直接进行互相通信(因为docker
会自动分配不同的子网地址给每个容器)。因此,需要有一种方式使得这多个主机上的容器能相互进行通信(其实跨主机容器通信可以通过一些方法来实现,比如使用OVS+GRE
,Flannel
或者直接路由
等方式)。现在,我们直接可以使用docker swarm
来实现(具体可查看docker swarm的官方文档)。
将要构建的网络架构如下图所示,该架构中包含了最基本的Hyperledger Fabric 1.0的组件,我们只需要两台主机,组件分布为包括:
CA
服务(在节点 1 上)Orderer
排序服务(在节点 1 上)Peer0
(组织 1 的节点 0, 在节点 1 上)CouchDB0
(连接到Peer0
来存储状态,在节点 1上)CouchDB1
(连接到Peer1
来存储状态,在节点 2上)Peer1
(组织 1 的节点 1, 在节点 2 上)Cli
(在节点 2 上)对于这两台主机:
节点1: 192.168.245.235 (Ubuntu 16.04)
节点2:192.168.245.236 (Ubuntu 16.04)
现在开始整个搭建过程。
swarm
(更多详情参见docker swarm官方文档),在节点1上执行$ docker swarm init
后续过程概述为:节点1创建swarm
,然后节点2以manager
身份加入创建的swarm
中。
$ docker swarm join-token manager
输出就会像是:
docker swarm join — token SWMTKN-1–3as8cvf3yxk8e7zj98954jhjza3w75mngmxh543llgpo0c8k7z-61zyibtaqjjimkqj8p6t9lwgu 192.168.245.235:2377
复制这段命令,然后在节点2中执行,成功输出结果会是一个字符串,这个不再理会。
$ docker network create --attachable --driver overlay mynetwork
mynetwork是网络名称,可以自己取,但后续配置需要保持一致
使用docker network ls
可以查看目前docker中有哪些网络。
$ git clone https://github.com/hesonglin006/Build-Multi-Host-Network-Hyperledger.git
cd Build-Multi-Host-Network-Hyperledger/
./bmhn.sh
上述命令会执行bmhn.sh
这个脚本文件,创建所需要的身份认证文件,证书和密钥等。运行成功后,可以看到当前目录下会有crypto-config
和channel-artifacts
这两个文件夹,复制这两个文件夹到节点2同样的位置(放到节点2的Build-Multi-Host-Network-Hyperledger文件夹下)。
如前面所述,在节点1上需要运行一个CA
服务,一个Orderer
服务,一个Peer0
还有一个CouchDB0
,下面将使用脚本依次启动:
CA
服务 #!/bin/sh
docker run --rm \
-it \
--network="mynetwork" \
--name ca.example.com \
-p 7054:7054 \
-e FABRIC_CA_HOME=/etc/hyperledger/fabric-ca-server-config \
-e FABRIC_CA_SERVER_CA_NAME=ca.example.com \
-e FABRIC_CA_SERVER_CA_CERTFILE=/etc/hyperledger/fabric-ca-server-config/ca.org1.example.com-cert.pem \
-e FABRIC_CA_SERVER_CA_KEYFILE=/etc/hyperledger/fabric-ca-server-config/{替换成自己实际的key} \
-v $(pwd)/crypto-config/peerOrganizations/org1.example.com/ca/:/etc/hyperledger/fabric-ca-server-config \
-e CORE_VM_DOCKER_HOSTCONFIG_NETWORKMODE=mynetwork hyperledger/fabric-ca sh \
-c 'fabric-ca-server start -b admin:adminpw -d'
Orderer
服务#!/bin/sh
docker run --rm -it \
--network="mynetwork" \
--name orderer.example.com \
-p 7050:7050 \
-e ORDERER_GENERAL_LOGLEVEL=debug \
-e ORDERER_GENERAL_LISTENADDRESS=0.0.0.0 \
-e ORDERER_GENERAL_LISTENPORT=7050 \
-e ORDERER_GENERAL_GENESISMETHOD=file \
-e ORDERER_GENERAL_GENESISFILE=/var/hyperledger/orderer/orderer.genesis.block \
-e ORDERER_GENERAL_LOCALMSPID=OrdererMSP \
-e ORDERER_GENERAL_LOCALMSPDIR=/var/hyperledger/orderer/msp \
-e ORDERER_GENERAL_TLS_ENABLED=false \
-e CORE_VM_DOCKER_HOSTCONFIG_NETWORKMODE=mynetwork \
-v $(pwd)/channel-artifacts/genesis.block:/var/hyperledger/orderer/orderer.genesis.block \
-v $(pwd)/crypto-config/ordererOrganizations/example.com/orderers/orderer.example.com/msp:/var/hyperledger/orderer/msp \
-w /opt/gopath/src/github.com/hyperledger/fabric hyperledger/fabric-orderer orderer
CouchDB0
用来连接到Peer0
#!/bin/sh
docker run --rm -it \
--network="mynetwork" \
--name couchdb0 \
-p 5984:5984 \
-e COUCHDB_USER=admin \
-e COUCHDB_PASSWORD=password \
-e CORE_VM_DOCKER_HOSTCONFIG_NETWORKMODE=mynetwork hyperledger/fabric-couchdb
Peer0
#!/bin/sh
docker run --rm -it \
--link orderer.example.com:orderer.example.com \
--network="mynetwork" \
--name peer0.org1.example.com \
-p 8051:7051 \
-p 8053:7053 \
-e CORE_LEDGER_STATE_STATEDATABASE=CouchDB \
-e CORE_LEDGER_STATE_COUCHDBCONFIG_COUCHDBADDRESS=couchdb0:5984 \
-e CORE_LEDGER_STATE_COUCHDBCONFIG_USERNAME=admin \
-e CORE_LEDGER_STATE_COUCHDBCONFIG_PASSWORD=password \
-e CORE_PEER_ADDRESSAUTODETECT=true \
-e CORE_VM_ENDPOINT=unix:///host/var/run/docker.sock \
-e CORE_LOGGING_LEVEL=DEBUG \
-e CORE_PEER_NETWORKID=peer0.org1.example.com \
-e CORE_NEXT=true \
-e CORE_PEER_ENDORSER_ENABLED=true \
-e CORE_PEER_ID=peer0.org1.example.com \
-e CORE_PEER_PROFILE_ENABLED=true \
-e CORE_PEER_COMMITTER_LEDGER_ORDERER=orderer.example.com:7050 \
-e CORE_PEER_GOSSIP_IGNORESECURITY=true \
-e CORE_VM_DOCKER_HOSTCONFIG_NETWORKMODE=mynetwork \
-e CORE_PEER_GOSSIP_EXTERNALENDPOINT=peer0.org1.example.com:7051 \
-e CORE_PEER_TLS_ENABLED=false \
-e CORE_PEER_GOSSIP_USELEADERELECTION=false \
-e CORE_PEER_GOSSIP_ORGLEADER=true \
-e CORE_PEER_LOCALMSPID=Org1MSP \
-v /var/run/:/host/var/run/ \
-v $(pwd)/crypto-config/peerOrganizations/org1.example.com/peers/peer0.org1.example.com/msp:/etc/hyperledger/fabric/msp \
-w /opt/gopath/src/github.com/hyperledger/fabric/peer hyperledger/fabric-peer peer node start
如果都启动成功,使用 docker ps
查看正处在运行状态的容器,如下图所示:
接下来在节点2
上执行脚本,启动一个Peer1
服务及CouchDB1
的服务:
CouchDB1
用来连接到Peer1
#!/bin/sh
docker run --rm -it \
--network="mynetwork" \
--name couchdb1 \
-p 6984:5984 \
-e COUCHDB_USER=admin \
-e COUCHDB_PASSWORD=password \
-e CORE_VM_DOCKER_HOSTCONFIG_NETWORKMODE=mynetwork hyperledger/fabric-couchdb
Peer1
#!/bin/sh
docker run --rm -it \
--network="mynetwork" \
--link orderer.example.com:orderer.example.com \
--link peer0.org1.example.com:peer0.org1.example.com \
--name peer1.org1.example.com \
-p 9051:7051 \
-p 9053:7053 \
-e CORE_LEDGER_STATE_STATEDATABASE=CouchDB \
-e CORE_LEDGER_STATE_COUCHDBCONFIG_COUCHDBADDRESS=couchdb1:5984 \
-e CORE_LEDGER_STATE_COUCHDBCONFIG_USERNAME=admin \
-e CORE_LEDGER_STATE_COUCHDBCONFIG_PASSWORD=password \
-e CORE_PEER_ADDRESSAUTODETECT=true \
-e CORE_VM_ENDPOINT=unix:///host/var/run/docker.sock \
-e CORE_LOGGING_LEVEL=DEBUG \
-e CORE_PEER_NETWORKID=peer1.org1.example.com \
-e CORE_NEXT=true \
-e CORE_PEER_ENDORSER_ENABLED=true \
-e CORE_PEER_ID=peer1.org1.example.com \
-e CORE_PEER_PROFILE_ENABLED=true \
-e CORE_PEER_COMMITTER_LEDGER_ORDERER=orderer.example.com:7050 \
-e CORE_PEER_GOSSIP_ORGLEADER=true \
-e CORE_PEER_GOSSIP_EXTERNALENDPOINT=peer1.org1.example.com:7051 \
-e CORE_PEER_GOSSIP_IGNORESECURITY=true \
-e CORE_PEER_LOCALMSPID=Org1MSP \
-e CORE_VM_DOCKER_HOSTCONFIG_NETWORKMODE=mynetwork \
-e CORE_PEER_GOSSIP_BOOTSTRAP=peer0.org1.example.com:7051 \
-e CORE_PEER_GOSSIP_USELEADERELECTION=false \
-e CORE_PEER_TLS_ENABLED=false \
-v /var/run/:/host/var/run/ \
-v $(pwd)/crypto-config/peerOrganizations/org1.example.com/peers/peer1.org1.example.com/msp:/etc/hyperledger/fabric/msp \
-w /opt/gopath/src/github.com/hyperledger/fabric/peer hyperledger/fabric-peer peer node start
客户端Cli
#!/bin/sh
docker run --rm -it \
--network="mynetwork" \
--name cli \
--link orderer.example.com:orderer.example.com \
--link peer0.org1.example.com:peer0.org1.example.com \
--link peer1.org1.example.com:peer1.org1.example.com \
-p 12051:7051 \
-p 12053:7053 \
-e GOPATH=/opt/gopath \
-e CORE_PEER_LOCALMSPID=Org1MSP \
-e CORE_PEER_TLS_ENABLED=false \
-e CORE_VM_ENDPOINT=unix:///host/var/run/docker.sock \
-e CORE_LOGGING_LEVEL=DEBUG \
-e CORE_PEER_ID=cli \
-e CORE_PEER_ADDRESS=peer0.org1.example.com:7051 \
-e CORE_PEER_NETWORKID=cli \
-e CORE_PEER_MSPCONFIGPATH=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org1.example.com/users/Admin@org1.example.com/msp \
-e CORE_VM_DOCKER_HOSTCONFIG_NETWORKMODE=mynetwork \
-v /var/run/:/host/var/run/ \
-v $(pwd)/chaincode/:/opt/gopath/src/github.com/hyperledger/fabric/examples/chaincode/go \
-v $(pwd)/crypto-config:/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/ \
-v $(pwd)/scripts:/opt/gopath/src/github.com/hyperledger/fabric/peer/scripts/ \
-v $(pwd)/channel-artifacts:/opt/gopath/src/github.com/hyperledger/fabric/peer/channel-artifacts \
-w /opt/gopath/src/github.com/hyperledger/fabric/peer hyperledger/fabric-tools /bin/bash -c './scripts/script.sh'
执行Cli
的脚本,实际上将会执行当前目录下./scripts/script.sh
,其将会:
- 创建通道,实例中通道名称为mychannel
- 将Peer0
和Peer1
加入通道
- 一旦成功加入通道,更新锚节点(即组织中负责和排序节点通信的节点,可以静态指定,也可以执行选举算法选出)
- 在两个peer
上安装链码
如果运行成功,会出现下图所示:
现在网络运行起来了,我们需要从节点2上invoke链码到两个peer上,在节点 2 上执行:
#!/bin/sh
docker run --rm -it \
--network="mynetwork" \
--name cli \
--link orderer.example.com:orderer.example.com \
--link peer0.org1.example.com:peer0.org1.example.com \
--link peer1.org1.example.com:peer1.org1.example.com \
-p 12051:7051 \
-p 12053:7053 \
-e GOPATH=/opt/gopath \
-e CORE_PEER_LOCALMSPID=Org1MSP \
-e CORE_PEER_TLS_ENABLED=false \
-e CORE_VM_ENDPOINT=unix:///host/var/run/docker.sock \
-e CORE_LOGGING_LEVEL=DEBUG \
-e CORE_PEER_ID=cli \
-e CORE_PEER_ADDRESS=peer0.org1.example.com:7051 \
-e CORE_PEER_NETWORKID=cli \
-e CORE_PEER_MSPCONFIGPATH=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org1.example.com/users/Admin@org1.example.com/msp \
-e CORE_VM_DOCKER_HOSTCONFIG_NETWORKMODE=mynetwork \
-v /var/run/:/host/var/run/ \
-v $(pwd)/chaincode/:/opt/gopath/src/github.com/hyperledger/fabric/examples/chaincode/go \
-v $(pwd)/crypto-config:/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/ \
-v $(pwd)/scripts:/opt/gopath/src/github.com/hyperledger/fabric/peer/scripts/ \
-v $(pwd)/channel-artifacts:/opt/gopath/src/github.com/hyperledger/fabric/peer/channel-artifacts \
-w /opt/gopath/src/github.com/hyperledger/fabric/peer hyperledger/fabric-tools /bin/bash
执行上述脚本后,会进入到CLI 容器中,我们将会执行命令来instantiate,invoke和query容器中的链码。
首先需要设置一些环境变量,粘贴如下的环境变量设置命令到刚才进入的CLI容器中:
CORE_PEER_MSPCONFIGPATH=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org1.example.com/users/Admin@org1.example.com/msp
CORE_PEER_LOCALMSPID="Org1MSP"
CORE_PEER_TLS_ROOTCERT_FILE=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org1.example.com/peers/peer0.org1.example.com/tls/ca.crt
CORE_PEER_ADDRESS=peer0.org1.example.com:7051
现在我们可以初始化链码,在刚才的CLI容器中继续执行:
peer chaincode instantiate -o orderer.example.com:7050 -C mychannel -n mycc -v 1.0 -c '{"Args":["init","a","100","b","200"]}' -P "OR ('Org1MSP.member','Org2MSP.member')"
该命令将会初始化链码并且设置初始值为:a=100,b=200。
此时,可以通过couchDB
的web
界面来查看,因为数据是以二进制来保存的,所以不会看到实际的值(事实上是hash值)但可以看到包括键mycca
的记录。
在
节点1
上:
http://localhost:5984/_utils/#/database/mychannel/_all_docs
,可以看到:
在
节点2
上:
http://localhost:6984/_utils/#/database/mychannel/_all_docs
,可以看到:
在节点2上,还是在刚才的CLI容器中,首先需要设置环境变量,代码如下:
CORE_PEER_MSPCONFIGPATH=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org1.example.com/users/Admin@org1.example.com/msp
CORE_PEER_LOCALMSPID="Org1MSP"
CORE_PEER_TLS_ROOTCERT_FILE=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org1.example.com/peers/peer0.org1.example.com/tls/ca.crt
CORE_PEER_ADDRESS=peer1.org1.example.com:7051
为了确认链码已经被正确安装且couchDB
中的状态值已经被正确初始化,我们先来查询一下a
的值:
peer chaincode query -C mychannel -n mycc -c '{"Args":["query","a"]}'
还是在节点2上的CLI容器中,首先需要更改环境变量,执行如下代码:
CORE_PEER_MSPCONFIGPATH=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org1.example.com/users/Admin@org1.example.com/msp
CORE_PEER_LOCALMSPID="Org1MSP"
CORE_PEER_TLS_ROOTCERT_FILE=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org1.example.com/peers/peer0.org1.example.com/tls/ca.crt
CORE_PEER_ADDRESS=peer0.org1.example.com:7051
现在执行invoke
操作,主要是从a转10给b,代码如下:
peer chaincode invoke -o orderer.example.com:7050 -C mychannel -n mycc -c '{"Args":["invoke","a","b","10"]}'
经过刚才的转账操作后,现在a
的值应该变为了90
,我们从peer0
上查询看看结果,执行代码如下:
peer chaincode query -C mychannel -n mycc -c '{"Args":["query","a"]}'
从图中可以看出,a
的值已经为了90
,同理,查看b
的值,已经变为了210
:
通过以上步骤,恭喜!你已经搭建好了多主机环境的基于Hyperledger Fabric 1.0的区块链平台。
后续应该通过使用SDK(目前Go和NodeJS已经相对完善,Python处于开发中)来完成对区块链平台的开发。
区块链能否落地将是目前区块链发展中最为重要的一点,目前各种应用及场景都在探索中(物联网,供应链,征信,众筹等等等等)。
未来FinTech2.0的三驾马车:认知物联网+区块链+人工智能
。
Keep Fighting!