Tutorial: Hyperledger Fabric v1.1 – Create a Development Business Network on zLinux

Petr Vlasek edited this page Sep 7, 2018 · 11 revisions

This article is a tutorial that guides you how to create a HyperLedger Fabric v1.1.0-preview business network on zLinux using the development tools that are found in the Hyperledge Fabric repository.

We will go through the process of setting up the Hyperledger Fabric prerequisites and later on we define and start Hyperledger Fabric blockchain network between three organizations.

Our network scenario described below is based on the “First-network” Hyperledger Fabric tutorial and samples published under official Hyperledger Fabric documentation (http://hyperledger-fabric.readthedocs.io/en/latest/build_network.html).

:bulb: Note: This article is not intended to be an example of how to create a production business network.

Contents

Recommended Reading

Before you start this tutorial, you may want to get familiar with the basic concepts of Hyperledger Fabric. Official Hyperledger Fabric documentation provides comprehensive source of information related to Hyperledger Fabric configuration, modes of operation and prerequisities. We recommend to read the following articles and use them as the reference when going through this tutorial.

Hyperledger Fabric Glossary - http://hyperledger-fabric.readthedocs.io/en/latest/glossary.html
Hyperledger Fabric Model - http://hyperledger-fabric.readthedocs.io/en/latest/fabric_model.html
Hyperledger Fabric Prerequisities - http://hyperledger-fabric.readthedocs.io/en/latest/prereqs.html
Hyperledger Fabric Samples - http://hyperledger-fabric.readthedocs.io/en/latest/samples.html
Building Your First Network - http://hyperledger-fabric.readthedocs.io/en/latest/build_network.html

Also, check our References section with various useful links related to the content of this tutorial.

Setup Your Environment

The following paragraphs describe how to set up Hyperledger Fabric dependencies on zLinux systems.

We recognize five key dependencies and packages required for Hyperledger Fabric to work on zLinux systems. Please note that availability of these dependencies in particular versions required by Hyperledger Fabric may vary between various zLinux distributions.

  • Utility packages
  • Node.js and NPM
  • Docker
  • Docker Compose
  • Go language

Utility Packages

You use the package manager to install the Hyperledger Fabric dependencies and any other programs that you feel might help you develop your projects.

  • Red Hat: yum is the name of the package manager and the command that you use to interact with the package manager on Red Hat systems.
  • Ubuntu: apt is the name of the package manager and the command that you use to interact with the package manager on Ubuntu systems.

In this topic, we use yum in the sample commands. However, on Ubuntu systems, you can replace yum with apt and the Ubuntu commands should behave in the same manner as the Red Hat commands - given that the same packages are available.

:bulb: Note: Some packages might be named differently in apt. As a result, you might need to locate the equivalent packages.

Issue the following command to install the dependencies and utility programs:

sudo yum install tar wget gcc java nano git

:bulb: Tip: The Nano package installs a text editing application that you can use from a command line terminal, which lets you easily edit text files during an ssh session.

Nodej.js and NPM

Node.js is a JavaScript-based run-time environment and its installer include NPM which is a package manager for JavaScript language and its tools and frameworks.

:warning: Note: Hyperledger Fabric v1.1.0-preview requires Node.js 6.9.x version or greater. Node.js version 7.x is not supported at this time.

You can download the zLinux installers for NodeJS and NPM from IBM at the following website: https://developer.ibm.com/node/sdk/v6/. Download and install the installation package that is named Linux on System z 64-bit.

:warning: Note: IBM Node.js installer requires Java Runtime Environment to be installed on your system.

Follow these steps:

  1. Download the installer to your computer.
  2. Mark the file as executable by issuing the following command:
chmod +x <the_ibm_installer_filename>
  1. Execute the files using the following command:
./<the ibm installer filename>
  1. After you execute the install command, the installer starts. Follow the onscreen prompts until you are prompted to specify an installation path. Select a location to install Node.js and NPM, such as /opt/ibm/node, and enter the path into the box. You can accept the remaining default options.
  2. After the installer completes the installation process, add the following line to /etc/profile:
export PATH=$PATH:<your_nodejs_path_e.g._/opt/ibm/node>/bin

Docker

Docker is a tool for deploying, executing, and managing containers. Hyperledger Fabric is by default packaged as a set of Docker images and it is ready to be run as Docker containers.

:warning: Note: Hyperledger Fabric v1.1.0-preview requires Docker version 17.06.2-ce or greater.

For information about where to download the installation media and how to install Docker, see Docker on the IBM developerWorks website: https://www.ibm.com/developerworks/linux/linux390/docker.html.

:bulb: Note: if you are going to install Docker package for Red Hat, we observe that the provided FTP link is not functioning properly. This direct link opened in browser will navigate you to the root of Red Hat packages: http://ftp.unicamp.br/pub/linuxpatch/s390x/redhat/.

:bulb: Note: rhel7.3 Docker package is fully working on Red Hat Enterprise Linux 7.2.

If installing Docker manually, simply copy the content of the archive to location of your choice (e.g. /usr/local/). In addition, add this location to your PATH, i.e. modify the export PATH line in your /etc/profile (note that the following example contains Node.js path added in previous step):

export PATH=$PATH:<your_nodejs_path_e.g._/opt/ibm/node>/bin:/usr/local/docker

For your convenience you may add dockerd daemon process to the list of automatically started services.

Docker Compose

Docker Compose is a tool for defining and running multi-container Docker applications. This is the case of Hyperledger Fabric default setup.

Docker Compose is typically installed as a part of your Docker installation. If not, it is necessary to install it separately. Run docker-compose --version command to find out if Docker Compose is present on your system.

:warning: Note: Hyperledger Fabric v1.1.0-preview requires Docker Compose version 1.14.0 or higher.

To install Docker Compose on your zLinux system, please follow the instructions described here: https://github.com/linux-on-ibm-z/docs/wiki/Building-Docker-Compose.

Go language

You use the Google Go language as the basis of chaincode in Hyperledger Fabric.

:warning: Note: Hyperledger Fabric v1.1.0-preview requires Go version 1.9.x.

For information about where to download the installation media and how to install the Google Go language, follow the steps described here: https://github.com/linux-on-ibm-z/docs/wiki/Building-Go.

In addition, add the Go location to your PATH, i.e. modify the export PATH line in your /etc/profile (note that the following listing contains additions done in previous steps):

export PATH=$PATH:<your_nodejs_path_e.g._/opt/ibm/node>/bin:/usr/local/docker:/usr/local/go/bin

Retrieve Artifacts from Hyperledger Fabric Repositories

In the previous chapter we have installed and configured all dependencies required by Hyperledger Fabric. Before we will begin to setup our Hyperledger Fabric blockchain network, we need to connect to Hyperledger Fabric Repositories and retrieve artifacts from there.

:bulb: Note: The command examples in this tutorial were ran on Red Hat Enterprise Linux Server 7.2.

First of all, we will clone fabric-samples repository. This will help us to use configuration used in the official Hyperledger Fabric example project as the base for our own configuration.

Navigate to the location of your choice on your filesystem and open it from terminal. Then run the following commands to clone remote Git repository:

git clone -b master https://github.com/hyperledger/fabric-samples.git
cd fabric-samples

Clone fabric-samples repository

As a next step, we are going to download and install Hyperledger Fabric binaries specific to your platform. This includes downloading of cryptogen, configtxgen, configtxlator and peer tools and placing them into bin directory in the directory of your choice. In addition, the script will download Hyperledger Docker images into your local Docker registry.

Execute the following command to download Hyperledger Fabric binaries and Docker images:

curl -sSL https://goo.gl/6wtTN5 | bash

:bulb: Note: The link above is pointing to Fabric v1.2 assets. The original link to v.1.1 assets (see below) is no longer working.

curl -sSL https://goo.gl/fMh2s3 | bash

Download fabric binaries

This might take some time, but after the docker pull process completes, you should see the following Docker images:

List of Fabric Docker images

For your convenience, you can add the directory with Hyperledger Fabric binaries to your PATH environment variable. You can modify the export PATH line in your /etc/profile (note that the following listing contains additions done in the previous steps):

export PATH=$PATH:opt/ibm/node/bin:/usr/local/docker:/usr/local/go/bin:<path_to_your_download_location>/bin

e.g.

export PATH=$PATH:/opt/ibm/node/bin:/usr/local/docker:/usr/local/go/bin:/data/fabric/fabric-samples/bin

For the purpose of this tutorial we are going to use basic-network directory. You can use any directory of your choice.

Create Hyperledger Fabric Business Network

This tutorial describes creating a business network and deploying chaincode using the Hyperledger Fabric v1.1.0-preview code base (found here: https://github.com/hyperledger/fabric.git). This tutorial exploits the Hyperledger Fabric v1.1.0-preview configuration toolset to create the business network consisting of the following items:

  • A single orderer
  • Three peers (each part of separate organization)
  • A single channel
  • Example chaincode

At the end of this tutorial, you will have constructed a running instance of Hyperledger Fabric business network, as well as deployed, instantiated, and executed chaincode. In addition, our network will have TLS enabled.

The tutorial describes the context of fictional "acme.com" company and its three organizations: Org1, Org2 and Org2.

For our tutorial we will make use of the Fabric deployment model based on Docker containers. Our network will run on Docker containers running on our target localhost.

Generate Peer and Orderer Certificates

Nodes (such as peers and orderers) are permitted to access business networks using a membership service provider, which is typically in the form of a certificate authority. In this example, we use the development tool named cryptogen to generate the required certificates. We use a local MSP to store the certs, which are essentially a local directory structure, for each peer and orderer. In production environments, you can exploit the fabric ca toolset introducing full-featured certificate authorities to generate the certificates. In addition our network implements TLS (transport layer security) when authenticating remote procedure calls (using grpc protocol for communication).

cryptogen tool uses a yaml configuration file as its configuration - based on the content of this file, the required certificates are generated. We are going to use crypto-config.yaml file from the fabric-samples\first-network directory as a base for our configuration. We are going to define three organizations for peers and single orderer organization.

Here is the listing of our crypto-config.yaml configuration file (for the purpose of simplicity, all comments are removed from this listing):

OrdererOrgs:
  - Name: Orderer
    Domain: acme.com
    Specs:
      - Hostname: orderer
PeerOrgs:
  - Name: Org1
    Domain: org1.acme.com
    Template:
      Count: 1
    Users:
      Count: 1
  - Name: Org2
    Domain: org2.acme.com
    Template:
      Count: 1
    Users:
      Count: 1
  - Name: Org3
    Domain: org3.acme.com
    Template:
      Count: 1
    Users:
      Count: 1

To generate certificates, run the following command:

cryptogen generate --config=./crypto-config.yaml

After running of cryptogen tool you should see the following output in console:

Run cryptogen tool

In addition, the new crypto-config directory has been created and contains various certificates and keys for orderer and peers. See the following screenshot that illustrate the content of this directory.

First, let's check content under ordererOrganizations: crypto-config directory content

In addition, here is the example of the content under peerOrganizations: crypto-config directory content

:bulb: Note: The crypto directories become the local MSP for each of the peers and orderers. This fact becomes clear when we populate the configuration and docker-compose yaml files.

Create channel.tx and the Genesis Block Using the configtxgen Tool

Now that we generated a certificates and keys, we can now configure the configtx.yaml file. This yaml file serves as input to the configtxgen tool and generates the following important artifacts:

  • channel.tx

The channel creation transaction. This transaction lets you create the Hyperledger Fabric channel. The channel is the location where the ledger exists and the mechanism that lets peers join business networks.

  • Genesis Block

The Genesis block is the first block in our blockchain. It is used to bootstrap the ordering service and holds the channel configuration.

  • Anchor peers transactions

The anchor peer transactions specify each Org’s Anchor Peer on this channel.

Creating/Modifying configtx.yaml

To customize this, you can copy the existing fabric-samples/first-network/configtx.yaml file or you can start a fresh copy. We recommend that you start with the existing configtx.yaml as it contains a template that requires only minor modifications for our needs.

The configtx.yaml file is broken into several sections:

Profile: Profiles describe the organizational structure of your network.

Organizations: The details regarding individual organizations.

Orderer: The details regarding the Orderer parameters.

Application: Application defaults – not needed for this tutorial.

:bulb: Note: This file describes the organizations and not necessarily the peers that exist in an organization. We specify the peers in the docker-compose.yaml file.

The following example illustrates a fully configured configtx.yaml file (for the purpose of simplicity, all comments are removed from this listing):

---
Profiles:

    ThreeOrgsOrdererGenesis:
        Orderer:
            <<: *OrdererDefaults
            Organizations:
                - *OrdererOrg
        Consortiums:
            SampleConsortium:
                Organizations:
                    - *Org1
                    - *Org2
                    - *Org3
    ThreeOrgsChannel:
        Consortium: SampleConsortium
        Application:
            <<: *ApplicationDefaults
            Organizations:
                - *Org1
                - *Org2
                - *Org3  
                              
Organizations:

    - &OrdererOrg
        Name: OrdererOrg

        ID: OrdererMSP

        MSPDir: crypto-config/ordererOrganizations/acme.com/msp

    - &Org1
        Name: Org1MSP

        ID: Org1MSP

        MSPDir: crypto-config/peerOrganizations/org1.acme.com/msp

        AnchorPeers:
            - Host: peer0.org1.acme.com
              Port: 7051

    - &Org2
        Name: Org2MSP

        ID: Org2MSP

        MSPDir: crypto-config/peerOrganizations/org2.acme.com/msp

        AnchorPeers:
            - Host: peer0.org2.acme.com
              Port: 7051

    - &Org3
        Name: Org3MSP

        ID: Org3MSP

        MSPDir: crypto-config/peerOrganizations/org3.acme.com/msp

        AnchorPeers:
            - Host: peer0.org3.acme.com
              Port: 7051

Orderer: &OrdererDefaults

    OrdererType: solo

    Addresses:
        - orderer.acme.com:7050

    BatchTimeout: 2s

    BatchSize:

        MaxMessageCount: 10

        AbsoluteMaxBytes: 99 MB

        PreferredMaxBytes: 512 KB

    Kafka:
        Brokers:
            - 127.0.0.1:9092

    Organizations:

Application: &ApplicationDefaults

    Organizations:

You can review the file or can modify it as necessary. However, the following items are key modifications:

  • The organizations that we specified in the profiles section are named exactly as we named them in the cryptogen tool and its crypto-config.yaml configuration file.
  • We modified the ID and Name fields to append MSP for the peers.
  • We modified the MSPDir to point to the output directories from the cryptogen tool.

Executing the configtxgen Tool

To create orderer genesis block, run the following commands

export FABRIC_CFG_PATH=$PWD
mkdir channel-artifacts
configtxgen -profile ThreeOrgsOrdererGenesis -outputBlock ./channel-artifacts/genesis.block

The output similar to the following will be displayed on console:

generate genesis block

After we created the orderer genesis block it is a time to create channel configuration transaction.

:bulb: Note: You can assign any name for your channel, in this tutorial we are going to use mychannel as the channel name. Be careful to use the correct channel name when issuing any of the following commands that are using channel name as parameter. Good practice is to assign channel name into environment variable.

export CHANNEL_NAME=mychannel
configtxgen -profile ThreeOrgsChannel -outputCreateChannelTx ./channel-artifacts/channel.tx -channelID $CHANNEL_NAME

The output in your terminal should be similar to this:

generate channel configuration transaction

The last operation we are going to perform with configtxgen is the definition of anchor peers for our organizations. This is especially important if there are more peers belonging to a single organization.

Run the following three commands to define anchor peers for each organization. Note that the asOrg parameter refers to the MSP ID definitions in configtx.yaml.

configtxgen -profile ThreeOrgsChannel -outputAnchorPeersUpdate ./channel-artifacts/Org1MSPanchors.tx -channelID $CHANNEL_NAME -asOrg Org1MSP
configtxgen -profile ThreeOrgsChannel -outputAnchorPeersUpdate ./channel-artifacts/Org2MSPanchors.tx -channelID $CHANNEL_NAME -asOrg Org2MSP
configtxgen -profile ThreeOrgsChannel -outputAnchorPeersUpdate ./channel-artifacts/Org3MSPanchors.tx -channelID $CHANNEL_NAME -asOrg Org3MSP

define anchor peer

After you execute all configtxgen commands, you can find various files in your channel-artefacts directory. The following example illustrates the results of the execution of the command:

channel-artefacts directory content

Start the Hyperledger Fabric blockchain network

To start our network we will use docker-compose tool. Based on its configuration, we launch containers based on the Docker images we downloaded in the beginning.

Modifying the docker-compose yaml Files

docker-compose tools is using yaml configuration files where various aspects of the containers and their network connection are defined. You can start with configuration yaml file from scratch or leverage yaml configuration from the "first-network" example.

To start, copy the docker-compose-cli.yaml file and base directory from the fabric-samples/first-network directory to your working directory. Inside your working directory, you should have a directory structure similar to the following:

directory structure

As you can see, we have three different yaml files: docker-compose-cli.yaml and two files in base directory: peer-base.yaml and docker-compose-base.yaml. This is quite convenient setup where various aspects of the configuration are defined in separate files - together they make the configuration for docker-compose. This allows, for example, to change particular attribute related to all peers on the single place rather than in definition of each particular peer.

Let's investigate all of these three files one by one. By # ---CHANGED--- label we indicate that the line below has been changed in comparison with the original yaml file from "first-network" directory.

The base directory contains the peer-base.yaml file which is extended by the docker-compose-cli.yaml file (serves a base configuration file for each peer). The only required modification to the peer-base.yaml file is to change the network id as illustrated by the following example:

version: '2'

services:
  peer-base:
    image: hyperledger/fabric-peer
    environment:
      - CORE_VM_ENDPOINT=unix:///host/var/run/docker.sock
      # the following setting starts chaincode containers on the same
      # bridge network as the peers
      # https://docs.docker.com/compose/networking/
      # ---CHANGED---
      - CORE_VM_DOCKER_HOSTCONFIG_NETWORKMODE=${COMPOSE_PROJECT_NAME}_basic
      #- CORE_LOGGING_LEVEL=ERROR
      - CORE_LOGGING_LEVEL=DEBUG
      - CORE_PEER_TLS_ENABLED=true
      - CORE_PEER_GOSSIP_USELEADERELECTION=true
      - CORE_PEER_GOSSIP_ORGLEADER=false
      - CORE_PEER_PROFILE_ENABLED=true
      - CORE_PEER_TLS_CERT_FILE=/etc/hyperledger/fabric/tls/server.crt
      - CORE_PEER_TLS_KEY_FILE=/etc/hyperledger/fabric/tls/server.key
      - CORE_PEER_TLS_ROOTCERT_FILE=/etc/hyperledger/fabric/tls/ca.crt
    working_dir: /opt/gopath/src/github.com/hyperledger/fabric/peer
    command: peer node start

The docker containers automatically create a local network that is based on the name that you specified.

:bulb: Note: The command: field specifies the command that is issued when the container starts. In the case of peers, the peer node start command starts the peer to install system chaincode and so on.

You use the docker-compose-base.yaml in base file to define the overall topology of your network as illustrated by the following example:

version: '2'

services:

  # ---CHANGED--- The orderer name is taken from the name generated by the "cryptogen" certs – it indicates the orderer orgs one and only orderer
  orderer.acme.com:
    # ---CHANGED--- The container name is a copy of the orderer name
    container_name: orderer.acme.com
    image: hyperledger/fabric-orderer
    environment:
      - ORDERER_GENERAL_LOGLEVEL=debug
      - ORDERER_GENERAL_LISTENADDRESS=0.0.0.0
      - ORDERER_GENERAL_GENESISMETHOD=file
      - ORDERER_GENERAL_GENESISFILE=/var/hyperledger/orderer/orderer.genesis.block
      - ORDERER_GENERAL_LOCALMSPID=OrdererMSP
      - ORDERER_GENERAL_LOCALMSPDIR=/var/hyperledger/orderer/msp
      # enabled TLS
      - ORDERER_GENERAL_TLS_ENABLED=true
      - ORDERER_GENERAL_TLS_PRIVATEKEY=/var/hyperledger/orderer/tls/server.key
      - ORDERER_GENERAL_TLS_CERTIFICATE=/var/hyperledger/orderer/tls/server.crt
      - ORDERER_GENERAL_TLS_ROOTCAS=[/var/hyperledger/orderer/tls/ca.crt]
    working_dir: /opt/gopath/src/github.com/hyperledger/fabric
    command: orderer
    volumes:
    - ../channel-artifacts/genesis.block:/var/hyperledger/orderer/orderer.genesis.block
    # ---CHANGED--- the path is different to reflect our company's domain
    - ../crypto-config/ordererOrganizations/acme.com/orderers/orderer.acme.com/msp:/var/hyperledger/orderer/msp
    # ---CHANGED--- the path is different to reflect our company's domain
    - ../crypto-config/ordererOrganizations/acme.com/orderers/orderer.acme.com/tls/:/var/hyperledger/orderer/tls
    ports:
      - 7050:7050

  # ---CHANGED--- The peer name is taken from the name generated by the "cryptogen" certs – it indicates the peer org 1 and one peer "peer0"
  peer0.org1.acme.com:
    # ---CHANGED--- Container name – same as the peer name
    container_name: peer0.org1.acme.com
    extends:
      file: peer-base.yaml
      service: peer-base
    environment:
      # ---CHANGED--- changed to reflect peer name, org name and our company's domain
      - CORE_PEER_ID=peer0.org1.acme.com
      # ---CHANGED--- changed to reflect peer name, org name and our company's domain
      - CORE_PEER_ADDRESS=peer0.org1.acme.com:7051
      # ---CHANGED--- changed to reflect peer name, org name and our company's domain
      - CORE_PEER_GOSSIP_EXTERNALENDPOINT=peer0.org1.acme.com:7051
      # ---CHANGED--- changed to reflect peer name, org name and our company's domain
      - CORE_PEER_GOSSIP_BOOTSTRAP=peer0.org1.acme.com:7051      
      - CORE_PEER_LOCALMSPID=Org1MSP
    volumes:
        - /var/run/:/host/var/run/
        # ---CHANGED--- changed to reflect peer name, org name and our company's domain
        - ../crypto-config/peerOrganizations/org1.acme.com/peers/peer0.org1.acme.com/msp:/etc/hyperledger/fabric/msp
        # ---CHANGED--- changed to reflect peer name, org name and our company's domain
        - ../crypto-config/peerOrganizations/org1.acme.com/peers/peer0.org1.acme.com/tls:/etc/hyperledger/fabric/tls
    ports:
      - 7051:7051
      - 7053:7053

  # ---CHANGED--- The peer name is taken from the name generated by the "cryptogen" certs – it indicates the peer org 2 and one peer "peer0"
  peer0.org2.acme.com:
    # ---CHANGED--- Container name – same as the peer name
    container_name: peer0.org2.acme.com
    extends:
      file: peer-base.yaml
      service: peer-base
    environment:
      # ---CHANGED--- changed to reflect peer name, org name and our company's domain
      - CORE_PEER_ID=peer0.org2.acme.com
      # ---CHANGED--- changed to reflect peer name, org name and our company's domain
      - CORE_PEER_ADDRESS=peer0.org2.acme.com:7051
      # ---CHANGED--- changed to reflect peer name, org name and our company's domain
      - CORE_PEER_GOSSIP_EXTERNALENDPOINT=peer0.org2.acme.com:7051
      # ---CHANGED--- changed to reflect peer name, org name and our company's domain
      - CORE_PEER_GOSSIP_BOOTSTRAP=peer0.org2.acme.com:7051
      # ---CHANGED--- ensure that the MSP ID is correctly set of Org2
      - CORE_PEER_LOCALMSPID=Org2MSP
    volumes:
        - /var/run/:/host/var/run/
        # ---CHANGED--- changed to reflect peer name, org name and our company's domain
        - ../crypto-config/peerOrganizations/org2.acme.com/peers/peer0.org2.acme.com/msp:/etc/hyperledger/fabric/msp
        # ---CHANGED--- changed to reflect peer name, org name and our company's domain
        - ../crypto-config/peerOrganizations/org2.acme.com/peers/peer0.org2.acme.com/tls:/etc/hyperledger/fabric/tls

    ports:
      - 8051:7051
      - 8053:7053

  # ---CHANGED--- The peer name is taken from the name generated by the "cryptogen" certs – it indicates the peer org 3 and one peer "peer0"
  peer0.org3.acme.com:
    # ---CHANGED--- Container name – same as the peer name
    container_name: peer0.org3.acme.com
    extends:
      file: peer-base.yaml
      service: peer-base
    environment:
      # ---CHANGED--- changed to reflect peer name, org name and our company's domain
      - CORE_PEER_ID=peer0.org3.acme.com
      # ---CHANGED--- changed to reflect peer name, org name and our company's domain
      - CORE_PEER_ADDRESS=peer0.org3.acme.com:7051
      # ---CHANGED--- changed to reflect peer name, org name and our company's domain
      - CORE_PEER_GOSSIP_EXTERNALENDPOINT=peer0.org3.acme.com:7051
      # ---CHANGED--- changed to reflect peer name, org name and our company's domain
      - CORE_PEER_GOSSIP_BOOTSTRAP=peer0.org3.acme.com:7051
      # ---CHANGED--- ensure that the MSP ID is correctly set of Org3
      - CORE_PEER_LOCALMSPID=Org3MSP
    volumes:
        - /var/run/:/host/var/run/
        # ---CHANGED--- changed to reflect peer name, org name and our company's domain
        - ../crypto-config/peerOrganizations/org3.acme.com/peers/peer0.org3.acme.com/msp:/etc/hyperledger/fabric/msp
        # ---CHANGED--- changed to reflect peer name, org name and our company's domain
        - ../crypto-config/peerOrganizations/org3.acme.com/peers/peer0.org3.acme.com/tls:/etc/hyperledger/fabric/tls
    ports:
      - 9051:7051
      - 9053:7053

:bulb: Note: Each peer is exposing ports 7051 and 7053. These ports are mapped to your network as 70xx, 80xx and 90xx respectively.

The last file we are going to modify is docker-compose-cli.yaml. It glues our orderer and peers together into one network and defines CLI container. The CLI container is where you enter to issue commands that interact with the peers (creating channels, deploying chaincode, and so on) and it represents "command line API" we will use for interaction with our peers.

version: '2'

# ---CHANGED--- our network is called "basic"
networks:
  basic:

services:

  # ---CHANGED--- The orderer name is taken from the name generated by the "cryptogen" certs – it indicates the orderer orgs one and only orderer
  orderer.acme.com:
    extends:
      file:   base/docker-compose-base.yaml
      # ---CHANGED--- refers to orderer name
      service: orderer.acme.com
    # ---CHANGED--- The container name is a copy of the orderer name
    container_name: orderer.acme.com
    networks:
      - basic

  # ---CHANGED--- The peer name is taken from the name generated by the "cryptogen" certs – it indicates the peer org 1 and one peer "peer0"
  peer0.org1.acme.com:
    # ---CHANGED--- Container name – same as the peer name
    container_name: peer0.org1.acme.com
    extends:
      file:  base/docker-compose-base.yaml
      # ---CHANGED--- Refers to peer name
      service: peer0.org1.acme.com
    networks:
      # ---CHANGED--- our network is called "basic"
      - basic

  # ---CHANGED--- The peer name is taken from the name generated by the "cryptogen" certs – it indicates the peer org 2 and one peer "peer0"
  peer0.org2.acme.com:
    # ---CHANGED--- Container name – same as the peer name
    container_name: peer0.org2.acme.com
    extends:
      file:  base/docker-compose-base.yaml
      # ---CHANGED--- Refers to peer name
      service: peer0.org2.acme.com
    networks:
      # ---CHANGED--- our network is called "basic"
      - basic

  # ---CHANGED--- The peer name is taken from the name generated by the "cryptogen" certs – it indicates the peer org 3 and one peer "peer0"
  peer0.org3.acme.com:
    # ---CHANGED--- Container name – same as the peer name
    container_name: peer0.org3.acme.com
    extends:
      file:  base/docker-compose-base.yaml
      # ---CHANGED--- Refers to peer name
      service: peer0.org3.acme.com
    networks:
      # ---CHANGED--- our network is called "basic"
      - basic

  cli:
    container_name: cli
    image: hyperledger/fabric-tools
    tty: true
    environment:
      - GOPATH=/opt/gopath
      - CORE_VM_ENDPOINT=unix:///host/var/run/docker.sock
      - CORE_LOGGING_LEVEL=DEBUG
      - CORE_PEER_ID=cli
      # ---CHANGED--- peer0 from Org1 is the default for this CLI container
      - CORE_PEER_ADDRESS=peer0.org1.acme.com:7051
      - CORE_PEER_LOCALMSPID=Org1MSP
      - CORE_PEER_TLS_ENABLED=true
      # ---CHANGED--- changed to reflect peer0 name, org1 name and our company's domain
      - CORE_PEER_TLS_CERT_FILE=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org1.acme.com/peers/peer0.org1.acme.com/tls/server.crt
      # ---CHANGED--- changed to reflect peer0 name, org1 name and our company's domain
      - CORE_PEER_TLS_KEY_FILE=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org1.acme.com/peers/peer0.org1.acme.com/tls/server.key
      # ---CHANGED--- changed to reflect peer0 name, org1 name and our company's domain
      - CORE_PEER_TLS_ROOTCERT_FILE=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org1.acme.com/peers/peer0.org1.acme.com/tls/ca.crt
      # ---CHANGED--- changed to reflect peer0 name, org1 name and our company's domain
      - CORE_PEER_MSPCONFIGPATH=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org1.acme.com/users/Admin@org1.acme.com/msp
    working_dir: /opt/gopath/src/github.com/hyperledger/fabric/peer
    # ---CHANGED--- command needs to be connected out as we will be issuing commands explicitly, not using by any script
    # command: /bin/bash -c './scripts/script.sh ${CHANNEL_NAME}; sleep $TIMEOUT'
    volumes:
        - /var/run/:/host/var/run/
        # ---CHANGED--- chaincode path adjusted
        - ./../chaincode/:/opt/gopath/src/github.com/chaincode
        - ./crypto-config:/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/
        - ./channel-artifacts:/opt/gopath/src/github.com/hyperledger/fabric/peer/channel-artifacts
    depends_on:
       # ---CHANGED--- reference to our orderer
      - orderer.acme.com
       # ---CHANGED--- reference to peer0 of Org1
      - peer0.org1.acme.com
       # ---CHANGED--- reference to peer0 of Org2
      - peer0.org2.acme.com
       # ---CHANGED--- reference to peer0 of Org3
      - peer0.org3.acme.com
    networks:
      # ---CHANGED--- our network is called "basic"
      - basic

Start the Docker Containers

After we have generated the certificates, the genesis block, the channel transaction, and created or modified the appropriate yaml files, we are ready to start our network. Use the following command to start the network.

CHANNEL_NAME=$CHANNEL_NAME docker-compose -f docker-compose-cli.yaml up -d

:bulb: Note: Observe the CHANNEL_NAME environment variable prefix. This value indicates the name of the channel that was specified as input for the configtxgen tool.

You can use docker ps to list the executing containers. In our case, you should see the following five containers executing:

  • Three peers (3)
  • The orderer (1)
  • The CLI (1)

After running docker-compose and docker ps you can expect output similar to the following:

docker-compose output

:bulb: Note: At any time, you can use the docker logs <container ID|Name> to view the logs.

:bulb: Note: You can shut down the containers (i.e. the whole Hyperledger Fabric blockchain network) using the docker rm -f $(docker ps -aq) command. The other option is to use ./byfn.sh -m down command which removes the previously created crypto artifacts as well.

As a verification, you should review the logs of the orderer to verify that it started properly. You should see no errors in the log, and the final message should indicate that it is ready to serve requests:

Orderer log

Reviewing the logs in depth, observe that the orderer creates a testchainid and installs system chaincode, which serves as the foundation of our network and gives the orderer and peers the capability to serve command line requests.

The channel

After the Docker containers start, you can enter the CLI container using the following command:

docker exec -it cli bash

Accessing CLI container

After you enter the CLI container, you can interact with the other peers by prefixing our peer commands with the appropriate environment variables. Usually, this means pointing to the certificates for that peer. You don`t need to do that when interacting with peer0 of Org1 which is set as the default one in CLI container.

Here is the list of environment variables that need to be used as the prefix for peer commands when interacting with peer0 of Org2 and Org3 respectively.

CORE_PEER_MSPCONFIGPATH=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org2.acme.com/users/Admin@org2.acme.com/msp
CORE_PEER_ADDRESS=peer0.org2.acme.com:7051
CORE_PEER_LOCALMSPID="Org2MSP"
CORE_PEER_TLS_ROOTCERT_FILE=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org2.acme.com/peers/peer0.org2.acme.com/tls/ca.crt
CORE_PEER_MSPCONFIGPATH=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org3.acme.com/users/Admin@org3.acme.com/msp
CORE_PEER_ADDRESS=peer0.org3.acme.com:7051
CORE_PEER_LOCALMSPID="Org3MSP"
CORE_PEER_TLS_ROOTCERT_FILE=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org3.acme.com/peers/peer0.org3.acme.com/tls/ca.crt

Create channel

The first command that we issue is the peer create channel command. This command targets the orderer (where the channels must be created) and uses the channel.tx and the channel name that is created using the configtxgen tool. As we are in the context of CLI container command line we define CHANNEL_NAME environment variable with our channel name.

export CHANNEL_NAME=mychannel

peer channel create -o orderer.acme.com:7050 -c $CHANNEL_NAME -f ./channel-artifacts/channel.tx --tls --cafile /opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/ordererOrganizations/acme.com/orderers/orderer.acme.com/msp/tlscacerts/tlsca.acme.com-cert.pem

Creating channel

The peer channel create command returns a genesis block which will be used to join the channel. You can check that by running ls command to review that the file mychannel.block has been created.

review output of peer channel create command

Join channel

After the orderer creates the channel, we can have the peers join the channel, again using the peer CLI:

peer channel join -b mychannel.block

Observe in this case, we issued the peer channel join command for peer0 of Org1 (which is the default peer of our CLI container) and specified the mychannel.block, which is the genesis block. There is not much output, but you can see a message that peer joined the channel.

review output of peer channel join command

Now, we will make our peers of Org2 and Org3 join the channel as well. Note, the in this case we prefix the command with respective variables for each peer.

CORE_PEER_MSPCONFIGPATH=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org2.acme.com/users/Admin@org2.acme.com/msp CORE_PEER_ADDRESS=peer0.org2.acme.com:7051 CORE_PEER_LOCALMSPID="Org2MSP" CORE_PEER_TLS_ROOTCERT_FILE=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org2.acme.com/peers/peer0.org2.acme.com/tls/ca.crt peer channel join -b mychannel.block

CORE_PEER_MSPCONFIGPATH=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org3.acme.com/users/Admin@org3.acme.com/msp CORE_PEER_ADDRESS=peer0.org3.acme.com:7051 CORE_PEER_LOCALMSPID="Org3MSP" CORE_PEER_TLS_ROOTCERT_FILE=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org3.acme.com/peers/peer0.org3.acme.com/tls/ca.crt peer channel join -b mychannel.block

We expect the similar output as we got for peer of Org1:

review output of peer channel join command

review output of peer channel join command

When you review the logs of the peers, you can see the channel creation messages and the system chaincode being deployed as illustrated by the following example. Run the following command from a separate terminal:

docker logs peer0.org1.acme.com

join channel log

Update anchor peers

Now, going back to our main terminal, as the last step before we will interact with our network is to update the anchor peers. The following three commands are channel updates and they will become a part of the definition of the channel:

peer channel update -o orderer.acme.com:7050 -c $CHANNEL_NAME -f ./channel-artifacts/Org1MSPanchors.tx --tls --cafile /opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/ordererOrganizations/acme.com/orderers/orderer.acme.com/msp/tlscacerts/tlsca.acme.com-cert.pem

CORE_PEER_MSPCONFIGPATH=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org2.acme.com/users/Admin@org2.acme.com/msp CORE_PEER_ADDRESS=peer0.org2.acme.com:7051 CORE_PEER_LOCALMSPID="Org2MSP" CORE_PEER_TLS_ROOTCERT_FILE=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org2.acme.com/peers/peer0.org2.acme.com/tls/ca.crt peer channel update -o orderer.acme.com:7050 -c $CHANNEL_NAME -f ./channel-artifacts/Org2MSPanchors.tx --tls --cafile /opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/ordererOrganizations/acme.com/orderers/orderer.acme.com/msp/tlscacerts/tlsca.acme.com-cert.pem

CORE_PEER_MSPCONFIGPATH=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org3.acme.com/users/Admin@org3.acme.com/msp CORE_PEER_ADDRESS=peer0.org3.acme.com:7051 CORE_PEER_LOCALMSPID="Org3MSP" CORE_PEER_TLS_ROOTCERT_FILE=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org3.acme.com/peers/peer0.org3.acme.com/tls/ca.crt peer channel update -o orderer.acme.com:7050 -c $CHANNEL_NAME -f ./channel-artifacts/Org3MSPanchors.tx --tls --cafile /opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/ordererOrganizations/acme.com/orderers/orderer.acme.com/msp/tlscacerts/tlsca.acme.com-cert.pem

In the screenshot below, we can see example output of the first command:

Update anchor peers

Install chaincode

Right now, we have fully configured and running Hyperledger Fabric network. However, the network does not contain any business logic - from this perspective, we can consider it as "empty" and as of now we have no way how to enter data there. Hyperledger Fabric blockchain applications interact with the ledger through the chaincode and its methods. So as a next step we need to install (deploy) chaincode on each peer.

:bulb: Note: Throughout this tutorial we are using the sample chaincode methods.

You can deploy chaincode on each peer using the following command:

peer chaincode install -n mycc -v 1.0 -p github.com/chaincode/chaincode_example02/go/

In this case, if the command succeed, you should see a response indicating a status of 200 and a payload of OK, as illustrated by the following example:

Install chaincode

:bulb: Note: Interactions with our Hyperledger Fabric blockchain network are internally multi-step operations. It might take some time for commands to finish their execution.

Using the above command we have installed chaincode on peer0 of the Org1 organization. In order to let other peers from other organizations to interact with the ledger as well, we need to install chaincode there as well.

CORE_PEER_MSPCONFIGPATH=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org2.acme.com/users/Admin@org2.acme.com/msp CORE_PEER_ADDRESS=peer0.org2.acme.com:7051 CORE_PEER_LOCALMSPID="Org2MSP" CORE_PEER_TLS_ROOTCERT_FILE=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org2.acme.com/peers/peer0.org2.acme.com/tls/ca.crt peer chaincode install -n mycc -v 1.0 -p github.com/chaincode/chaincode_example02/go/

CORE_PEER_MSPCONFIGPATH=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org3.acme.com/users/Admin@org3.acme.com/msp CORE_PEER_ADDRESS=peer0.org3.acme.com:7051 CORE_PEER_LOCALMSPID="Org3MSP" CORE_PEER_TLS_ROOTCERT_FILE=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org3.acme.com/peers/peer0.org3.acme.com/tls/ca.crt peer chaincode install -n mycc -v 1.0 -p github.com/chaincode/chaincode_example02/go/

Instantiate chaincode

After we installed the chaincode, we need to instantiate it. This will initialize the chaincode on the channel - the init function of chaincode will be called.

:bulb: Note: In the Hyperledger Fabric setup the chaincode instantiation creates (for the peer specified) a new docker container that is known as a chaincode container. The chaincode is deployed in this container and accessible from the peer.

Use the following command to instantiate the chaincode from one of the peers:

peer chaincode instantiate -o orderer.acme.com:7050 --tls --cafile /opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/ordererOrganizations/acme.com/orderers/orderer.acme.com/msp/tlscacerts/tlsca.acme.com-cert.pem -C $CHANNEL_NAME -n mycc -v 1.0 -c '{"Args":["init","a", "100", "b","200"]}' -P "OR ('Org1MSP.member','Org2MSP.member','Org3MSP.member')"

This command causes the following actions to occur:

  • Spawns a chaincode Docker container to house the chaincode.
  • Invokes the init function of the chaincode.

:bulb: Note: The init function (in this case) primes the ledger with the initial values for a and b (a = 100, b = 200). These can be thought of as our available assets.

  • Defines the endorsement policy for our channel. In this example, the [-P "OR ('Org1MSP.member','Org2MSP.member','Org3MSP.member')"] statement indicates that one (OR) of the peers (members of organizations) is enough to endorse a transaction for it to be valid.

When the command completes, you should see the "exiting..." message for the CLI, as illustrated by the following example (note the initial values specified for a and b as a parameter of peer chaincode instantiate command):

Instantiate chaincode

Execute tutorial scenario

Our Hyperledger Fabric blockchain network is now finally ready - it contains initial data and provide read/write data capabilities through the chaincode.

What we are going to do now is a series of read and writes to the ledger to demonstrate how to query the ledger and how to invoke chaincode in order to change the data in the ledger.

Query for initial state of the ledger

To quickly verify what data are in our ledger we can query the ledger for the value of a:

peer chaincode query -C $CHANNEL_NAME -n mycc -c '{"Args":["query","a"]}'

As expected, the value of 100 is being returned:

Initial query

Invoke chaincode

You can invoke the chaincode to transfer assets. In our example, we issue the following commands on each peer to transfer assets from a to b. In particular, we are taking 10 from a and transferring it to b.

Let's start with peer0 of Org1:

peer chaincode invoke -o orderer.acme.com:7050  --tls --cafile /opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/ordererOrganizations/acme.com/orderers/orderer.acme.com/msp/tlscacerts/tlsca.acme.com-cert.pem  -C $CHANNEL_NAME -n mycc -c '{"Args":["invoke","a","b","10"]}'

As this command completes, you should see response message indicating status:200 and message:OK:

Invoke chaincode

We will continue with chaincode invocation from peer0 of Org2 and Org3:

CORE_PEER_MSPCONFIGPATH=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org2.acme.com/users/Admin@org2.acme.com/msp CORE_PEER_ADDRESS=peer0.org2.acme.com:7051 CORE_PEER_LOCALMSPID="Org2MSP" CORE_PEER_TLS_ROOTCERT_FILE=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org2.acme.com/peers/peer0.org2.acme.com/tls/ca.crt peer chaincode invoke -o orderer.acme.com:7050  --tls --cafile /opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/ordererOrganizations/acme.com/orderers/orderer.acme.com/msp/tlscacerts/tlsca.acme.com-cert.pem  -C $CHANNEL_NAME -n mycc -c '{"Args":["invoke","a","b","10"]}'

CORE_PEER_MSPCONFIGPATH=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org3.acme.com/users/Admin@org3.acme.com/msp CORE_PEER_ADDRESS=peer0.org3.acme.com:7051 CORE_PEER_LOCALMSPID="Org3MSP" CORE_PEER_TLS_ROOTCERT_FILE=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org3.acme.com/peers/peer0.org3.acme.com/tls/ca.crt peer chaincode invoke -o orderer.acme.com:7050  --tls --cafile /opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/ordererOrganizations/acme.com/orderers/orderer.acme.com/msp/tlscacerts/tlsca.acme.com-cert.pem  -C $CHANNEL_NAME -n mycc -c '{"Args":["invoke","a","b","10"]}'

Query for the new values

We did some changes to the values of our assets a and b. If you recall, we instantiated a to be 100 and invoked the 3 times by transferring 10 from a to b. So we expect the queries return the value of 70 for a and 230 for b.

:bulb: Note: The following queries could be run from any peer. Remember, to run it from the context of different peers, you need to prefix the command with the respective environment variables.

Query for a:

peer chaincode query -C $CHANNEL_NAME -n mycc -c '{"Args":["query","a"]}'

As shown in the following screenshot, the value of a is 70 as expected:

Query chaincode

And let's query for b:

peer chaincode query -C $CHANNEL_NAME -n mycc -c '{"Args":["query","b"]}'

Again, the result meets our expectation - value of b is 230:

Query chaincode

Summary

At this point, our network is fully operational and this concludes our tutorial.

Here is a summary of the tasks that we completed:

  • We installed all dependencies required to run Hyperledger Fabric v1.1 on zLinux.
  • We retrieved Hyperledger Fabric v1.1 artificats and binaries.
  • We used the cryptogen tool to generate certificates and keys for the local MSP.
  • We used the configtxgen tool to create our channel transaction and genesis block.
  • We configured the docker-compose.yaml files to describe our networks and peers.
  • We used the CLI container to perform the following tasks:
    • Create the channel
    • Deploy chaincode
    • Initialize chaincode
    • Invoke chaincode
    • Query chaincode

What is Next?

Our tutorial describes how to setup simple Hyperledger Fabric blockchain network on zLinux instance. There are advanced topics that might be covered in the future tutorials build on top of this one:

  • Use the fabric ca and fabric ca server to generate the certificates (instead of the cryptogen tool).
  • Setup Hyperledger Fabric blockchain network on physical nodes across the network.
  • Describe chaincode development.
  • Build a client application on top of your network using available Fabric SDKs.

References

Links

Topic Location
HyperLedger Fabric Documentation Home http://hyperledger-fabric.readthedocs.io/en/latest/
Hyperledger Fabric Glossary http://hyperledger-fabric.readthedocs.io/en/latest/glossary.html
Hyperledger Fabric Model http://hyperledger-fabric.readthedocs.io/en/latest/fabric_model.html
Hyperledger Fabric Prerequisities http://hyperledger-fabric.readthedocs.io/en/latest/prereqs.html
Hyperledger Fabric Samples http://hyperledger-fabric.readthedocs.io/en/latest/samples.html
Building Your First Network http://hyperledger-fabric.readthedocs.io/en/latest/build_network.html
Hyperledger Fabric GitHub https://github.com/hyperledger/fabric
Hyperledger Fabric Samples GitHub https://github.com/hyperledger/fabric-samples
HyperLedger Fabric youtube channel https://www.youtube.com/channel/UCCFdgCWH_1vCndMPVqQlwZw
IBM Blockchain 101 https://www.ibm.com/developerworks/cloud/library/cl-ibm-blockchain-101-quick-start-guide-for-developers-bluemix-trs/index.html
Node.js http://nodejs.org/
Node.js on Linux on z https://developer.ibm.com/node/sdk/v6/
Docker https://docs.docker.com
Docker on Linux on z https://www.ibm.com/developerworks/linux/linux390/docker.html
Docker Compose https://docs.docker.com/compose/
Docker Compose on Linux on z https://github.com/linux-on-ibm-z/docs/wiki/Building-Docker-Compose
Golang https://golang.org/
Golang on Linux on z https://github.com/linux-on-ibm-z/docs/wiki/Building-Go

Useful Docker Commands

docker ps -a - Show all docker processes.

docker-compose -f <your_docker_compose_file.yaml> up -d - Start docker containers that are described by docker-compose.yaml.

docker rm -f $(docker ps -aq) - Shut down/remove all docker processes.

docker network ls - Shows docker networks.

docker logs <id|name> - Show the logs for a docker container. Logs are useful for debugging problems.

docker exec -it <id|name> bash - Enter the docker container and start an interactive bash terminal.

You can’t perform that action at this time.
You signed in with another tab or window. Reload to refresh your session. You signed out in another tab or window. Reload to refresh your session.
Press h to open a hovercard with more details.