AWS와 Puppeth를 활용한 Ethereum Private Blockchain (POA) 구축하기

in #ethereum6 years ago (edited)

포스팅 목적: 일반적으로 Private Network를 구축할 때 필요한 까다롭고 다소 번거로운 작업들을 빠르고 효율적으로 할 수 있는 Puppeth의 사용법과 AWS EC2에게 각 역할(faucet, wallet, dashboard etc...)을 부여하여 필수요소들을 갖춘 프라이빗 네트워크를 구축하는 방법을 알아본다.

1. 들어가기 전에

Proof Of Authority

먼저, Proof Of Authority(POA)에 대해 알아볼 필요가 있다. Proof Of Work와는 다르게 POA는 일반적인 노드들이 Miner로 참여할 수 없다. 그 말인 즉슨, 네트워크에 기여할 수 있는 혹은 네트워크를 운영하는 주체는 아무나 될 수 없다는 말이다. 네트워크를 운영할 수 있는 건 해당 네트워크를 제작한 설계자가 초기에 결정하게 되어 있다.

여기서 네트워크를 운영한다는 말은 거래기록들을 담는 블록을 생성하는 컴퓨팅 작업을 의미한다. POW에서는 이 작업을 mining 이라 표현하고 작업을 진행하고 있는 노드를 miner라 칭한다. 그러나 POA에서는 이 작업을 signing이라 표현하고 작업을 진행하는 노드를 signer라고 표현한다.

처음 네트워크를 생성할 때 3개의 Sealer 노드를 지정했다면, 그 3개의 노드가 블록을 생성하는 역할을 담당하게 된다. 만약 특정 노드가 새로운 Sealer로 네트워크에 참여하고 싶다면, 기존의 Sealer들이 투표 매커니즘을 진행해서 참여여부를 결정해야 한다.

일반적으로 POW에서는 특정 이더리움 노드의 genesis file 그리고 enode 정보를 알고 있으면 네트워크에 접속해서 miner로 활동할 수 있었지만, POA에서는 그러한 작업이 기본적으로 차단되어 있다. 즉, 네트워크의 내용은 볼 수 있지만 블록을 생성하거나 거래를 검증하는 작업 등은 할 수 없다.

필자가 준비하고 있는 서비스에는 POA 환경이 좀 더 적합하기에 이더리움의 POA 합의 알고리즘 엔진(Clique)을 이용하여 환경구성을 진행해보려 한다.

2. AWS EC2 환경 구축 진행

2.1 AWS EC2

포스팅에서 사용되는 EC2인스턴스들의 정보는 아래와 같다.

  • 컨트롤러 역할(다른 geth 노드들을 제어)을 하는 t2.micro Ubuntu 16.04 인스턴스 1개
  • 노드 역할을 하는 t2.medium (10 gb) Ubuntu 16.04 인스턴스 3개

각 인스턴스를 생성할 때 주의할 점은 보안 그룹과 Keypair를 모두 동일한 세팅으로 맞춰줘야 한다는 점이다. 또한 puppeth 프로그램 소스코드 내에서 취급되는 키의 이름은 id_rsa이므로 동일한 이름으로 keypair를 생성해주는 게 좋다.

keypair를 생성해주면 id_rsa.pem 파일을 다운로드 받을 수 있을 것이다. 각 인스턴스에 접근할 때 사용되므로 잘 보관해두도록 한다. 맥이나 리눅스의 경우 ~/.ssh 디렉토리 내에 위치시켜두는 것이 좋다. 해당 디렉토리 내에서도 용도에 따라 구분할 수 있게 디렉토리를 생성하여 체계적으로 관리할 수 있도록 한다. 필자의 경우 ~/.ssh/AWS 디렉토리 내에 모든 키를 보관한다.

이로써 id_rsa.pem 파일만 있으면 ssh -i "id_rsa.pem" ubuntu@<ip of EC2 instance 명령어 포맷으로 원격접속을 할 수 있게 된다.

다운로드 받은 키는 미리 실행권한을 400으로 바꿔주도록 한다. chmod 400 id_rsa.pem 명령으로 오직 관리자만이 write 권한을 가지게끔 변경해준다. AWS 인스턴스에 원격접속을 할 때는 실행권한이 400이 아니면 원격접속이 되지 않는다.

2.2 EC2 Security Setting

각 인스턴스는 아래의 보안 규칙을 따른다. 새로 생성하여 적용하도록 한다. 실제 서비스를 제작하여 운용할 방침이라면, 좀 더 깐깐하게 설정해주는 게 맞지만 테스트용이기 때문에 약간 더 넓게 설정해두었다.

