github.com/hechain20/hechain@v0.0.0-20220316014945-b544036ba106/docs/source/cc_service.md (about) 1 # Chaincode as an external service 2 3 Fabric v2.0 supports chaincode deployment and execution outside of Fabric that enables users to manage a chaincode runtime independently of the peer. This facilitates deployment of chaincode on Fabric cloud deployments such as Kubernetes. Instead of building and launching the chaincode on every peer, chaincode can now run as a service whose lifecycle is managed outside of Fabric. This capability leverages the Fabric v2.0 external builder and launcher functionality which enables operators to extend a peer with programs to build, launch, and discover chaincode. Before reading this topic you should become familiar with the [External Builder and Launcher](./cc_launcher.html) content. 4 5 Prior to the availability of the external builders, the chaincode package content was required to be a set of source code files for a particular language which could be built and launched as a chaincode binary. The new external build and launcher functionality now allows users to optionally customize the build process. With respect to running the chaincode as an external service, the build process allows you to specify the endpoint information of the server where the chaincode is running. Hence the package simply consists of the externally running chaincode server endpoint information and TLS artifacts for secure connection. TLS is optional but highly recommended for all environments except a simple test environment. 6 7 The rest of this topic describes how to configure chaincode as an external service: 8 9 * [Packaging chaincode](#packaging-chaincode) 10 * [Configuring a peer to process external chaincode](#configuring-a-peer-to-process-external-chaincode) 11 * [External builder and launcher sample scripts](#external-builder-and-launcher-sample-scripts) 12 * [Writing chaincode to run as an external service](#writing-chaincode-to-run-as-an-external-service) 13 * [Deploying the chaincode](#deploying-the-chaincode) 14 * [Running the chaincode as an external service](#running-the-chaincode-as-an-external-service) 15 16 **Note:** This is an advanced feature that will likely require custom packaging of the peer image. For example, the following samples use `jq` and `bash`, which are not included in the current official `fabric-peer` image. 17 18 ## Packaging chaincode 19 20 With the Fabric v2.0 chaincode lifecycle, chaincode is [packaged](./cc_launcher.html#chaincode-packages) and installed in a `.tar.gz` format. The following `myccpackage.tgz` archive demonstrates the required structure: 21 22 ```sh 23 $ tar xvfz myccpackage.tgz 24 metadata.json 25 code.tar.gz 26 ``` 27 28 The chaincode package should be used to provide two pieces of information to the external builder and launcher process 29 * identify if the chaincode is an external service. The `bin/detect` section describes an approach using the `metadata.json` file 30 * provide chaincode endpoint information in a `connection.json` file placed in the release directory. The `bin/run` section describes the `connection.json` file 31 32 There is plenty of flexibility to gathering the above information. The sample scripts in the [External builder and launcher sample scripts](#external-builder-and-launcher-sample-scripts) illustrate a simple approach to providing the information. 33 As an example of flexibility, consider packaging couchdb index files (see [Add the index to your chaincode folder](couchdb_tutorial.html#add-the-index-to-your-chaincode-folder)). Sample scripts below describe an approach to packaging the files into myccpackage.tar.gz. 34 35 ``` 36 tar cfz code.tar.gz connection.json metadata 37 tar cfz myccpackage.tgz metadata.json code.tar.gz 38 ``` 39 40 ## Configuring a peer to process external chaincode 41 42 In this section we go over the configuration needed 43 * to detect if the chaincode package identifies an external chaincode service 44 * to create the `connection.json` file in the release directory 45 46 ### Modify the peer core.yaml to include the externalBuilder 47 48 Assume the scripts are on the peer in the `bin` directory as follows 49 ``` 50 <fully qualified path on the peer's env> 51 └── bin 52 ├── build 53 ├── detect 54 └── release 55 ``` 56 57 Modify the `chaincode` stanza of the peer `core.yaml` file to include the `externalBuilders` configuration element: 58 59 ```yaml 60 externalBuilders: 61 - name: myexternal 62 path: <fully qualified path on the peer's env> 63 ``` 64 65 ### External builder and launcher sample scripts 66 67 To help understand what each script needs to contain to work with the chaincode as an external service, this section contains samples of `bin/detect` `bin/build`, `bin/release`, and `bin/run` scripts. 68 69 **Note:** These samples use the `jq` command to parse json. You can run `jq --version` to check if you have it installed. Otherwise, install `jq` or suitably modify the scripts. 70 71 #### bin/detect 72 73 The `bin/detect script` is responsible for determining whether or not a buildpack should be used to build a chaincode package and launch it. For chaincode as an external service, the sample script looks for a `type` property set to `external` in the `metadata.json` file: 74 75 ```json 76 {"path":"","type":"external","label":"mycc"} 77 ``` 78 79 The peer invokes detect with two arguments: 80 81 ``` 82 bin/detect CHAINCODE_SOURCE_DIR CHAINCODE_METADATA_DIR 83 ``` 84 85 A sample `bin/detect` script could contain: 86 87 ```sh 88 89 #!/bin/bash 90 91 set -euo pipefail 92 93 METADIR=$2 94 #check if the "type" field is set to "external" 95 if [ "$(jq -r .type "$METADIR/metadata.json")" == "external" ]; then 96 exit 0 97 fi 98 99 exit 1 100 101 ``` 102 103 #### bin/build 104 105 For chaincode as an external service, the sample build script assumes the chaincode package's `code.tar.gz` file contains `connection.json` which it simply copies to the `BUILD_OUTPUT_DIR`. The peer invokes the build script with three arguments: 106 107 ``` 108 bin/build CHAINCODE_SOURCE_DIR CHAINCODE_METADATA_DIR BUILD_OUTPUT_DIR 109 ``` 110 111 A sample `bin/build` script could contain: 112 113 ```sh 114 115 #!/bin/bash 116 117 set -euo pipefail 118 119 SOURCE=$1 120 OUTPUT=$3 121 122 #external chaincodes expect connection.json file in the chaincode package 123 if [ ! -f "$SOURCE/connection.json" ]; then 124 >&2 echo "$SOURCE/connection.json not found" 125 exit 1 126 fi 127 128 #simply copy the endpoint information to specified output location 129 cp $SOURCE/connection.json $OUTPUT/connection.json 130 131 if [ -d "$SOURCE/metadata" ]; then 132 cp -a $SOURCE/metadata $OUTPUT/metadata 133 fi 134 135 exit 0 136 137 ``` 138 139 #### bin/release 140 141 For chaincode as an external service, the `bin/release` script is responsible for providing the `connection.json` to the peer by placing it in the `RELEASE_OUTPUT_DIR`. The `connection.json` file has the following JSON structure 142 143 * **address** - chaincode server endpoint accessible from peer. Must be specified in “<host>:<port>” format. 144 * **dial_timeout** - interval to wait for connection to complete. Specified as a string qualified with time units (e.g, "10s", "500ms", "1m"). Default is “3s” if not specified. 145 * **tls_required** - true or false. If false, "client_auth_required", "client_key", "client_cert", and "root_cert" are not required. Default is “true”. 146 * **client_auth_required** - if true, "client_key" and "client_cert" are required. Default is false. It is ignored if tls_required is false. 147 * **client_key** - PEM encoded string of the client private key. 148 * **client_cert** - PEM encoded string of the client certificate. 149 * **root_cert** - PEM encoded string of the server (peer) root certificate. 150 151 For example: 152 153 ```json 154 { 155 "address": "your.chaincode.host.com:9999", 156 "dial_timeout": "10s", 157 "tls_required": "true", 158 "client_auth_required": "true", 159 "client_key": "-----BEGIN EC PRIVATE KEY----- ... -----END EC PRIVATE KEY-----", 160 "client_cert": "-----BEGIN CERTIFICATE----- ... -----END CERTIFICATE-----", 161 "root_cert": "-----BEGIN CERTIFICATE---- ... -----END CERTIFICATE-----" 162 } 163 ``` 164 165 As noted in the `bin/build` section, this sample assumes the chaincode package directly contains the `connection.json` file which the build script copies to the `BUILD_OUTPUT_DIR`. The peer invokes the release script with two arguments: 166 167 ``` 168 bin/release BUILD_OUTPUT_DIR RELEASE_OUTPUT_DIR 169 ``` 170 171 A sample `bin/release` script could contain: 172 173 174 ```sh 175 176 #!/bin/bash 177 178 set -euo pipefail 179 180 BLD="$1" 181 RELEASE="$2" 182 183 if [ -d "$BLD/metadata" ]; then 184 cp -a "$BLD/metadata/"* "$RELEASE/" 185 fi 186 187 #external chaincodes expect artifacts to be placed under "$RELEASE"/chaincode/server 188 if [ -f $BLD/connection.json ]; then 189 mkdir -p "$RELEASE"/chaincode/server 190 cp $BLD/connection.json "$RELEASE"/chaincode/server 191 192 #if tls_required is true, copy TLS files (using above example, the fully qualified path for these fils would be "$RELEASE"/chaincode/server/tls) 193 194 exit 0 195 fi 196 197 exit 1 198 ``` 199 200 ## Writing chaincode to run as an external service 201 202 Currently, the chaincode as an external service model is supported by Go chaincode shim and Node.js chaincode shim. 203 204 ### Go 205 206 In Fabric v2.0, the Go shim API provides a `ChaincodeServer` type that developers should use to create a chaincode server. The `Invoke` and `Query` APIs are unaffected. Developers should write to the `shim.ChaincodeServer` API, then build the chaincode and run it in the external environment of choice. Here is a simple sample chaincode program to illustrate the pattern: 207 208 ```go 209 210 package main 211 212 import ( 213 "fmt" 214 215 "github.com/hyperledger/fabric-chaincode-go/shim" 216 pb "github.com/hyperledger/fabric-protos-go/peer" 217 ) 218 219 // SimpleChaincode example simple Chaincode implementation 220 type SimpleChaincode struct { 221 } 222 223 func (s *SimpleChaincode) Init(stub shim.ChaincodeStubInterface) pb.Response { 224 // init code 225 } 226 227 func (s *SimpleChaincode) Invoke(stub shim.ChaincodeStubInterface) pb.Response { 228 // invoke code 229 } 230 231 //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 232 func main() { 233 //The ccid is assigned to the chaincode on install (using the “peer lifecycle chaincode install <package>” command) for instance 234 ccid := "mycc:fcbf8724572d42e859a7dd9a7cd8e2efb84058292017df6e3d89178b64e6c831" 235 236 server := &shim.ChaincodeServer{ 237 CCID: ccid, 238 Address: "myhost:9999" 239 CC: new(SimpleChaincode), 240 TLSProps: shim.TLSProperties{ 241 Disabled: true, 242 }, 243 } 244 err := server.Start() 245 if err != nil { 246 fmt.Printf("Error starting Simple chaincode: %s", err) 247 } 248 } 249 ``` 250 The key to running the chaincode as an external service is the use of `shim.ChaincodeServer`. This uses the new shim API `shim.ChaincodeServer` with the chaincode service properties described below: 251 252 * **CCID** (string)- CCID should match chaincode's package name on peer. This is the `CCID` associated with the installed chaincode as returned by the `peer lifecycle chaincode install <package>` CLI command. This can be obtained post-install using the "peer lifecycle chaincode queryinstalled" command. 253 * **Address** (string) - Address is the listen address of the chaincode server 254 * **CC** (Chaincode) - CC is the chaincode that handles Init and Invoke 255 * **TLSProps** (TLSProperties) - TLSProps is the TLS properties passed to chaincode server 256 * **KaOpts** (keepalive.ServerParameters) - KaOpts keepalive options, sensible defaults provided if nil 257 258 Then build the chaincode as suitable to your Go environment. 259 260 ### Node.js 261 262 `fabric-shim` package for Node.js chaincode provides the `shim.server` API to run chaincode as an external service. If you are using contract APIs, you may want to use the `server` command provided by `fabric-chaincode-node` CLI to run a contract in the external service mode. 263 264 The following is a sample chaincode using `fabric-shim`: 265 ```javascript 266 const shim = require('fabric-shim'); 267 268 class SimpleChaincode extends shim.ChaincodeInterface { 269 async Init(stub) { 270 // ... Init code 271 } 272 async Invoke(stub) { 273 // ... Invoke code 274 } 275 } 276 277 const server = shim.server(new SimpleChaincode(), { 278 ccid: "mycc:fcbf8724572d42e859a7dd9a7cd8e2efb84058292017df6e3d89178b64e6c831", 279 address: "0.0.0.0:9999" 280 }); 281 282 server.start(); 283 ``` 284 285 To run a chaincode with the `fabric-contract` API as an external service, simply use `fabric-chaincode-node server` instead of `fabric-chaincode-node start`. Here is a sample for `package.json`: 286 ```javascript 287 { 288 "scripts": { 289 "start": "fabric-chaincode-node server" 290 }, 291 ... 292 } 293 ``` 294 295 When `fabric-chaincode-node server` is used, the following options should be set as either arguments or environment variables: 296 * **CORE_CHAINCODE_ID (--chaincode-id)**: See **CCID** in the Go chaincode above. 297 * **CORE_CHAINCODE_ADDRESS (--chaincode-address)**: See **Address** in the Go chaincode above. 298 299 If TLS is enabled, the following additional options are required: 300 * **CORE_CHAINCODE_TLS_CERT_FILE (--chaincode-tls-cert-file)**: path to a certificate 301 * **CORE_CHAINCODE_TLS_KEY_FILE (--chaincode-tls-key-file)**: path to a private key 302 303 When mutual TLS is enabled, **CORE_CHAINCODE_TLS_CLIENT_CACERT_FILE (--chaincode-tls-client-cacert-file)** option should be set to specify the path to the CA certificate for acceptable client certificates. 304 305 ## Deploying the chaincode 306 307 When the chaincode is ready for deployment, you can package the chaincode as explained in the [Packaging chaincode](#packaging-chaincode) section and deploy the chaincode as explained in the [Fabric chaincode lifecycle](./chaincode_lifecycle.html) concept topic. 308 309 ## Running the chaincode as an external service 310 311 Create the chaincode as specified in the [Writing chaincode to run as an external service](#writing-chaincode-to-run-as-an-external-service) section. Run the built executable in your environment of choice, such as Kubernetes or directly as a process on the peer machine. 312 313 Using this chaincode as an external service model, installing the chaincode on each peer is no longer required. With the chaincode endpoint deployed to the peer instead and the chaincode running, you can continue the normal process of committing the 314 chaincode definition to the channel and invoking the chaincode. 315 316 <!--- 317 Licensed under Creative Commons Attribution 4.0 International License https://creativecommons.org/licenses/by/4.0/ 318 -->