前言
在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 組織相關配置):
- 設定環境變數
source envpeer1soft
- 啟動CA網路
./0_Restart.sh
本實驗初始 docker 網路為:
本文工作
以無排序組織的方式啟動 Hyperledger Fabric 網路,其中包含三個組織—— soft 、 web 、 hard , 每個組織都執行維護著一個 peer 節點和一個 orderer,並使用 osnadmin
工具通過 orderer
的 admin
服務使 orderer
加入這兩條通道(實驗程式碼已上傳至:https://github.com/wefantasy/FabricLearn 的 4-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 服務 |
實驗步驟
配置檔案
- 修改配置檔案
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
-
修改配置檔案
config/configtx.yaml
,原始檔太長在此不貼,其主要修改內容為:- 每個組織
MSP
下增加本組織維護的OrdererEndpoints
配置:
Orderer
配置下修改 orderer 服務的地址:
Profiles
配置下修改排序節點的維護組織為softMSP
、webMSP
、hardMSP
:
- 必須有一個組織 MSP 的
Policies
中的Readers
和Writers
下Rule
值為member
,文末會有解釋:
- 每個組織
-
各組織的環境變數檔案中新增 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
- 將
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:orderer1@soft.ifantasy.net: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:orderer1soft@council.ifantasy.net: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
中建立通道並使所有節點加入:
- 啟動 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
- 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
- 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
- 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
- 將通道檔案複製到各組織資產目錄下:
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/
- 各組織 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
中安裝鏈碼並呼叫執行:
- 各組織安裝測試鏈碼:
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
- 設定鏈碼 ID 環境變數:
export CHAINCODE_ID=basic_1:06613e463ef6694805dd896ca79634a2de36fdf019fa7976467e6e632104d718
- 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
- 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
- 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
- 測試呼叫鏈碼:
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"]}'
常見錯誤
- 沒有領導節點
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
配置是否正確
- 排序節點之間無法通訊
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)。
- 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)的 Readers
和 Writers
的 Rule
改為 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')"
至於為什麼會導致如此尚未發現,猜測是普通組織的策略與排序節點所需要的策略存在衝突,因此建議排序服務獨立於普通組織。