필자가 생성한 인스턴스 정보는 아래와 같다. 각 인스턴스의 이름도 신경써서 제작해주도록 하자. 필자의 인스턴스는 하나의 컨트롤러와 3개의 노드(0, 1, 2번)로 구성되어 있다.

// name, ip, vpc 순으로 기재했다.
duel.poa.controller:52.79.234.126   vpc:172-31-31-148   t2.micro
duel.poa.node0:     13.124.241.99   vpc: 172-31-30-127  t2.medium
duel.poa.node1:     52.78.17.178    vpc: 172-31-17-195  t2.medium
duel.poa.node2:     13.209.73.50    vpc: 172-31-22-46   t2.medium

id_rsa.pem 파일만 있으면 위 4개의 인스턴스에 얼마든지 접근할 수 있다.

2.3 Install Geth

각 인스턴스에 geth를 설치하는 작업을 진행한다. 이 포스티에서는 4개의 terminal를 띄워 명령어를 복사하여 실행하는 방법으로 진행한다.
하나의 인스턴스를 만들어 놓은 뒤 이미지로 따로 추출하여 해당 이미지로 인스턴스를 생성하는 방법이 존재하기 때문에, 이 방법을 이용해도 된다.

먼저 4개의 터미널에서 인스턴스에 접속한다. id_rsa.pem파일이 있는 폴더 내에서 아래의 명령을 실행하면 된다.

ssh -i "id_rsa.pem" [email protected]
ssh -i "id_rsa.pem" [email protected]
ssh -i "id_rsa.pem" [email protected]
ssh -i "id_rsa.pem" [email protected]

각 인스턴스에 원격접속을 했다면, 아래의 커맨드로 4개의 instance에 각각 적용해준다.

sudo apt-get install software-properties-common
sudo add-apt-repository -y ppa:ethereum/ethereum
sudo apt-get update
sudo apt-get install ethereum

3. 각 Node의 역할에 맞게 환경 구성

3.1 Controller

t2.micro 인스턴스 1개가 컨트롤러 역할을 진행한다. 나머지 3개의 t2.medium 인스턴스에 접근하고 제어하는 역할을 할 것이다. 위에서 EC2 인스턴스들을 생성하고 접근할 때 사용한 id_rsa.pem 파일의 내용을 복사해두도록 한다. 복사를 했으면 다음의 작업을 진행한다.

복사를 진행한 이유: .pem 파일은 프라이빗 키로 사용된다. 퍼블릭 프라이빗 키 개념을 간단하게 설명하자면, 각 EC2 인스턴스에는 퍼블릭 키가 존재하고 해당 퍼블릭키와 매칭되는 프라이빗키가 존재할 때 프라이빗 키로 해당 인스턴스에 원격으로 접속할 수가 있다.

  • cd ~/.ssh 디렉토리에 접근하여 아래의 작업들을 진행한다.
  • vim id_rsa.pem 명령으로 파일을 생성한 후 복사한 내용(우리 컴퓨터에 존재하는 id_rsa.pem 파일의 내용)을 붙여넣는다.

우리가 현재 사용하고 있는 컴퓨터에서 제어할 수 있지만 이렇게 했을 경우, 컴퓨터가 망가지거나 가지고 나오지 않았을 시 접근할 수 있는 방법이 존재하지 않는다. 따라서 별도의 컨트롤러 노드를 구성한 것이고 이 노드에서 각 인스턴스 노드에 접근하는 역할을 할 것이므로, 우리 컴퓨터에 있던 id_rsa.pem 파일을 그대로 Controller 노드에도 넣어주는 것이다.

  • sudo perl -pi -e 'chomp if elf' id_rsa.pem 명령을 실행하여 불필요한 라인들을 제거해준다. (있을 경우 제대로 키의 역할을 할 수 없게된다.)
  • 다른 노드를 컨트롤 할 때 사용할 자신만의 키를 생성해준다. 이미 id_rsa.pem 파일이 있는데 굳이 별도의 작업을 진행하는 이유는 ssh ubuntu@<ip of node> 명령어 만으로 각 노드에 접근하기 위해서다. 또한 puppeth를 실행할 때도 puppeth가 찾는 것은 id_rsa.pub 파일이고 파일을 별도로 지정하는 기능은 현재 puppeth에 구현되어 있지 않으므로 별도로 생성해주고 설정하는 작업이 필요한 것이다.
  • ssh-keygen 명령어로 키를 생성하며 passphrase를 묻는 입력란들이 나오면 전부 Enter를 치고 넘어간다. (원할 경우 따로 지정해줘도 상관은 없다. 실제로 서비스를 상용화할 때는 하는 것이 더 좋겠다.)
  • 명령어를 실행하면 id_rsa, id_rsa.pub 이라는 두 개의 파일이 생성된 것을 확인할 수 있다. 각각 개인키, 공개키를 의미한다.
  • 이제 각 t2.medium 노드들에 접근하기 위해 ~/.ssh/authorized_keys 파일의 내용을 편집해줄 차례다. 다음 명령어를 실행해주도록 한다. ssh -i id_rsa.pem ubuntu@<ip of node> 'cat >> ~/.ssh/authorized_keys' < ~/.ssh/id_rsa.pub
    필자의 경우 아래와 같은 3개의 명령어를 Controller node에서 실행해주었다.
