将链码作为外部服务

Fabric v2.0支持将链码在Faric外部部署与执行以方便用户独立管理每个节点的代码运行环境。这有助于Kubernetes等Fabric云上部署链码。不再需要在每个节点构建和运行,链码现在可以作为一个服务运行,其生命周期在Fabric之外进行管理。此功能利用了Fabric v2.0外部构建器和启动器功能,使操作员能够通过程序扩展对等方来构建、启动和发现链码。在阅读本主题之前,您应该熟悉外部构建器和启动器 内容。

在外部构建器可用之前,链码包内容要求是一组特定语言的源代码文件,可以作为链码二进制文件构建和启动。新的外部构建和启动程序功能现在允许用户有选择地定制构建过程。为了将链码作为外部服务运行,构建过程允许您指定正在运行链码的服务器的端点信息。因此,该包只包含外部运行的链码服务器端点信息和用于安全连接的TLS构件。TLS是可选的,但强烈建议用于除简单测试环境以外的所有环境。

本主题的其余部分介绍如何将链码配置为外部服务:

注意: 这是一个需要定制封装peer镜像的高级功能。例如,接下来的示例需要用到的jqbash并不包含在当前的fabric-peer官方镜像中。

打包链码

在Fabricv2.0的链码生命周期中,链码是被打包为.tar.gz的格式并安装的。下述myccpackage.tgz文件将展示所需要的结构。

$ tar xvfz myccpackage.tgz
metadata.json
code.tar.gz

链码包应向外部构建器和启动器进程提供两个信息

  • 标记链码是否为外部服务。bin/detect 章节描述了使用metadata.json的要求。
  • connection.json 中提供链码端点信息并放置在发布目录中。bin/run 章节描述了 connection.json文件。

收集上述信息可以有很多灵活的方法。[外部构建器和运行器示例脚本]示例脚本中展示了收集这些信息的简单方法。 作为一个灵活性的例子,考虑打包couchdb索引文件(参考将索引添加至你的链码文件夹)。下列示例脚本展示了打包文件到 code.tar.gz的方法。

tar cfz code.tar.gz connection.json metadata
tar cfz $1-pkg.tgz metadata.json code.tar.gz

设置一个节点来执行外部链码

在本章节中文名将介绍所需的配置

  • 检测链码是否被标记为外部链码服务
  • 在发布目录中创建connection.json文件

修改 peer core.yaml文件以包含externalBuilder(外部构建器)

假设peer上的脚本被如下所示放置在bin文件夹下

    <fully qualified path on the peer's env>
    └── bin
        ├── build
        ├── detect
        └── release

修改peer的 core.yamlchaincode段落以包含externalBuilders(外部构建器)配置元素:

externalBuilders:
     - name: myexternal
       path: <fully qualified path on the peer's env>   

外部构建器和启动器示例脚本

为了帮助理解外部服务链码需要包含的每个脚本,本章节包含bin/detect bin/build, bin/release, 和 bin/run 脚本。

注意: 本示例使用了jq命令来分析json。你可以运行jq --version来检查你是否已安装。如果没有,则安装jq或适当调整脚本。

bin/detect

bin/detect script 负责确定是否应使用buildpack来生成链码包并启动它。对于作为外部服务的链码,示例脚本在metadata.json文件中设置type属性为external

{"path":"","type":"external","label":"mycc"}

Peer调用detect需要两个参数:

bin/detect CHAINCODE_SOURCE_DIR CHAINCODE_METADATA_DIR

bin/detect示例脚本包含:

#!/bin/bash

set -euo pipefail

METADIR=$2
#check if the "type" field is set to "external"
if [ "$(jq -r .type "$METADIR/metadata.json")" == "external" ]; then
    exit 0
fi

exit 1

bin/build

作为一个外部服务的链码,示例构建脚本假设链码包code.tar.gz 的文件内的connection.json仅简单复制BUILD_OUTPUT_DIR。节点需要三个参数调用构建脚本:

bin/build CHAINCODE_SOURCE_DIR CHAINCODE_METADATA_DIR BUILD_OUTPUT_DIR

bin/build示例脚本包含:

#!/bin/bash

set -euo pipefail

SOURCE=$1
OUTPUT=$3

#external chaincodes expect connection.json file in the chaincode package
if [ ! -f "$SOURCE/connection.json" ]; then
    >&2 echo "$SOURCE/connection.json not found"
    exit 1
fi

#simply copy the endpoint information to specified output location
cp $SOURCE/connection.json $OUTPUT/connection.json

if [ -d "$SOURCE/metadata" ]; then
    cp -a $SOURCE/metadata $OUTPUT/metadata
fi

exit 0

bin/release

