0. Fabric e2e_cli 案例的运行流程
在部署好 Hyperledger Fabric v1.0.0 的环境之后,我们通常会运行其 e2e_cli 的案例。在 fabric/examples/e2e_cli
目录下有一个 shell 脚本:network_setup.sh
,这就是 e2e_cli 项目的入口,运行 ./network_setup.sh up
即可启动 fabric 的网络并完成相关的测试,然后运行 ./network_setup.sh down
可以删除相关的容器以及文件来结束测试。下面我们就来分析 e2e_cli 案例的运行流程。
首先看看 network_setup.sh 的具体内容:
#!/bin/bash
#
# Copyright IBM Corp. All Rights Reserved.
#
# SPDX-License-Identifier: Apache-2.0
#
UP_DOWN="$1"
CH_NAME="$2"
CLI_TIMEOUT="$3"
IF_COUCHDB="$4"
: ${CLI_TIMEOUT:="10000"}
COMPOSE_FILE=docker-compose-cli.yaml
COMPOSE_FILE_COUCH=docker-compose-couch.yaml
#COMPOSE_FILE=docker-compose-e2e.yaml
function printHelp () {
echo "Usage: ./network_setup <up|down> <\$channel-name> <\$cli_timeout> <couchdb>.\nThe arguments must be in order."
}
function validateArgs () {
if [ -z "${UP_DOWN}" ]; then
echo "Option up / down / restart not mentioned"
printHelp
exit 1
fi
if [ -z "${CH_NAME}" ]; then
echo "setting to default channel 'mychannel'"
CH_NAME=mychannel
fi
}
function clearContainers () {
CONTAINER_IDS=$(docker ps -aq)
if [ -z "$CONTAINER_IDS" -o "$CONTAINER_IDS" = " " ]; then
echo "---- No containers available for deletion ----"
else
docker rm -f $CONTAINER_IDS
fi
}
function removeUnwantedImages() {
DOCKER_IMAGE_IDS=$(docker images | grep "dev\|none\|test-vp\|peer[0-9]-" | awk '{print $3}')
if [ -z "$DOCKER_IMAGE_IDS" -o "$DOCKER_IMAGE_IDS" = " " ]; then
echo "---- No images available for deletion ----"
else
docker rmi -f $DOCKER_IMAGE_IDS
fi
}
function networkUp () {
if [ -f "./crypto-config" ]; then
echo "crypto-config directory already exists."
else
#Generate all the artifacts that includes org certs, orderer genesis block,
# channel configuration transaction
source generateArtifacts.sh $CH_NAME
fi
if [ "${IF_COUCHDB}" == "couchdb" ]; then
CHANNEL_NAME=$CH_NAME TIMEOUT=$CLI_TIMEOUT docker-compose -f $COMPOSE_FILE -f $COMPOSE_FILE_COUCH up -d 2>&1
else
CHANNEL_NAME=$CH_NAME TIMEOUT=$CLI_TIMEOUT docker-compose -f $COMPOSE_FILE up -d 2>&1
fi
if [ $? -ne 0 ]; then
echo "ERROR !!!! Unable to pull the images "
exit 1
fi
docker logs -f cli
}
function networkDown () {
docker-compose -f $COMPOSE_FILE down
#Cleanup the chaincode containers
clearContainers
#Cleanup images
removeUnwantedImages
# remove orderer block and other channel configuration transactions and certs
rm -rf channel-artifacts/*.block channel-artifacts/*.tx crypto-config
}
validateArgs
#Create the network using docker compose
if [ "${UP_DOWN}" == "up" ]; then
networkUp
elif [ "${UP_DOWN}" == "down" ]; then ## Clear the network
networkDown
elif [ "${UP_DOWN}" == "restart" ]; then ## Restart the network
networkDown
networkUp
else
printHelp
exit 1
fi
1. 启动过程
1.1 network_setup.sh 参数设置
传入参数
在运行
./network_setup.sh up
启动的时候,我们传入了up
这个参数,使得内置的 UP_DOWN 参数设置成了 up,也就是确定执行类型为启动 fabric 网络,但其实该脚本还允许传入 3 个参数,分别是 CH_NAME(通道名称,默认设为 ”mychannel“)、CLI_TIMEOUT(客户端超时设置,默认设为 10000)和 IF_COUCHDB(是否启动 CouchDB 版本 yaml 文件,默认为不启用)。验证参数:validateArgs
在
network_setup.sh
脚本 88 行左右,调用了validateArgs
函数来验证参数是否合法,如果不合法,也就是没有指定up/down/restart
,会打印帮助信息并退出脚本;如果合法,$UP_DOWN
设置合法,则判断是否指定$CH_NAME
,没有指定则设置为默认的mychannel
。
1.2 启动网络:networkUp
判断 crypto-config 目录是否存在
如果该目录不存在,会调用 generateArtifacts.sh 脚本创建 crypto-config 目录及所需的区块链网络整数等文件,命令如下:
source generateArtifacts.sh $CH_NAME
详细请看 1.3 节
判断是否启用 CouchDB
如果启用了 CouchDB,会执行以下 docker-compose 命令:
CHANNEL_NAME=$CH_NAME TIMEOUT=$CLI_TIMEOUT docker-compose -f $COMPOSE_FILE -f $COMPOSE_FILE_COUCH up -d 2>&1
否则执行:
CHANNEL_NAME=$CH_NAME TIMEOUT=$CLI_TIMEOUT docker-compose -f $COMPOSE_FILE up -d 2>&1
详细请看 1.4 节
最后会查看 cli 容器的日志:
docker logs -f cli
详细请看 1.5 节
1.3 调用 generateArtifacts.sh
先看看 generateArtifacts.sh 脚本的内容:
#!/bin/bash +x
#
# Copyright IBM Corp. All Rights Reserved.
#
# SPDX-License-Identifier: Apache-2.0
#
#set -e
CHANNEL_NAME=$1
: ${CHANNEL_NAME:="mychannel"}
echo $CHANNEL_NAME
export FABRIC_ROOT=$PWD/../..
export FABRIC_CFG_PATH=$PWD
echo
OS_ARCH=$(echo "$(uname -s|tr '[:upper:]' '[:lower:]'|sed 's/mingw64_nt.*/windows/')-$(uname -m | sed 's/x86_64/amd64/g')" | awk '{print tolower($0)}')
## Using docker-compose template replace private key file names with constants
function replacePrivateKey () {
ARCH=`uname -s | grep Darwin`
if [ "$ARCH" == "Darwin" ]; then
OPTS="-it"
else
OPTS="-i"
fi
cp docker-compose-e2e-template.yaml docker-compose-e2e.yaml
CURRENT_DIR=$PWD
cd crypto-config/peerOrganizations/org1.example.com/ca/
PRIV_KEY=$(ls *_sk)
cd $CURRENT_DIR
sed $OPTS "s/CA1_PRIVATE_KEY/${PRIV_KEY}/g" docker-compose-e2e.yaml
cd crypto-config/peerOrganizations/org2.example.com/ca/
PRIV_KEY=$(ls *_sk)
cd $CURRENT_DIR
sed $OPTS "s/CA2_PRIVATE_KEY/${PRIV_KEY}/g" docker-compose-e2e.yaml
}
## Generates Org certs using cryptogen tool
function generateCerts (){
CRYPTOGEN=$FABRIC_ROOT/release/$OS_ARCH/bin/cryptogen
if [ -f "$CRYPTOGEN" ]; then
echo "Using cryptogen -> $CRYPTOGEN"
else
echo "Building cryptogen"
make -C $FABRIC_ROOT release
fi
echo
echo "##########################################################"
echo "##### Generate certificates using cryptogen tool #########"
echo "##########################################################"
$CRYPTOGEN generate --config=./crypto-config.yaml
echo
}
## Generate orderer genesis block , channel configuration transaction and anchor peer update transactions
function generateChannelArtifacts() {
CONFIGTXGEN=$FABRIC_ROOT/release/$OS_ARCH/bin/configtxgen
if [ -f "$CONFIGTXGEN" ]; then
echo "Using configtxgen -> $CONFIGTXGEN"
else
echo "Building configtxgen"
make -C $FABRIC_ROOT release
fi
echo "##########################################################"
echo "######### Generating Orderer Genesis block ##############"
echo "##########################################################"
# Note: For some unknown reason (at least for now) the block file can't be
# named orderer.genesis.block or the orderer will fail to launch!
$CONFIGTXGEN -profile TwoOrgsOrdererGenesis -outputBlock ./channel-artifacts/genesis.block
echo
echo "#################################################################"
echo "### Generating channel configuration transaction 'channel.tx' ###"
echo "#################################################################"
$CONFIGTXGEN -profile TwoOrgsChannel -outputCreateChannelTx ./channel-artifacts/channel.tx -channelID $CHANNEL_NAME
echo
echo "#################################################################"
echo "####### Generating anchor peer update for Org1MSP ##########"
echo "#################################################################"
$CONFIGTXGEN -profile TwoOrgsChannel -outputAnchorPeersUpdate ./channel-artifacts/Org1MSPanchors.tx -channelID $CHANNEL_NAME -asOrg Org1MSP
echo
echo "#################################################################"
echo "####### Generating anchor peer update for Org2MSP ##########"
echo "#################################################################"
$CONFIGTXGEN -profile TwoOrgsChannel -outputAnchorPeersUpdate ./channel-artifacts/Org2MSPanchors.tx -channelID $CHANNEL_NAME -asOrg Org2MSP
echo
}
generateCerts
replacePrivateKey
generateChannelArtifacts
脚本最后调用了三个函数:
generateCerts
该函数首先判断
fabric/release/linux-amd64/bin/cryptogen
是否存在,如果不存在则先编译生成,这个是 Fabric 基于 Go 语言的 crypto 库提供的工具,通过 cryptogen 可以快速地根据配置自动批量生成所需要的密钥和证书文件。cryptogen 根据 crypto-config.yaml 文件读入网络的拓扑结构,执行的命令如下:fabric/release/linux-amd64/bin/cryptogen generate --config=./crypto-config.yaml
crypto-config.yaml 配置文件可以指定两类组织的信息:
- OrdererOrgs:构成 Orderer 集群的节点所属组织;
- PeerOrgs:构成 Peer 集群的节点所属的组织
每个组织拥有:
- 名称(name):组织的名称;
- 组织域(Domain):组织的命名域;
- CA:组织的 CA 地址,包括 Hostname 域;
- 若干节点(Node):一个节点包括 Hostname、CommonName、SANS 等域,可以用 Specs 字段指定一组节点,或者用 Template 字段指定自动生成节点的个数;
- 用户(User)模板:自动生成除 admin 外的用户个数。
示例如下:
OrdererOrgs: - Name: Orderer Domain: example.com Specs: - Hostname: orderer PeerOrgs: - Name: Org1 Domain: org1.example.com Template: Count: 2 Users: Count: 1 - Name: Org2 Domain: org2.example.com Template: Count: 2 Users: Count: 1
上面的示例配置中,Orderer 组织通过 Specs 字段指定了一个主机 order.example.com;而两个 Peer 组织则采用 Template 来自动生成了 Count 个数的主机。
同样,Users 字段下的 Count 字段值会让 cryptogen 工具以自动顺序生成指定个数的普通用户(除默认的 Admin 用户外)。
replacePrivatekey
在这个函数中,会替换 docker-compose-e2e-template.yaml 文件中的 CA1_PRIVATE_KEY 为当前目录 crypto-config/peerOrganizationsorg1.example.com/ca/ 下的以
_sk
结尾的文件名,同时替换 CA2_PRIVATE_KEY 为当前目录crypto-config/peerOrganizationsorg2.example.com/ca/ 下的以_sk
结尾的文件名。最终生成的新文件被创建在当前文件夹下并命名为 docker-compose-e2e.yaml,在该文件中定义了 CA 的 CERTFILE 及 KEYFILE,同时也通过 command 内置参数显示启动了 Fabric-CA 服务端。
其实在后续的过程中并没有使用这里生成的这个 docker-compose-e2e.yaml 配置文件,而使用的是 docker-compose-cli.yaml,在 network_setup.sh 脚本中的 COMPOSE_FILE 变量指定了要使用的配置文件,代码如下:
COMPOSE_FILE=docker-compose-cli.yaml #COMPOSE_FILE=docker-compose-e2e.yaml
generateChannelArtifacts
这个函数首先判断
fabric/release/linux-amd64/bin/configtxgen
是否存在,如果不存在则先编译生成。configtxgen 可以配合 cryptogen 生成的组织结构身份文件使用,离线生成跟通道有关的配置信息,其主要功能有如下三个:- 生成启动 Orderer 需要的初始化区块,并支持检查区块内容;
- 生成创建应用通道需要的配置交易,并支持检查交易内容;
- 生成 2 个锚节点 Peer 的更新配置交易。
configtxgen 调用的配置文件为 configtx.yaml ,该配置文件一般包括四个部分:
- Profiles:一系列通道配置模板,包括 Orderer 系统通道模板和应用通道类型模板;
- Organization:一系列组织结构定义,被其他部分引用;
- Orderer:Orderer 系统通道相关配置,包括 Orderer 服务配置和参与 Ordering 服务的可用组织信息;
- Application:应用通道相关配置,主要包括参与应用网络的可用组织信息。
调用该函数最终在 channel-artifacts 目录下生成了四个文件,对应于上述的三个功能:
channel.tx genesis.block Org1MSPanchors.tx Org2MSPanchors.tx
1.4 docker-compose 启动容器服务
如 1.2 节所述,默认情况下,会执行以下命令启动容器服务:
CHANNEL_NAME=$CH_NAME TIMEOUT=$CLI_TIMEOUT docker-compose -f $COMPOSE_FILE up -d 2>&1
其中变量的值为: CHANNEL_NAME=mychannel,TIMEOUT=10000,COMPOSE_FILE=docker-compose-cli.yaml。
-d:指定在后台运行容器;
2>&1:指定同时重定向标准输出(stdout)与标准错误(stderr)
容器启动配置文件 docker-compose-cli.yaml 的内容如下:
# Copyright IBM Corp. All Rights Reserved.
#
# SPDX-License-Identifier: Apache-2.0
#
version: '2'
services:
orderer.example.com:
extends:
file: base/docker-compose-base.yaml
service: orderer.example.com
container_name: orderer.example.com
peer0.org1.example.com:
container_name: peer0.org1.example.com
extends:
file: base/docker-compose-base.yaml
service: peer0.org1.example.com
peer1.org1.example.com:
container_name: peer1.org1.example.com
extends:
file: base/docker-compose-base.yaml
service: peer1.org1.example.com
peer0.org2.example.com:
container_name: peer0.org2.example.com
extends:
file: base/docker-compose-base.yaml
service: peer0.org2.example.com
peer1.org2.example.com:
container_name: peer1.org2.example.com
extends:
file: base/docker-compose-base.yaml
service: peer1.org2.example.com
cli:
container_name: cli
image: hyperledger/fabric-tools
tty: true
environment:
- GOPATH=/opt/gopath
- CORE_VM_ENDPOINT=unix:///host/var/run/docker.sock
- CORE_LOGGING_LEVEL=DEBUG
- 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 -c './scripts/script.sh ${CHANNEL_NAME}; sleep $TIMEOUT'
volumes:
- /var/run/:/host/var/run/
- ../chaincode/go/:/opt/gopath/src/github.com/hyperledger/fabric/examples/chaincode/go
- ./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:
- orderer.example.com
- peer0.org1.example.com
- peer1.org1.example.com
- peer0.org2.example.com
- peer1.org2.example.com
从配置文件不难看出,排序服务节点继承了 base/docker-compose-base.yaml 中的 order.example.com 属性;而其他四个 Peer 节点继承了 base/docker-compose-base.yaml 中的与之容器名称对应的属性,并又都继承了 base/peer-base.yaml 中的属性。
在上述文件所继承的文件属性中,关键属性如下:
- environment:当前所配置的外界的环境变量
- working_dir:当前容器启动后的工作路径
- volumes:外界物理机路径挂载或指引到容器内的路径
- ports:指定当前容器启动后映射到物理机上的端口号
- depends_on:指定当前容器启动所依赖的启动容器对象
在 docker-compose-cli.yaml 文件中,官方的节点配置信息基本已经满足实际生产应用,而真正使用这些服务节点进行数据维护和管理的则是交由客户端或 SDK 来执行。这里也就是配置文件中的 cli 客户端容器。cli 容器的 command 属性指定当 cli 容器启动后会执行当前目录中的 scripts 目录下的 script.sh 脚本,也就是:
/bin/bash -c './scripts/script.sh ${CHANNEL_NAME}; sleep $TIMEOUT'
在 1.5 节中将会分析 cli 容器执行上述脚本的日志记录。
1.5 docker logs -f cli 查看容器日志
script.sh 脚本的执行则是 e2e_cli 中真正地对 Peer 节点、频道以及合约的集合操作演示,先来看看该脚本内的函数执行顺序:
## Create channel
echo "Creating channel..."
createChannel
## Join all the peers to the channel
echo "Having all peers join the channel..."
joinChannel
## Set the anchor peers for each org in the channel
echo "Updating anchor peers for org1..."
updateAnchorPeers 0
echo "Updating anchor peers for org2..."
updateAnchorPeers 2
## Install chaincode on Peer0/Org1 and Peer2/Org2
echo "Installing chaincode on org1/peer0..."
installChaincode 0
echo "Install chaincode on org2/peer2..."
installChaincode 2
#Instantiate chaincode on Peer2/Org2
echo "Instantiating chaincode on org2/peer2..."
instantiateChaincode 2
#Query on chaincode on Peer0/Org1
echo "Querying chaincode on org1/peer0..."
chaincodeQuery 0 100
#Invoke on chaincode on Peer0/Org1
echo "Sending invoke transaction on org1/peer0..."
chaincodeInvoke 0
## Install chaincode on Peer2/Org2
echo "Installing chaincode on org2/peer3..."
installChaincode 3
#Query on chaincode on Peer2/Org2, check if the result is 90
echo "Querying chaincode on org2/peer3..."
chaincodeQuery 3 90
该脚本设计 Peer 节点及排序服务节点的操作分别有如下九个步骤:
- createChannel:根据之前在 generateArtifacts.sh 脚本中通过 configtx.yaml 配置文件生成的频道文件创建频道;
- joinChannel:Peer 节点加入指定频道;
- updateAnchorPeers 0/2:为频道中的每个组织设置 Peer 节点;
- installChaincode 0/2:在 Peer0/Org1 和 Peer0/Org2 上安装智能合约;
- instantiateChaincode 2:在 Peer0/Org2 上对智能合约进行实例化操作;
- chaincodeQuery 0 100:在 Peer0/Org1 上执行智能合约中的查询方法,判断是否等于 100;
- chaincodeInvoke 0:在 Peer0/Org1 上执行智能合约中的交易方法;
- installChaincode 3:在 Peer1/Org2 上安装智能合约;
- chaincodeQuery 3 90:在 Peer1/Org2 上执行智能合约中的查询方法,判断是否等于 90;
注:这里需要说明的是脚本中最后两步的注释以及回显命令中的内容都有点小问题,”Peer2/Org2” 和 “org2/peer3” 都是不存在的,“3” 其实对应的节点是 Peer1/Org2。看到有的资料在分析这部分内容的时候也是按照错误的注释来解释的,让人很困惑。
上面的脚本会在 cli 客户端容器启动之后执行,在 network_setup.sh 脚本的最后会执行 docker logs -f cli
来查看 cli 容器的日志信息,在开始时会出现如下的字符图:
echo
echo " ____ _____ _ ____ _____ _____ ____ _____ "
echo "/ ___| |_ _| / \ | _ \ |_ _| | ____| |___ \ | ____|"
echo "\___ \ | | / _ \ | |_) | | | _____ | _| __) | | _| "
echo " ___) | | | / ___ \ | _ < | | |_____| | |___ / __/ | |___ "
echo "|____/ |_| /_/ \_\ |_| \_\ |_| |_____| |_____| |_____|"
echo
在结束时显示的字符图如下,并提示执行完成:
echo
echo "===================== All GOOD, End-2-End execution completed ===================== "
echo
echo
echo " _____ _ _ ____ _____ ____ _____ "
echo "| ____| | \ | | | _ \ | ____| |___ \ | ____|"
echo "| _| | \| | | | | | _____ | _| __) | | _| "
echo "| |___ | |\ | | |_| | |_____| | |___ / __/ | |___ "
echo "|_____| |_| \_| |____/ |_____| |_____| |_____|"
echo
以上就是e2e_cli 案例启动 fabric 网络的流程分析,接下来分析关闭 fabric 网络的流程。
2. 关闭过程
2.1 network_setup.sh 参数设置
当执行 ./network_setup.sh down
命令时,down 参数传入脚本作为变量 UP_DOWN 的值,表示关闭 fabric 网络的操作。脚本中判断 $UP_DOWN=down 后将执行 networkDown 函数,如 2.2 节。
2.2 关闭网络:networkDown
在 networkDown 函数中,首先会执行 docker-compose 相关的命令来关闭容器服务,然后调用 clearContainers 和 removeUnwantedImages 函数删除相应的容器及镜像,最后会删除 generateArtifacts.sh 脚本创建的 crypto-config 目录和区块链网络证书等文件。如后面 3 节。
2.3 docker-compose 关闭容器服务
执行的命令为:
docker-compose -f $COMPOSE_FILE down
$COMPOSE_FILE 就是 docker-compose-cli.yaml 文件。
该命令会将由 docker-compose-cli.yaml 文件创建的容器服务全部关闭。
2.4 删除容器及镜像
在运行 e2e_cli 案例之后,环境中会启动总共 9 个容器,使用 docker ps -a
命令查看,这里只列出容器的名字:
orderer.example.com <==Orderer排序节点
peer0.org1.example.com <==Peer0/Org1
peer1.org1.example.com <==Peer1/Org2
peer0.org2.example.com <==Peer0/Org2
peer1.org2.example.com <==Peer1/Org2
cli <==客户端容器
dev-peer0.org1.example.com-mycc-1.0 <==运行智能合约的容器,基于Peer0/Org1安装智能合约后生成
dev-peer0.org2.example.com-mycc-1.0 <==运行智能合约的容器,基于Peer0/Org2安装智能合约后生成
dev-peer1.org2.example.com-mycc-1.0 <==运行智能合约的容器,基于Peer1/Org2安装智能合约后生成
另外也会生成上面运行智能合约的容器对应的镜像,使用 docker image ls
列出如下:
$ docker image ls
REPOSITORY TAG IMAGE ID CREATED SIZE
dev-peer1.org2.example.com-mycc-1.0 latest 8c03b3cb4f3f 36 hours ago 173MB
dev-peer0.org1.example.com-mycc-1.0 latest 9aa4e69a149f 36 hours ago 173MB
dev-peer0.org2.example.com-mycc-1.0 latest f3fef07f5dcc 36 hours ago 173MB
以上的容器和镜像就是要被删除的,这里调用了两个函数:clearContainers 和 removeUnwantedImages 分别来删除,函数内容如下:
function clearContainers () {
CONTAINER_IDS=$(docker ps -aq)
if [ -z "$CONTAINER_IDS" -o "$CONTAINER_IDS" = " " ]; then
echo "---- No containers available for deletion ----"
else
docker rm -f $CONTAINER_IDS
fi
}
function removeUnwantedImages() {
DOCKER_IMAGE_IDS=$(docker images | grep "dev\|none\|test-vp\|peer[0-9]-" | awk '{print $3}')
if [ -z "$DOCKER_IMAGE_IDS" -o "$DOCKER_IMAGE_IDS" = " " ]; then
echo "---- No images available for deletion ----"
else
docker rmi -f $DOCKER_IMAGE_IDS
fi
}
2.5 删除相关目录文件
在 networkDown 函数的最后就是要删除由 generateArtifacts.sh 脚本创建的 crypto-config 目录和区块链网络证书等文件,命令如下:
# remove orderer block and other channel configuration transactions and certs
rm -rf channel-artifacts/*.block channel-artifacts/*.tx crypto-config
结束以上步骤之后就完成了关闭 fabric 网络的操作。