ssh -i id_rsa.pem [email protected] 'cat >> ~/.ssh/authorized_keys' < ~/.ssh/id_rsa.pub
ssh -i id_rsa.pem [email protected] 'cat >> ~/.ssh/authorized_keys' < ~/.ssh/id_rsa.pub
ssh -i id_rsa.pem [email protected] 'cat >> ~/.ssh/authorized_keys' < ~/.ssh/id_rsa.pub

명령어를 실행해준 이후에 Controller node(t2.micro)에서 ssh ubuntu@<ip of node>로 3개의 t2.medium 노드들에 접근이 되는지 확인해보고 접속이 되었다면 작업이 잘 마무리 된 것이다.

3.2 3개의 인스턴스(t2.medium)들을 설정

각 인스턴스의 의존성 문제를 해결, 그리고 도커를 사용하기 위해 아래의 모든 명령어를 각 인스턴스에서 실행하도록 한다.

sudo apt-get update
sudo apt-get upgrade
sudo apt-get install linux-image-extra-4.4.0–59-generic linux-image-extra-virtual
sudo apt-get install \
 apt-transport-https \
 ca-certificates \
 curl \
 software-properties-common
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add -
sudo apt-key fingerprint 0EBFCD88
sudo add-apt-repository \
 “deb [arch=amd64] https://download.docker.com/linux/ubuntu \
 $(lsb_release -cs) \
 stable”
sudo apt-get update
sudo apt-get install docker-ce
sudo apt-get install docker-compose

각 인스턴스를 재부팅하여 docker ps 명령으로 도커가 잘 실행되는지 확인해주도록 한다.

3.3 Controller 노드에서 Wallet 생성하기

puppeth를 이용하여 네트워크를 생성하기 전에 해줘야 할 작업들이 있는데, Ethereum Wallet을 생성하는 것과 pre-funded accounts를 미리 만들어두는 것이다. Controller(t2.micro)에서 해당 작업을 진행한다.

3.3.1 비밀번호 생성하기

각 계정을 사용할 때 필요한 비밀번호를 파일로 만들어 놓도록 한다. 필자의 경우 1234라는 비밀번호로 passfile에 내용을 기록했다. 파일을 생성한 후에 관리자에게 read, write, execute 권한을 부여한다.

cd ~/ethereum/duelnet
vim passfile
sudo perl -pi -e 'chomp if eof' passfile
chmod 700 passfile

3.3.2 이더리움 계정 10개 생성하기

계정을 미리 생성해둬야 puppeth를 이용해서 네트워크를 설정할 때 Signer의 역할을 특정 계정에 부여하고 미리 이더를 지급받을 수 있다. 따라서 간단한 쉘 스크립트를 제작하여 이더리움 계정을 생성해주도록 한다.

실습을 진행하기 전에 먼저 디렉토리 구조를 신경쓸 필요가 있다. 필자의 경우 먼저 ~/ethereum/duelnet/node와 같은 구조로 디렉토리를 구성했고 향후 추가된 네트워크 설정이나 별도의 네트워크 설정을 구축하고 싶다면 ~/ethereum/<other network setup>와 같이 디렉토리를 구성할 계획이다.

