使用扩展构建器和启动器在peer容器内部构建、运行链码。在Fabric2.0之前,链码的构建和运行由peer节点的实现,是一种强耦合的模式,peer必须依赖本地宿主机的docker来构建和启动链码容器。Fabric2.0将链码的构建和运行逻辑抽离出来了,用户可以自定义构建起和启动器来实现链码的构建和运行。基于自定义的扩展构建器和启动器,可以实现在peer容器内部构建链码和运行链码。
扩展构建启动器
Hyperledger Fabric扩展构建器和启动器基于Heroku Buildpacks,是一套程序脚本的集合,可以支持发现链码、执行链码功能。
扩展构建启动器由以下4部分程序脚本组成:
- detect 接收两个参数CHAINCODE_SOURCE_DIR、CHAINCODE_METADATA_DIR,确定是否应使用此buildpack来构建和启动chaincode程序包。
- build 接收三个参数CHAINCODE_SOURCE_DIR、CHAINCODE_METADATA_DIR、BUILD_OUTPUT_DIR将chaincode源码包转换为可执行的chaincode包。
- release(可选)接收两个参数BUILD_OUTPUT_DIR、RELEASE_OUTPUT_DIR,向peer提供有关链码的元数据。
- run (可选)接收两个参数BUILD_OUTPUT_DIR、RUN_METADATA_DIR,运行链码。
这里可以参考官方文档实现
实现external-builder
-
创建external-builder/bin文件夹。
-
实现detect,在bin目录下创建
detect文件(无需后缀)。以detect返回码来决定构建和启动,返回码为0,则启用buidpack,否则使用docker build的方式。#!/bin/sh # Copyright IBM Corp. All Rights Reserved. # # SPDX-License-Identifier: Apache-2.0 set -euo pipefail if [ "$#" -ne 2 ]; then >&2 echo "Expected 2 directories got $#" exit 2 fi >&2 jq . "$2/metadata.json" if [ "$(jq -r .type "$2/metadata.json" | tr '[:upper:]' '[:lower:]')" != "golang" ]; then >&2 echo "only golang chaincode supported" exit 1 fi -
实现build,在bin目录下创建build文件。build对chaincode源码进行编译,并存储到peer指定的目录中。
#!/bin/sh # Copyright IBM Corp. All Rights Reserved. # # SPDX-License-Identifier: Apache-2.0 set -euo pipefail if [ "$#" -ne 3 ]; then >&2 echo "Expected 3 directories got $#" exit 1 fi SOURCE=$1 METADATA=$2 OUTPUT=$3 >&2 jq . "$2/metadata.json" if [[ "$(jq -r .label "$METADATA/metadata.json")" == *fail* ]]; then >&2 echo "Exiting with failure..." exit 1 fi GO_PACKAGE_PATH="$(jq -r .path "$METADATA/metadata.json")" if [ -f "$SOURCE/src/go.mod" ]; then cd "$SOURCE/src" go build -v -mod=readonly -o "$OUTPUT/chaincode" "$GO_PACKAGE_PATH" else GO111MODULE=off go build -v -o "$OUTPUT/chaincode" "$GO_PACKAGE_PATH" fi # copy index metadata if present if [ -d "$SOURCE/META-INF" ]; then cp -a "$SOURCE/META-INF" "$OUTPUT" fi -
实现release,在bin目录下创建release文件。复制编译后的链码相关文件,为run过程做准备。
#!/bin/sh # Copyright IBM Corp. All Rights Reserved. # # SPDX-License-Identifier: Apache-2.0 set -euo pipefail if [ "$#" -ne 2 ]; then >&2 echo "Expected 2 directories got $#" exit 2 fi BLD="$1" RELEASE="$2" if [ -d "$BLD/META-INF" ] ; then cp -a "$BLD/META-INF"/* "$RELEASE" fi -
实现run,在bin目录下创建run文件。run将会为chaincode生成一个chaincode.json文件(包含一些tls证书,chaincode id等必要信息),最后使用chaincode.json 文件配和chaincode二进制文件来运行chaincode。
#!/bin/sh # Copyright IBM Corp. All Rights Reserved. # # SPDX-License-Identifier: Apache-2.0 set -euo pipefail if [ "$#" -ne 2 ]; then >&2 echo "Expected 2 directories got $#" exit 1 fi OUTPUT=$1 ARTIFACTS=$2 # shellcheck disable=SC2155 export CORE_CHAINCODE_ID_NAME="$(jq -r .chaincode_id "$ARTIFACTS/chaincode.json")" export CORE_PEER_TLS_ENABLED="true" export CORE_TLS_CLIENT_CERT_FILE="$ARTIFACTS/client.crt" export CORE_TLS_CLIENT_KEY_FILE="$ARTIFACTS/client.key" export CORE_PEER_TLS_ROOTCERT_FILE="$ARTIFACTS/root.crt" export CORE_PEER_LOCALMSPID="$(jq -r .mspid "$ARTIFACTS/chaincode.json")" jq -r .client_cert "$ARTIFACTS/chaincode.json" > "$CORE_TLS_CLIENT_CERT_FILE" jq -r .client_key "$ARTIFACTS/chaincode.json" > "$CORE_TLS_CLIENT_KEY_FILE" jq -r .root_cert "$ARTIFACTS/chaincode.json" > "$CORE_PEER_TLS_ROOTCERT_FILE" if [ -z "$(jq -r .client_cert "$ARTIFACTS/chaincode.json")" ]; then export CORE_PEER_TLS_ENABLED="false" fi exec "$OUTPUT/chaincode" -peer.address="$(jq -r .peer_address "$ARTIFACTS/chaincode.json")" -
赋予以上4个文件可执行权限
chmod -R 777 ./
配置扩展构建启动器
修改core.yaml(当然你也可以直接获取这个文件core.yaml),添加扩展构建启动器配置,peer启动时会读取这个文件。
externalBuilders:
- path: /etc/hyperledger/external-builder
name: external-builder
environmentWhitelist:
- GOPROXY
- GONOPROXY
- GOSUMDB
- GONOSUMDB
- GOCACHE
- GOPATH
- GOROOT
- HOME
- XDG_CACHE_HOME
上述的环境变量列表是peer将这些环境变量传给chaincode。
重新构建peer镜像
这里重新构建镜像,是因为原生镜像没有包含go环境和jq命令,将external-builder和core.yaml文件打到镜像中,后面只需替换镜像即可实现cc运行在peer容器中,方便使用。
构建镜像
创建peer-with-external-builder目录,并将扩展构建器和core.yaml拷贝至这里,获取fabric源码并切换到v2.0.0。
完整结构如下:
.
├── Dockerfile
├── core.yaml
├── external-builder
└── fabric
具体的Dockerfile如下:
# Copyright IBM Corp. All Rights Reserved.
#
# SPDX-License-Identifier: Apache-2.0
ARG GO_VER=1.13.4
ARG ALPINE_VER=3.10
FROM golang:${GO_VER}-alpine${ALPINE_VER} as peer-base
RUN sed -i 's/dl-cdn.alpinelinux.org/mirrors.ustc.edu.cn/g' /etc/apk/repositories
RUN apk add --no-cache tzdata jq
FROM golang:${GO_VER}-alpine${ALPINE_VER} as golang
RUN sed -i 's/dl-cdn.alpinelinux.org/mirrors.ustc.edu.cn/g' /etc/apk/repositories
RUN apk add --no-cache \
bash \
gcc \
git \
make \
musl-dev
ADD . $GOPATH/src/github.com/hyperledger
WORKDIR $GOPATH/src/github.com/hyperledger/fabric
FROM golang as peer
ARG GO_TAGS=""
RUN make peer GO_TAGS=${GO_TAGS}
FROM peer-base
ENV FABRIC_CFG_PATH /etc/hyperledger/fabric
ENV CGO_ENABLED=0
VOLUME /etc/hyperledger/fabric
VOLUME /var/hyperledger
COPY --from=peer /go/src/github.com/hyperledger/fabric/build/bin /usr/local/bin
COPY --from=peer /go/src/github.com/hyperledger/fabric/sampleconfig/msp ${FABRIC_CFG_PATH}/msp
COPY --from=peer /go/src/github.com/hyperledger/core.yaml ${FABRIC_CFG_PATH}
COPY --from=peer /go/src/github.com/hyperledger/external-builder /etc/hyperledger/external-builder
EXPOSE 7051
CMD ["peer","node","start"]
打包镜像
docker build -f Dockerfile -t fabric-peer:2.0.0-external-builder .
当然你也可以直接获取我打包好的镜像
docker pull tianrandailoving/fabric-peer:2.0.0-external-builder
test-network测试
使用官方fabric-samples的test-network快速创建fabric网络验证可扩展构建器。
获取test-network启动脚本及相应的二进制工具包文件
curl -sSL https://bit.ly/2ysbOFE | bash -s -- 2.0.0
脚本获取之后,进入test-network目录,修改docker目录下的docker-compose-test-net.yaml文件,将peer镜像替换成我们刚刚打好的镜像。
启动test-network网络,在test-network目录下执行
./network.sh up createChannel
部署链码
./network.sh deployCC
链码部署成功后,可进peer容器,使用top查看进程,可以发现链码以一个进程的方式运行在peer容器中了。

总结
使用自定义扩展构建器,可以脱离原来docker build的方式,自定义扩展构建器可以使cc作为一个单独的进程运行在peer中,也可以实现cc运行在k8s的pod中。