콘텐츠로 건너뛰기

Hyperledger Fabric Configure Network – byfn 뜯어보기

fabric-logo

Intro

Hyperledger Fabric docs를 읽다보면 가장 처음 접하게 되는 것이 byfn 튜토리얼이다. 처음에 개념적인 부분과 실제 네트워크의 흐름을 맞춰보는데 정말 도움이 되는 것 같다. 그런데 정말 찬찬히 뜯어봐야한다. 하나씩 ‘이건 왜 이렇게 동작하는거지?’, ‘이건 왜 안되는거지?’ 하면서 Fabric의 개념적인 부분하고 연결시키면서 생각하면 어느순간 전체적인 그림이 그려지게 된다.

처음에 sample을 받고, develop 혹은 연습하는데 도움이 되는 바이너리 파일을 받게 된다. 바이너리 파일에는 cryptogen, configtxlator, peer 등 우리가 네트워크를 쉽게 구성할 수 있게 도움을 줄 수 있는 프로그램들이 있다.

    bin/
        cryptogen
        configtxlator
        ...
    first-network/
        byfn.sh
        configtx.yaml
        crypto-config.yaml
        docker-compose-cli.yaml
        scripts/
            script.sh
            utils.sh
        base/
            docker-compose-base.yaml
        channel-artifacts/

전체 디렉토리 구조이다. 빠진 파일들도 있지만 일단 우리에게 필요한 것은 이 정도이다. 간단하게 각각을 살펴보면 다음과 같다.

  • cryptogen: 간단하게 네트워크 구성원들에게 certificates들을 발급해준다. production에서는 사용하지 않는 것이 좋다. 대신 CA에서 받아야한다.
  • configtxlator: protobuf와 json 변환 및 파싱을 도와준다.
  • byfn.sh: Hyperledger Fabric에서 만들어준 sample 스크립트이다. 이번 포스팅에서는 이 파일을 하나하나 다 뜯어 볼 것이다.
  • configtx.yaml: 네트워크의 channel과 genesis block을 만들고 anchor peer를 설정한다. 파일 이름에서 유추할 수 있듯이 네트워크 전체의 설정 내용을 담고 있다.
  • crypto-config.yaml: cryptogen이 이 파일을 사용한다. 이 파일을 이용해서 organization와 그 구성원들에게 각각의 certificate을 발급한다. 그래서 각각의 organization들이 독자적인 CA를 가지고 있는 것처럼 보이게 할 수 있다.
  • docker-compose-cli.yaml, docker-compose-base.yaml: 전체 네트워크 노드들의 docker-compose 설정들이다.

튜토리얼에서는 ./byfn.sh -m generate ./byfn.sh -m up으로 전체적인 네트워크 구성이 시작되고 완성된다. 우리는 byfn.sh에서 어떤 일이 일어나는지 알아보고 싶다.

Generate Crypto Artifacts, Channel Configuration

#Create the network using docker compose
if [ "${MODE}" == "up" ]; then
  networkUp
elif [ "${MODE}" == "down" ]; then ## Clear the network
  networkDown
elif [ "${MODE}" == "generate" ]; then ## Generate Artifacts
  generateCerts
  replacePrivateKey
  generateChannelArtifacts
...
fi

시작 부분이다. generate 에서는 generateCerts, replacePrivateKey, generateChannelArtifacts 가 일어난다.

Create Certificates Using Cryptogen

# Generates Org certs using cryptogen tool
function generateCerts (){
  ...
  cryptogen generate --config=./crypto-config.yaml
  ...
}

generateCerts 부분이다. 여기서는 cryptogen 을 이용해서 네트워크의 ceritificates를 만들게 된다. cryptogen 의 사용법은 다음 링크에 자세히 나와있다.(사용법) cryptogen으로 만든 crypto artifacts들은 crypto-config에서 확인할 수 있다. 펼쳐보면 확인할 수 있겠지만 ordererOrganizations, peerOragnizations 디렉토리로 나눠져있고, 각각은 orderer의 certificate, peer들의 certificate들이 담겨져있다.

