--- tags: 套件部署,BlockChain GA: UA-79596126-4 title: 第一個區塊鏈服務 Fabric-sample --- ###### 作者: 史綽琳 ###### 撰寫日期: 2021/01/19 {%hackmd BJrTq20hE %} # 第一個區塊鏈服務 Fabric-sample Fabric-sample 是官方提供給初學 Hyperledger Fabric 的人使用的一套範例,github 連結在[這裡](https://github.com/hyperledger/fabric-samples),我下面所示範的是v2.2的版本,我不確定你看到的時候已經更新到哪裡了,至少保證2.2是這樣玩! 請放心服用 [TOC] ## 環境安裝 首先我們先讓環境符合 Hyperledger Fabric 的需求,以下是我的環境 - ubuntu 20.04 - go1.15.2 linux/amd64 - docker 19.03.8 - fabric-sample 2.2 部署完成後的架構會長這樣 ![](https://i.imgur.com/w5hlzSg.jpg) ### 相依套件安裝 基本套件 ``` $ sudo apt update && sudo apt upgrade -y $ sudo apt-get install git curl wget -y ``` 安裝 docker ``` $ sudo apt-get -y install docker-compose $ sudo usermod -aG docker ${USER} ===> logout and login are needed after adding docker group, check everything is ok with the command below $ id -nG ``` 安裝 go ``` $ wget https://golang.org/dl/go1.15.2.linux-amd64.tar.gz $ sudo tar -xvf go1.15.2.linux-amd64.tar.gz $ sudo chown -R root:root ./go $ sudo mv go /usr/local $ sudo ln -s /usr/local/go/bin/go /usr/bin/go $ go version ``` 取得 fabric-sample ``` # 若後續均不接任何版本資訊,則預設抓最新版本 $ curl -sSL http://bit.ly/2ysbOFE | bash # 若取得特定版本,則在後方接版本編號 $ curl -sSL http://bit.ly/2ysbOFE | bash -s -- <fabric_version> <fabric-ca_version> <thirdparty_version> $ curl -sSL http://bit.ly/2ysbOFE | bash -s -- 2.2 ``` 完成後就會像這樣 ![](https://i.imgur.com/KS64F7J.png) 這樣就算完成環境的準備了,接下來... ## 進行操作 ### 建立container fabric 其實是由許多的container組合而成的服務,官方提供的 sample 中會建出1個 orderer、2個 peer 與2個 org,我們直接用官方提供的 script 進行演練。 進入操作資料夾位置 ``` $ cd fabric-samples/test-network $ ls ``` ![](https://i.imgur.com/SWLA4R2.png) 這邊可以看到幾個資料夾,我們針對重點進行說明 1. configtx - 此資料夾裝著創世區塊的設定 configtx.yaml ,這個設定檔會定義你的 orderer 共識演算法、組織的 policy、peer 和 orderer 的名稱與金鑰擺放位置。(以後再細說)。 2. docker - 此資料夾中裝著基本的 orderer 和 peer 的 docker-compose 設定檔,這當中定義了 orderer 和 peer 的初始設定,對於金鑰、憑證、身分、地址等都會在此定義,其他檔案則是示範ca server 與 couch db 的範例,此處不多贅述。 3. network.sh - 就是 fabric-sample 的主要運作流程啦,把它裡面的指令一個一個拆出來就是手動部署的步驟了,那後面我會快速地針對這作解釋。 4. organizations - 這裡存放著此範例中所有的金鑰與憑證,是重要的資料夾,之後透過 ca server 的部署,有很大一部分是參考此處作比較的,因此之後也會對此資料夾中的東西作說明。 5. 其他... 暫時用不到,不再贅述。 啟動 fabric 的 container 吧 ``` ./network.sh up ``` 完成後可以看到3個container啟動了,分別是 peer0.org2.example.com、peer0.org1.example.com 和 orderer.example.com ![](https://i.imgur.com/euXCSZc.png) 至此代表 fabric 環境已經建立完成了 ### 建立通道 如果看過前面介紹 Hyperledger Fabric ,相信你已經大略了解 channel 的重要性,部署 channel 便會用到上面提到的 configtx ,透過該 yaml 檔可以定義整個 Fabric 的走向,再加上區塊鏈不可逆的特性,初始區塊是非常重要的,記得開始部署前先想好自己要的架構再部署,否則就是無限的重頭。這裡因為官方都幫你設定好了,就直接下手吧 ``` ./network.sh createChannel ``` 完成後可以看到這句小小的提示 ![](https://i.imgur.com/5oUlfsz.png) 至此代表 channel 建立成功並且 peer 也加入 channel ### 部署 chaincode Chaincdoe 就是核心啦,官方 script 都幫妳寫好了就簡單這個指令下下去就對了 ``` ./network.sh deployCC ``` 完成後可以看到這令人匪夷所思的結語 ![](https://i.imgur.com/hULqzhh.png) 「Chaincode initialization is not required」什麼意思呢? 是不是部署出錯了? 別擔心,因為2.2開始此 script 只是幫你把預設的 chaincode 打包好並且安裝在 peer 和 channel 中,而1.4的則是連 init 都幫你完成了,所以如果你是從1.4跳到2.2的話,別緊張~這一切還在掌控中。 接著,我們便需要手動 init 該 chaincode。在 init 之前請先切換身分成 peer0.org1.example.com (當然你要使用 peer0.org2.example.com 也可以),才可以接續操作喔! :::info :bulb: **提示**: 如果你實際部署的環境中有cli的話,直接進去cli進行操作就可以了! ex: docker exec -it <container name of cli> /bin/bash kubectl exec -it <container/pod name of cli> -- /bin/bash ::: ``` export PATH=${PWD}/../bin:$PATH export FABRIC_CFG_PATH=$PWD/../config/ export CORE_PEER_TLS_ENABLED=true export CORE_PEER_LOCALMSPID="Org1MSP" export CORE_PEER_TLS_ROOTCERT_FILE=${PWD}/organizations/peerOrganizations/org1.example.com/peers/peer0.org1.example.com/tls/ca.crt export CORE_PEER_MSPCONFIGPATH=${PWD}/organizations/peerOrganizations/org1.example.com/users/Admin@org1.example.com/msp export CORE_PEER_ADDRESS=localhost:7051 ``` 身份切換完成後,便可以 init 已安裝的 chaincode 了,如下 ``` peer chaincode invoke -o localhost:7050 --ordererTLSHostnameOverride orderer.example.com --tls --cafile ${PWD}/organizations/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem -C mychannel -n basic --peerAddresses localhost:7051 --tlsRootCertFiles ${PWD}/organizations/peerOrganizations/org1.example.com/peers/peer0.org1.example.com/tls/ca.crt --peerAddresses localhost:9051 --tlsRootCertFiles ${PWD}/organizations/peerOrganizations/org2.example.com/peers/peer0.org2.example.com/tls/ca.crt -c '{"function":"InitLedger","Args":[]}' ``` 當你看到如下圖的提示訊息,便代表 init 成功了,接著便可以嘗試查詢帳本內容 ![](https://i.imgur.com/5iaJpi3.png) ``` peer chaincode query -C mychannel -n basic -c '{"Args":["GetAllAssets"]}' ``` ![](https://i.imgur.com/d4KbuB7.png) 這樣一個熱騰騰的 Hyperledger Fabric 的服務便完成了,真是可喜可賀!! ## 過程說明 好啦,如果你只是想要透過 fabric-sample 先成功部署出來的話,上述的過程就足夠了,接下來我們會來拆解上述過程中跑出來的訊息所代表的意思 ### 建立container 在此步驟中,我們主要是透要取得 peer 、orderer 與 org 的相關憑證與金鑰,在沒有 ca server 的狀況下,Fabric 提供一個產金鑰的套件叫做「cryptogen」,此套件的原理跟 openssl 差不多~有興趣的自己去找資料。 我們會提供他一個設定檔,定義出我們有幾個 organization 、該 organization 有幾個 peer、同時我們會有幾個 orderer 等資訊,因此我們先來看看 script 輸出的流程 ![](https://i.imgur.com/gNYmUia.png) 在圖中我們可以看到提供給 cryptogen 的 config 檔案,以下先貼出 org1 的設定 ``` # Copyright IBM Corp. All Rights Reserved. # # SPDX-License-Identifier: Apache-2.0 # # --------------------------------------------------------------------------- # "PeerOrgs" - Definition of organizations managing peer nodes # --------------------------------------------------------------------------- PeerOrgs: # --------------------------------------------------------------------------- # Org1 # --------------------------------------------------------------------------- - Name: Org1 Domain: org1.example.com EnableNodeOUs: true # --------------------------------------------------------------------------- # "Specs" # --------------------------------------------------------------------------- # Uncomment this section to enable the explicit definition of hosts in your # configuration. Most users will want to use Template, below # # Specs is an array of Spec entries. Each Spec entry consists of two fields: # - Hostname: (Required) The desired hostname, sans the domain. # - CommonName: (Optional) Specifies the template or explicit override for # the CN. By default, this is the template: # # "{{.Hostname}}.{{.Domain}}" # # which obtains its values from the Spec.Hostname and # Org.Domain, respectively. # --------------------------------------------------------------------------- # - Hostname: foo # implicitly "foo.org1.example.com" # CommonName: foo27.org5.example.com # overrides Hostname-based FQDN set above # - Hostname: bar # - Hostname: baz # --------------------------------------------------------------------------- # "Template" # --------------------------------------------------------------------------- # Allows for the definition of 1 or more hosts that are created sequentially # from a template. By default, this looks like "peer%d" from 0 to Count-1. # You may override the number of nodes (Count), the starting index (Start) # or the template used to construct the name (Hostname). # # Note: Template and Specs are not mutually exclusive. You may define both # sections and the aggregate nodes will be created for you. Take care with # name collisions # --------------------------------------------------------------------------- Template: Count: 1 SANS: - localhost # Start: 5 # Hostname: {{.Prefix}}{{.Index}} # default # --------------------------------------------------------------------------- # "Users" # --------------------------------------------------------------------------- # Count: The number of user accounts _in addition_ to Admin # --------------------------------------------------------------------------- Users: Count: 1 ``` 前面比較不需要說明,org 的 Name 和 Domain 在此進行定義,EnableNodeOUs 則是 Fabric 中 policy 會用到的一種功能,未來在針對 fabric ca 的時候會在說明,這邊先知道就好。 下面 Template 與 Users 的部分雖然註解也有說明了,我這邊就簡要的提一下: - Template 是在說明我這個組織要建立多少個 peer 節點 - Users 則是說已 admin 權限進行操作的最大連線數,簡單來說我同時只允許一個連線是已 admin 的身分進行的 Org2 的部分也相同,這邊就跳過,接著來看看 orderer 的 ``` # Copyright IBM Corp. All Rights Reserved. # # SPDX-License-Identifier: Apache-2.0 # # --------------------------------------------------------------------------- # "OrdererOrgs" - Definition of organizations managing orderer nodes # --------------------------------------------------------------------------- OrdererOrgs: # --------------------------------------------------------------------------- # Orderer # --------------------------------------------------------------------------- - Name: Orderer Domain: example.com EnableNodeOUs: true # --------------------------------------------------------------------------- # "Specs" - See PeerOrgs for complete description # --------------------------------------------------------------------------- Specs: - Hostname: orderer SANS: - localhost ``` 對 orderer 來說就相對簡單一點,前面的 Name、Domain、EnableNodeOUs 是相同的意思,代過 Spec 則是你會用到的主機名稱,在實際的 fabric 環境中我們會使用多主機的方式進行分布式部署,而在 k8s 的環境中更是會透過多個 pod 組合成一個完整的 k8s 環境,因此此處便需要定義你環境中會用的主機名稱或是域名。 此範例因為都在 localhost,所以就不需要設定額外的域名了。 當產生完這些金鑰與憑證後,script 便會開始產生創世區塊,也就是前面所說會用到的 configtx.yaml,因為該 yaml 的內容太多了這邊實在沒辦法一次說完,因此先針對執行的指令進行說明 ``` configtxgen -profile TwoOrgsOrdererGenesis -channelID system-channel -outputBlock ./system-genesis-block/genesis.block ``` - TwoOrgsOrdererGenesis 是你想要執行的共識演算法,在 configtx 定義最後一項 Profiles 中的名稱,他會帶入上面所有設定的參數。 - genesis.block 便是此區塊鏈的最根本 - 「創世區塊」,此創世區塊便是所有節點在加入時必須取得的訊息,因此請好好保留著! 接著就是啟動各個 container,當然所有設定檔都已經先行完成了,在 fabric-samples/test-network/docker 資料夾中,以後在 k8s 部署的時候再說明。 ### 建立通道 channel 的部分我們將所有的指令分成兩個部分來看 #### 第一部分,基本節點結構 主要來說在創建通道前,我們要提供一些訊息給通道知道,例如有多少組織在上面跑、組織對外溝通的 anchorpeer 是誰等 ``` configtxgen -profile TwoOrgsChannel -outputCreateChannelTx ./channel-artifacts/mychannel.tx -channelID mychannel configtxgen -profile TwoOrgsChannel -outputAnchorPeersUpdate ./channel-artifacts/Org1MSPanchors.tx -channelID mychannel -asOrg Org1MSP configtxgen -profile TwoOrgsChannel -outputAnchorPeersUpdate ./channel-artifacts/Org2MSPanchors.tx -channelID mychannel -asOrg Org2MSP ``` - TwoOrgsChannel 也是在 configtx.yaml 中定義的,並產出一個名為[mychannel]的通道資訊 (mychannel.tx) - mychannel.tx 是在上述產生創世區塊時一併產出的,用以作為 channel 的初始區塊作使用 - Org1MSPanchors.tx 是在上述產生創世區塊時一併產出的,根據 configtx 定義 org1 的 anchorpeer 是誰。 - Org2MSPanchors.tx 是在上述產生創世區塊時一併產出的,根據 configtx 定義 org2 的 anchorpeer 是誰。 #### 第二部分,通道結構 接著就是實際建立通道的時候了,首先先根據先前產生的通道初始訊息 (mychannel.tx) 進行搭建通道的初始區塊 (mychannel.block) ``` peer channel create -o localhost:7050 -c mychannel --ordererTLSHostnameOverride orderer.example.com -f ./channel-artifacts/mychannel.tx --outputBlock ./channel-artifacts/mychannel.block --tls --cafile /root/max-test/fabric-samples/test-network/organizations/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem ``` 由於 fabric-sample 預設是有啟用 TLS 的服務,因此此處必須將 cafile 帶入執行參數中,所以指令才會這麼長,當你看到下圖的狀況,即代表你成功創建通道了! ![](https://i.imgur.com/bkGTwOd.png) 接著將兩個組織加入此通道之中,並且告訴通道組織中的 anchorpeer 是誰 ``` peer channel join -b ./channel-artifacts/mychannel.block peer channel update -o localhost:7050 --ordererTLSHostnameOverride orderer.example.com -c mychannel -f ./channel-artifacts/Org1MSPanchors.tx --tls --cafile /root/max-test/fabric-samples/test-network/organizations/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem ``` 這邊或許有小夥伴會搞混,上面不是已經知道 anchorpeer 是誰了嗎? 為甚麼還要再說一次? 上面產出的 Org1MSPanchors.tx 有注意到副檔名嗎? 沒錯,他對於通道來說只是一個敘述設定檔,因此必須透過 *.tx 這個檔案告訴通道說,org1 和 org2 的 anchorpeer 是誰,這樣才會確實在該通道上運行。 ![](https://i.imgur.com/iPn8KKW.png) 之後看到上圖,就可以結束這回合了!! ### 部署 chaincode 接著就是部署 chaincode 的時間啦,首先 fabric 預設是會使用 go 進行部署,畢竟整個 fabric 就是透過 go 寫出來的,當然她也支援 java、javascript,有興趣的可以試著透過 java 等方式部署看看 在部署之前,系統會先幫你透過 go 安裝一些相依的組件,會先運行下列指令,在 script 輸出的狀況下應該是看不到的 ``` GO111MODULE=on go mod vendor ``` 完成後查看檔案會多了一個 vendor 的檔案,這是該 chaincode 會用到的相依檔案 接著我們要將 chaincode 打包成 tar 檔,讓 peer 可以進行安裝。 ``` peer lifecycle chaincode package basic.tar.gz --path ../asset-transfer-basic/chaincode-go/ --lang golang --label basic_1.0 ``` 之後便是讓兩個組織的 peer 安裝該 tar 檔 ``` peer lifecycle chaincode install basic.tar.gz ``` 當完成此步驟後,系統會輸出一串 packet ID,這個 ID 是根據你所安裝的 chaincode 隨機產出的代碼,主要用於識別在通道的 chaincode。 ![](https://i.imgur.com/1c8OSyt.png) 通常當看到這個 packet ID 即代表 chaincode 已經安裝完成了,不過我們還是可以透過指令查看在 peer 上已經安裝的 chaincode ``` peer lifecycle chaincode queryinstalled ``` ![](https://i.imgur.com/eqfC4BK.png) 接著讓我們允許該 chaincode 允許在我們的通道上運行吧!! ``` peer lifecycle chaincode approveformyorg -o localhost:7050 --ordererTLSHostnameOverride orderer.example.com --tls --cafile /root/max-test/fabric-samples/test-network/organizations/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem --channelID mychannel --name basic --version 1.0 --package-id basic_1.0:4ec191e793b27e953ff2ede5a8bcc63152cecb1e4c3f301a26e22692c61967ad --sequence 1 ``` - version 由於你的 chaincode 可能還會需要修正,但已經在上面的 chaincode 是不可以被刪除的,所以當更新該 chaincode 基礎設定時,便會有 version 的差異 - sequence 這是確認該 chaincode 是否有重複安裝在相同的 channel 中,以此避免相同的 chaincode 發生打架事件 這時候我們可以來看看,在通道上的組織們是不是都批准了該 chaincode 的運行,當組織都批准後便會出現下圖狀況 ``` peer lifecycle chaincode checkcommitreadiness --channelID mychannel --name basic --version 1.0 --sequence 1 --output json ``` ![](https://i.imgur.com/8kz0LbW.png) 當組織們都批准這隻 chaincode 之後,我們便可以把 chaincode 上傳到通道上進行使用了;是的不要懷疑,你上面剛剛作的所有事情都只是把 chaincode 安裝到 peer,並且允許他運行而已,不代表他已經在通道中使用了! ``` peer lifecycle chaincode commit -o localhost:7050 --ordererTLSHostnameOverride orderer.example.com --tls --cafile /root/max-test/fabric-samples/test-network/organizations/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem --channelID mychannel --name basic --peerAddresses localhost:7051 --tlsRootCertFiles /root/max-test/fabric-samples/test-network/organizations/peerOrganizations/org1.example.com/peers/peer0.org1.example.com/tls/ca.crt --peerAddresses localhost:9051 --tlsRootCertFiles /root/max-test/fabric-samples/test-network/organizations/peerOrganizations/org2.example.com/peers/peer0.org2.example.com/tls/ca.crt --version 1.0 --sequence 1 ``` 完成上船之後,我就來檢查看看該 channel 上的 chaincode 有哪些 ``` peer lifecycle chaincode querycommitted --channelID mychannel --name basic ``` ![](https://i.imgur.com/fRoCW0s.png) 這樣就恭喜你完成了 chaincode 的部署了!! 也恭喜你終於看完了整個 fabric 的部署流程了!! ## 事後屁話 乾,不得不說這真的很煩,裡面的毛很多,一下又要因為權限失敗、一下又因為網路失敗、還有ㄊㄇㄉ版本問題,搞的心很累。前期因為版本的問題,用的設定檔會有點不一樣,尤其不知道原理的時候看著 script 跑完之後心裡只有一種「阿...然後呢?」的想法,拆他 script 的時候也花了不少時間去理解他到底在公三小,希望這篇可以提升你對於 fabric 的一些流程概念,這樣之後手動分布式部署、手動k8s 部署都是遵從這些規則,之後再慢慢更新吧,我先睡了... じゃあまたね