Hyperledger Fabric無排序組織以Raft共識演算法啟動多個Orderer服務、多組織共同執行維護Orderer服務

語言: CN / TW / HK

前言

Hyperledger Fabric無系統通道啟動及通道的建立和刪除中,我們已經完成了以無系統通道的方式啟動 Hyperledger Fabric 網路,並將鏈碼安裝到指定通道。但目前為止,實驗中的 orderer 服務都是通過單獨的排序組織來維護且只有一個,那能不能不要排序組織而使用普通組織來執行維護多個 orderer 服務呢?當然是可以的,本文將在之前的實驗基礎上,啟動一個沒有 orderer 組織的 Fabric 網路,網路中包含三個組織且每個組織執行維護著一個 Raft 協議的 orderer 節點,最後成功在其上部署執行鏈碼。

背景介紹

實驗準備

本文網路結構直接將 Hyperledger Fabric無系統通道啟動及通道的建立和刪除 中建立的 3_RunWithNoSystemChannel 複製為 4-1_RunOrdererByOneself (建議直接將本案例倉庫 FabricLearn 下的 4-1_RunOrdererByOneself 目錄拷貝到本地執行)。預設情況下,所有命令皆在 4-1_RunOrdererByOneself 根目錄下執行,在開始後面的實驗前按照以下命令啟動基礎實驗網路(主要修改為刪除 orderer 組織相關配置):

  1. 設定環境變數 source envpeer1soft
  2. 啟動CA網路 ./0_Restart.sh

本實驗初始 docker 網路為: 初始 docker 網路

本文工作