# Using docker-compose-e2e-template.yaml, replace constants with private key file names
# generated by the cryptogen tool and output a docker-compose.yaml specific to this
# configuration
function replacePrivateKey () {
 ...
  # Copy the template to the file that will be modified to add the private key
  cp docker-compose-e2e-template.yaml docker-compose-e2e.yaml
 ...
  cd crypto-config/peerOrganizations/org1.example.com/ca/
  PRIV_KEY=$(ls *_sk)
  cd "$CURRENT_DIR"
  sed $OPTS "s/CA1_PRIVATE_KEY/${PRIV_KEY}/g" docker-compose-e2e.yaml
  cd crypto-config/peerOrganizations/org2.example.com/ca/
  PRIV_KEY=$(ls *_sk)
  cd "$CURRENT_DIR"
  sed $OPTS "s/CA2_PRIVATE_KEY/${PRIV_KEY}/g" docker-compose-e2e.yaml
  ...
}

여기서 잠깐 지금 포스팅을 하는 이유가 나온다. replacePrivateKey 를 보면 알겠지만 organization domain이 hardcoding 되어있다. 그렇기 때문에 우리는 이 스크립트를 이용하는 것이 아니라, 이해한 뒤에 우리가 필요한 부분에 대해서 스크립트를 다시 짜야한다.

이 부분은 Fabric과 관련은 없는 부분이다. 단순히 docker-compose template파일을 복사한 뒤에 이전에 우리가 cryptogen으로부터 받은 certifcate들을 리눅스의 sed 명령어를 이용해 compose 파일 적절한 부분에 바꿔넣기 하고 있다.

Generate Channel Config Transaction

# Generate orderer genesis block, channel configuration transaction and
# anchor peer update transactions
function generateChannelArtifacts() {
...
  echo "##########################################################"
  echo "#########  Generating Orderer Genesis block ##############"
  echo "##########################################################"
  configtxgen -profile TwoOrgsOrdererGenesis -outputBlock ./channel-artifacts/genesis.block
...
  echo
  echo "#################################################################"
  echo "### Generating channel configuration transaction 'channel.tx' ###"
  echo "#################################################################"
  set -x
  configtxgen -profile TwoOrgsChannel -outputCreateChannelTx ./channel-artifacts/channel.tx -channelID $CHANNEL_NAME
...
  echo
  echo "#################################################################"
  echo "#######    Generating anchor peer update for Org1MSP   ##########"
  echo "#################################################################"
  set -x
  configtxgen -profile TwoOrgsChannel -outputAnchorPeersUpdate ./channel-artifacts/Org1MSPanchors.tx -channelID $CHANNEL_NAME -asOrg Org1MSP
...
  echo
  echo "#################################################################"
  echo "#######    Generating anchor peer update for Org2MSP   ##########"
  echo "#################################################################"
  set -x
  configtxgen -profile TwoOrgsChannel -outputAnchorPeersUpdate \
  ./channel-artifacts/Org2MSPanchors.tx -channelID $CHANNEL_NAME -asOrg Org2MSP
...
}

Channel configuration들을 만드는 부분이다. 이 부분은 echo 부분이 이해하는데 도움이 되어서 남겨놓았다.

configtxgen -profile TwoOrgsOrdererGenesis -outputBlock ./channel-artifacts/genesis.block

먼저 configtxgen 을 이용해서 Genesis block을 만든다. genesis block의 설정과 관련된 부분은 configtx.yaml 파일의 Profiles 에 명시되어있다.

 TwoOrgsOrdererGenesis:
        Capabilities:
            <<: *ChannelCapabilities
        Orderer:
            <<: *OrdererDefaults
            Organizations:
                - *OrdererOrg
            Capabilities:
                <<: *OrdererCapabilities
        Consortiums:
            NewConsortium:
                Organizations:
                    - *Org1
                    - *Org2
                    # - *NewOrg
    TwoOrgsChannel:
        Consortium: NewConsortium
        Application:
            <<: *ApplicationDefaults
            Organizations:
                - *Org1
                - *Org2
                # - *NewOrg
            Capabilities:
                <<: *ApplicationCapabilities

TwoOrgsOrdererGenesis 를 보면 genesis block의 설정을 알 수 있다. 네트워크의 Orderer를 설정하고 Consortiums 섹션에서 consortium 이름과 거기에 속한 organization들을 설정할 수 있다. (configtx.yaml 더 자세한 설명 보기)

마찬가지로

configtxgen -profile TwoOrgsChannel -outputCreateChannelTx ./channel-artifacts/channel.tx -channelID $CHANNEL_NAME

으로 channel configuration 파일을 만든다.

