本篇中,我们来分析在一个Fabric网络中添加与删除排序节点的问题。其实,从全局来看,本文中我们主要分析CLI工具osnadmin的channel子命令的如下三项功能:
join:添加新排序节点到通道中
- list:列出通道中的所有排序节点
- remove::把指定排序节点从通道中删除
Ordering Service Node (OSN):排序服务节点,对应于CLI命令行工具osnadmin的前三个字母。
此外,本文中还用到peer channel子命令和configtxlator这两个CLI命令行工具。
一、创建初始集群¶
Fabric支持将新的排序节点添加到现有的正常运行的网络中。本篇中,我们将使用官方的测试网络test-network来展示这项功能的一种简单应用场景。
(一)扩展测试网络以支持第五个排序节点¶
我们将扩展docker-compose-bft和crypto-config-order.yaml,以便实现支持5个排序节点。
Q:这两个文件分别位于什么位置?
A:
首先,我们应该在crypto-config-order.yaml配置文件中添加如下内容:
- Hostname: orderer5
SANS:
- localhost
在docker-compose-bft中,我们应该在volumes节部分创建一个新卷:
volumes:
...
orderer5.example.com
现在,添加新排序节点的定义:(注意,您可以根据需要更改端口)
orderer5.example.com:
container_name: orderer5.example.com
image: hyperledger/fabric-orderer:latest
labels:
service: hyperledger-fabric
environment:
- FABRIC_LOGGING_SPEC=DEBUG
- ORDERER_GENERAL_LISTENADDRESS=0.0.0.0
- ORDERER_GENERAL_LISTENPORT=7060
- 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]
- ORDERER_GENERAL_BOOTSTRAPMETHOD=none
- ORDERER_CHANNELPARTICIPATION_ENABLED=true
- ORDERER_ADMIN_TLS_ENABLED=true
- ORDERER_ADMIN_TLS_CERTIFICATE=/var/hyperledger/orderer/tls/server.crt
- ORDERER_ADMIN_TLS_PRIVATEKEY=/var/hyperledger/orderer/tls/server.key
- ORDERER_ADMIN_TLS_ROOTCAS=[/var/hyperledger/orderer/tls/ca.crt]
- ORDERER_ADMIN_TLS_CLIENTROOTCAS=[/var/hyperledger/orderer/tls/ca.crt]
- ORDERER_ADMIN_LISTENADDRESS=0.0.0.0:7061
- ORDERER_OPERATIONS_LISTENADDRESS=orderer5.example.com:9450
- ORDERER_METRICS_PROVIDER=prometheus
working_dir: /root
command: orderer
volumes:
- ../organizations/ordererOrganizations/example.com/orderers/orderer5.example.com/msp:/var/hyperledger/orderer/msp
- ../organizations/ordererOrganizations/example.com/orderers/orderer5.example.com/tls:/var/hyperledger/orderer/tls
- orderer5.example.com:/var/hyperledger/production/orderer
ports:
- 7060:7060
- 7061:7061
- 9450:9450
networks:
- test
我们还将以下卷添加到CLI容器定义中:
volumes:
- ../organizations/ordererOrganizations/example.com/users/Admin@example.com/msp:/var/hyperledger/orderer/msp
- ../organizations/ordererOrganizations/example.com/users/Admin@example.com/tls:/var/hyperledger/orderer/tls
(二)运行集群
使用如下命令:
./network.sh createChannel -bft
此命令将启动一个由4个排序节点、2个Peer节点和1个CLI组成的网络,第五个排序节点的容器也将启动,但在此阶段不属于网络的一部分。该命令还将创建一个名为“mychannel”的通道,4名排序节点和2名Peer节点将参与其中。
二、使用osnadmin CLI将新排序节点添加到测试通道¶
(一)获取最后一个配置块¶
peer命令使用环境变量来定义它将在其中运行的组织的上下文,我们将上下文更改为:
export CORE_PEER_LOCALMSPID="Org1MSP"
export CORE_PEER_TLS_ROOTCERT_FILE=$PEER0_ORG1_CA
export CORE_PEER_MSPCONFIGPATH=${PWD}/organizations/peerOrganizations/org1.example.com/users/Admin@org1.example.com/msp
export CORE_PEER_ADDRESS=localhost:7051
export ORDERER_CA=${PWD}/organizations/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem
为了获得最后一个配置块,我们将进行如下命令:
peer channel fetch config config_block.pb -o orderer.example.com:7050 --ordererTLSHostnameOverride orderer.example.com -c mychannel --tls --cafile "$ORDERER_CA"
(二)将新排序节点添加到通道¶
现在需要将环境变量更新到新的排序节点,并使用新获取的块运行以下命令:
export OSN_TLS_CA_ROOT_CERT=${PWD}/organizations/ordererOrganizations/example.com/tlsca/tlsca.example.com-cert.pem
export ADMIN_TLS_SIGN_CERT=${PWD}/organizations/ordererOrganizations/example.com/orderers/orderer5.example.com/tls/server.crt
export ADMIN_TLS_PRIVATE_KEY=${PWD}/organizations/ordererOrganizations/example.com/orderers/orderer5.example.com/tls/server.key
osnadmin channel join --channelID [CHANNEL_NAME] --config-block [CHANNEL_CONFIG_BLOCK] -o [ORDERER_ADMIN_LISTENADDRESS] --ca-file $OSN_TLS_CA_ROOT_CERT --client-cert $ADMIN_TLS_SIGN_CERT --client-key $ADMIN_TLS_PRIVATE_KEY
进行如下替换:
- CHANNEL_NAME:修改成您要调用此通道的名称。
- CHANNEL_CONFIG_BLOCK:带有创世区块或最新配置块的路径和文件名。
- ORDEER_ADMIN_LISTENADDRESS:对应于在ORDERER.yaml中为此排序节点定义的ORDERER.ADMIN.LISTENADDRESS。
- OSN_TLS_CA_ROOT_CERT:其中包含排序组织TLS CA根证书和中间证书(如果使用中间TLS CA)的路径和文件名。
- ADMIN_TLS_SIGN_CERT:其中包含来自TLS CA的管理员客户端签名证书的路径和文件名。
- ADMIN_TLS_PRIVATE_KEY:其中包含TLS CA的管理客户端私钥的路径和文件名。
例如:
osnadmin channel join --channelID mychannel --config-block config_block.pb -o localhost:7061 --ca-file "$OSN_TLS_CA_ROOT_CERT" --client-cert "$ADMIN_TLS_SIGN_CERT" --client-key "$ADMIN_TLS_PRIVATE_KEY"
注意:由于osnadmin CLI和排序节点之间的连接需要双向TLS,因此需要在每个osadmin命令上传递--client cert和--client密钥参数。--client cert参数指向管理客户端证书,--client密钥指的是管理客户端私钥,两者都由管理客户端TLS CA颁发。
此命令的输出类似如下:
Status: 201
{
"name": "mychannel",
"url": "/participation/v1/channels/mychannel",
"consensusRelation": "follower",
"status": "onboarding",
"height": 0
}
三、更改配置¶
以下命令应在CLI容器中执行。
(一)将块转换为JSON¶
使用上一节中提取的块,为了更改它,首先将块转换为JSON:
configtxlator proto_decode --input config_block.pb --type common.Block --output config_block.json
然后,从JSON块中提取配置:
jq .data.data[0].payload.data.config config_block.json > original_config.json
(二)将第五个排序节点添加到配置¶
此阶段的输出是更新交易,您可以从CLI容器计算交易,或者复制original_config.json并在本地计算机上进行所有更改。
创建一个名为modified_config.json的original_config.json副本。在新的json文件中,我们需要做4个更改:
1.将排序节点添加到已知端点¶
找到channel_group → groups → Orderer → groups → OrdererOrg → values → Endpoints → value → addresses位置处,添加如下所示的排序端点:
[
"orderer.example.com:7050",
"orderer2.example.com:7052",
"orderer3.example.com:7056",
"orderer4.example.com:7058",
"orderer5.example.com:7060"
]
2.将排序节点添加到已知身份¶
转到channel_group → groups → Orderer → policies → BlockValidation → policy → value → identities处,添加身份证书的base64编码,请根据您的需要更正路径。
{
"principal": {
"id_bytes": ".../test-network/organizations/ordererOrganizations/example.com/orderers/orderer5.example.com/msp/signcerts/orderer5.example.com-cert.pem",
"mspid": "OrdererMSP"
},
"principal_classification": "IDENTITY"
}
3.将排序节点添加到策略规则¶
转到channel_group → groups → Orderer → policies → BlockValidation → policy → value → rule,把n修改成:
# Given that the new number of nodes in cluster is num_of_nodes:
f = int((num_of_nodes - 1) / 3)
n = ceil((num_of_nodes + f + 1) / 2)
并为新排序节点添加一个signed_by对象:
{
"n_out_of": {
"n": 4,
"rules": [
{
"signed_by": 0
},
{
"signed_by": 1
},
{
"signed_by": 2
},
{
"signed_by": 3
},
{
"signed_by": 4
}
]
}
}
4.将排序节点添加到共识映射¶
转到channel_group → groups → Orderer → values → Orderers → value → consenter_mapping位置,并添加身份、客户端TLS和服务器TLS证书的base64编码,请根据您的需要更正路径。
{
"client_tls_cert": ".../test-network/organizations/ordererOrganizations/example.com/orderers/orderer5.example.com/msp/tlscacerts/tlsca.example.com-cert.pem",
"host": "orderer5.example.com",
"id": 5,
"identity": ".../test-network/organizations/ordererOrganizations/example.com/orderers/orderer5.example.com/msp/signcerts/orderer5.example.com-cert.pem",
"msp_id": "OrdererMSP",
"port": 7060,
"server_tls_cert": ".../test-network/organizations/ordererOrganizations/example.com/orderers/orderer5.example.com/msp/tlscacerts/tlsca.example.com-cert.pem"
}
我们简化了这个过程,并创建了一个Python脚本,该脚本可以在脚本子文件夹中找到(步骤1-4)!脚本用法示例:
python3 scripts/add_new_orderer_to_config.py original_config.json modified_config.json \
-a orderer5.example.com:7060 \
-i ./organizations/ordererOrganizations/example.com/orderers/orderer5.example.com/msp/signcerts/orderer5.example.com-cert.pem \
-s ./organizations/ordererOrganizations/example.com/orderers/orderer5.example.com/msp/tlscacerts/tlsca.example.com-cert.pem \
-c ./organizations/ordererOrganizations/example.com/orderers/orderer5.example.com/msp/tlscacerts/tlsca.example.com-cert.pem
使用以下方法计算更新:
configtxlator proto_encode --input original_config.json --type common.Config --output original_config.pb
configtxlator proto_encode --input modified_config.json --type common.Config --output modified_config.pb
configtxlator compute_update --channel_id mychannel --original original_config.pb --updated modified_config.pb --output config_update.pb
configtxlator proto_decode --input config_update.pb --type common.ConfigUpdate --output config_update.json
echo '{"payload":{"header":{"channel_header":{"channel_id":"mychannel", "type":2}},"data":{"config_update":'$(cat config_update.json)'}}}' | jq . >config_update_in_envelope.json
configtxlator proto_encode --input config_update_in_envelope.json --type common.Envelope --output envelope.pb
envelope.pb是配置更新交易,请注意,它不包含任何路径,如果它是在本地计算机上创建的,请将其复制到CLI容器中。
进行更新¶
通过CLI方式,我们需要使用组织的Peer节点之一和排序组织来签署交易。由于我们处于Peer组织Org1的背景下,我们可以简单地:
peer channel signconfigtx -f envelope.pb
现在,我们切换到排序组织Orderer:
export CORE_PEER_TLS_ENABLED=true
export CORE_PEER_LOCALMSPID="OrdererMSP"
export CORE_PEER_TLS_ROOTCERT_FILE=/var/hyperledger/orderer/tls/ca.crt
export CORE_PEER_MSPCONFIGPATH=/var/hyperledger/orderer/msp
export CORE_PEER_ADDRESS=localhost:7050
现在,我们开始更新排序节点:
peer channel update -o orderer.example.com:7050 --ordererTLSHostnameOverride orderer.example.com -c mychannel -f envelope.pb --tls --cafile "$ORDERER_CA"
此命令的输出类似于:
INFO [channelCmd] InitCmdFactory -> Endorser and orderer connections initialized
INFO [channelCmd] update -> Successfully submitted channel update
您还可以使用以下命令来确认添加的排序节点的状态:
osnadmin channel list --channelID mychannel -o localhost:7061 --ca-file "$OSN_TLS_CA_ROOT_CERT" --client-cert "$ADMIN_TLS_SIGN_CERT" --client-key "$ADMIN_TLS_PRIVATE_KEY"
此时,应该看到类似于以下输出的内容,即添加的排序节点的consensusRelation状态自动更改为conseter:
{
"name": "mychannel",
"url": "/participation/v1/channels/mychannel",
"consensusRelation": "consenter",
"status": "active",
"height": 4
}
您应该在新的排序节点日志中看到类似于以下的内容:
DEBU [orderer.consensus.smartbft.consensus] ProcessMessages -> 5 got message from 1: <HeartBeat with view: 0, seq: 7 channel=mychannel
DEBU [orderer.consensus.smartbft.consensus] handleHeartBeat -> Received heartbeat from 1, last heartbeat was 5.995614586s ago channel=mychannel
DEBU [orderer.consensus.smartbft.consensus] followerTick -> Last heartbeat from 1 was 1.000437542s ago channel=mychannel
DEBU [orderer.consensus.smartbft.consensus] followerTick -> Last heartbeat from 1 was 2.003549876s ago channel=mychannel
DEBU [orderer.consensus.smartbft.consensus] followerTick -> Last heartbeat from 1 was 3.00110746s ago channel=mychannel
DEBU [orderer.consensus.smartbft.consensus] followerTick -> Last heartbeat from 1 was 3.99966021s ago channel=mychannel
DEBU [orderer.consensus.smartbft.consensus] followerTick -> Last heartbeat from 1 was 5.000054669s ago channel=mychannel
DEBU [orderer.consensus.smartbft.consensus] followerTick -> Last heartbeat from 1 was 5.999811586s ago channel=mychannel
注:您可以在官方网站的此处进一步了解osnadmin这个CLI命令行工具。
从现有网络中删除排序节点¶
切换到Peer组织:
export CORE_PEER_TLS_ENABLED=true
export CORE_PEER_LOCALMSPID=Org1MSP
export CORE_PEER_TLS_ROOTCERT_FILE=/opt/gopath/src/github.com/hyperledger/fabric/peer/organizations/peerOrganizations/org1.example.com/tlsca/tlsca.org1.example.com-cert.pem
export CORE_PEER_MSPCONFIGPATH=/opt/gopath/src/github.com/hyperledger/fabric/peer/organizations/peerOrganizations/org1.example.com/users/Admin@org1.example.com/msp
export CORE_PEER_ADDRESS=localhost:7051
使用以下方法获取最后一个块:
peer channel fetch config config_block.pb -o orderer.example.com:7050 --ordererTLSHostnameOverride orderer.example.com -c mychannel --tls --cafile "$ORDERER_CA"
将其转换为JSON:
configtxlator proto_decode --input config_block.pb --type common.Block --output config_block.json
jq .data.data[0].payload.data.config config_block.json > original_config.json
现在,从JSON中删除新的orderer(按照与上一节相反的顺序),并将其保存为modified_config.JSON。
现在计算更新:
echo '{"payload":{"header":{"channel_header":{"channel_id":"mychannel", "type":2}},"data":{"config_update":'$(cat config_update.json)'}}}' | jq . >config_update_in_envelope.json
configtxlator proto_encode --input config_update_in_envelope.json --type common.Envelope --output envelope.pb
然后,使用Peer组织进行签名:
peer channel signconfigtx -f envelope.pb
现在切换到排序组织并发布:
export CORE_PEER_TLS_ENABLED=true
export CORE_PEER_LOCALMSPID="OrdererMSP"
export CORE_PEER_TLS_ROOTCERT_FILE=/var/hyperledger/orderer/tls/ca.crt
export CORE_PEER_MSPCONFIGPATH=/var/hyperledger/orderer/msp
export CORE_PEER_ADDRESS=localhost:7050
peer channel update -o orderer.example.com:7050 --ordererTLSHostnameOverride orderer.example.com -c mychannel -f envelope.pb --tls --cafile "$ORDERER_CA"
结果应该成为:
INFO [channelCmd] InitCmdFactory -> Endorser and orderer connections initialized
INFO [channelCmd] update -> Successfully submitted channel update
现在,让我们使用以下命令从通道中删除排序节点即可:
osnadmin channel remove -o localhost:7061 --ca-file "$ORDERER_CA" --client-cert "$ORDERER_ADMIN_TLS_SIGN_CERT" --client-key "$ORDERER_ADMIN_TLS_PRIVATE_KEY" --channelID mychannel
结果应该成为:
Status: 204
您应该在排序节点日志中看到类似于以下的内容:
INFO [orderer.consensus.smartbft.chain] Halt -> Shutting down chain channel=mychannel
INFO [orderer.consensus.smartbft.consensus] func1 -> Exiting channel=mychannel
INFO [orderer.consensus.smartbft.consensus] func1 -> Exiting channel=mychannel
INFO [orderer.common.multichannel] removeMember -> Removed channel: mychannel
重要引用
- https://hyperledger-fabric.readthedocs.io/en/latest/create_channel/add_orderer.html#removing-an-orderer-from-an-existing-network
- https://www.geeksforgeeks.org/hyperledger-fabric-component-design/
- https://www.geeksforgeeks.org/using-configtx-yaml-to-build-a-channel-configuration-in-hyperledger/
- https://blog.blockmagnates.com/setting-up-a-hyperledger-fabric-network-and-deploying-chain-code-onto-peers-7f9376ae0f28
- https://hyperledger-fabric.readthedocs.io/en/latest/create_channel/create_channel_participation.html#step-two-use-the-osnadmin-cli-to-add-the-first-orderer-to-the-channel