Building Your First Network: Run the tools

Source: https://hyperledger-fabric.readthedocs.io/en/latest/build_network.html

Notice

2020-02-11: Fabric v1.4 -> Farbic v2.0 기준으로 업데이트

이전 포스트의 first-network 예제는 간단하게 bash shell을 실행하는 것만으로 HLF 네트워크 구축이 가능했다. 이번에는 fabric-tools를 이용하여 HLF 네트워크를 구축해보자.

HLF 네트워크 관련 파일이 저장될 디렉토리를 지정한다. (필자는 my-network로 지정하였지만 다른 이름도 상관없다.)

새로운 디렉토리에서 네트워크를 구성하는 엔티티들의 크리덴셜 (인증서와 서명을 위한 개인키)을 만들기 위해 Crypto Generator를 사용한다. Crypto Generator를 실행할 때는 yaml 형식의 설정 파일이 인자로 들어가야 하는데, 파일 이름은 crypto-config.yaml로 지정한다. crypto-config.yaml에는 Org 이름, 도메인 접속 정보와 Org를 구성하는 피어의 수가 정의되어 있다.

crypto-config.yaml은 https://github.com/hyperledger/fabric-samples/blob/master/first-network/crypto-config.yaml에서 다운로드받아 사용하거나, ~/fabric-samples/first-network/crypto-config.yaml을 \my-network에 복사하여 사용할 수 있다.

# Moving to \fabric-samples
cd ~/fabric-samples
# Create new directory
mkdir my-network
# Copy Cryptogen configuration file (used first-network example) to current directory 
cp ../first-network/crypto-config.yaml ./
# Execute Crypto Generator (for generating x.509 certs and signing keys)
../bin/cryptogen generate --config=./crypto-config.yaml

Crypto Generator를 위 명령어로 실행시키면 현재 작업 중인 폴더 (my-network)에 crypto-config 폴더가 만들어지고 하위로 ordererOrganizations과 peerOrganizations 폴더가 만들어졌음을 확인할 수 있다.

generated security credentials (for peers and orderer)

tree 명령어를 사용하여 ordererOrganizations의 구조를 살펴보자. 도메인명 (example.com)으로 되어 있는 디렉토리 하위에 \ca, \msp, \orderers, \tlsca, \users가 위치하고, Root CA, TLS CA, Orderer, User의 인증서와 서명 키가 각각 들어 있다.

HLF에서는 MSP (Membership Service Provider) 모듈을 이용하여 사용자의 신분을 확인하고, 채널 내에서의 접근 제어 및 멤버를 관리한다.MSP에 대한 상세 설명은 https://hyperledger-fabric.readthedocs.io/en/latest/membership/membership.html를 참고하자.

peerOrganizations는 org1, org2로 구분되어 있다. orderer의 인증서/서명 키 대신 peer들의 인증서/서명 키가 저장된다는 것만 제외하고 ordererOrganizations 디렉토리와 거의 흡사한 구조를 보인다.

다음으로 현재 작업 중인 디렉토리에 /channel-artifacts를 만들고 Configuration Transaction Generator (configtxgen)를 이용하여 제네시스 블록을 만들어보자. 이를 위해 설정 파일인 configtx.yaml을 작성한다. \first-network\configtx.yaml은 아래와 같이 총 6개의 Section으로 구성되어 있다.

  1. Organizations: Org 정보, MSP 위치, anchor peer 접속 정보, 접근 제어 정책

  2. Capabilities: 사용할 수 있는 Channel, Orderer, Application 버전

  3. Applications: 어플리케이션 접근 제어 정책

  4. Orderer: 오더링 타입 (etcdraft), 접속 정보, 일괄 처리 관련 옵션, Broker 접속 정보, 접근 제어 정책

  5. Channel: 채널 접근 제어 정책

  6. Profiles: 설정 프로파일. 위의 옵션들을 하나의 프로파일로 만들어 configtxgen에서 파라미터로 사용