configtxgen -profile TwoOrgsChannel -outputAnchorPeersUpdate ./channel-artifacts/Org1MSPanchors.tx -channelID $CHANNEL_NAME -asOrg Org1MSP

여기서는 각 organization의 anchor peer들의 정보를 transaction으로 만든다.

여기서 만들어진 네 파일들은 모두 channel을 만드는데 설정파일같은 역할을 하게 된다. 네이밍에서 알 수 있지만 모두 .block, .tx로 끝나는 것을 볼 수 있다. 각각은 모두 block이거나 transaction들로 channel이 만들어지고 그 channel의 peer들의 ledger에 모두 기록되게 된다.

지금까지 우리가 만든 것을 정리하면 다음과 같다.

  • Orderer, Peer certificates
  • Genesis Block
  • Channel config transaction
  • Anchor peer config transaction for each organization

이제 channel을 만들기 위한 준비는 끝났고, 노드들을 띄워서 channel을 만들고 네트워크를 형성할 순서이다.

Build Network

# Generate the needed certificates, the genesis block and start the network.
function networkUp () {
  ...
  if [ "${IF_COUCHDB}" == "couchdb" ]; then
    IMAGE_TAG=$IMAGETAG docker-compose -f $COMPOSE_FILE -f $COMPOSE_FILE_COUCH up -d 2>&1
  else
    IMAGE_TAG=$IMAGETAG docker-compose -f $COMPOSE_FILE up -d 2>&1
  fi
  ...
  docker exec cli scripts/script.sh $CHANNEL_NAME $CLI_DELAY $LANGUAGE $CLI_TIMEOUT
}

network를 띄우는데 두 가지 옵션이 있다. state database로 default인 leveldb를 쓸 것이냐 couchdb를 쓸 것이냐가 그것인데 일단은 leveldb를 쓰기로 한다.(바꾸는 것은 정말 어렵지 않다.) 그리고 지금 단계부터는 스크립트를 돌리지 말고 직접 docker에 들어가서 놀아보는 것을 권장한다.

docker-compose -f docker-compose-cli.yaml up 으로 노드들을 띄운다. 여기 -d 옵션을 빼고 실행해서 직접 로그들을 살펴보는 것이 좋다. 또 cli docker container에서 script.sh 을 실행시키는데 이 부분도 직접 container에서 돌려보는 것이 좋다.

더 나아가기 전에 우리가 주로 놀 곳이 cli 이기때문에 docker-compose-cli.yaml 에서 cli 가 어떻게 생겼는지부터 살펴보자. 그리고 혹시 docker나 docker-compose에 익숙하지 않은 사람들은 간단하게나마 어떤 것인지, 무엇을 하는 것인지 이해하고 보면 더 좋을 것 같다.

 # docker-compose-cli.yaml
cli:
    container_name: cli
    image: hyperledger/fabric-tools:$IMAGE_TAG
    tty: true
    stdin_open: true
    environment:
      - GOPATH=/opt/gopath
      - CORE_VM_ENDPOINT=unix:///host/var/run/docker.sock
      - CORE_LOGGING_LEVEL=DEBUG
      #- CORE_LOGGING_LEVEL=INFO
      - CORE_PEER_ID=cli
      - CORE_PEER_ADDRESS=peer0.org1.example.com:7051
      - CORE_PEER_LOCALMSPID=Org1MSP
      - CORE_PEER_TLS_ENABLED=true
      - CORE_PEER_TLS_CERT_FILE=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org1.example.com/peers/peer0.org1.example.com/tls/server.crt
      - CORE_PEER_TLS_KEY_FILE=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org1.example.com/peers/peer0.org1.example.com/tls/server.key
      - 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
      - CORE_PEER_MSPCONFIGPATH=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org1.example.com/users/Admin@org1.example.com/msp
    working_dir: /opt/gopath/src/github.com/hyperledger/fabric/peer
    command: /bin/bash
    volumes:
        - /var/run/:/host/var/run/
        - ./../chaincode/:/opt/gopath/src/github.com/chaincode
        - ./crypto-config:/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/
        - ./scripts:/opt/gopath/src/github.com/hyperledger/fabric/peer/scripts/
        - ./channel-artifacts:/opt/gopath/src/github.com/hyperledger/fabric/peer/channel-artifacts
    depends_on:
      - orderer.example.com
      - peer0.org1.example.com
      - peer1.org1.example.com
      - peer0.org2.example.com
      - peer1.org2.example.com
    networks:
      - byfn