以無排序組織的方式啟動 Hyperledger Fabric 網路,其中包含三個組織—— soft 、 web 、 hard , 每個組織都執行維護著一個 peer 節點和一個 orderer,並使用 osnadmin 工具通過 ordereradmin 服務使 orderer 加入這兩條通道(實驗程式碼已上傳至:https://github.com/wefantasy/FabricLearn4-1_RunOrdererByOneself 下):

執行埠 說明
council.ifantasy.net 7050 council 組織的 CA 服務, 為聯盟鏈網路提供 TLS-CA 服務
soft.ifantasy.net 7250 soft 組織的 CA 服務, 包含成員: peer1 、 admin1
peer1.soft.ifantasy.net 7251 soft 組織的 peer1 成員節點
orderer1.soft.ifantasy.net 8251 soft 組織的 orderer1 服務
orderer1.soft.ifantasy.net 8252 soft 組織的 orderer1 服務的 admin 服務
web.ifantasy.net 7350 web 組織的 CA 服務, 包含成員: peer1 、 admin1
peer1.web.ifantasy.net 7351 web 組織的 peer1 成員節點
orderer1.soft.ifantasy.net 8351 web 組織的 orderer1 服務
orderer1.soft.ifantasy.net 8352 web 組織的 orderer1 服務的 admin 服務
hard.ifantasy.net 7450 hard 組織的 CA 服務, 包含成員: peer1 、 admin1
peer1.hard.ifantasy.net 7451 hard 組織的 peer1 成員節點
orderer1.soft.ifantasy.net 8451 hard 組織的 orderer1 服務
orderer1.soft.ifantasy.net 8452 hard 組織的 orderer1 服務的 admin 服務

實驗步驟

配置檔案

  1. 修改配置檔案 compose/docker-compose.yaml ,刪除所有關於 orderer 組織的配置,並新增 hard 組織相關容器和普通組織的 orderer 容器:
  hard.ifantasy.net:
    container_name: hard.ifantasy.net
    extends:
      file: docker-base.yaml
      service: ca-base
    command: sh -c 'fabric-ca-server start -d -b ca-admin:ca-adminpw --port 7050'
    environment:
      - FABRIC_CA_SERVER_CSR_CN=hard.ifantasy.net
      - FABRIC_CA_SERVER_CSR_HOSTS=hard.ifantasy.net
    volumes:
      - ${LOCAL_CA_PATH}/hard.ifantasy.net/ca:${DOCKER_CA_PATH}/ca
    ports:
      - 7450:7050
  peer1.hard.ifantasy.net:
    container_name: peer1.hard.ifantasy.net
    extends:
      file: docker-base.yaml
      service: peer-base
    environment:
      - CORE_PEER_ID=peer1.hard.ifantasy.net
      - CORE_PEER_LISTENADDRESS=0.0.0.0:7051
      - CORE_PEER_ADDRESS=peer1.hard.ifantasy.net:7051
      - CORE_PEER_LOCALMSPID=hardMSP
      - CORE_PEER_GOSSIP_EXTERNALENDPOINT=peer1.hard.ifantasy.net:7051
    volumes:
      - ${LOCAL_CA_PATH}/hard.ifantasy.net/registers/peer1:${DOCKER_CA_PATH}/peer
    ports:
      - 7451:7051

  orderer1.soft.ifantasy.net:
    container_name: orderer1.soft.ifantasy.net
    extends:
      file: docker-base.yaml
      service: orderer-base
    environment:
      - ORDERER_HOST=orderer1.soft.ifantasy.net
      - ORDERER_GENERAL_LOCALMSPID=softMSP
      - ORDERER_GENERAL_LISTENPORT=8251
    volumes:
      - ${LOCAL_CA_PATH}/soft.ifantasy.net/registers/orderer1:${DOCKER_CA_PATH}/orderer
    ports:
      - 8251:8251
      - 8252:8888
      - 8253:9999
      
  orderer1.web.ifantasy.net:
    container_name: orderer1.web.ifantasy.net
    extends:
      file: docker-base.yaml
      service: orderer-base
    environment:
      - ORDERER_HOST=orderer1.web.ifantasy.net
      - ORDERER_GENERAL_LOCALMSPID=webMSP
      - ORDERER_GENERAL_LISTENPORT=8351
    volumes:
      - ${LOCAL_CA_PATH}/web.ifantasy.net/registers/orderer1:${DOCKER_CA_PATH}/orderer
    ports:
      - 8351:8351
      - 8352:8888
      - 8353:9999

  orderer1.hard.ifantasy.net:
    container_name: orderer1.hard.ifantasy.net
    extends:
      file: docker-base.yaml
      service: orderer-base
    environment:
      - ORDERER_HOST=orderer1.hard.ifantasy.net
      - ORDERER_GENERAL_LOCALMSPID=hardMSP
      - ORDERER_GENERAL_LISTENPORT=8451
    volumes:
      - ${LOCAL_CA_PATH}/hard.ifantasy.net/registers/orderer1:${DOCKER_CA_PATH}/orderer
    ports:
      - 8451:8451
      - 8452:8888
      - 8453:9999
  1. 修改配置檔案 config/configtx.yaml原始檔太長在此不貼,其主要修改內容為:

    • 每個組織 MSP 下增加本組織維護的 OrdererEndpoints 配置: 增加本組織維護的 OrdererEndpoints
    • Orderer 配置下修改 orderer 服務的地址:
      Orderer 配置
    • Profiles 配置下修改排序節點的維護組織為 softMSPwebMSPhardMSPProfiles 配置
    • 必須有一個組織 MSP 的 Policies 中的 ReadersWritersRule 值為 member ,文末會有解釋: 組織 Policies
  2. 各組織的環境變數檔案中新增 orderer 服務的管理證書環境變數,以 envpeer1soft 為例:

export ORDERER_CA=$LOCAL_CA_PATH/soft.ifantasy.net/registers/orderer1/tls-msp/tlscacerts/tls-council-ifantasy-net-7050.pem
export ORDERER_ADMIN_TLS_SIGN_CERT=$LOCAL_CA_PATH/soft.ifantasy.net/registers/orderer1/tls-msp/signcerts/cert.pem
export ORDERER_ADMIN_TLS_PRIVATE_KEY=$LOCAL_CA_PATH/soft.ifantasy.net/registers/orderer1/tls-msp/keystore/key.pem
  1. envpeer1soft 複製為 envpeer1hard 作為 hard 組織的環境變數,其內容為:
export LOCAL_ROOT_PATH=$PWD
export LOCAL_CA_PATH=$LOCAL_ROOT_PATH/orgs
export DOCKER_CA_PATH=/tmp
export COMPOSE_PROJECT_NAME=fabriclearn
export DOCKER_NETWORKS=network
export FABRIC_BASE_VERSION=2.4
export FABRIC_CA_VERSION=1.5
echo "init terminal hard"
export FABRIC_CFG_PATH=$LOCAL_ROOT_PATH/config
export CORE_PEER_TLS_ENABLED=true
export CORE_PEER_LOCALMSPID="hardMSP"
export CORE_PEER_ADDRESS=peer1.hard.ifantasy.net:7451
export CORE_PEER_TLS_ROOTCERT_FILE=$LOCAL_CA_PATH/hard.ifantasy.net/assets/tls-ca-cert.pem
export CORE_PEER_MSPCONFIGPATH=$LOCAL_CA_PATH/hard.ifantasy.net/registers/admin1/msp

export ORDERER_CA=$LOCAL_CA_PATH/hard.ifantasy.net/registers/orderer1/tls-msp/tlscacerts/tls-council-ifantasy-net-7050.pem
export ORDERER_ADMIN_TLS_SIGN_CERT=$LOCAL_CA_PATH/hard.ifantasy.net/registers/orderer1/tls-msp/signcerts/cert.pem
export ORDERER_ADMIN_TLS_PRIVATE_KEY=$LOCAL_CA_PATH/hard.ifantasy.net/registers/orderer1/tls-msp/keystore/key.pem

註冊使用者

在註冊指令碼 1_RegisterUser.sh 中刪除 orderer 組織賬戶並新增三組織排序服務的 msp 賬戶和 tls-msp 賬戶,預設密碼與賬戶名相同:
註冊使用者

登入賬戶

在登入指令碼 2_EnrollUser.sh 中刪除 orderer 組織相關內容並新增三組織排序服務的 msp 賬戶和 tls-msp 賬戶,如 soft 組織下新增登入 orderer1 的程式碼:

echo "Enroll Orderer1"
export FABRIC_CA_CLIENT_HOME=$LOCAL_CA_PATH/soft.ifantasy.net/registers/orderer1
export FABRIC_CA_CLIENT_TLS_CERTFILES=$LOCAL_CA_PATH/soft.ifantasy.net/assets/ca-cert.pem
export FABRIC_CA_CLIENT_MSPDIR=msp
fabric-ca-client enroll -d -u https://orderer1:[email protected]:7250
# for TLS
export FABRIC_CA_CLIENT_MSPDIR=tls-msp
export FABRIC_CA_CLIENT_TLS_CERTFILES=$LOCAL_CA_PATH/soft.ifantasy.net/assets/tls-ca-cert.pem
fabric-ca-client enroll -d -u https://orderer1soft:[email protected]:7050 --enrollment.profile tls --csr.hosts orderer1.soft.ifantasy.net
cp $LOCAL_CA_PATH/soft.ifantasy.net/registers/orderer1/tls-msp/keystore/*_sk $LOCAL_CA_PATH/soft.ifantasy.net/registers/orderer1/tls-msp/keystore/key.pem
mkdir -p $LOCAL_CA_PATH/soft.ifantasy.net/registers/orderer1/msp/admincerts
cp $LOCAL_CA_PATH/soft.ifantasy.net/registers/admin1/msp/signcerts/cert.pem $LOCAL_CA_PATH/soft.ifantasy.net/registers/orderer1/msp/admincerts/cert.pem

建立通道並加入

在執行上述指令碼後,在指令碼 3_CreateChannel.sh 中建立通道並使所有節點加入:

  1. 啟動 peer 和 orderer 容器:
docker-compose -f $LOCAL_ROOT_PATH/compose/docker-compose.yaml up -d peer1.soft.ifantasy.net peer1.web.ifantasy.net peer1.hard.ifantasy.net 
docker-compose -f $LOCAL_ROOT_PATH/compose/docker-compose.yaml up -d orderer1.soft.ifantasy.net orderer1.web.ifantasy.net orderer1.hard.ifantasy.net

此時本實驗所有容器啟動完成:
所有容器啟動完成 2. 建立通道檔案 testchannel

configtxgen -profile OrgsChannel -outputCreateChannelTx $LOCAL_ROOT_PATH/data/testchannel.tx -channelID testchannel
configtxgen -profile OrgsChannel -outputBlock $LOCAL_ROOT_PATH/data/testchannel.block -channelID testchannel
  1. soft 組織的 orderer 服務加入通道:
source envpeer1soft
osnadmin channel list -o orderer1.soft.ifantasy.net:8252 --ca-file $ORDERER_CA --client-cert $ORDERER_ADMIN_TLS_SIGN_CERT --client-key $ORDERER_ADMIN_TLS_PRIVATE_KEY
osnadmin channel join -o orderer1.soft.ifantasy.net:8252 --channelID testchannel --config-block $LOCAL_ROOT_PATH/data/testchannel.block --ca-file "$ORDERER_CA" --client-cert "$ORDERER_ADMIN_TLS_SIGN_CERT" --client-key "$ORDERER_ADMIN_TLS_PRIVATE_KEY"
osnadmin channel list -o orderer1.soft.ifantasy.net:8252 --ca-file $ORDERER_CA --client-cert $ORDERER_ADMIN_TLS_SIGN_CERT --client-key $ORDERER_ADMIN_TLS_PRIVATE_KEY
  1. web 組織的 orderer 服務加入通道:
source envpeer1web
osnadmin channel list -o orderer1.web.ifantasy.net:8352 --ca-file $ORDERER_CA --client-cert $ORDERER_ADMIN_TLS_SIGN_CERT --client-key $ORDERER_ADMIN_TLS_PRIVATE_KEY
osnadmin channel join -o orderer1.web.ifantasy.net:8352 --channelID testchannel --config-block $LOCAL_ROOT_PATH/data/testchannel.block --ca-file "$ORDERER_CA" --client-cert "$ORDERER_ADMIN_TLS_SIGN_CERT" --client-key "$ORDERER_ADMIN_TLS_PRIVATE_KEY"
osnadmin channel list -o orderer1.web.ifantasy.net:8352 --ca-file $ORDERER_CA --client-cert $ORDERER_ADMIN_TLS_SIGN_CERT --client-key $ORDERER_ADMIN_TLS_PRIVATE_KEY
  1. hard 組織的 orderer 服務加入通道:
source envpeer1hard
osnadmin channel list -o orderer1.hard.ifantasy.net:8452 --ca-file $ORDERER_CA --client-cert $ORDERER_ADMIN_TLS_SIGN_CERT --client-key $ORDERER_ADMIN_TLS_PRIVATE_KEY
osnadmin channel join -o orderer1.hard.ifantasy.net:8452 --channelID testchannel --config-block $LOCAL_ROOT_PATH/data/testchannel.block --ca-file "$ORDERER_CA" --client-cert "$ORDERER_ADMIN_TLS_SIGN_CERT" --client-key "$ORDERER_ADMIN_TLS_PRIVATE_KEY"
osnadmin channel list -o orderer1.hard.ifantasy.net:8452 --ca-file $ORDERER_CA --client-cert $ORDERER_ADMIN_TLS_SIGN_CERT --client-key $ORDERER_ADMIN_TLS_PRIVATE_KEY
  1. 將通道檔案複製到各組織資產目錄下:
cp $LOCAL_ROOT_PATH/data/testchannel.block $LOCAL_CA_PATH/soft.ifantasy.net/assets/
cp $LOCAL_ROOT_PATH/data/testchannel.block $LOCAL_CA_PATH/web.ifantasy.net/assets/
cp $LOCAL_ROOT_PATH/data/testchannel.block $LOCAL_CA_PATH/hard.ifantasy.net/assets/
  1. 各組織 peer 節點加入通道:
cp $LOCAL_ROOT_PATH/data/testchannel.block $LOCAL_CA_PATH/soft.ifantasy.net/assets/
cp $LOCAL_ROOT_PATH/data/testchannel.block $LOCAL_CA_PATH/web.ifantasy.net/assets/
cp $LOCAL_ROOT_PATH/data/testchannel.block $LOCAL_CA_PATH/hard.ifantasy.net/assets/

部署測試鏈碼

在執行上述指令碼後,在指令碼 4_TestChaincode.sh 中安裝鏈碼並呼叫執行:

  1. 各組織安裝測試鏈碼:
source envpeer1soft
# peer lifecycle chaincode package basic.tar.gz --path asset-transfer-basic/chaincode-go --label basic_1
peer lifecycle chaincode install basic.tar.gz
peer lifecycle chaincode queryinstalled
source envpeer1web
peer lifecycle chaincode install basic.tar.gz
peer lifecycle chaincode queryinstalled
source envpeer1hard
peer lifecycle chaincode install basic.tar.gz
peer lifecycle chaincode queryinstalled
  1. 設定鏈碼 ID 環境變數:
export CHAINCODE_ID=basic_1:06613e463ef6694805dd896ca79634a2de36fdf019fa7976467e6e632104d718
  1. soft 組織批准鏈碼:
source envpeer1soft
peer lifecycle chaincode approveformyorg -o orderer1.soft.ifantasy.net:8251 --tls --cafile $ORDERER_CA  --channelID testchannel --name basic --version 1.0 --sequence 1 --waitForEvent --init-required --package-id $CHAINCODE_ID
peer lifecycle chaincode queryapproved -C testchannel -n basic --sequence 1
  1. web 組織批准鏈碼:
source envpeer1web
peer lifecycle chaincode approveformyorg -o orderer1.web.ifantasy.net:8351 --tls --cafile $ORDERER_CA  --channelID testchannel --name basic --version 1.0 --sequence 1 --waitForEvent --init-required --package-id $CHAINCODE_ID
peer lifecycle chaincode queryapproved -C testchannel -n basic --sequence 1
  1. hard 組織批准鏈碼:
source envpeer1hard
peer lifecycle chaincode approveformyorg -o orderer1.hard.ifantasy.net:8451 --tls --cafile $ORDERER_CA  --channelID testchannel --name basic --version 1.0 --sequence 1 --waitForEvent --init-required --package-id $CHAINCODE_ID
peer lifecycle chaincode queryapproved -C testchannel -n basic --sequence 1

注意,這裡各組織批准鏈碼時的 -o 引數可以指定任意一個 orderer 服務批准鏈碼 4. 檢查鏈碼批准情況:

peer lifecycle chaincode checkcommitreadiness -o orderer1.soft.ifantasy.net:8251 --tls --cafile $ORDERER_CA --channelID testchannel --name basic --version 1.0 --sequence 1 --init-required
  1. 測試呼叫鏈碼:
source envpeer1soft
peer lifecycle chaincode commit -o orderer1.soft.ifantasy.net:8251 --tls --cafile $ORDERER_CA --channelID testchannel --name basic --init-required --version 1.0 --sequence 1 --peerAddresses peer1.soft.ifantasy.net:7251 --tlsRootCertFiles $CORE_PEER_TLS_ROOTCERT_FILE --peerAddresses peer1.web.ifantasy.net:7351 --tlsRootCertFiles $CORE_PEER_TLS_ROOTCERT_FILE
peer lifecycle chaincode querycommitted --channelID testchannel --name basic -o orderer1.soft.ifantasy.net:8251 --tls --cafile $ORDERER_CA --peerAddresses peer1.soft.ifantasy.net:7251 --tlsRootCertFiles $CORE_PEER_TLS_ROOTCERT_FILE
peer chaincode invoke --isInit -o orderer1.soft.ifantasy.net:8251 --tls --cafile $ORDERER_CA --channelID testchannel --name basic --peerAddresses peer1.soft.ifantasy.net:7251 --tlsRootCertFiles $CORE_PEER_TLS_ROOTCERT_FILE --peerAddresses peer1.web.ifantasy.net:7351 --tlsRootCertFiles $CORE_PEER_TLS_ROOTCERT_FILE -c '{"Args":["InitLedger"]}'
sleep 3
peer chaincode invoke -o orderer1.soft.ifantasy.net:8251 --tls --cafile $ORDERER_CA --channelID testchannel --name basic --peerAddresses peer1.soft.ifantasy.net:7251 --tlsRootCertFiles $CORE_PEER_TLS_ROOTCERT_FILE --peerAddresses peer1.web.ifantasy.net:7351 --tlsRootCertFiles $CORE_PEER_TLS_ROOTCERT_FILE -c '{"Args":["GetAllAssets"]}'

測試呼叫鏈碼:

常見錯誤

  1. 沒有領導節點
Error: failed to send transaction: got unexpected status: SERVICE_UNAVAILABLE -- no Raft leader

上述錯誤歸結起來就是 orderer 之間沒有選出領導節點,此時應該檢查:

  • 網路中 orderer 節點的數量是否為 2n+1 個,否則可能無法完成選舉
  • 各 orderer 容器的 ORDERER_GENERAL_LOCALMSPID 配置是否正確,必須為自身所屬組織的 MSPID
  • 檢查 configtx.yaml 中各組織的 Policies 配置是否正確
  1. 排序節點之間無法通訊
2022-04-09 05:32:07.086 UTC 0032 ERRO [orderer.consensus.etcdraft] logSendFailure -> Failed to send StepRequest to 3, because: rpc error: code = Unavailable desc = connection error: desc = "transport: Error while dialing dial tcp 172.19.0.10:8451: connect: connection refused" channel=syschannel node=1

上述錯誤的原因是 orderer 節點間無法通訊, 此時應該檢查 configtx.yaml 中相關的 orderer 地址是否正確。這裡有個大坑:所有 configtx.yaml 檔案內的 orderer 相關配置的埠必須設定為容器內 ORDERER_GENERAL_LISTENPORT 的監聽埠,而不是容器外的對映埠,假如 orderer 容器配置如下圖, configtx.yaml 中的 orderer 埠必須為 7050 而不能填 8251 (所以為了避免衝突,強烈建議這兩個埠設定成一樣的 8251)。 orderer 示例配置

  1. peer 節點之間無法通訊
Error: timed out waiting for txid on all peers
2022-04-10 02:57:37.135 UTC 00a1 WARN [peer.blocksprovider] DeliverBlocks -> Got error while attempting to receive blocks: block from orderer could not be verified: implicit policy evaluation failed - 0 sub-policies were satisfied, but this policy requires 1 of the 'Writers' sub-policies to be satisfied channel=testchannel orderer-address=orderer1.soft.ifantasy.net:8251

錯誤原因是沒有操作許可權,通常是 configtx.yaml 中的策略問題,在本實驗中如果三個組織的 Policies 都設定為下列內容則會觸發本錯誤:

Policies:
    Readers:
        Type: Signature
        Rule: "OR('softMSP.admin', 'softMSP.peer', 'softMSP.client')"
    Writers:
        Type: Signature
        Rule: "OR('softMSP.admin', 'softMSP.client')"
    Admins:
        Type: Signature
        Rule: "OR('softMSP.admin')"
    Endorsement:
        Type: Signature
        Rule: "OR('softMSP.peer')"

此時需要將任意組織(比如 web)的 ReadersWritersRule 改為 menber 即可解決,解決後實驗各步驟結果符合預期:

Policies:
    Readers:
        Type: Signature
        Rule: "OR('webMSP.member')"
    Writers:
        Type: Signature
        Rule: "OR('webMSP.member')"
    Admins:
        Type: Signature
        Rule: "OR('webMSP.admin')"
    Endorsement:
        Type: Signature
        Rule: "OR('webMSP.peer')"

至於為什麼會導致如此尚未發現,猜測是普通組織的策略與排序節點所需要的策略存在衝突,因此建議排序服務獨立於普通組織。