作为外部服务的链码,bin/release脚本负责将 connection.json放置在RELEASE_OUTPUT_DIR以提供给peer。connection.json 文件包含下列JSON结构

  • address - 可供peer访问的链码服务端点. 必须写为 “:” 格式.
  • dial_timeout - 等待连接完成的间隔。指定为带有时间单位的字符串(例如,“10s”、“500ms”、“1m”)。如果未指定,默认值为“3s”。
  • tls_required - true 或 false. 如果为 false, “client_auth_required”, “client_key”, “client_cert” 和 “root_cert” 则不需要填写. 默认 “true”.
  • client_auth_required - 如果为 true, “client_key” and “client_cert” 需要填写. 默认 false. 它将忽略 tls_required 为 false.
  • client_key - 客户端的私钥PEM 加密字串。
  • client_cert - 客户端的PEM加密证书字串。
  • root_cert - 服务器(peer)根节点证书的PEM加密字串。

例如:

{
  "address": "your.chaincode.host.com:9999",
  "dial_timeout": "10s",
  "tls_required": "true",
  "client_auth_required": "true",
  "client_key": "-----BEGIN EC PRIVATE KEY----- ... -----END EC PRIVATE KEY-----",
  "client_cert": "-----BEGIN CERTIFICATE----- ... -----END CERTIFICATE-----",
  "root_cert": "-----BEGIN CERTIFICATE---- ... -----END CERTIFICATE-----"
}

bin/build章节提示的,本示例假设链码包的connection.json文件直接复制到BUILD_OUTPUT_DIR。节点使用两个参数调用发布脚本:

bin/release BUILD_OUTPUT_DIR RELEASE_OUTPUT_DIR

bin/release示例脚本包含:

#!/bin/bash

set -euo pipefail

BLD="$1"
RELEASE="$2"

if [ -d "$BLD/metadata" ]; then
   cp -a "$BLD/metadata/"* "$RELEASE/"
fi

#external chaincodes expect artifacts to be placed under "$RELEASE"/chaincode/server
if [ -f $BLD/connection.json ]; then
   mkdir -p "$RELEASE"/chaincode/server
   cp $BLD/connection.json "$RELEASE"/chaincode/server

   #if tls_required is true, copy TLS files (using above example, the fully qualified path for these fils would be "$RELEASE"/chaincode/server/tls)

   exit 0
fi

exit 1

编写作为外部服务的链码

当前,外部服务模式的链码仅支持 GO chaincode shim.在Fabric v2.0中,GO shim API添加了ChaincodeServer 类型使开发者可以使用它来创建一个链码服务。Invoke and Query APIs 不受影响。开发者应将其写入shim.ChaincodeServer API,然后构建链码并运行在选择的外部环境中。下面是一个简单的链码程序示例来说明这个模式:

package main

import (
        "fmt"

        "github.com/hyperledger/fabric-chaincode-go/shim"
        pb "github.com/hyperledger/fabric-protos-go/peer"
)

// SimpleChaincode example simple Chaincode implementation
type SimpleChaincode struct {
}

func (s *SimpleChaincode) Init(stub shim.ChaincodeStubInterface) pb.Response {
        // init code
}

func (s *SimpleChaincode) Invoke(stub shim.ChaincodeStubInterface) pb.Response {
        // invoke code
}

//NOTE - parameters such as ccid and endpoint information are hard coded here for illustration. This can be passed in in a variety of standard ways
func main() {
       //The ccid is assigned to the chaincode on install (using the “peer lifecycle chaincode install <package>” command) for instance
        ccid := "mycc:fcbf8724572d42e859a7dd9a7cd8e2efb84058292017df6e3d89178b64e6c831"

        server := &shim.ChaincodeServer{
                        CCID: ccid,
                        Address: "myhost:9999"
                        CC: new(SimpleChaincode),
                        TLSProps: shim.TLSProperties{
                                Disabled: true,
                        },
                }
        err := server.Start()
        if err != nil {
                fmt.Printf("Error starting Simple chaincode: %s", err)
        }
}

使用shim.ChaincodeServer是将链码作为外部服务的关键。新的shim API shim.ChaincodeServer服务配置参数的作用如下:

  • CCID (string)- CCID 应匹配peer节点上链码的包名.这是CCID关联链码并使用 peer lifecycle chaincode install <package> 命令获取返回值。这可以在安装后使用peer lifecycle chaincode queryinstalled命令获得。
  • Address (string) - 是链码服务的监听地址
  • CC (Chaincode) - CC 是初始化(Init)和调用(Invoke)的链码。
  • TLSProps (TLSProperties) - TLSProps 是发送给链码服务的TLS属性
  • KaOpts (keepalive.ServerParameters) - KaOpts keepalive 属性, 如果为空,则提供合理的默认值

然后构建适合您的GO环境的链码

部署链码

当GO链码可以被部署时,你可以如打包链码 章节描述的一样打包,并按照Fabric 链码生命周期 中说明的进行部署。

将链码作为外部服务运行

编写外部服务链码章节描述的创建链码。在您选择的环境中运行构建可执行文件,如Kubernetes或直接在 peer 机器上作为一个进程运行。

以外部服务模式使用链码便不需要再每个节点上安装链码。当链码端点部署并替换运行后,你依然能执行提交通道上定义的链码和调用链码。