여기서 우리가 살펴볼 부분은 environment 부분들이다. 앞으로 cli에서 우리는 environment variable들을 바꾸면서 다른 organization, 다른 peer들로 옮겨다닐 것이다. 현재 CORE_PEER_ADDRESS=peer0.org1.example.com:7051 로 되어있고 volumes 에서 crypto-config 가 mount 되어있으므로 environment variable을 바꾸지 않는 이상 cli는 peer0.org1처럼 행동하게 된다. 또한 volumes 에서 chancode, crypto-config, scripts, channel-artifacts 가 mount 되어 있는데 이 덕분에 나중에 cli에서 chaincode install과 channel을 만들거나 join할 수 있다.

그럼 이제 다른 터미널을 띄워서 cli로 들어가보자.

docker exec -it cli bash

Create Channel

먼저 channel을 만들기 전에 transaction을 제출할 Orderer의 certificate을 환경변수로 설정해주어야한다. 그리고 설명의 편의를 위해 channel이름도 ‘mychannel’이란 이름으로 환경변수에 넣어주자.

ORDERER_CA=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem
CHANNEL_NAME=mychannel

설정을 마쳤으면 아까 만들어놓은 channel config transaction을 Orderer에게 제출하자.

peer channel create -o orderer.example.com:7050 -c $CHANNEL_NAME -f ./channel-artifacts/channel.tx --tls true --cafile $ORDERER_CA

2018-07-03 08:09:03.373 UTC [msp] GetDefaultSigningIdentity -> DEBU 018 Obtaining default signing identity
2018-07-03 08:09:03.386 UTC [channelCmd] InitCmdFactory -> INFO 019 Endorser and orderer connections initialized
2018-07-03 08:09:03.587 UTC [msp] GetLocalMSP -> DEBU 01a Returning existing local MSP
2018-07-03 08:09:03.588 UTC [msp] GetDefaultSigningIdentity -> DEBU 01b Obtaining default signing identity
2018-07-03 08:09:03.588 UTC [msp] GetLocalMSP -> DEBU 01c Returning existing local MSP
2018-07-03 08:09:03.588 UTC [msp] GetDefaultSigningIdentity -> DEBU 01d Obtaining default signing identity
2018-07-03 08:09:03.588 UTC [msp/identity] Sign -> DEBU 01e Sign: plaintext: 0AED060A1508021A06089FDEECD90522...71A3C9D8BE9212080A021A0012021A00
2018-07-03 08:09:03.588 UTC [msp/identity] Sign -> DEBU 01f Sign: digest: DBFB983C0301B6C58B078AB417AF6C60A4BA1054032DE7D2C1E16A343443BFFB
2018-07-03 08:09:03.592 UTC [channelCmd] readBlock -> DEBU 020 Received block: 0

그러면 Received block: 0을 통해 mychannel channel가 성공적으로 생성되었고, mychannel.block 이라는 genesis block을 받았음을 알 수 있다.

orderer.example.com       | 2018-07-03 08:09:03.382 UTC [orderer/commmon/multichannel] newChain -> INFO 00b Created and starting new chain mychannel

docker-compose 의 로그를 통해서도 성공적으로 orderer에 transaction이 제출되었음을 확인할 수 있다.

Join Organizations to Channel

이제 우리는 성공적으로 channel을 생성했다. 이제 남은 일은 org1.peer1과 org2.peer0, org2.peer1들을 channel에 참여시키는 것이다. 아까도 말했지만 cli docker container는 crypto-config를 mount해놓았기 때문에 환경변수를 다른 peer로 바꾸는 것만으로도 그 peer처럼 행동할 수 있다. 그럼 먼저 org1.peer1으로 바꿔보자.

export CORE_PEER_ADDRESS=peer1.org1.example.com:7051
export CORE_PEER_TLS_CERT_FILE=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org1.example.com/peers/peer1.org1.example.com/tls/server.crt
export CORE_PEER_TLS_KEY_FILE=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org1.example.com/peers/peer1.org1.example.com/tls/server.key
export CORE_PEER_TLS_ROOTCERT_FILE=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org1.example.com/peers/peer1.org1.example.com/tls/ca.crt

그리고 mychannel 에 join 요청을 보내자. Hyperledger Fabric에서는 channel에 join 요청을 할 때는 channel의 Genesis block을 가지고 요청을 보낸다. 그런데 우리는 아까 Genesis block을 만들어 놓았다. 그러면 이것을 가지고 join 요청을 보내보자.

