基于Raft共识搭建单机Fabric1.4网络环境
这个时间段,fabric社区已经更新到了2.3版本了,但是当前在使用fabric业务的公司里面大多数公司使用的还是fabric1.4版本,写这篇博客的目的就是为了补充一下fabric搭建的工具文章。
1.搭建fabric网络环境的准备工作介绍
本次搭建的fabric1.4网络环境中,准备搭建3个orderer节点组成一个顺序共识组织orderer;两个组织Org1,Org2,其中每个组织包含两个peer节点,另外还包括客户端节点,couchdb等,具体节点情况见表1.1:
fabric1.4网络各个节点 |
---|
orderer.example.com |
orderer1.example.com |
orderer2.example.com |
peer0.org1.example.com |
peer1.org1.example.com |
peer0.org2.example.com |
peer1.org2.example.com |
cli |
couchdb |
首先搭建fabric网络的宿主机的环境配置,其中包括安装go语言,安装docker,docker-compose等,具体安装步骤如下:
- 执行命令
wget https://dl.google.com/go/go1.11.linux-amd64.tar.gz
下载go语言二进制源码包。 - 执行命令
tar -zxvf go1.11.linux-amd64.tar.gz -C /usr/local/
解压。 - 执行命令
mkdir $HOME/code/go -p
创建go的工程目录。 - 在home目录下执行命令
vim .bashrc
,并在文件的最后面填写go语言的环境变量,即将下面代码块中的配置添加到文件的末尾。
export GOROOT="/usr/local/go"
export GOPATH="$HOME/code/go"
export GOBIN="$HOME/code/go/bin"
export PATH="$PATH:$GOROOT/bin:$GOPATH/bin"
- 执行命令
source .bashrc
,让刚配置好的环境变量生效。 - 执行命令
go version
查看当前的go语言的版本。 - 执行命令
go env
查看当前go语言的配置环境。 - 查看当前主机home目录下是否有.pip目录,如果没有,执行命令
mkdir .pip
,并执行命令vim pip.conf
,将下面代码块中的内容写入pip.conf的文件中。
[global]
index-url=http://mirrors.aliyun.com/pypi/simple/
[install]
trusted-host=mirrors.aliyun.com
- 执行命令
pip install docker-compose
,安装docker-compose(docker编排工具)。 - 执行命令
docker-compose version
来查看当前安装的docker-compose工具的版本。 - 执行命令
apt-get install docker.io
来安装docker,如果报错说定位不到docker.io,那就先执行命令apt-get update
,再安装docker. - 执行命令
cd $GOPATH/src/github.com/hyperledger
进入到指定的目录下,这个目录路径和后面拉去二进制源码工具有关,路径不对会导致二进制工具拉取失败,执行命令git clone https://github.com/hyperledger/fabric.git
,将fabric源码拉取下来,如果主机中没有git命令,通过执行apt-get install git
来自行下载。 - 执行命令
git clone https://github.com/hyperledger/fabric-samples.git
将fabric-samples拉取下来,这里会有官方的一些示例以及各种模式下的fabric环境配置。 - 执行命令
cd fabric-samples/scripts
,进入到scripts目录下,修改bootstrap.sh中的镜像版本,1.4.0->1.4.1,0.4.14->0.4.15,搭建raft下的fabric网络需要最新的docker镜像的版本,如果版本不够新,会导致容器的一场退出,比如orderer容器会出现识别不了etcdraft共识的错误。 - 执行bootstrap.sh脚本主要是拉去二进制工具包以及拉取到最新的docker镜像,执行脚本完成后,二进制工具会在fabric-samples/bin目录中,只需要再执行
cp cryptogen configtxlator configtxgen /usr/local/bin
命令,就可以将工具放在全局进行使用了。 - 如果无法成功执行bootstrap.sh脚本,需要分两步去将二进制源码工具和docker镜像拉取到,首先关于二进制工具,可以先进入到/fabric目录下,执行
git checkout -b v1.4.1
,来到当前fabric源码分支v1.4.1,执行make release拉去二进制源码工具,执行完成后,在fabric/release/linux-amd64/bin的目录下就会有下载好的二进制源码工具了,再执行cp cryptogen configtxgen configlator /usr/local/bin
命令,将工具设置为全局使用。 - 另外docker的最新镜像可以shell脚本来拉去,执行命令
vim docker_images.sh
,将下面代码块中的内容填写进文件中,然后执行chmod +x docker_images.sh
为脚本文件添加执行权限,最后执行./docker_images.sh
将fabric1.4匹配的docker镜像拉取下来。
#!/bin/bash
docker pull hyperledger/fabric-ca:1.4.1
docker tag hyperledger/fabric-ca:1.4.1 hyperledger/fabric-ca:latest
docker pull hyperledger/fabric-tools:1.4.1
docker tag hyperledger/fabric-tools:1.4.1 hyperledger/fabric-tools:latest
docker pull hyperledger/fabric-ccenv:1.4.1
docker tag hyperledger/fabric-ccenv:1.4.1 hyperledger/fabric-ccenv:latest
docker pull hyperledger/fabric-orderer:1.4.1
docker tag hyperledger/fabric-orderer:1.4.1 hyperledger/fabric-orderer:latest
docker pull hyperledger/fabric-peer:1.4.1
docker tag hyperledger/fabric-peer:1.4.1 hyperledger/fabric-peer:latest
docker pull hyperledger/fabric-javaenv:1.4.1
docker tag hyperledger/fabric-javaenv:1.4.1 hyperledger/fabric-javaenv:latest
docker pull hyperledger/fabric-zookeeper:0.4.15
docker tag hyperledger/fabric-zookeeper:0.4.15 hyperledger/fabric-zookeeper:latest
docker pull hyperledger/fabric-kafka:0.4.15
docker tag hyperledger/fabric-kafka:0.4.15 hyperledger/fabric-kafka:latest
docker pull hyperledger/fabric-couchdb:0.4.15
docker tag hyperledger/fabric-couchdb:0.4.15 hyperledger/fabric-couchdb:latest
docker pull hyperledger/fabric-baseos:0.4.15
docker tag hyperledger/fabric-baseos:0.4.15 hyperledger/fabric-baseos:latest
到这里已经完成了fabric网络宿主机环境的搭建,下面进入到fabric网络配置部分。
2.基于Raft共识的多机的fabric网络部署
这一节是对fabric网络中各个节点进行配置,其中主要涉及的是fabric中的节点证书,通道文件,创世块文件以及各个节点的dock er配置文件。
- 在home 目录下或者创建fabric网络项目的根目录,执行
mkdir raft-example
。 - 进入到刚刚创建的根目录中,创建生成fabric网络各个节点的证书所需的静态配置文件crypto-config.yaml,这个文件可以去fabric-samples中进行拷贝,或者执行命令
vi crypto-config.yaml
创建该文件,并将下面代码块中的内容复制到文件中。
OrdererOrgs:
- Name: Orderer
Domain: example.com
Specs:
- Hostname: orderer
- Hostname: orderer1
- Hostname: orderer2
PeerOrgs:
- Name: Org1
Domain: org1.example.com
EnableNodeOUs: true
Template:
Count: 2
Users:
Count: 2
- Name: Org2
Domain: org2.example.com
EnableNodeOUs: true
Template:
Count: 2
Users:
Count: 2
- 执行命令cryptogen generate --config ./crypto-config.yaml在当前目录下生成各个节点的证书。
- 下面配置一下fabric网络所需的静态配置文件configtx.yaml,这个文件可以去farbic-samples中进行拷贝,或者执行命令vi configtx.yaml,并将下列代码块中的内容填写进文件中。
---
Organizations:
- &OrdererOrg
Name: OrdererOrg
ID: OrdererMSP
MSPDir: crypto-config/ordererOrganizations/example.com/msp
Policies:
Readers:
Type: Signature
Rule: "OR('OrdererMSP.member')"
Writers:
Type: Signature
Rule: "OR('OrdererMSP.member')"
Admins:
Type: Signature
Rule: "OR('OrdererMSP.admin')"
- &Org1
Name: Org1MSP
ID: Org1MSP
MSPDir: crypto-config/peerOrganizations/org1.example.com/msp
AnchorPeers:
- Host: peer0.org1.example.com
Port: 7051
Policies:
Readers:
Type: Signature
Rule: "OR('Org1MSP.admin','Org1MSP.peer','Org1MSP.client')"
Writers:
Type: Signature
Rule: "OR('Org1MSP.admin','Org1MSP.client')"
Admins:
Type: Signature
Rule: "OR('Org1MSP.admin')"
- &Org2
Name: Org2MSP
ID: Org2MSP
MSPDir: crypto-config/peerOrganizations/org2.example.com/msp
AnchorPeers:
- Host: peer0.org2.example.com
Port: 9051
Policies:
Readers:
Type: Signature
Rule: "OR('Org2MSP.admin','Org2MSP.peer','Org2MSP.client')"
Writers:
Type: Signature
Rule: "OR('Org2MSP.admin','Org2MSP.client')"
Admins:
Type: Signature
Rule: "OR('Org2MSP.admin')"
Capabilities:
Channel: &ChannelCapabilities
V1_3: true
Orderer: &OrdererCapabilities
V1_1: true
Application: &ApplicationCapabilities
V1_3: true
V1_2: false
V1_1: false
Application: &ApplicationDefaults
Organizations:
Policies:
Readers:
Type: ImplicitMeta
Rule: "ANY Readers"
Writers:
Type: ImplicitMeta
Rule: "ANY Writers"
Admins:
Type: ImplicitMeta
Rule: "MAJORITY Admins"
Capabilities:
<<: *ApplicationCapabilities
Orderer: &OrdererDefaults
OrdererType: solo
Addresses:
- orderer.example.com:7050
BatchTimeout: 2s
BatchSize:
MaxMessageCount: 200
AbsoluteMaxBytes: 2 MB
PreferredMaxBytes: 512 KB
Kafka:
Brokers:
- 127.0.0.1:9092
Organizations:
Policies:
Readers:
Type: ImplicitMeta
Rule: "ANY Readers"
Writers:
Type: ImplicitMeta
Rule: "ANY Writers"
Admins:
Type: ImplicitMeta
Rule: "MAJORITY Admins"
BlockValidation:
Type: ImplicitMeta
Rule: "ANY Writers"
Channel: &ChannelDefaults
Policies:
Readers:
Type: ImplicitMeta
Rule: "ANY Readers"
Writers:
Type: ImplicitMeta
Rule: "ANY Writers"
Admins:
Type: ImplicitMeta
Rule: "MAJORITY Admins"
Capabilities:
<<: *ChannelCapabilities
Profiles:
TwoOrgsOrdererGenesis:
<<: *ChannelDefaults
Capabilities:
<<: *ChannelCapabilities
Orderer:
<<: *OrdererDefaults
OrdererType: etcdraft
EtcdRaft:
Consenters:
- Host: orderer.example.com
Port: 7050
ClientTLSCert: crypto-config/ordererOrganizations/example.com/orderers/orderer.example.com/tls/server.crt
ServerTLSCert: crypto-config/ordererOrganizations/example.com/orderers/orderer.example.com/tls/server.crt
- Host: orderer1.example.com
Port: 8050
ClientTLSCert: crypto-config/ordererOrganizations/example.com/orderers/orderer1.example.com/tls/server.crt
ServerTLSCert: crypto-config/ordererOrganizations/example.com/orderers/orderer1.example.com/tls/server.crt
- Host: orderer2.example.com
Port: 9050
ClientTLSCert: crypto-config/ordererOrganizations/example.com/orderers/orderer2.example.com/tls/server.crt
ServerTLSCert: crypto-config/ordererOrganizations/example.com/orderers/orderer2.example.com/tls/server.crt
Addresses:
- orderer0.example.com:7050
- orderer1.example.com:8050
- orderer2.example.com:9050
Organizations:
- *OrdererOrg
Capabilities:
<<: *OrdererCapabilities
Application:
<<: *ApplicationDefaults
Organizations:
- <<: *OrdererOrg
Consortiums:
SampleConsortium:
Organizations:
- *Org1
- *Org2
TwoOrgsChannel:
Consortium: SampleConsortium
<<: *ChannelDefaults
Application:
<<: *ApplicationDefaults
Organizations:
- *Org1
- *Org2
Capabilities:
<<: *ApplicationCapabilities
- 下面需要创建一个目录来放置通道文件,创世块文件等,执行命令
mkdir channel-artifacts
创建文件夹。 - 产生创世块文件,执行命令
configtxgen -profile TwoOrgsOrdererGenesis -channelID systemchannel -outputBlock ./channel-artifacts/genesis.block
生成系统文件的创世块。 - 产生应用通道文件,执行命令
configtxgen -profile TwoOrgsChannel -outputCreateChannelTx ./channel-artifacts/channel.tx -channelID testchannel
生成用于创建应用通道的通道文件。 - 产生Org1的锚节点文件,执行命令
configtxgen -profile TwoOrgsChannel -outputAnchorPeersUpdate ./channel-artifacts/Org1MSPanchors.tx -channelID appchannel -asOrg Org1MSP
,生成Org1进行锚节点更新的文件。 - 产生Org2的锚节点文件,执行命令
configtxgen -profile TwoOrgsChannel -outputAnchorPeersUpdate ./channel-artifacts/Org2MSPanchors.tx -channelID appchannel -asOrg Org2MSP
,生成Org2进行锚节点更新的文件。 - 下面需要配置orderer组织的docker配置文件,这部分会配置orderer组织下三个orderer节点的配置,这里需要配置两个文件,先说第一个文件为docker-compose-orderer.yaml,这里配置三个orderer节点非公共的配置,执行
vi docker-compose-orderer.yaml
,并将下面代码块的内容复制到文件中。
version: '2'
volumes:
orderer1.example.com:
orderer2.example.com:
orderer.example.com:
networks:
byfn:
services:
orderer1.example.com:
extends:
file: base/orderer-base.yaml
service: orderer-base
environment:
- ORDERER_GENERAL_LISTENPORT=8050
container_name: orderer1.example.com
networks:
- byfn
volumes:
- ./channel-artifacts/genesis.block:/var/hyperledger/orderer/orderer.genesis.block
- ./crypto-config/ordererOrganizations/example.com/orderers/orderer1.example.com/msp:/var/hyperledger/orderer/msp
- ./crypto-config/ordererOrganizations/example.com/orderers/orderer1.example.com/tls/:/var/hyperledger/orderer/tls
- orderer1.example.com:/var/hyperledger/production/orderer
ports:
- 8050:8050
orderer2.example.com:
extends:
file: base/orderer-base.yaml
service: orderer-base
environment:
- ORDERER_GENERAL_LISTENPORT=9050
container_name: orderer2.example.com
networks:
- byfn
volumes:
- ./channel-artifacts/genesis.block:/var/hyperledger/orderer/orderer.genesis.block
- ./crypto-config/ordererOrganizations/example.com/orderers/orderer2.example.com/msp:/var/hyperledger/orderer/msp
- ./crypto-config/ordererOrganizations/example.com/orderers/orderer2.example.com/tls/:/var/hyperledger/orderer/tls
- orderer2.example.com:/var/hyperledger/production/orderer
ports:
- 9050:9050
orderer.example.com:
extends:
file: base/orderer-base.yaml
service: orderer-base
environment:
- ORDERER_GENERAL_LISTENPORT=7050
container_name: orderer.example.com
networks:
- byfn
volumes:
- ./channel-artifacts/genesis.block:/var/hyperledger/orderer/orderer.genesis.block
- ./crypto-config/ordererOrganizations/example.com/orderers/orderer.example.com/msp:/var/hyperledger/orderer/msp
- ./crypto-config/ordererOrganizations/example.com/orderers/orderer.example.com/tls/:/var/hyperledger/orderer/tls
- orderer.example.com:/var/hyperledger/production/orderer
ports:
- 7050:7050
- 配置orderer的第二个文件为orderer-base.yaml,这里放置的是orderer节点的公共配置,因为这里属于公共基础的配置,建议在根目录raft-example下创建base文件夹,在base文件夹下执行命令
vi orderer-base.yaml
,并将下面代码块中的内容复制到文件内。
version: '2'
services:
orderer-base:
image: hyperledger/fabric-orderer:latest
environment:
- FABRIC_LOGGING_SPEC=INFO
- ORDERER_GENERAL_LISTENADDRESS=0.0.0.0
- ORDERER_GENERAL_BOOTSTRAPMETHOD=file
- ORDERER_GENERAL_BOOTSTRAPFILE=/var/hyperledger/orderer/orderer.genesis.block
- ORDERER_GENERAL_LOCALMSPID=OrdererMSP
- ORDERER_GENERAL_LOCALMSPDIR=/var/hyperledger/orderer/msp
# enabled TLS
- ORDERER_GENERAL_TLS_ENABLED=true
- ORDERER_GENERAL_TLS_PRIVATEKEY=/var/hyperledger/orderer/tls/server.key
- ORDERER_GENERAL_TLS_CERTIFICATE=/var/hyperledger/orderer/tls/server.crt
- ORDERER_GENERAL_TLS_ROOTCAS=[/var/hyperledger/orderer/tls/ca.crt]
- ORDERER_GENERAL_CLUSTER_CLIENTCERTIFICATE=/var/hyperledger/orderer/tls/server.crt
- ORDERER_GENERAL_CLUSTER_CLIENTPRIVATEKEY=/var/hyperledger/orderer/tls/server.key
- ORDERER_GENERAL_CLUSTER_ROOTCAS=[/var/hyperledger/orderer/tls/ca.crt]
working_dir: /opt/gopath/src/github.com/hyperledger/fabric
command: orderer
- 下面需要配置两个组织Org1,Org2的peer节点,这里需要配置两个文件,同orderer一样,第一个文件需要配置各个peer之间不同的配置信息;第二个文件需要配置peer之间的公共的配置信息,先说第一个文件docker-compose-peer.yaml,执行命令
vi docker-compose-peer.yaml
,将下面代码块中的内容粘贴到文件内。
version: '2'
services:
peer0.org1.example.com:
container_name: peer0.org1.example.com
extends:
file: peer-base.yaml
service: peer-base
environment:
- CORE_PEER_ID=peer0.org1.example.com
- CORE_PEER_ADDRESS=peer0.org1.example.com:7051
- CORE_PEER_LISTENADDRESS=0.0.0.0:7051
- CORE_PEER_CHAINCODEADDRESS=peer0.org1.example.com:7052
- CORE_PEER_CHAINCODELISTENADDRESS=0.0.0.0:7052
- CORE_PEER_GOSSIP_USELEADERELECTION=true
- CORE_PEER_GOSSIP_BOOTSTRAP=peer1.org1.example.com:8051
- CORE_PEER_GOSSIP_EXTERNALENDPOINT=peer0.org1.example.com:7051
- CORE_PEER_LOCALMSPID=Org1MSP
- CORE_LEDGER_STATE_STATEDATABASE=CouchDB
- CORE_LEDGER_STATE_COUCHDBCONFIG_COUCHDBADDRESS=couchdb:5984
- CORE_LEDGER_STATE_COUCHDBCONFIG_USERNAME=
- CORE_LEDGER_STATE_COUCHDBCONFIG_PASSWORD=
volumes:
- /var/run/:/host/var/run/
- ../crypto-config/peerOrganizations/org1.example.com/peers/peer0.org1.example.com/msp:/etc/hyperledger/fabric/msp
- ../crypto-config/peerOrganizations/org1.example.com/peers/peer0.org1.example.com/tls:/etc/hyperledger/fabric/tls
- peer0.org1.example.com:/var/hyperledger/production
ports:
- 7051:7051
networks:
- byfn
peer1.org1.example.com:
container_name: peer1.org1.example.com
extends:
file: peer-base.yaml
service: peer-base
environment:
- CORE_PEER_ID=peer1.org1.example.com
- CORE_PEER_ADDRESS=peer1.org1.example.com:8051
- CORE_PEER_LISTENADDRESS=0.0.0.0:8051
- CORE_PEER_CHAINCODEADDRESS=peer1.org1.example.com:8052
- CORE_PEER_CHAINCODELISTENADDRESS=0.0.0.0:8052
- CORE_PEER_GOSSIP_EXTERNALENDPOINT=peer1.org1.example.com:8051
- CORE_PEER_GOSSIP_BOOTSTRAP=peer0.org1.example.com:7051
- CORE_PEER_LOCALMSPID=Org1MSP
- CORE_LEDGER_STATE_STATEDATABASE=CouchDB
- CORE_LEDGER_STATE_COUCHDBCONFIG_COUCHDBADDRESS=couchdb:5984
- CORE_LEDGER_STATE_COUCHDBCONFIG_USERNAME=
- CORE_LEDGER_STATE_COUCHDBCONFIG_PASSWORD=
volumes:
- /var/run/:/host/var/run/
- ../crypto-config/peerOrganizations/org1.example.com/peers/peer1.org1.example.com/msp:/etc/hyperledger/fabric/msp
- ../crypto-config/peerOrganizations/org1.example.com/peers/peer1.org1.example.com/tls:/etc/hyperledger/fabric/tls
- peer1.org1.example.com:/var/hyperledger/production
ports:
- 8051:8051
networks:
- byfn
peer0.org2.example.com:
container_name: peer0.org2.example.com
extends:
file: peer-base.yaml
service: peer-base
environment:
- CORE_PEER_ID=peer0.org2.example.com
- CORE_PEER_ADDRESS=peer0.org2.example.com:9051
- CORE_PEER_LISTENADDRESS=0.0.0.0:9051
- CORE_PEER_CHAINCODEADDRESS=peer0.org2.example.com:9052
- CORE_PEER_CHAINCODELISTENADDRESS=0.0.0.0:9052
- CORE_PEER_GOSSIP_USELEADERELECTION=true
- CORE_PEER_GOSSIP_EXTERNALENDPOINT=peer0.org2.example.com:9051
- CORE_PEER_GOSSIP_BOOTSTRAP=peer1.org2.example.com:10051
- CORE_PEER_LOCALMSPID=Org2MSP
- CORE_LEDGER_STATE_STATEDATABASE=CouchDB
- CORE_LEDGER_STATE_COUCHDBCONFIG_COUCHDBADDRESS=couchdb:5984
- CORE_LEDGER_STATE_COUCHDBCONFIG_USERNAME=
- CORE_LEDGER_STATE_COUCHDBCONFIG_PASSWORD=
volumes:
- /var/run/:/host/var/run/
- ../crypto-config/peerOrganizations/org2.example.com/peers/peer0.org2.example.com/msp:/etc/hyperledger/fabric/msp
- ../crypto-config/peerOrganizations/org2.example.com/peers/peer0.org2.example.com/tls:/etc/hyperledger/fabric/tls
- peer0.org2.example.com:/var/hyperledger/production
ports:
- 9051:9051
networks:
- byfn
peer1.org2.example.com:
container_name: peer1.org2.example.com
extends:
file: peer-base.yaml
service: peer-base
environment:
- CORE_PEER_ID=peer1.org2.example.com
- CORE_PEER_ADDRESS=peer1.org2.example.com:10051
- CORE_PEER_LISTENADDRESS=0.0.0.0:10051
- CORE_PEER_CHAINCODEADDRESS=peer1.org2.example.com:10052
- CORE_PEER_CHAINCODELISTENADDRESS=0.0.0.0:10052
- CORE_PEER_GOSSIP_EXTERNALENDPOINT=peer1.org2.example.com:10051
- CORE_PEER_GOSSIP_BOOTSTRAP=peer0.org2.example.com:9051
- CORE_PEER_LOCALMSPID=Org2MSP
- CORE_LEDGER_STATE_STATEDATABASE=CouchDB
- CORE_LEDGER_STATE_COUCHDBCONFIG_COUCHDBADDRESS=couchdb:5984
- CORE_LEDGER_STATE_COUCHDBCONFIG_USERNAME=
- CORE_LEDGER_STATE_COUCHDBCONFIG_PASSWORD=
volumes:
- /var/run/:/host/var/run/
- ../crypto-config/peerOrganizations/org2.example.com/peers/peer1.org2.example.com/msp:/etc/hyperledger/fabric/msp
- ../crypto-config/peerOrganizations/org2.example.com/peers/peer1.org2.example.com/tls:/etc/hyperledger/fabric/tls
- peer1.org2.example.com:/var/hyperledger/production
ports:
- 10051:10051
networks:
- byfn
- 配置peer的第二个文件为peer-base.yaml,将此文件与orderer-base.yaml一同放置在base目录下,执行命令
vi peer-base.yaml
,将下面代码块中的内容复制到文件中。
version: '2'
services:
peer-base:
image: hyperledger/fabric-peer:latest
environment:
- CORE_VM_ENDPOINT=unix:///host/var/run/docker.sock
# the following setting starts chaincode containers on the same
# bridge network as the peers
# https://docs.docker.com/compose/networking/
- CORE_VM_DOCKER_HOSTCONFIG_NETWORKMODE=raft-example_byfn
- GODEBUG=netdns=go
- FABRIC_LOGGING_SPEC=info
#- FABRIC_LOGGING_SPEC=DEBUG
- CORE_PEER_TLS_ENABLED=true
- CORE_PEER_GOSSIP_USELEADERELECTION=true
- CORE_PEER_GOSSIP_ORGLEADER=false
- CORE_PEER_PROFILE_ENABLED=true
- CORE_PEER_TLS_CERT_FILE=/etc/hyperledger/fabric/tls/server.crt
- CORE_PEER_TLS_KEY_FILE=/etc/hyperledger/fabric/tls/server.key
- CORE_PEER_TLS_ROOTCERT_FILE=/etc/hyperledger/fabric/tls/ca.crt
# Allow more time for chaincode container to build on install.
- CORE_CHAINCODE_EXECUTETIMEOUT=300s
working_dir: /opt/gopath/src/github.com/hyperledger/fabric/peer
command: peer node start
- 下面配置couchdb的docker文件,couchdb这里用作四个peer的索引数据库,执行命令
vi docker-compose-couchdb.yaml
,将下面代码块的内容复制到文件中。
version: '2'
networks:
byfn:
services:
couchdb:
container_name: couchdb
image: couchdb:latest
# Populate the COUCHDB_USER and COUCHDB_PASSWORD to set an admin user and password
# for CouchDB. This will prevent CouchDB from operating in an "Admin Party" mode.
environment:
- COUCHDB_USER=
- COUCHDB_PASSWORD=
# Comment/Uncomment the port mapping if you want to hide/expose the CouchDB service,
# for example map it to utilize Fauxton User Interface in dev environments.
ports:
- "5984:5984"
networks:
- byfn
- 最后配置用于访问fabric网络的客户端容器,该容器的docker配置其实就是peer的配置,这个客户端容器是使用其中一个peer的身份去访问整个fabric网络,执行命令
vi docker-compose-cli.yaml
,将下面代码块的内容复制到文件中。
version: '2'
cli:
container_name: cli
image: hyperledger/fabric-tools:latest
tty: true
stdin_open: true
environment:
- GOPATH=/opt/gopath
- CORE_VM_ENDPOINT=unix:///host/var/run/docker.sock
#- FABRIC_LOGGING_SPEC=DEBUG
- FABRIC_LOGGING_SPEC=INFO
- CORE_PEER_ID=cli
- CORE_PEER_ADDRESS=peer0.org1.example.com:7051
- CORE_PEER_LOCALMSPID=Org1MSP
- CORE_PEER_TLS_ENABLED=true
- CORE_PEER_TLS_CERT_FILE=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org1.example.com/peers/peer0.org1.example.com/tls/server.crt
- CORE_PEER_TLS_KEY_FILE=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org1.example.com/peers/peer0.org1.example.com/tls/server.key
- 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_MSPCONFIGPATH=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org1.example.com/users/Admin@org1.example.com/msp
working_dir: /opt/gopath/src/github.com/hyperledger/fabric/peer
command: /bin/bash
volumes:
- /var/run/:/host/var/run/
- ./chaincode/:/opt/gopath/src/github.com/hyperledger/fabric-samples/chaincode
- ./crypto-config:/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/
- ./scripts:/opt/gopath/src/github.com/hyperledger/fabric/peer/scripts/
- ./channel-artifacts:/opt/gopath/src/github.com/hyperledger/fabric/peer/channel-artifacts
depends_on:
- peer0.org1.example.com
- peer1.org1.example.com
- peer0.org2.example.com
- peer1.org2.example.com
networks:
- byfn
- 到这里,fabric中的各个节点的docker配置就已经配置好了,下面在根目录下创建chaincode文件夹用于存放链码,将链码放在chaincode文件夹下,我本次使用的链码是官方实例的链码,如下代码块所示。
/*
Copyright IBM Corp. 2016 All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package main
//WARNING - this chaincode's ID is hard-coded in chaincode_example04 to illustrate one way of
//calling chaincode from a chaincode. If this example is modified, chaincode_example04.go has
//to be modified as well with the new ID of chaincode_example02.
//chaincode_example05 show's how chaincode ID can be passed in as a parameter instead of
//hard-coding.
import (
"fmt"
"strconv"
"github.com/hyperledger/fabric/core/chaincode/shim"
pb "github.com/hyperledger/fabric/protos/peer"
)
// SimpleChaincode example simple Chaincode implementation
type SimpleChaincode struct {
}
func (t *SimpleChaincode) Init(stub shim.ChaincodeStubInterface) pb.Response {
fmt.Println("ex02 Init")
_, args := stub.GetFunctionAndParameters()
var A, B string // Entities
var Aval, Bval int // Asset holdings
var err error
if len(args) != 4 {
return shim.Error("Incorrect number of arguments. Expecting 4")
}
// Initialize the chaincode
A = args[0]
Aval, err = strconv.Atoi(args[1])
if err != nil {
return shim.Error("Expecting integer value for asset holding")
}
B = args[2]
Bval, err = strconv.Atoi(args[3])
if err != nil {
return shim.Error("Expecting integer value for asset holding")
}
fmt.Printf("Aval = %d, Bval = %d\n", Aval, Bval)
// Write the state to the ledger
err = stub.PutState(A, []byte(strconv.Itoa(Aval)))
if err != nil {
return shim.Error(err.Error())
}
err = stub.PutState(B, []byte(strconv.Itoa(Bval)))
if err != nil {
return shim.Error(err.Error())
}
return shim.Success(nil)
}
func (t *SimpleChaincode) Invoke(stub shim.ChaincodeStubInterface) pb.Response {
fmt.Println("ex02 Invoke")
function, args := stub.GetFunctionAndParameters()
if function == "invoke" {
// Make payment of X units from A to B
return t.invoke(stub, args)
} else if function == "delete" {
// Deletes an entity from its state
return t.delete(stub, args)
} else if function == "query" {
// the old "Query" is now implemtned in invoke
return t.query(stub, args)
}
return shim.Error("Invalid invoke function name. Expecting \"invoke\" \"delete\" \"query\"")
}
// Transaction makes payment of X units from A to B
func (t *SimpleChaincode) invoke(stub shim.ChaincodeStubInterface, args []string) pb.Response {
var A, B string // Entities
var Aval, Bval int // Asset holdings
var X int // Transaction value
var err error
if len(args) != 3 {
return shim.Error("Incorrect number of arguments. Expecting 3")
}
A = args[0]
B = args[1]
// Get the state from the ledger
// TODO: will be nice to have a GetAllState call to ledger
Avalbytes, err := stub.GetState(A)
if err != nil {
return shim.Error("Failed to get state")
}
if Avalbytes == nil {
return shim.Error("Entity not found")
}
Aval, _ = strconv.Atoi(string(Avalbytes))
Bvalbytes, err := stub.GetState(B)
if err != nil {
return shim.Error("Failed to get state")
}
if Bvalbytes == nil {
return shim.Error("Entity not found")
}
Bval, _ = strconv.Atoi(string(Bvalbytes))
// Perform the execution
X, err = strconv.Atoi(args[2])
if err != nil {
return shim.Error("Invalid transaction amount, expecting a integer value")
}
Aval = Aval - X
Bval = Bval + X
fmt.Printf("Aval = %d, Bval = %d\n", Aval, Bval)
// Write the state back to the ledger
err = stub.PutState(A, []byte(strconv.Itoa(Aval)))
if err != nil {
return shim.Error(err.Error())
}
err = stub.PutState(B, []byte(strconv.Itoa(Bval)))
if err != nil {
return shim.Error(err.Error())
}
return shim.Success(nil)
}
// Deletes an entity from state
func (t *SimpleChaincode) delete(stub shim.ChaincodeStubInterface, args []string) pb.Response {
if len(args) != 1 {
return shim.Error("Incorrect number of arguments. Expecting 1")
}
A := args[0]
// Delete the key from the state in ledger
err := stub.DelState(A)
if err != nil {
return shim.Error("Failed to delete state")
}
return shim.Success(nil)
}
// query callback representing the query of a chaincode
func (t *SimpleChaincode) query(stub shim.ChaincodeStubInterface, args []string) pb.Response {
var A string // Entities
var err error
if len(args) != 1 {
return shim.Error("Incorrect number of arguments. Expecting name of the person to query")
}
A = args[0]
// Get the state from the ledger
Avalbytes, err := stub.GetState(A)
if err != nil {
jsonResp := "{\"Error\":\"Failed to get state for " + A + "\"}"
return shim.Error(jsonResp)
}
if Avalbytes == nil {
jsonResp := "{\"Error\":\"Nil amount for " + A + "\"}"
return shim.Error(jsonResp)
}
jsonResp := "{\"Name\":\"" + A + "\",\"Amount\":\"" + string(Avalbytes) + "\"}"
fmt.Printf("Query Response:%s\n", jsonResp)
return shim.Success(Avalbytes)
}
func main() {
err := shim.Start(new(SimpleChaincode))
if err != nil {
fmt.Printf("Error starting Simple chaincode: %s", err)
}
}
- 创建启动fabric网络的脚本,在根目录下创建目录scripts用于存放脚本,执行命令
vi up.sh
,将下面代码块中的内容复制到文件中。
docker-compose -f ../docker-compose-orderer.yaml up -d
sleep 5
docker-compose -f ../docker-compose-couchdb.yaml up -d
docker-compose -f ../docker-compose-peer.yaml up -d
docker-compose -f ../docker-compose-cli.yaml up -d
-创建关闭fabric网络的脚本,同样也在scripts目录下,执行命令vi down.sh
,将下面代码块中内容复制到文件中。
docker-compose -f ../docker-compose-orderer.yaml down --volume --remove-orphans
docker-compose -f ../docker-compose-couchdb.yaml down --volume --remove-orphans
docker-compose -f ../docker-compose-peer.yaml down --volume --remove-orphans
docker-compose -f ../docker-compose-cli.yaml down. --volume --remove-orphans
docker network prune
- 最后一步在宿主机上为容器添加dns映射,执行命令
vi /etc/hosts
进入到host文件中,将下面代码块中的内容复制到文件的最后一行。
127.0.0.1 couchdb orderer.example.com orderer1.example.com orderer2.example.com peer0.org1.example.com peer1.org1.example.com peer0.org2.example.com peer1.org2.example.com cli
- 执行脚本up.sh 启动整个fabric网络,接下来进入到与farbic网络交互的部分。
使用cli容器与fabric底层网络进行交互
这一部分是通过cli容器与fabric网络进行交互,包括比如创建通道,节点加入通道,节点安装链码,invoke调用链码等。
- 首先进入到cli容器内,执行命令
docker exec -it cli bash
进入到cli容器中,默认cli容器的工作路径是/opt/gopath/src/github.com/hyperledger/fabric/peer,这个在docker-compose-cli.yaml容器中配置的工作目录。 - 下面首先创建应用通道,这个会用到之前产生的通道文件了,这个文件已经通过数据卷挂载在当前工作目录下的channel-artifacts文件夹下,执行命令
peer channel create -o orderer.exam ple.com:7050 -c testchannel -f ./channel-artifacts/channel.tx --tls true --cafile /opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem
创建通道,创建成功后会在当前目录下生成一个以通道名命令的block文件,比如当前生成的文件为testchannel.block。 - 下面对org1进行锚节点更新操作,这个操作在每个组织的任意一个节点上执行一次就行了,执行命令
peer channel update -o orderer.example.com:7050 -c testchannel -f ./channel-artifacts/Org1MSPanchors.tx --tls true --cafile /opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem
- 下面将当前的peer节点的加入到通道中,当前节点指的是c li环境变量中配置的节点,这里在docker-compose-cli.yaml中配置的是peer0.org1.example.com,执行命令
peer channel join -b testchannel.block
,此时peer0.org1.example.com节点已经加入了该通道,可以通过执行命令peer channel list
来查看当前节点加入的通道。 - 下面为当前节点安装链码,执行命令
peer chaincode install -n mycc -v 1.0 -l golang -p github.com/chaincode
,此时peer0.org1.example.com节点已经安装好了链码。 - 下面需要为其他的peer节点执行加入通道,安装链码的操作,这个需要通过切换cli的环境变量来切换节点,下面代码块中有切换其他三个节点的环境变量的设置,按条执行即可。
#切换到peer1.org1.example.com节点
export CORE_PEER_ADDRESS=peer1.org1.example.com:8051
export CORE_PEER_TLS_CERT_FILE=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org1.example.com/peers/peer1.org1.example.com/tls/server.crt
export CORE_PEER_TLS_KEY_FILE=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org1.example.com/peers/peer1.org1.example.com/tls/server.key
export CORE_PEER_TLS_ROOTCERT_FILE=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org1.example.com/peers/peer1.org1.example.com/tls/ca.crt
#将peer1.org1.example.com节点加入到通道中
peer channel join -b testchannel.block
#为peer1.org1.example.com节点安装链码
peer chaincode install -n mycc -v 1.0 -l golang -p github.com/chaincode
#切换到peer0.org2.example.com节点
export CORE_PEER_LOCALMSPID=Org2MSP
export CORE_PEER_ADDRESS=peer0.org2.example.com:9051
export CORE_PEER_TLS_CERT_FILE=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org2.example.com/peers/peer0.org2.example.com/tls/server.crt
export CORE_PEER_TLS_KEY_FILE=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org2.example.com/peers/peer0.org2.example.com/tls/server.key
export CORE_PEER_TLS_ROOTCERT_FILE=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org2.example.com/peers/peer0.org2.example.com/tls/ca.crt
export CORE_PEER_MSPCONFIGPATH=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org2.example.com/users/Admin@org2.example.com/msp
#对org2进行更新锚节点的操作
peer channel update -o orderer.example.com:7050 -c testchannel -f ./channel-artifacts/Org2MSPanchors.tx --tls true --cafile /opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem
#将peer0.org2.example.com节点加入通道
peer channel join -b testchannel.block
#为peer0.org2.example.com节点安装链码
peer chaincode install -n mycc -v 1.0 -l golang -p github.com/chaincode
#切换到peer1.org2.example.com节点
export CORE_PEER_ADDRESS=peer1.org2.example.com:10051
export CORE_PEER_TLS_CERT_FILE=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org2.example.com/peers/peer1.org2.example.com/tls/server.crt
export CORE_PEER_TLS_KEY_FILE=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org2.example.com/peers/peer1.org2.example.com/tls/server.key
export CORE_PEER_TLS_ROOTCERT_FILE=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org2.example.com/peers/peer1.org2.example.com/tls/ca.crt
#将peer1.org2.example.com节点加入到通道中
peer channel join -b testchannel.block
#为peer1.org2.example.com节点安装链码
peer chaincode install -n mycc -v 1.0 -l golang -p github.com/chaincode
- 到这里所有的peer均已加入到了通道中,且已安装好了链码,下面进行实例化链码,这个操作无论从哪个节点上执行都是可以的,执行命令
peer chaincode instantiate iate -o orderer.example.com:7050 --tls true --cafile /opt/gopath/src/github.com/hyperledg er/fabric/peer/crypto/ordererOrganizations/example.com/orderers/orderer.example.com /msp/tlscacerts/tlsca.example.com-cert.pem -C testchannel -n mycc -l golang -v 1.0 -c '{"Arg s":["init","a","100","b","200"]}' -P 'AND("Org1MSP.peer")'
- 下面进行初始化链码的操作,这个操作对应的是链码中的init函数,执行命令
peer chaincode instantiate iate -o orderer.example.com:7050 --tls true --cafile /opt/gopath/src/github.com/hyperledg er/fabric/peer/crypto/ordererOrganizations/example.com/orderers/orderer.example.com /msp/tlscacerts/tlsca.example.com-cert.pem -C testchannel -n mycc -l golang -v 1.0 -c '{"Arg s":["init","a","100","b","200"]}' -P 'AND("Org1MSP.peer")'
。 - 下面对链码进行一次invoke的操作,从a向b转账10块钱,执行命令
peer chaincode invoke -o orderer.example.com:7050 --tls true --cafile /opt/gopath/src /github.com/hyperledger/fabric/peer/crypto/ordererOrganizations/example.com/orderers/orderer0.example.com/msp/tlscacerts/tlsca.example.com-cert.pem -C testchannel -n myc c --peerAddresses peer0.org1.example.com:7051 --tlsRootCertFiles /opt/gopath/src/github. com/hyperledger/fabric/peer/crypto/peerOrganization/org1.example.com/peers/peer0.org1.example.com/tls/ca.crt -c '{"Args":["invoke","a","b","10"]}'
。 - 下面对链码进行一次query的操作,查询a现在还剩下多少钱,执行命令
peer chaincode query -C testchannel -n mycc -c '{"Args":["query","a"]}'
。
到此基于Raft的fabric1.4的单机网络搭建到此结束了,欢迎评论区讨论,或者对我的博客提出意见。