Hyperledger Fabric with ELK Stack

์žญ์žญ์ดยท2021๋…„ 5์›” 3์ผ
2

Hyperledger Fabric

๋ชฉ๋ก ๋ณด๊ธฐ
3/3
post-thumbnail

Hyperledger Fabric with ELK Stack

๐ŸŽ Contents

0. Summary

Fabric์—์„œ ๋ฐœ์ƒํ•˜๋Š” ํŠธ๋žœ์žญ์…˜๋“ค์„ ํ•„ํ„ฐ๋งํ•˜์—ฌ ELK Stack์— ์ €์žฅํ•ด ๋ณด์•˜๋‹ค.

Elastic Search๋Š” ๋ฐ์ดํ„ฐ๋“ค์„ ์ธ๋ฑ์‹ฑํ•˜์—ฌ ๋น ๋ฅธ ๊ฒ€์ƒ‰์„ ๋„์™€์ค€๋‹ค.
ํ–ฅํ›„ ํŽธ์˜๋ฅผ ์œ„ํ•œ ๋‹จ์ˆœ ๋ฐ์ดํ„ฐ ์กฐํšŒ์™€ ๊ฐ™์€ ๊ธฐ๋Šฅ์€ ELK๋กœ ๋”ฐ๋กœ ๋ถ„๋ฆฌํ•  ์ˆ˜ ์žˆ์„ ๊ฒƒ ๊ฐ™๋‹ค.
๋˜ํ•œ, Kibana๋ฅผ ํ†ตํ•ด ์ €์žฅ๋œ ๋ฐ์ดํ„ฐ๋“ค์„ ๋‹ค์–‘ํ•œ ๊ฐ๋„๋กœ ๋ชจ๋‹ˆํ„ฐ๋ง ํ•  ์ˆ˜ ์žˆ๋‹ค๋Š” ์žฅ์ ์„ ๊ฐ€์ง€๊ณ  ์žˆ๋‹ค.

0.1. Data Flow

Fabric Network      1. Block Commit
      |             
      |
      V
 EventListener      2. Block Log & Filtered Log ์ €์žฅ
      |
      |
      V
   Filebeat         3. LogํŒŒ์ผ์˜ ๋ณ€๊ฒฝ์„ ํ™•์ธํ•˜๊ณ  Logstash์—๊ฒŒ ์ „๋‹ฌ
      |
      |
      V
   Logstash         4. Filebeat์˜ Output์„ ํ•„ํ„ฐ๋ง ํ•˜์—ฌ Elaticsearch์—๊ฒŒ ์ „๋‹ฌ
      |
      |
      V
Elasticsearch       5. ๋ฐ์ดํ„ฐ๋ฅผ ์ €์žฅ
      |
      |
      V
    Kibana          6. ์ €์žฅ๋œ ๋ฐ์ดํ„ฐ๋“ค์„ ๋ชจ๋‹ˆํ„ฐ๋ง

0.2. ์‹ค์Šต ํ™˜๊ฒฝ

  • Virtual Box ํ™˜๊ฒฝ ๊ตฌ์„ฑ
    |๋…ธ๋“œ|OS|HOST|MEMORY|CPU|SERVICES|
    |:-:|:-:|:-:|:-:|:-:|:-:|
    |Node 1|Ubuntu|192.168.56.10|4096 MB|2|ELK Stack( Elastic, Logstash, Kibana )
    |Node 2|Ubuntu|192.168.56.20|2048 MB|2|Fabric, Event Listener, File Beat
  • ๋ฒ„์ „
    |์ด๋ฆ„|๋ฒ„์ „|
    |:-:|:-:|
    |Ubuntu|16.04.6 LTS|
    |Docker|18.09.6|
    |Docker-compose|1.23.2|
    |Fabric|1.4.0|
    |Elasticsearch|7.2.0|
    |Logstash|7.2.0|
    |Kibana|7.2.0|
    |Filebeat|7.2.0|

1. Create Fabric network

fabric version 1.4๋ฅผ ์‚ฌ์šฉํ•˜์˜€๊ณ , ๊ณผ์ •์€ ์ƒ๋žตํ•œ๋‹ค.

2. Customize EventListener

2.1. Edit eventsclient.go

์ œ๊ณต๋œ ํŒŒ์ผ eventsclient.go๋ฅผ ์ˆ˜์ •ํ•œ๋‹ค.

๋ชจ๋“  ์†Œ์Šค๋Š” ํ•˜๋‹จ ๋งํฌ์— ์žˆ๋‹ค.

  • ROOT ๋ณ€์ˆ˜์— configtx.yaml์˜ ํŒŒ์ผ ๋ช… ์„ค์ •