peer channel join -b mychannel.block

2018-07-03 08:18:46.537 UTC [msp] GetLocalMSP -> DEBU 001 Returning existing local MSP
2018-07-03 08:18:46.537 UTC [msp] GetDefaultSigningIdentity -> DEBU 002 Obtaining default signing identity
2018-07-03 08:18:46.544 UTC [channelCmd] InitCmdFactory -> INFO 003 Endorser and orderer connections initialized
2018-07-03 08:18:46.545 UTC [msp/identity] Sign -> DEBU 004 Sign: plaintext: 0AB4070A5C08011A0C08E6E2ECD90510...43ADE79D1BB51A080A000A000A000A00
2018-07-03 08:18:46.545 UTC [msp/identity] Sign -> DEBU 005 Sign: digest: E24CAA368E27DE5E4426119254F09ED6CD03ACF40325AA4D3980931B30EB0091
2018-07-03 08:18:46.599 UTC [channelCmd] executeJoin -> INFO 006 Successfully submitted proposal to join channel
2018-07-03 08:18:46.599 UTC [main] main -> INFO 007 Exiting.....

peer1.org1.example.com    | 2018-07-03 08:18:46.547 UTC [ledgermgmt] CreateLedger -> INFO 027 Creating ledger [mychannel] with genesis block
peer1.org1.example.com    | 2018-07-03 08:18:46.555 UTC [fsblkstorage] newBlockfileMgr -> INFO 028 Getting block information from block storage
peer1.org1.example.com    | 2018-07-03 08:18:46.566 UTC [kvledger] CommitWithPvtData -> INFO 029 Channel [mychannel]: Committed block [0] with 1 transaction(s)
peer1.org1.example.com    | 2018-07-03 08:18:46.570 UTC [ledgermgmt] CreateLedger -> INFO 02a Created ledger [mychannel] with genesis block
peer1.org1.example.com    | 2018-07-03 08:18:46.595 UTC [cscc] Init -> INFO 02b Init CSCC

성공적으로 join 했음을 확인할 수 있다. 마찬가지 방법으로 peer0.org2, peer1.org2도 channel에 join하면 된다. 이제 남은 일은 각 organization에서 anchor peer를 업데이트시키면 네트워크 구성이 마무리된다. Anchor peer의 설정파일은 아까 만들어둔 Org1MSPanchors.tx, Org2MSPanchors.tx 를 이용해서 업데이트하면 된다.

peer channel update -o orderer.example.com:7050 -c mychannel -f ./channel-artifacts/Org1MSPanchors.tx --tls true --cafile $ORDERER_CA

2018-07-03 08:27:42.756 UTC [msp/identity] Sign -> DEBU 00e Sign: plaintext: 0AED060A1508021A0608FEE6ECD90522...605AD9E0CE98DA17B2310EC95E5E5998
2018-07-03 08:27:42.756 UTC [msp/identity] Sign -> DEBU 00f Sign: digest: 61F0D4C012E90F323065F079D00FBB564A3D389FF8E299680973A4F9FD861A88
2018-07-03 08:27:42.775 UTC [channelCmd] update -> INFO 010 Successfully submitted channel update

peer1.org1.example.com    | 2018-07-03 08:27:42.838 UTC [kvledger] CommitWithPvtData -> INFO 037 Channel [mychannel]: Committed block [1] with 1 transaction(s)

성공적으로 업데이트 시켰음을 확인할 수 있다.

Conclusion

Hyperledger Fabric이 어떤 식으로 돌아가는지 확인하고 싶다면, sample로 주어진 shell script를 찬찬히 뜯어보라고 권하고 싶다. 이번 포스팅에서 cryptogen으로 organization과 orderer의 certificate을 만들고, configtxgen으로 channel config transaction을 만든 다음, channel 하나에 두 개의 organization이 있는 네트워크를 만들었다. Channel join은 해당 channel의 genesis block을 가지고 join하게 된다.

다음 시리즈에서는 이제 이 네트워크를 확장하고 싶다면? 어떻게 하면 좋을 지에 대해서 얘기해보겠다.

“Hyperledger Fabric Configure Network – byfn 뜯어보기”의 2개의 댓글

Leave a comment

%d 블로거가 이것을 좋아합니다: