基于Raft共识搭建单机Fabric1.4网络

基于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的单机网络搭建到此结束了,欢迎评论区讨论,或者对我的博客提出意见。

  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
raft共识算法是一种分布式一致性算法,用于解决分布式系统中节点之间达成一致性的问题。它主要包含了Leader选举、日志复制和安全性等基本机制。 在raft算法中,节点分为Leader、Follower和Candidate三种状态。初始状态下所有节点都是Follower,然后它们通过相互通信进行Leader选举。选出的Leader负责接收客户端请求并进行日志复制等操作。如果Leader出现故障或无法通信,那么其他节点会重新进行选举,选出新的Leader。 日志复制是raft算法的关键过程,Leader负责将客户端请求记录在日志中,然后将日志复制给所有的Follower节点。Follower节点在接收到Leader的日志之后进行存储,然后发送应答给Leader确认。只有当大多数节点都复制了同一条日志之后,这条日志才算是已提交的。 raft算法还通过逻辑时钟和心跳机制来保证系统的一致性。每个节点都有自己的逻辑时钟,用于识别事件的顺序。Leader节点会定期发送心跳信号给Follower节点,以确保它们的存活状态。 在raft算法中,安全性是非常重要的一部分。它通过限制节点之间的信息交换,避免了“脑裂”等问题的发生。同时,每个节点都有持久性的存储,当节点宕机之后可以通过快照恢复。 总的来说,raft共识算法通过Leader选举、日志复制和安全性等机制,实现了分布式系统中节点之间的一致性。它比Paxos算法更容易理解和实现,因此在实际应用中被广泛使用。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值