// eventsclient.go
const (
	OLDEST = -2
	NEWEST = -1

	ROOT = "configtx"
)
  • readCLInputs() ๋ณ€๊ฒฝ
// eventsclient.go
// ์ด๋ฒคํŠธ ์ˆ˜์‹  PEER ์„ค์ • host:port
//    +   /etc/hosts ํŒŒ์ผ์— PEER ์ถ”๊ฐ€

flag.StringVar(&serverAddr, "server", "peer0.org1.example.com:7051", "The RPC se
rver to connect to.")

// ์ด๋ฒคํŠธ ์ˆ˜์‹  CHANNEL ์„ค์ •
flag.StringVar(&channelID, "channelID", "mychannel", "The channel ID to deliver 
from.")

// server.key, server.crt, ca.crt||ca.pem ๊ฒฝ๋กœ ์„ค์ •
flag.StringVar(&clientKeyPath, "clientKey", "/crypto-config/peerOrganizations/or
g1.example.com/peers/peer0.org1.example.com/tls/server.key", "Specify path to th
e client TLS key")
flag.StringVar(&clientCertPath, "clientCert", "/crypto-config/peerOrganizations/
org1.example.com/peers/peer0.org1.example.com/tls/server.crt", "Specify path to 
the client TLS certificate")
flag.StringVar(&serverRootCAPath, "rootCert", "/crypto-config/peerOrganizations/
org1.example.com/peers/peer0.org1.example.com/tls/ca.crt", "Specify path to the 
server root CA certificate")
  • initMSP() ๋ณ€๊ฒฝ
// eventclient.go
var mspMgrConfigDir = "/crypto-config/peerOrganizations/org1.example.com/users/Admin@org1.example.com/msp"
var mspID = "Org1MSP"
var mspType = "bccsp"

2.2. FABRIC_CFG_PATH ์„ค์ •

export FABRIC_CFG_PATH=~/fabric-samples/first-network

์œ„ ๊นŒ์ง€ ์ง„ํ–‰ํ•œ ํ›„ ์‹คํ–‰ํ•˜๋ฉด, PEER์˜ commit ๋ฐœ์ƒ ์‹œ BLOCK ์ •๋ณด๊ฐ€ console์— ๋‚˜ํƒ€๋‚œ๋‹ค.
BLOCK์—์„œ ์‚ฌ์šฉํ•˜๊ณ ์ž ํ•˜๋Š” ๋ฐ์ดํ„ฐ๋ฅผ ์ถ”์ถœํ•ด์„œ ์‚ฌ์šฉํ•  ๊ฒƒ์ด๋‹ค. (write set)

saveFilteredLog() : JSON Parsing function
์œ„ ๋ฉ”์†Œ๋“œ๋Š” JSON์„ Parsingํ•˜์—ฌ .log ํŒŒ์ผ๋กœ ์ €์žฅํ•œ๋‹ค.
๋ณธ ๊ธ€์—์„œ๋Š” Docker๋ฅผ ์ด์šฉํ•˜์—ฌ EventListener๋ฅผ ํ™œ์„ฑํ™” ์‹œํ‚ฌ ๊ฒƒ์ด๋‹ค.

3. Dockerizing EventListener

EventListener๋ฅผ Docker containerํ™” ์‹œํ‚ค๋Š” ์ž‘์—…์ด๋‹ค.
EventListener์˜ image๋Š” eventsclient.go๋ฅผ ์‹คํ–‰ํ•˜๊ธฐ์œ„ํ•œ ํ™˜๊ฒฝ ์…‹ํŒ…์ด ๋˜์–ด ์žˆ๋‹ค.

3.1. Create docker-eventlistener.yaml

vim docker-eventlistener.yaml
version: '2'

services:
  eventlistener:
    container_name: eventlistener
    image: eventlistener:1.0
    command: go run /eventsclient.go
    volumes:
      - ./crypto-config:/crypto-config
      - ./eventdir/log:/log
      - ./eventdir/eventsclient.go:/eventsclient.go
    extra_hosts:
      - peer0.org1.example.com:192.168.56.20

3.2. Execute

docker-compose -f ./docker-eventlistener.yaml up -d
docker logs -f eventlistener

๋‹ค์Œ๊ณผ ๊ฐ™์€ ๋กœ๊ทธ๊ฐ€ ๋‚จ๋Š”๋‹ค.

{
	"data": {
		"data": [
			{
				"payload": {
					"data": {
						"actions": [
							{
								...
								},

๋˜ํ•œ docker-eventlistener.yaml์—์„œ ์ง€์ •ํ•œ ๊ฒฝ๋กœ๋กœ logํŒŒ์ผ 2๊ฐœ๊ฐ€ ๋‚จ๋Š”๋‹ค.

cd $PATH/eventdir/log

ls
# blockhistory.log  filteredblockhistory.log

4. ELK Stack

ELK Stack์„ ํ™œ์„ฑํ™” ์‹œํ‚จ๋‹ค.

4.1. Install ELK Stack

๋ณธ ๋ฌธ์„œ๋Š” ELK Stack์„ Docker๋กœ ์‹คํ–‰ํ•œ๋‹ค.

# Native
wget https://artifacts.elastic.co/downloads/elasticsearch/elasticsearch-7.2.0-linux-x86_64.tar.gz
wget https://artifacts.elastic.co/downloads/logstash/logstash-7.2.0.tar.gz
wget https://artifacts.elastic.co/downloads/kibana/kibana-7.2.0-linux-x86_64.tar.gz
tar -zxvf elasticsearch-7.2.0-linux-x86_64.tar.gz
tar -zxvf logstash-7.2.0.tar.gz
tar -zxvf kibana-7.2.0-linux-x86_64.tar.gz

# Docker
docker pull docker.elastic.co/elasticsearch/elasticsearch:7.2.0
docker pull docker.elastic.co/logstash/logstash:7.2.0
docker pull docker.elastic.co/kibana/kibana:7.2.0

4.2. Create ELK's docker-compose.yaml

version: '2.2'

services:
  elasticsearch01:
    image: docker.elastic.co/elasticsearch/elasticsearch:7.2.0
    container_name: elasticsearch01
    environment:
      - node.name=elasticsearch01
      - cluster.initial_master_nodes=elasticsearch01
    volumes:
      - ./elasticsearch/config/elasticsearch.yml:/usr/share/elasticsearch/config/elasticsearch.yml
      - ./elasticsearch/data/:/usr/share/elasticsearch/data
    ports:
      - 9200:9200

  kibana:
    image: docker.elastic.co/kibana/kibana:7.2.0
    container_name: kibana
    environment:
      SERVER_NAME: kibana.example.com
    volumes:
      - ./kibana/config/:/usr/share/kibana/config/
    ports:
      - 5601:5601
    depends_on:
      - elasticsearch01

  logstash:
    image: docker.elastic.co/logstash/logstash:7.2.0
    container_name: logstash
    volumes:
      - ./logstash/:/usr/share/logstash/
    command: ./bin/logstash -f /usr/share/logstash/config/logstash.conf
    ports:
      - 5044:5044
      - 9600:9600
    depends_on:
      - elasticsearch01

4.3. Execute ELK Stack

sudo sysctl vm.max_map_count=262144
docker-compose -f ./docker-compose.yaml up -d

4.4. Edit ELK's configuration

  • elasticsearch.yml
network.host: 0.0.0.0
http.port: 9200
  • logstash.conf
input {
  beats {
    port => 5044
  }
  stdin { }
}

filter {
        json {
                source => "message"
                add_field => {
                        "%{key}" => "%{value}"
                }
        }
}

output {
  elasticsearch {
    hosts => ["192.168.56.10:9200"]
  }

  stdout {
    codec => rubydebug
  }
}
  • kibana.yml
server.port: 5601
server.host: "0.0.0.0"
elasticsearch.hosts: ["http://192.168.56.10:9200"]

5. Filebeat

ELK Stack๊ณผ Fabric, EventListener๋ฅผ ์‹คํ–‰ํ–ˆ๋‹ค.
Event๋ฐœ์ƒ ์‹œ, ์ƒˆ๋กœ ์ž‘์„ฑ๋˜๋Š” .logํŒŒ์ผ์„ logstash์—๊ฒŒ ๋„˜๊ฒจ์ฃผ๋Š” ๋งค๊ฐœ์ฒด๊ฐ€ ํ•„์š”ํ•˜๋‹ค.
์šฐ๋ฆฌ๋Š” Filebeat๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ELK Stack๊ณผ Fabric์„ ์—ฐ๊ฒฐ ์‹œ์ผœ ์ค„ ๊ฒƒ์ด๋‹ค.

Exception in thread "main" java.nio.file.AccessDeniedException

์œ„์™€ ๊ฐ™์€ ์˜ค๋ฅ˜๊ฐ€ ๋‚œ๋‹ค๋ฉด ํŒŒ์ผ ๊ถŒํ•œ์„ ํ™•์ธ!

5.1. Install Filebeat

docker pull docker.elastic.co/beats/filebeat:7.2.0

5.2. Write Filebeat Configuration

touch filebeat.yaml
vim filebeat.yml
filebeat.inputs:
- type: log
  enabled: true
  paths:
    - /var/log/filteredblockhistory.log
  multiline.pattern: "{\"write\":\"generated\"}"
  multiline.negate: true
  multiline.match: after

filebeat.config:
  modules:
    path: ${path.config}/modules.d/*.yml
    reload.enabled: false

filebeat.autodiscover:
  providers:
    - type: docker
      hints.enabled: true

processors:
- add_cloud_metadata: ~

output.logstash:
  hosts: "192.168.56.10:5044"

5.3. Filebeat to docker

touch docker-filebeat.yaml
vim docker-filebeat.yaml
version: '2.2'

services:
  filebeat:
    image: docker.elastic.co/beats/filebeat:7.2.0
    container_name: filebeat
    command: filebeat -e -strict.perms=false
    volumes:
      - ./filebeat.yml:/usr/share/filebeat/filebeat.yml
      - /var/run/docker.sock:/var/run/docker.sock
      - /home/jack2/fabric-samples/first-network/eventdir/log:/var/log

5.4. Run Filebeat

docker-compose -f docker-filebeat.yml up -d

6. Check

์ •๋ฆฌํ•˜์ž๋ฉด, ์˜ˆ์ œ(mycc)์—์„œ๋Š” ์ด 2๊ฐœ์˜ Key(a, b)๊ฐ€ ๋ณ€๊ฒฝ๋œ๋‹ค.
Block์˜ Read/Write Set์—๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์€ ๊ธฐ๋ก์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋‹ค.
(value๋Š” base64 encoded ๋˜์–ด์žˆ๋‹ค.)

"writes": [
	{
		"is_delete": false,
		"key": "a",
		"value": "OTA="
	},
	{
		"is_delete": false,
		"key": "b",
		"value": "MjEw"
	}
]

์œ„ ์ •๋ณด๋“ค์ด Filtered ๋˜์–ด filteredblockhistory.log๋กœ ์ €์žฅ๋œ๋‹ค.

{"write":"generated"}
{"is_delete":"false","key":"a","value":"90"}
{"write":"generated"}
{"is_delete":"false","key":"b","value":"210"}

Log ํŒŒ์ผ์€ Filebeat๋ฅผ ํ†ตํ•ด Logstash๋กœ ์ „๋‹ฌ๋˜๊ณ , ์ตœ์ข…์ ์œผ๋กœ Elasticsearch์— ์ ์žฌ๋œ๋‹ค.

์ด์ œ, ์ •์ƒ์ ์œผ๋กœ Elasticsearch์— ์ €์žฅ๋˜์—ˆ๋Š”์ง€ ํ™•์ธ ํ•  ๊ฒƒ์ด๋‹ค.
ํ™•์ธ์€ Kibana(Elasticsearch์˜ ๋ชจ๋‹ˆํ„ฐ๋ง ํˆด)์„ ์ด์šฉํ•œ๋‹ค.
์ธํ„ฐ๋„ท ๋ธŒ๋ผ์šฐ์ €์—์„œ Kibana๊ฐ€ ํ™œ์„ฑํ™” ๋˜์–ด ์žˆ๋Š” port๋กœ ์ ‘๊ทผํ•˜๋ฉด ๋œ๋‹ค.
๋ณธ ์˜ˆ์ œ๋ฅผ ๊ทธ๋Œ€๋กœ ๋”ฐ๋ผํ–ˆ๋‹ค๋ฉด 192.168.56.10:5601 ์„ ํ†ตํ•ด Kibana๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.

์•„๋ž˜์™€ ๊ฐ™์ด ์ด 2๊ฐœ์˜ Key์— ๋Œ€ํ•œ ๋ณ€๊ฒฝ์ด ์ €์žฅ๋œ ๊ฒƒ์„ ํ™•์ธ ํ•  ์ˆ˜ ์žˆ๋‹ค.

๊ฐ๊ฐ์˜ txn์„ ๋ˆŒ๋Ÿฌ ํ™•์ธํ•ด ๋ณธ๋‹ค.

Kibana์˜ ์‚ฌ์šฉ๋ฒ•์„ ์ตํžŒ๋‹ค๋ฉด ํšจ๊ณผ์ ์œผ๋กœ ๋ฐ์ดํ„ฐ๋ฅผ ๋ชจ๋‹ˆํ„ฐ๋ง, ๊ด€๋ฆฌ ํ•  ์ˆ˜ ์žˆ๋‹ค.


๋ชจ๋“  ์†Œ์Šค๋Š” ๊นƒํ—ˆ๋ธŒ์— ์˜ฌ๋ ค๋†“์•˜๋‹ค.

0๊ฐœ์˜ ๋Œ“๊ธ€