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  -->