configtx.yaml은 https://github.com/hyperledger/fabric-samples/blob/master/first-network/configtx.yaml에서 다운로드받아 사용하거나, ~/fabric-samples/first-network/configtx.yaml을 \my-network에 복사하여 사용할 수 있다.

# Create new directory
mkdir channel-artifacts
# Copy channel configuration file (used first-network example) to current directory 
cp ../first-network/configtx.yaml ./
# Generate orderer genesis block
../bin/configtxgen -profile SampleMultiNodeEtcdRaft -channelID byfn-sys-channel -outputBlock ./channel-artifacts/genesis.block

제네시스 블록은 orderer에 의해 네트워크에 처음 만들어지는 블록을 의미한다. cryptogen와 같이 상대 경로를 이용하기 때문에 현재 위치를 잘 확인한 후 실행해야 한다. 현재 디렉토리를 FABIC_CFG_PATH 환경 변수에 등록한다.

Set the Environment Variable (FABRIC_CFG_PATH)
export FABRIC_CFG_PATH=$PWD

다음은 채널 설정 트랜잭션 (channel.tx) 과 각 Org 별 앵커 피어 업데이트 트랜잭션 (Org1MSPanchors.tx, Org2MSPanchors.tx)을 생성한다.

# Generate channel.tx
../bin/configtxgen -profile TwoOrgsChannel -outputCreateChannelTx ./channel-artifacts/channel.tx -channelID mychannel
# Generate Org1MSPanchors.tx
../bin/configtxgen -profile TwoOrgsChannel -outputAnchorPeersUpdate ./channel-artifacts/Org1MSPanchors.tx -channelID mychannel -asOrg Org1MSP
# Generate Org2MSPanchors.tx
../bin/configtxgen -profile TwoOrgsChannel -outputAnchorPeersUpdate ./channel-artifacts/Org2MSPanchors.tx -channelID mychannel -asOrg Org2MSP

각각 명령어를 실행했을 때 아래의 화면이 나오는지 확인 후 다음으로 진행한다.

After generating channel.tx
After generating Org2MSPanchors.tx

이게 도커 설정 파일을 작성하고 HLF 네트워크를 실행할 차례이다. 도커 설정 파일도 위와 마찬가지로 first-network에서 사용한 것을 현재 디렉토리에 복사한다. 명령어를 차례대로 수행하면 5개의 orderer 컨테이너와 네 개의 peer 컨테이너, cli 컨테이너가 생성된다.

컨테이너 실행을 위해서는 COMPOSE_PROJECT_NAME, IMAGE_TAG 환경 변수를 지정할 필요가 있다. docker-compose를 실행할 때마다 지정할 수 있지만, 현재 디렉토리에 .env 파일을 생성하여 컨테이너에 적용될 환경 변수를 모아서 관리하는 것이 더 유용하다.

# Copy ../first-network/base
cp -r ../first-network/base . 
# Copy docker configuration file(cli)
cp ../first-network/docker-compose-cli.yaml .
# Copy docker compose configuration file(orderers)
cp ../first-network/docker-compose-etcdraft2.yaml .
# Start HLF network(daemon)
COMPOSE_PROJECT_NAME=net IMAGE_TAG=latest docker-compose -f docker-compose-cli.yaml -f docker-compose-etcdraft2.yaml up -d
# View running docker containers
docker ps

생성 후에는 9개의 컨테이너가 실행 중인지 반드시 확인한다.

Running containers

실행 중인 컨테이너 내부로 접근하여 직접 명령어를 내려보자. 만약 CLI container에 정상적으로 접속이 되었다면 아래와 같은 화면이 나타날 것이다. hostname이 컨테이너 id로 바뀌어있음을 확인할 수 있다.

# Enter the CLI container
docker exec -it cli bash
# Set channel name(CHANNEL_NAME) in the cli container
export CHANNEL_NAME=mychannel

피어가 원장을 공유하기 위한 채널을 생성한다. 채널 생성을 위해서 오더러 접속 정보, TLS CA의 인증서 경로, 채널 이름, 채널 구성 트랜잭션 경로를 지정한다. 생성이 완료되면 채널의 첫 번째 블록이 만들어진다. (mychannel.block)

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

