github.com/yous1230/fabric@v2.0.0-beta.0.20191224111736-74345bee6ac2+incompatible/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. And 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  ## Packaging chaincode
    17  
    18  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:
    19  
    20  ```sh
    21  $ tar xvfz myccpackage.tgz
    22  code.tar.gz
    23  metadata.json
    24  ```
    25  
    26  ### code.tar.gz archive requirements
    27  
    28  The `code.tar.gz` archive must include connection information for the chaincode endpoint. This information is packaged into `connection.json` in the `/bin/release` step (see below). In this example we package `connection.json` directly in `code.tar.gz` so the `release` step can just copy it over.
    29  
    30  * **address** - chaincode server endpoint accessible from peer. Must be specified in “<host>:<port>” format.
    31  * **dial_timeout** - interval to wait for connection to complete. Specified as a string qualified with units (examples : "10s", "500ms", "1m"). Default is “3s” if not specified.
    32  * **tls_required** - true or false. If false "client_auth_required", "key_path", "cert_path", "root_cert_path" are not required. Default is “true”.
    33  * **client_auth_required** - if true then you need to specify "key_path" and "cert_path" for client authentication. Default is false. Ignored if tls_required is false.
    34  * **key_path** - path to the key file. This path is relative to the “release directory” (see “release” below). Required if client_auth_required is true. Ignored if tls_required is false.
    35  * **cert_path**  - path to the certificate file. This path is relative to the “release directory” (see “release” below). Required if client_auth_required is true. Ignored if tls_required is false.
    36  * **root_cert_path**  - path to the root cert for authenticating the server. Required when tls_required is set to true.
    37  
    38  For example:
    39  ```json
    40  {
    41    "address": "your.chaincode.host.com:9999",
    42    "dial_timeout": "10s",
    43    "tls_required": true,
    44    "client_auth_required": "true",
    45    "key_path": "path/rooted/in/release/directory/key.pem",
    46    "cert_path": "path/rooted/in/release/directory/cert.pem",
    47    "root_cert_path": "path/rooted/in/release/directory/rootcert.pem"
    48  }
    49  ```
    50  
    51  **Note:** The TLS files can be placed anywhere in the `code.tar.gz` archive because the directory of the contents of the `.tar.gz` will be provided to the external chaincode builder scripts. The `/bin/release` script, described later in this document, should move the files to the appropriate paths relative to the release directory. In the future, the PEM may be included in the JSON itself.
    52  
    53  ### metadata.json requirements
    54  
    55  When using chaincode as an external service, you can set the `type` field in `metadata.json` to `external` in order to indicate that the external service is being used. For example:
    56  
    57  ```json
    58  {"path":"","type":"external","label":"mycc"}
    59  ```
    60  
    61  ## Configuring a peer to process external chaincode
    62  
    63  If you have reviewed the external build and chaincode launcher documentation then these steps will be familiar to you. We leverage those scripts to define the external chaincode service information. These scripts are located on the peer file system that is accessible to the peer process from the `core.yaml` file in the `externalBuilders` element under the `chaincode` stanza. An example is included in the steps which follow.
    64  
    65  ### Create the set of external builder and launcher scripts on the peer
    66  
    67  In order to configure chaincode as an external service, you must use the scripts as follows:
    68  * **detect** - examines the chaincode package and accept if metadata.json `type` is set to `external`.
    69  * **build** - builds the chaincode and places the build artifacts in the `BUILD_OUTPUT_DIR`. For chaincode as an external service, the peer does not build the chaincode. Instead, the script extracts the chaincode endpoint information in the `connection.json` file and other artifacts from the `code.tar.gz` and places them in the specified location.
    70  * **release** - copies the built artifacts (in our case the `connection.json` file) to a specified release location.
    71  
    72  You may notice that the external builder and launcher `bin/run` script is not required for the chaincode as an external service.
    73  
    74  The scripts that are required to exist in the peer `/bin` directory:
    75  ```
    76      <fully qualified path on the peer's env>
    77      └── bin
    78          ├── build
    79          ├── detect
    80          └── release
    81  ```
    82  
    83  ### Modify the peer core.yaml to include the externalBuilder
    84  
    85  Finally, in order for the peer to use the external builder and launcher scripts, you need to modify the `chaincode` stanza of the peer `core.yaml` file to include the `externalBuilders` configuration element.
    86  
    87  ```yaml
    88  externalBuilders:
    89       - name: myexternal
    90         path: <fully qualified path on the peer's env>   
    91  ```
    92  
    93  ## External builder and launcher sample scripts
    94  
    95  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.
    96  
    97  **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 yourself.
    98  
    99  ### /bin/detect
   100  
   101  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 script should detect that metadata.json `type` is set to `external`.  The peer invokes detect with two arguments:
   102  
   103  ```
   104  bin/detect CHAINCODE_SOURCE_DIR CHAINCODE_METADATA_DIR
   105  ```
   106  
   107  A typical `detect` script could contain:
   108  
   109  ```sh
   110  
   111  #!/bin/bash
   112  
   113  set -euo pipefail
   114  
   115  if [ "$#" -ne 2 ]; then
   116      >&2 echo "Expected 2 directories got $#"
   117      exit 2
   118  fi
   119  
   120  #check if the "type" field is set to "external"
   121  if [ "$(jq -r .type "$2/metadata.json")" == "external" ]; then
   122      exit 0
   123  fi
   124  
   125  exit 1
   126  ```
   127  Recall that the metadata.json file should contain the following keys:
   128  
   129  ```json
   130  {"path":"","type":"external","label":"mycc"}
   131  ```
   132  
   133  ### /bin/build
   134  
   135  The `bin/build` script is responsible for building, compiling, or transforming the contents of a chaincode package into artifacts that can be used by release and run. For chaincode as an external service, the build script copies the `connection.json` to the `BUILD_OUTPUT_DIR`. The peer invokes the build script with three arguments:
   136  
   137  ```
   138  bin/build CHAINCODE_SOURCE_DIR CHAINCODE_METADATA_DIR BUILD_OUTPUT_DIR
   139  ```
   140  
   141  A typical `build` script could contain:
   142  
   143  ```sh
   144  
   145  #!/bin/bash
   146  
   147  set -euo pipefail
   148  
   149  if [ "$#" -ne 3 ]; then
   150      >&2 echo "Expected 3 directories got $#"
   151      exit 1
   152  fi
   153  
   154  SOURCE=$1
   155  OUTPUT=$3
   156  
   157  #external chaincodes expect connection.json file in the chaincode package
   158  if [ ! -f "$SOURCE/connection.json" ]  ; then
   159      >&2 echo "$SOURCE/connection.json not found"
   160      exit 1
   161  fi
   162  
   163  #do more validation here if needed
   164  
   165  #simply copy the endpoint information to specified output location
   166  cp $SOURCE/connection.json $OUTPUT/connection.json
   167  
   168  exit 0
   169  
   170  ```
   171  
   172  ### /bin/release
   173  
   174  The `bin/release script` is responsible for providing metadata chaincode to the peer. 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 peer invokes the release script with two arguments:
   175  
   176  ```
   177  bin/release BUILD_OUTPUT_DIR RELEASE_OUTPUT_DIR
   178  ```
   179  
   180  A typical `release` script could contain:
   181  
   182  ```sh
   183  
   184  #!/bin/bash
   185  
   186  set -euo pipefail
   187  
   188  set -x
   189  
   190  if [ "$#" -ne 2 ]; then
   191      >&2 echo "Expected 2 directories got $#"
   192      exit 2
   193  fi
   194  
   195  BLD="$1"
   196  RELEASE="$2"
   197  
   198  #external chaincodes expect artifacts to be placed under "$RELEASE"/chaincode/server
   199  if [ -f $BLD/connection.json ]; then
   200     mkdir -p "$RELEASE"/chaincode/server
   201     cp $BLD/connection.json "$RELEASE"/chaincode/server
   202     exit 0
   203  fi
   204  
   205  exit 1
   206  ```    
   207  
   208  ## Writing chaincode to run as an external service
   209  
   210  Currently, the chaincode as an external service model is only supported by GO chaincode shim. In Fabric v2.0, the GO shim API adds 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:
   211  
   212  ```go
   213  
   214  package main
   215  
   216  import (
   217          "fmt"
   218  
   219          "github.com/hyperledger/fabric-chaincode-go/shim"
   220          pb "github.com/hyperledger/fabric-protos-go/peer"
   221  )
   222  
   223  // SimpleChaincode example simple Chaincode implementation
   224  type SimpleChaincode struct {
   225  }
   226  
   227  func (s *SimpleChaincode) Init(stub shim.ChaincodeStubInterface) pb.Response {
   228          // init code
   229  }
   230  
   231  func (s *SimpleChaincode) Invoke(stub shim.ChaincodeStubInterface) pb.Response {
   232          // invoke code
   233  }
   234  
   235  //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
   236  func main() {
   237         //The ccid is assigned to the chaincode on install (using the “peer lifecycle chaincode install <package>” command) for instance
   238          ccid := "mycc:fcbf8724572d42e859a7dd9a7cd8e2efb84058292017df6e3d89178b64e6c831"
   239  
   240          server := &shim.ChaincodeServer{
   241                          CCID: ccid,
   242                          Address: "myhost:9999"
   243                          CC: new(SimpleChaincode),
   244                          TLSProps: shim.TLSProperties{
   245                                  Disabled: true,
   246                          },
   247                  }
   248          err := server.Start()
   249          if err != nil {
   250                  fmt.Printf("Error starting Simple chaincode: %s", err)
   251          }
   252  }
   253  ```
   254  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:
   255  
   256  * **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.
   257  * **Address** (string) - Address is the listen address of the chaincode server
   258  * **CC** (Chaincode) -  CC is the chaincode that handles Init and Invoke
   259  * **TLSProps** (TLSProperties) - TLSProps is the TLS properties passed to chaincode server
   260  * **KaOpts** (keepalive.ServerParameters) -  KaOpts keepalive options, sensible defaults provided if nil
   261  
   262  Then build the chaincode as suitable to your GO environment.
   263  
   264  ## Deploying the chaincode
   265  
   266  When the GO 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 [chaincode lifecycle](./chaincode4noah.html#chaincode-lifecycle) documentation.
   267  
   268  ## Running the chaincode as an external service
   269  
   270  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.
   271  
   272  Using this chaincode as an external service model, installing chaincode on each peer is no longer required. With the chaincode endpoint deployed to the peer instead and the chaincode running, you can continue to instantiate and invoke chaincode normally.
   273  
   274  <!---
   275  Licensed under Creative Commons Attribution 4.0 International License https://creativecommons.org/licenses/by/4.0/
   276  -->