먼저, 하나의 계정을 생성하려면 다음의 명령을 터미널에서 입력한다. geth --datadir ~/ethereum/duelnet/node account new --password ~/passfile이 명령을 실행하면~/ethereum/duelnet/node` 디렉토리를 기준으로 geth의 설정이 적용된다.

방금 생성된 계정을 Sealer0라고 칭하도록 한다. Signer라는 명칭도 있지만, Sealer라는 명칭도 있다고 이해하면 된다. 스크립트 파일을 하나 생성(vim ~/ethereum/duelnet/create_accounts.sh)해주고 아래의 내용을 써주도록 한다.

for ((n=0; n<10; n++)); do geth --datadir ~/ethereum/duelnet/node account new --password ~/ethereum/duelnet/passfile; done

그 후 duelnet$ chmod +x create_accounts.sh 명령어로 실해우건한을 추가시켜주고 duelnet$ ./create_accounts.sh 명령으로 스크립트를 실행하도록 한다.

10개의 계정이 추가로 생성되는 것을 확인할 수 있고 생성된 계정들을 따로 텍스트 편집기를 열어 기록을 해둔다. 필자의 경우 아래의 10개 계정들에 차례로 Sealer, Faucet과 같은 역할을 부여했다.

sealer0: 81bb2bfb72fc0ab991f79a58e9568d7cae57ccaa
sealer1: 4a4961cd93da26276a22d5145a296c3a5a1b83fb
sealer2: 78ddd4d1fa2036547687655fcac914852e558740
faucet: 3417cf2e1dea066db77239ad99f7e07f1e14cf0a

accounts:
d723a1aa820a5c5aa9ed1999e860f045038b2b05
5e5e07c810948e7e837679db1b51e02cc936afc2
096156b2c9a8043b6b288d0d40903f1dbe74b432
a0cff2ac0a0ca43fa0dcdb2b77b02bf6c3c2a86d
16966aebf2532170a04efe2eb05b4a1d1acc9b62
273dae58a042de73b4831cc7381020abe4921768
c730380e33404f2b90be965fc04e1050cac0555a

검증을 하는 노드가 3개 있고, 네트워크 참가자들이 이더를 요청해서 받을 때 각 계정에게 전송시켜주는 faucet 노드가 1개 있다. 나머지 7개 노드들은 네트워크 내에 존재하는 일반 계정들로 의미를 부여했다.

이제 본격적으로 puppeth를 실행하도록 한다.

3.4 Puppeth를 사용하여 이더리움 네트워크 정의 및 생성

~/ethereum/duelnet$ puppeth로 명령을 실행한다. 각자의 디렉토리에서 puppeth를 입력하여 실행하면 된다. 실행한 뒤에 쭈욱 과정을 진행하면 되는데, 아래를 참조하도록 한다.

+-----------------------------------------------------------+
| Welcome to puppeth, your Ethereum private network manager |
|                                                           |
| This tool lets you create a new Ethereum network down to  |
| the genesis block, bootnodes, miners and ethstats servers |
| without the hassle that it would normally entail.         |
|                                                           |
| Puppeth uses SSH to dial in to remote servers, and builds |
| its network components out of Docker containers using the |
| docker-compose toolset.                                   |
+-----------------------------------------------------------+

// 자신이 생성할 네트워크 설정의 이름을 정한다.
Please specify a network name to administer (no spaces or hyphens, please)
> duelnet

Sweet, you can set this via --network=duelnet_demo next time!

INFO [08-08|09:39:10.290] Administering Ethereum network           name=duelnet_demo
WARN [08-08|09:39:10.291] No previous configurations found         path=/home/ubuntu/.puppeth/duelnet_demo

// 제네시스 파일을 새로 구성할 수 있는 2번을 선택한다.
What would you like to do? (default = stats)
 1. Show network stats
 2. Configure new genesis
 3. Track new remote server
 4. Deploy network components
> 2

// PoA 엔진인 Clique를 선택한다.
Which consensus engine to use? (default = clique)
 1. Ethash - proof-of-work
 2. Clique - proof-of-authority
> 2

// 블록의 생성시간을 기술해준다.
How many seconds should blocks take? (default = 15)
> 1

// 위에서 의미룰 부여해주었던 계정들을 쭈욱 적어준다.
Which accounts are allowed to seal? (mandatory at least one)
> 0x81bb2bfb72fc0ab991f79a58e9568d7cae57ccaa
> 0x4a4961cd93da26276a22d5145a296c3a5a1b83fb
> 0x78ddd4d1fa2036547687655fcac914852e558740
> 0x

// sealer 노드들과 faucet, accounts 0~2까지 이더를 지급한다.
// PoA에서는 채굴 과정이 따로 없기 때문에 sealer 노드를 제외한 다른 노드들이 faucet 말고는 이더를 지급받을 방법이 존재하지 않는다.
Which accounts should be pre-funded? (advisable at least one)
> 0x81bb2bfb72fc0ab991f79a58e9568d7cae57ccaa
> 0x4a4961cd93da26276a22d5145a296c3a5a1b83fb
> 0x78ddd4d1fa2036547687655fcac914852e558740
> 0x3417cf2e1dea066db77239ad99f7e07f1e14cf0a
> 0xd723a1aa820a5c5aa9ed1999e860f045038b2b05
> 0x5e5e07c810948e7e837679db1b51e02cc936afc2
> 0x096156b2c9a8043b6b288d0d40903f1dbe74b432
> 0x

// 디폴트로 랜덤한 네트워크 아이디를 생성해준다. Enter만 입력하고 넘어가자.
Specify your chain/network ID if you want an explicit one (default = random)
>
INFO [08-08|09:40:54.552] Configured new genesis block

// 위에서 생성한 제네시스 파일을 추출하기 위해 2번을 선택한다.
What would you like to do? (default = stats)
 1. Show network stats
 2. Manage existing genesis
 3. Track new remote server
 4. Deploy network components
> 2

 1. Modify existing fork rules
 2. Export genesis configuration
 3. Remove genesis configuration
> 2

// 디폴트로 설정하기 위해 Enter만 입력하여 진행
Which file to save the genesis into? (default = duelnet_demo.json)
>
INFO [08-08|09:40:58.932] Exported existing genesis block

What would you like to do? (default = stats)
 1. Show network stats
 2. Manage existing genesis
 3. Track new remote server
 4. Deploy network components
> Ctrl + c // 원하는 작업을 실행했으니 종료한다.

생성된 제네시스 파일을 편집기로 열어보면 내용이 꽤 길다는 것을 확인할 수 있다. 이는 puppeth가 중간 중간 dummy accounts를 미리 생성해두었기 때문이다. 자신이 설정한 계정들을 제외하고는 전부 지워주어 파일을 보기 편리하게 만들어두는 것을 추천한다. 따로 조작하지 않아도 상관은 없다.

3.5 Get Wallet Keyfiles

Sealer 노드들의 개인키를 복사하여 puppeth 내에서 사용가능하도록 환경을 구성할 것이다. 따라서 Sealer 노드들과 Faucet 노드의 개인키 파일의 내용을 따로 복사해두어 기록해두도록 한다. 아래와 같이 실행하여 나온 결과들을 따로 기록해두자.

Sealer 0:
cat UTC--2018-08-08T09-27-37.241084125Z--81bb2bfb72fc0ab991f79a58e9568d7cae57ccaa
Sealer 1:
cat UTC--2018-08-08T09-33-06.102438431Z--4a4961cd93da26276a22d5145a296c3a5a1b83fb
Sealer 2:
cat UTC--2018-08-08T09-33-07.073882847Z--78ddd4d1fa2036547687655fcac914852e558740
Faucet:
cat UTC--2018-08-08T09-33-08.031975188Z--3417cf2e1dea066db77239ad99f7e07f1e14cf0a

각 명령어들에 대한 결과는 아래와 같고 아래의 내용들을 따로 기록해두면 된다.

Sealer 0:
{"address":"81bb2bfb72fc0ab991f79a58e9568d7cae57ccaa","crypto":{"cipher":"aes-128-ctr","ciphertext":"a30dc751a3ec33bb36b8ce5ca78abe7dbdf8ca68892bc2c9f8509cfc49077ee1","cipherparams":{"iv":"2263fd6f213c7030abdc12b56acf3ce8"},"kdf":"scrypt","kdfparams":{"dklen":32,"n":262144,"p":1,"r":8,"salt":"4c84fd7a79af24a7be3c5314b37aa08998ec69188c324181e19357ddc51497ce"},"mac":"eae031ffa5e046a8dd17d07d7b7f7c235cc0ea8c726f4eac32b0a302e7107754"},"id":"befc4d2e-9244-46d9-8227-cd37da8efd6f","version":3}

Sealer 1:
{"address":"4a4961cd93da26276a22d5145a296c3a5a1b83fb","crypto":{"cipher":"aes-128-ctr","ciphertext":"e176164bfd9fc6653e0ea3e418084af1de528a2dd10ffe29b9b35ea733f3ca9b","cipherparams":{"iv":"cd7ea7ae9426d56138d62cbb3e9b14dd"},"kdf":"scrypt","kdfparams":{"dklen":32,"n":262144,"p":1,"r":8,"salt":"e82093953f5892b60c84749e98fb575e2acd1a43d4477f6ea6f682974a806d13"},"mac":"e6c56be76c1009fafc2c921ab1b331f7c3874590f9e5e6a8b36dc98169abfa28"},"id":"528c9ce2-b4fe-4403-be02-898c0a0b096b","version":3}

Sealer 2:
{"address":"78ddd4d1fa2036547687655fcac914852e558740","crypto":{"cipher":"aes-128-ctr","ciphertext":"1730c3d659528dafab59f854cccef85714cfab2f7e3c573058aa0e7c6bdc282a","cipherparams":{"iv":"f56a23b8b4d4377510655a775e90df2f"},"kdf":"scrypt","kdfparams":{"dklen":32,"n":262144,"p":1,"r":8,"salt":"2373352ac9a663385100c45d93287263153e1dc020cbc884294370d261033450"},"mac":"7a745f562fd47a1cdf9e65597e76ceb4f7d27f4d469469ccea41f54ad733ea3d"},"id":"42356c9d-e3fd-4790-8c7f-9ca629d2f3f5","version":3}

Faucet:
{"address":"3417cf2e1dea066db77239ad99f7e07f1e14cf0a","crypto":{"cipher":"aes-128-ctr","ciphertext":"e9be248ab81f9a6b9536827fb53160a5567825628ec952d8599837e78e024cb5","cipherparams":{"iv":"48ec9dbf1ea1a555f5f8860f70f6d183"},"kdf":"scrypt","kdfparams":{"dklen":32,"n":262144,"p":1,"r":8,"salt":"180ab095a7f3de3427a0b67028b5e5e74afa43cfc42310f9bb422268aa8a41b9"},"mac":"19e401009907e226b2c73bc7c06d33fb6bbc0fb90e378cd2afd33f0ad19f3019"},"id":"661b0dd8-0176-4beb-9fa7-bd81dcd2d533","version":3}

4. Puppeth를 이용한 네트워크 배포

puppeth --network duelnet 와 같은 포맷으로 네트워크 옵션을 주어 자신이 기존에 생성해두었던 네트워크 세팅을 불러온다. 진행해야 할 작업들은 아래와 같다.

4.1 node0에게 Ethstat 역할을 부여한다.

    1. Deploy network components를 선택한다. puppeth에서 미리 제작해둔 편리한 기능들을 이용하여 각 노드들에 알맞은 역할을 부여하는 작업을 진행할 것이다.
    1. Connect another server를 선택한다.
  1. 13.124.241.99 // 자신의 인스턴스 중 node0의 아이피 주소를 입력해준다.
  2. 8080 // 다른 포트로 진행해도 상관은 없지만, 필자는 8080으로 설정했다.
  3. n // 포트 공유를 하지 못하게 설정해놓는다. 온전한 서비스를 위해서.
  4. secret password는 설정해도 되고 설정하지 않아도 된다.

위의 과정을 진행하면 아래와 같은 결과물을 확인할 수 있다.

Creating network "duelnet_default" with the default driver
Building ethstats
Step 1/2 : FROM puppeth/ethstats:latest
latest: Pulling from puppeth/ethstats
Digest: sha256:1728c03555d3327f68be924116eed9f9de56671949c21505f4b78518f06e687e
Status: Downloaded newer image for puppeth/ethstats:latest
 ---> fb62abe59cb2
Step 2/2 : RUN echo 'module.exports = {trusted: ["13.124.241.99"], banned: [], reserved: ["yournode"]};' > lib/utils/config.js
 ---> Running in 08f875b59b9b
 ---> 2b43e5422c5d
Removing intermediate container 08f875b59b9b
Successfully built 2b43e5422c5d
Creating duelnetdemo_ethstats_1
INFO [08-08|09:54:02.907] Starting remote server health-check      server=13.124.241.99
+---------------+---------------+---------+--------+-------+
|    SERVER     |    ADDRESS    | SERVICE | CONFIG | VALUE |
+---------------+---------------+---------+--------+-------+
| 13.124.241.99 | 13.124.241.99 |         |        |       |
+---------------+---------------+---------+--------+-------+

이로써 Ethstat이 동작함을 확인할 수 있다. http://<ip of node0>:8080 포맷으로 웹 브라우저 주소창에 검색해서 들어가면 Ethstat 페이지를 만나볼 수 있다.

우리가 한 작업을 요약하자면, Controller 노드에서 puppeth를 사용하여 node0(t2.medium)이 Docker를 활용하여 Ethstat 서비스를 실행하게끔 구성한 것이다. puppethnode0을 제어할 수 있었던 이유는 사전에 우리가 public key, private key 작업을 미리 해줬기 때문이다.

4.2 node0에게 Bootnode의 역할을 추가로 부여한다.

역할을 추가로 부여한다는 건 결국 해당 노드의 도커 컨테이너를 하나 더 생성해서 실행시켜준다는 의미다. puppeth를 실행하여 아래의 과정을 진행하여 부트노드를 동작 시켜보자.

    1. Manage network components 메뉴를 선택한다.
    1. Deploy new network component 항목을 선택한다.
    1. Bootnode 항목을 선택한다.
    1. 13.124.241.99를 선택한다. // 현재 Ethstat의 역할을 담당하고 있는 node0에게 추가로 권한을 부여하는 것이므로 node0의 ip주소를 그대로 사용하는 것이 맞다.
  1. /home/ubuntu/ethereum/duelnet/bootnode // 이 포맷으로 Bootnode관련 데이터를 어디 저장시킬 것인지를 기술한다.
  2. 나머지 옵션들(connecting peer number, light peers number)은 생략한다.
  3. duelnet_bootnode으로 이름을 작성한다. // 해당 노드의 역할에 이름을 붙이는 것이라고 생각하면 된다.

실행하면 아래와 같은 결과를 만나볼 수 있다.

+---------------+---------------+----------+--------------------------+----------------------------------------+
|    SERVER     |    ADDRESS    | SERVICE  |          CONFIG          |                 VALUE                  |
+---------------+---------------+----------+--------------------------+----------------------------------------+
| 13.124.241.99 | 13.124.241.99 | bootnode | Data directory           | /home/ubuntu/ethereum/duelnet/bootnode |
|               |               |          | Ethstats username        | duelnet_bootnode                       |
|               |               |          | Listener port            | 30305                                  |
|               |               |          | Peer count (all total)   | 512                                    |
|               |               |          | Peer count (light nodes) | 256                                    |
|               |               |          | ------------------------ | -------------------------------------- |
|               |               | ethstats | Banned addresses         |                                        |
|               |               |          | Login secret             | 2326                                   |
|               |               |          | Website address          | 13.124.241.99                          |
|               |               |          | Website listener port    | 8080                                   |
+---------------+---------------+----------+--------------------------+----------------------------------------+

4.3 노드0, 노드1, 노드2에게 각각 Sealer 역할 부여

현재까지 노드0에서는 Ethstats 및 Bootnode 역할을 담당하고 있다. 이제는 블록 검증을 위한 Sealer 노드들을 설정하고 이용할 차례다. 아래의 과정을 각 노드(노드0 ~ 노드2)에게 진행하면 된다. 즉 Controller 노드에서 puppeth를 실행시키고 노드0~노드2의 ip주소를 각각 입력하여 Sealer 역할을 부여하면 된다.

    1. Deploy network components 메뉴를 선택한다.
    1. Deploy new network component 메뉴를 선택한다. // 참여하는 노드가 점점 많아질 수록 이 수치는 점점 높아진다.
    1. Sealer // 새로운 블록을 생성하는 Full node 역할을 담당하게 된다.
    1. Connect another server 메뉴를 선택해서 각 노드의 ip를 적용시켜주면 된다.
  1. /home/ubuntu/ethereum/duelnet/sealernode 디렉토리를 데이터 디렉토리로 설정한다.
  2. 나머지 옵션들(TCP/IP port, number of peer connections, light peers)은 default로 둬도 상관 없으므로 Enter로 넘어가도록 한다.
  3. 이름을 지정해줘야 하므로 duelnet_sealer_0, duelnet_sealer_1, duelnet_sealer2 와 같이 체계적인 이름으로 각 노드의 역할에 이름을 부여한다.

위의 과정을 진행하여 3개의 노드가 Sealer 노드로 모두 참여시켜 네트워크를 구성한 후 다시한 번 웹 브라우저에서 Ethstats 방문한다면 아래와 같은 화면을 만나볼 수 있을 것이다.

Sealer 노드가 3개 존재하고 Bootnode가 존재하고 있다. 모든 노드들이 정상적으로 등록되고 인식되고 있다는 것을 확인할 수 있다.

4.4 채굴 진행해보기 (채굴이라 칭하기도 하고, Sealing | Validating로 불리기도 한다.)

지금까지 구성한 환경만으로는 아직 블록이 하나도 생성되지 않고 있다. Ethstats 에 웹브라우저로 접근하면 알 수 있다. 네트워크가 현재 멈춰 있으므로 채굴 과정을 진행시켜보겠다.

먼저 Controller node에서 ssh ubuntu@<ip of boonode> 를 입력하여 Bootnode 역할을 담당하고 있는 node0에게 접속한다. 그리고 docker ps 명령어로 현재 실행중인 컨테이너를 확인한다. 명령의 결과는 아래와 같다.

CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS              PORTS                                                                                     NAMES
c1efa7cbe17f        duelnet/sealnode    "/bin/sh geth.sh"   11 minutes ago      Up 11 minutes       8545-8546/tcp, 0.0.0.0:30303->30303/tcp, 0.0.0.0:30303->30303/udp                         duelnet_sealnode_1
8266f6a9d2d2        duelnet/bootnode    "/bin/sh geth.sh"   22 minutes ago      Up 22 minutes       8545-8546/tcp, 30303/tcp, 30303/udp, 0.0.0.0:30305->30305/tcp, 0.0.0.0:30305->30305/udp   duelnet_bootnode_1
0a1265c25972        duelnet/ethstats    "npm start"         33 minutes ago      Up 33 minutes       0.0.0.0:8080->3000/tcp                                                                    duelnet_ethstats_1

가장 첫번째로 출력되는 duelnet/sealnodeCONTAINER ID 를 확인하여 다음의 명령어 포맷으로 geth 콘솔을 실행하도록 하자. docker exec -it <container_id> geth attach ipc:/root/.ethereum/geth.ipc

필자의 경우는 docker exec -it c1e geth attach 명령으로 geth console을 실행할 수 있었다. 콘솔에 접근한 이유는 해당 노드의 enode 정보를 알아내기 위한 것이다. 콘솔 내에서 admin.nodeInfo.enode 명령으로 해당 노드의 enode정보를 추출하고 위 작업을 node1, node2에게도 동일하게 진행한다.

각 과정을 진행하면서 enode 정보를 기록하고, 그렇게 하면 아래와 같은 정보를 얻어낼 수 있을 것이다.

Sealer 0:
"enode://ec9046bc66b3c5f940bcb8023f127aa721da61c02ae4906e8e2c738295aa17bf18acbcb5f27f21f026f4b37eb44fe24e0b674ac69a01e4db0b4a4007a5e5e6b2@13.124.241.98:30303"

Sealer 1:
"enode://cf47010e6b3a855511eb719cfd29c6b80e74e7e5a023878eaf393fc157b9f9a7704f6650f7719a247c64cf68903738233483f83a7081e12a4f641c11564e7431@52.78.17.178:30303"

Sealer 2:
"enode://87789df6d972fc357384606cd833dc8529a178cfde411d81125ba229c8be1e1425483ca97da055a2a5bc8419d38c859181ff152678e7f7bc6c5339090e4122db@13.209.73.50:30303"

기억한 enode 정보를 가지고 Bootnode 역할을 맡고 있는 node0의 geth console을 실행시켜 다음의 명령어 포맷으로 각 노드를 연결시켜주도록 한다. admin.addPeer("<enode from other nodes>") 현재 노드0에서 실행하고 있으므로 노드1과 노드2의 enode 정보를 확인한 후에 적용시켜주면 된다.

5. 나머지 서비스 구현

나머지 서비스들은 모두 Ethstats 서비스를 제공하고 있는 노드0에서 이루어져야 한다. 구축해야 할 서비스들은 아래와 같다.

  1. The MyCrypto wallet app
  2. A faucet
  3. A block explorer (PoA 네트워크에서는 아직 지원되지 않는다.)
  4. A dashboard to tie it all together

puppeth 를 사용하여 배포할 때 헷갈리는 점이 있는 건 faucet app 뿐이므로 해당 절차만 기술하도록 하겠다. 나머지는 필요에 따라 본인이 직접 조작해보는 걸 추천한다.

5.1 Wallet App

5.2 Faucet App

특이하게도 reCAPCHA v2 서비스의 Site key Secret key를 요구한다. 이 경우 reCAPCHA 사이트에 접속하여 생성할 수 있고 생성하여 얻은 각 키 정보를 입력해주면 된다.

5.3 Dashboard

6. 마치며

개발자가 해야할 일을 대폭 줄여주는 puppeth는 확실히 효자 프로그램이라 할 수 있다. 그러나 여전히 근본적인 구조에 대한 이해는 필요하다고 생각한다. 각 앱을 구현하는데 있어서 필요한 정보가 어떤 것이 있을지 생각해보고 puppeth 를 이용하는 것이 개발 실력 뿐만 아니라 통찰에도 많은 도움을 줄 거라 생각한다.

마침 아직 PoA에 관한 Block Explorerpuppeth 에서 제공하지 않으니 직접 구현해보는 건 어떨까? 이번 포스팅은 여기서 마치도록 한다.