mychannel에 peer0.org1와 peer0.org2을 참여시킨다. cli는 기본적으로 peer0.org1의 역할을 수행하므로 컨테이너 안에서 환경변수를 바꿔주어야 cli 컨테이너를 peer0.org2로 사용할 수 있다.

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

peer0.org1과 peer0.org2를 mychannel의 anchor peer로 지정한다.

# Set peer0.org1 to anchor peer of Org1
peer channel update -o orderer.example.com:7050 -c $CHANNEL_NAME -f ./channel-artifacts/Org1MSPanchors.tx --tls --cafile /opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem
# Set peer0.org2 to anchor peer of Org2
CORE_PEER_MSPCONFIGPATH=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org2.example.com/users/Admin@org2.example.com/msp CORE_PEER_ADDRESS=peer0.org2.example.com:9051 CORE_PEER_LOCALMSPID="Org2MSP" CORE_PEER_TLS_ROOTCERT_FILE=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org2.example.com/peers/peer0.org2.example.com/tls/ca.crt peer channel update -o orderer.example.com:7050 -c $CHANNEL_NAME -f ./channel-artifacts/Org2MSPanchors.tx --tls --cafile /opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem

이제 체인코드를 채널 냉 피어에 설치하고 배포한다. v2.0부터는 합의 알고리즘이 RAFT로 바뀌면서 Orderer 간의 체인코드 검증과 Go module 적용으로 인한 체인코드 패키징 과정이 새로 추가되었다. peer 명령어를 통해 컨테이너 안에서 체인코드를 패키징한다. 패키징 후 cli 컨테이너의 루트 디렉토리 안에 mycc.tar.gz가 생성된 것을 확인할 수 있다.

# before packaging Golang chaincode, vendoring Go dependencies is required like the following commands.
cd /opt/gopath/src/github.com/hyperledger/fabric-samples/chaincode/abstore/go
GO111MODULE=on go mod vendor
cd -
# this packages a Golang chaincode.
# make note of the --lang flag to indicate "golang" chaincode
# for go chaincode --path takes the relative path from $GOPATH/src
# The --label flag is used to create the package label
peer lifecycle chaincode package mycc.tar.gz --path github.com/hyperledger/fabric-samples/chaincode/abstore/go/ --lang golang --label mycc_1

peer0.org1에 체인 코드 패키지를 설치한다. 설치 후 queryinstalled 명령어를 이용하면 체인코드의 패키지 ID를 얻을 수 있는데 이를 CC_PACKAGE_ID 환경 변수로 설정한다. CC_PACKAGE_ID는 체인코드 검증에 사용된다. 아래 코드의 ID는 필자의 실습 환경에서 얻은 값이니 환경에 따라 다를 수 있다.

peer lifecycle chaincode install mycc.tar.gz
# this returns the details of the chaincode packages installed on your peers
peer lifecycle chaincode queryinstalled
CC_PACKAGE_ID=mycc_1:d881a1edbfb7d5fbbbfba6272158e24381826157c2f6b42a3245b454cffc908e

peer0.org2에 체인 코드 패키지를 설치한다. 피어에 체인 코드가 설치되어 있어야 체인코드를 호출할 수 있다.

CORE_PEER_MSPCONFIGPATH=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org2.example.com/users/Admin@org2.example.com/msp
CORE_PEER_ADDRESS=peer0.org2.example.com:9051
CORE_PEER_LOCALMSPID="Org2MSP"
CORE_PEER_TLS_ROOTCERT_FILE=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org2.example.com/peers/peer0.org2.example.com/tls/ca.crt
peer lifecycle chaincode install mycc.tar.gz

패키지 설치 후 Org 별로 체인 코드를 검증한다. 앞에서 환경 변수 설정을 통해 cli를 peer0.org2로 만들었으니 Org2부터 검증 과정을 수행한다.

# Approve mycc for Org2
peer lifecycle chaincode approveformyorg --channelID $CHANNEL_NAME --name mycc --version 1.0 --init-required --package-id $CC_PACKAGE_ID --sequence 1 --tls true --cafile /opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem
# Approve mycc for Org1
CORE_PEER_MSPCONFIGPATH=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org1.example.com/users/Admin@org1.example.com/msp
CORE_PEER_ADDRESS=peer0.org1.example.com:7051
CORE_PEER_LOCALMSPID="Org1MSP"
CORE_PEER_TLS_ROOTCERT_FILE=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org1.example.com/peers/peer0.org1.example.com/tls/ca.crt
peer lifecycle chaincode approveformyorg --channelID $CHANNEL_NAME --name mycc --version 1.0 --init-required --package-id $CC_PACKAGE_ID --sequence 1 --tls true --cafile /opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem

채널에 체인코드를 배포하기 전에 OrdererOrg에서도 검증 과정을 거친다. 정상적으로 검증했을 경우 true 메시지를 확인할 수 있다.

peer lifecycle chaincode checkcommitreadiness --channelID $CHANNEL_NAME --name mycc --version 1.0 --init-required --sequence 1 --tls true --cafile /opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem --output json

채널에 체인코드를 배포한다.

peer lifecycle chaincode commit -o orderer.example.com:7050 --channelID $CHANNEL_NAME --name mycc --version 1.0 --sequence 1 --init-required --tls true --cafile /opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem --peerAddresses peer0.org1.example.com:7051 --tlsRootCertFiles /opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org1.example.com/peers/peer0.org1.example.com/tls/ca.crt --peerAddresses peer0.org2.example.com:9051 --tlsRootCertFiles /opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org2.example.com/peers/peer0.org2.example.com/tls/ca.crt

배포된 체인코드의 함수를 호출한다. 체인코드 내 함수를 호출할 때는 invoke 명령어를 사용하고, Args에 함수이름과 파라미터 값을 입력한다. (a = 100, b = 100 으로 초기화)

peer chaincode invoke -o orderer.example.com:7050 --isInit --tls true --cafile /opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem -C $CHANNEL_NAME -n mycc --peerAddresses peer0.org1.example.com:7051 --tlsRootCertFiles /opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org1.example.com/peers/peer0.org1.example.com/tls/ca.crt --peerAddresses peer0.org2.example.com:9051 --tlsRootCertFiles /opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org2.example.com/peers/peer0.org2.example.com/tls/ca.crt -c '{"Args":["Init","a","100","b","100"]}' --waitForEvent

초기화된 값이 잘 적용되었는지 확인하기 위해 query 명령을 통해 a의 값을 읽어보자. (a = 100)

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

이제 a의 값을 10만큼 b로 이동시키는 함수를 실행해보자. 함수가 실행되고 정상적으로 결과가 적용되면 원장 데이터가 'a = 90, b = 110'으로 바뀔 것이다.

peer chaincode invoke -o orderer.example.com:7050 --tls true --cafile /opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem -C $CHANNEL_NAME -n mycc --peerAddresses peer0.org1.example.com:7051 --tlsRootCertFiles /opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org1.example.com/peers/peer0.org1.example.com/tls/ca.crt --peerAddresses peer0.org2.example.com:9051 --tlsRootCertFiles /opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org2.example.com/peers/peer0.org2.example.com/tls/ca.crt -c '{"Args":["invoke","a","b","10"]}' --waitForEvent

잘 적용되었는지 확인하기 위해 다시 query 명령을 통해 a의 값을 읽어보자. (a = 90)

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

cli 컨테이너에서 터미널로 이동하고 싶을 때는 exit 명령어로 빠져나온다. 더이상 확인할 것이 없다면 네트워크를 종료하고 실행 중인 컨테이너를 삭제한다.

# 실행 중인 docker container 모두 종료
docker rm -f $(docker ps -aq)
# docker container에서 사용하지 않는 모든 docker volume을 삭제
docker volume prune
# cached network 초기화
docker network prune

Last updated