美文网首页区块链-下一场变更
Step by Step 搭建私有链集群(Docker版本)

Step by Step 搭建私有链集群(Docker版本)

作者: f9b6fcef1d35 | 来源:发表于2018-04-13 14:49 被阅读141次

    前言

    最近研究了一段时间以太坊,各种专业术语解释,各种文档资料查看,尝试多种方案,最佳入手的体验私有链模式还是Docker,所以本文将详细描述利用Docker一步一步搭建私有链集群的过程。说是集群,其实不过搭建3个节点,没有优化过程,旨在给大家提供一个可以模拟入门的教程。

    私有链:控制权限集中在某个公司或者组织,读取权限可以根据业务情况进行公开或者限定范围内公开,应用范围比较广泛。有关公有链、联盟链和私有链之间的关系,建议详细延伸阅读原版英文说明 Public and Private Blockchains

    前提准备:

    • CentOS 7.* 64bit 操作系统,虚拟化即可 【主要用于运行Docker宿主机器以及节点监控服务
    • 服务器配置(4核心,4G内存,硬盘容量20G即可)
    • 必要的Docker容器知识,本文档不会详细解释具体的含义

    目标:

    • 搭建3个节点的Docker服务,运行私有链服务geth
    • 创建6个钱包地址
    • 同节点和不同节点间钱包之间相互转账
    • 查看区块,节点间的区块同步
    • 查询挂起交易,同步交易,交易详情
    • 搭建私有链节点运行的监控平台

    1. 服务器环境配置

    1.1 安装EPEL

    相关软件我们基本全部采用yum进行安装,由于一些软件包只包含在EPEL资源库中,所以必须引入EPEL源,以便整个演示能够顺利进行。

    # yum install -y epel-release
    
    1.2 安装必要的软件包

    演示过程中,我们需要下载github项目,所以必须安装git;
    其他软件主要是方便查看和测试使用;
    Docker容器服务是我们主要使用的软件;

    # yum install -y git wget tree telnet
    # yum install -y docker
    

    查看Docker容器是否安装成功,出现如下的版本号提示,说明成功。

    # docker --version
    Docker version 1.13.1, build 774336d/1.13.1
    

    2. 配置Docker容器

    2.1 启动Docker容器服务
    # /bin/systemctl start docker.service
    # /bin/systemctl  enable docker.service
    设置开机自动启动
    
    2.2 下载官方镜像文件

    由于下载镜像需要时间较长,我们提前将需要的ubuntu 镜像下载下来,减少后续的等待时间。本演示我们使用ubuntu作为基础镜像。

    # docker pull ubuntu
    
    2.3 定制化Dockerfile文件

    为什么使用Dockerfile?以及如何自己写Dockerfile?这里不做更详细的解释,建议用户参看官方说明文档Best practices for writing Dockerfiles

    # mkdir /home/ethereum
    # cd /home/ethereum
    # vi Dockerfile
    

    文件的详细内容如下:

    FROM ubuntu
    
    LABEL version="1.0"
    LABEL maintainer="zhoulg@outlook.com"
    
    ENV DEBIAN_FRONTEND=noninteractive
    RUN apt-get update && apt-get install --yes software-properties-common
    RUN add-apt-repository ppa:ethereum/ethereum
    RUN apt-get update && apt-get install --yes geth
    
    RUN adduser --disabled-login --gecos "ethereum user" eth
    
    COPY genesis.json /home/eth/genesis.json
    RUN chown -R eth:eth /home/eth/genesis.json
    
    USER eth
    WORKDIR /home/eth
    
    RUN geth init genesis.json
    
    ENTRYPOINT bash
    

    文件内容简要说明:

    • 使用ubuntu:latest 作为基础镜像
    • 更新和安装必要的软件包
    • 创建运行账号,复制必要的文件以及工作目录
    • 运行geth(go-ethereum)初始化程序
    2.4 生成genesis.json文件

    注意上面的Dockerfile文件里面有一行,COPY命令,复制了本地的一个文件到容器内指定目录下。该文件是运行私有链的必须的配置文件,我们需要手工生成。网络上面有很多genesis.json文件的例子,官方也有很多介绍,这里不做过多的解释,如果用户对于该文件每一项的含义需要更加详细的说明,建议延伸阅读What does each genesis.json parameter mean?
    这里我们的演示文件内容如下:

    {
      "alloc": {
      },
      "config": {
        "chainId": 15,
        "homesteadBlock": 0,
        "eip155Block": 0,
        "eip158Block": 0
      },
      "nonce": "0x000000000000002a",
      "difficulty": "0x002000",
      "mixhash": "0x0000000000000000000000000000000000000000000000000000000000000000",
      "coinbase": "0x0000000000000000000000000000000000000000",
      "timestamp": "0x00",
      "parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
      "extraData": "0x",
      "gasLimit": "0x2fefd8"
    }
    

    genesis.json必须在所有的节点都是相同的,所以我们直接复制到镜像文件里面,保持统一。

    2.5 编译自定义的镜像
    # docker build  -t  eth_node . 
    Sending build context to Docker daemon 3.072 kB
    Step 1/14 : FROM ubuntu
     ---> c9d990395902
    Step 2/14 : LABEL version "1.0"
     ---> Running in c2efa7362edd
    ...... 其他信息省略,软件包下载需要一段时间......
    Step 14/14 : ENTRYPOINT bash
     ---> Running in 69887d7b1b15
     ---> 32bb44f69667
    Removing intermediate container 69887d7b1b15
    Successfully built 32bb44f69667
    

    最后出现Successfully 说明创建自定义镜像成功
    查看镜像列表验证下

    # docker images
    REPOSITORY          TAG                 IMAGE ID            CREATED              SIZE
    eth_node            latest              32bb44f69667        About a minute ago   287 MB
    docker.io/ubuntu    latest              c9d990395902        14 hours ago         113 MB
    

    其中 eth_node 就是我们刚才自定义的镜像文件,接下来我们的演示将主要以这个镜像文件为基础。

    3. 搭建私有链节点

    3.1 启动3个节点

    由于我们是在一台宿主机上面运行,所以我们需要规划好节点的对应端口号,默认geth 运行端口为 8545,我们定义节点如下:
    +节点一名称:eth_node_1 映射宿主机端口号:8545
    +节点二名称:eth_node_2 映射宿主机端口号:8546
    +节点三名称:eth_node_3 映射宿主机端口号:8547

    节点一启动

    # docker run --rm -it -p 8545:8545  --name eth_node_1  eth_node 
    默认启动后,已经登录进入容器内部,保持终端不动
    

    容器内部退出终端的命令:ctrl + p + q (不要使用exit命令,会结束容器进程)

    节点一已经启动,暂时保持不动
    现在,重新打开一个新的终端
    节点二启动

    # docker run --rm -it -p 8546:8545  --name eth_node_2  eth_node
    默认启动后,已经登录进入容器内部,保持终端不动
    留意端口映射的不同,和节点名称
    

    再次重新打开一个新的终端
    节点三启动

    # docker run --rm -it -p 8547:8545  --name eth_node_3  eth_node
    默认启动后,已经登录进入容器内部,保持终端不动
    留意端口映射的不同,和节点名称
    

    以上我们已经启动了3个节点的容器服务

    3.2 查询节点容器信息

    检查节点的运行情况

    # docker ps -a
    CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS              PORTS                    NAMES
    20b354c9dd15        eth_node            "/bin/sh -c bash"   5 seconds ago       Up 4 seconds        0.0.0.0:8547->8545/tcp   eth_node_3
    f5d2b9558a3b        eth_node            "/bin/sh -c bash"   17 seconds ago      Up 16 seconds       0.0.0.0:8546->8545/tcp   eth_node_2
    4cd4ed065442        eth_node            "/bin/sh -c bash"   56 seconds ago      Up 55 seconds       0.0.0.0:8545->8545/tcp   eth_node_1
    

    几个重要的信息点,节点的名称,节点对应的ID(前4个字母即可),以及对应的端口映射,容器的运行状态
    查询节点对应的IP地址

    # 查询节点一的IP
    # docker inspect 4cd4 --format='{{.NetworkSettings.IPAddress}}'
    172.17.0.2
    # 查询节点二的IP
    # docker inspect f5d2 --format='{{.NetworkSettings.IPAddress}}'
    172.17.0.3
    # 查询节点三的IP
    # docker inspect 20b3 --format='{{.NetworkSettings.IPAddress}}'
    172.17.0.4
    

    查询后对应的信息如下,需要记录下来备用:

    节点 IP地址 映射端口 容器ID
    eth_node_1 172.17.0.2 8545 4cd4
    eth_node_2 172.17.0.3 8546 f5d2
    eth_node_3 172.17.0.4 8547 20b3
    3.3 配置节点容器
    3.3.1 创建初始钱包

    创建初始节点钱包,用于接收记账奖励,每个节点都需要创建。节点记账(挖矿)程序运行前,必须先建立初始的钱包,用于接收记账奖励,否则会无法启动记账程序。
    %进入节点一(eth_node_1)的终端,创建初始钱包。为了减少交互输入密码步骤,我们简单采用密码文件的形式,正式环境不能这么操作。

    # echo "123456" > mypassword.txt
    # geth --password mypassword.txt account new
    INFO [04-16|02:32:39] Maximum peer count                       ETH=25 LES=0 total=25
    Address: {5edd8d6c2ea0328f54f91cd7714d1f9bbcb9911d}
    

    记录下生成的钱包地址:5edd8d6c2ea0328f54f91cd7714d1f9bbcb9911d
    查看钱包列表

    # geth account list
    INFO [04-16|02:35:02] Maximum peer count                       ETH=25 LES=0 total=25
    Account #0: {5edd8d6c2ea0328f54f91cd7714d1f9bbcb9911d} keystore:///home/eth/.ethereum/keystore/UTC--2018-04-16T02-32-39.350890163Z--5edd8d6c2ea0328f54f91cd7714d1f9bbcb9911d
    

    PS:keystore指向的加密的密钥文件


    同样在eth_node_2 和 eth_node_3 上面执行同样的命令,全部创建好初始钱包地址。

    3.3.2 创建用户钱包

    同时创建一个用户钱包,稍后将用于本节点钱包转账以及不同节点间钱包转账。

    # geth --password mypassword.txt account new
    INFO [04-16|05:34:15] Maximum peer count                       ETH=25 LES=0 total=25
    Address: {ef41fbddb7fd8cc42076c7613f98c533d55e8d99}
    

    记录下用户钱包地址,后续将会使用到。
    查看创建的钱包地址列表

    # geth account list
    INFO [04-16|05:34:58] Maximum peer count                       ETH=25 LES=0 total=25
    Account #0: {5edd8d6c2ea0328f54f91cd7714d1f9bbcb9911d} keystore:///home/eth/.ethereum/keystore/UTC--2018-04-16T02-32-39.350890163Z--5edd8d6c2ea0328f54f91cd7714d1f9bbcb9911d
    Account #1: {ef41fbddb7fd8cc42076c7613f98c533d55e8d99} keystore:///home/eth/.ethereum/keystore/UTC--2018-04-16T05-34-15.740848716Z--ef41fbddb7fd8cc42076c7613f98c533d55e8d99
    

    #0号钱包地址为初始的钱包。
    同样在eth_node_2 和 eth_node_3 上面执行同样的命令,全部创建一个用户钱包备用。

    3.3.3 开始运行记账程序(也叫挖矿程序)

    准备工作全部完成后,现在开始启动记账(挖矿)程序,需要指定一些参数。
    节点一(eth_node_1)

    # geth --identity="NODE_1" --networkid="500" --verbosity=1 --mine --minerthreads=1 --rpc --rpcaddr 0.0.0.0 console
    Welcome to the Geth JavaScript console!
    
    instance: Geth/NODE_1/v1.8.2-stable-b8b9f7f4/linux-amd64/go1.9.4
    coinbase: 0x5edd8d6c2ea0328f54f91cd7714d1f9bbcb9911d
    at block: 0 (Thu, 01 Jan 1970 00:00:00 UTC)
     datadir: /home/eth/.ethereum
     modules: admin:1.0 debug:1.0 eth:1.0 miner:1.0 net:1.0 personal:1.0 rpc:1.0 txpool:1.0 web3:1.0
    
    >eth.blockNumber
    0
    >... 一般需要等待10分钟左右创建创世块...
    >eth.blockNumber
    7
    

    节点二(eth_node_2)

    # geth --identity="NODE_2" --networkid="500" --verbosity=1 --mine --minerthreads=1 --rpc  --rpcaddr 0.0.0.0 console
    Welcome to the Geth JavaScript console!
    
    instance: Geth/NODE_2/v1.8.2-stable-b8b9f7f4/linux-amd64/go1.9.4
    coinbase: 0xa849dc84dc2500ac4abb872b125c7e67012f6ec2
    at block: 0 (Thu, 01 Jan 1970 00:00:00 UTC)
     datadir: /home/eth/.ethereum
     modules: admin:1.0 debug:1.0 eth:1.0 miner:1.0 net:1.0 personal:1.0 rpc:1.0 txpool:1.0 web3:1.0
    
    > 
    

    节点三(eth_node_3)

    # geth --identity="NODE_3" --networkid="500" --verbosity=1 --mine --minerthreads=1 --rpc  --rpcaddr 0.0.0.0 console
    Welcome to the Geth JavaScript console!
    
    instance: Geth/NODE_3/v1.8.2-stable-b8b9f7f4/linux-amd64/go1.9.4
    coinbase: 0x26214c499202e2e6021f820e58c1eff2b1a252f9
    at block: 0 (Thu, 01 Jan 1970 00:00:00 UTC)
     datadir: /home/eth/.ethereum
     modules: admin:1.0 debug:1.0 eth:1.0 miner:1.0 net:1.0 personal:1.0 rpc:1.0 txpool:1.0 web3:1.0
    
    >
    

    留意3个节点的启动参数不同

    参数 说明
    --identity 每个节点必须唯一
    --networkid 在集群里面保持一致,所有的节点都相同
    --verbosity 有几个级别0=silent, 1=error, 2=warn, 3=info, 4=core, 5=debug, 6=detail (default: 3)
    --mine 开始启动记账(挖矿)程序
    --rpc 允许RPC
    --rpcport 指定的RPC端口号,演示保持一样,宿主机映射不一样
    3.3.4 命令参数简要说明

    所有的参数的详细解释说明,请参看geth命令行参数说明

    4 配置节点集群

    4.1 查询节点信息IPC RPC 参数

    所有的节点都开始记账(挖矿)程序后,我们就需要配置集群的节点信息,让3个节点能够互相发现,并同步信息。

    > admin.peers
    []
    

    默认都是空的,没有其他节点信息
    现在查询出来3个节点的参数
    节点一(eth_node_1)

    > admin.nodeInfo.enode
    "enode://4250b0b6b1029b0874a5c5b0806299b04206ef21c22a0a5a476fe87ae622beeca69c55b4e000110d2cb2a72da3af15325bb4e43d4ee6d19111ec4319256348e1@[::]:30303"
    

    我们将末尾的 [::]替换为我们的内网IP地址,前面有记录,172.17.0.2
    现在节点一对应的修改如下:
    enode://4250b0b6b1029b0874a5c5b0806299b04206ef21c22a0a5a476fe87ae622beeca69c55b4e000110d2cb2a72da3af15325bb4e43d4ee6d19111ec4319256348e1@172.17.0.2:30303

    其他2个节点执行同样的操作和替换,实例如下:
    eth_node_1:"enode://4250b0b6****6348e1@172.17.0.2:30303"
    eth_node_2:"enode://ddd066c0****35f75c5@172.17.0.3:30303"
    eth_node_3:"enode://af5e0fd27****31d170@172.17.0.4:30303"
    为了演示,中间的字符省略

    4.2 添加节点

    现在我们已经得到所有节点的URL信息,我们需要让其他节点能够发现另外的2个节点,手工添加上对应的参数。

    在所有的节点都执行如下的定义
    > enode1 = "enode://4250b0b6b1029b0874***48e1@172.17.0.2:30303"
    > 
    > enode2 = "enode://ddd066c0442662976ac***75c5@172.17.0.3:30303"
    > 
    > enode3 = "enode://af5e0fd27bf9480621****d170@172.17.0.4:30303"
    > 
    

    在每个几点上面增加其他节点的信息
    节点一(eth_node_1)

    > admin.addPeer(enode2)
    true
    > admin.addPeer(enode3)
    true
    

    节点二(eth_node_2)

    > admin.addPeer(enode1)
    true
    > admin.addPeer(enode3)
    true
    

    节点三(eth_node_3)

    > admin.addPeer(enode1)
    true
    > admin.addPeer(enode2)
    true
    
    4.3 查询节点集群信息

    所有的节点都添加信息后,再次查看节点情况

    > admin.peers
    [{
        caps: ["eth/63"],
        id: "af5e0fd27bf94806213b824ac0814c45106bbc834e406fd617f838af61dd98fd131c83c2394145678367ee0d80ea55a066283a4f7586793143c35aa2c431d170",
        name: "Geth/NODE_3/v1.8.2-stable-b8b9f7f4/linux-amd64/go1.9.4",
        network: {
          inbound: false,
          localAddress: "172.17.0.2:37019",
          remoteAddress: "172.17.0.4:30303",
          static: true,
          trusted: false
        },
        protocols: {
          eth: {
            difficulty: 8192,
            head: "0x8e3caef2da8f25e5172a86bac2dbd2611fa4de6c3cc28c301186d537afc77943",
            version: 63
          }
        }
    }, {
        caps: ["eth/63"],
        id: "ddd066c0442662976ac3765d80929ad76ea0420b915b69ac7ca5bc7002b37f180b6129af7e7a9e0dc2a5d9153578a05810a409a57878ef919ddc2edd735f75c5",
        name: "Geth/NODE_2/v1.8.2-stable-b8b9f7f4/linux-amd64/go1.9.4",
        network: {
          inbound: false,
          localAddress: "172.17.0.2:42844",
          remoteAddress: "172.17.0.3:30303",
          static: true,
          trusted: false
        },
        protocols: {
          eth: {
            difficulty: 4337792,
            head: "0xb54837c3a85bac126297b3b6be302f22b31aa66c78f7089584b7d787808cf5b4",
            version: 63
          }
        }
    }]
    

    其他2个节点的信息基本一致,除了remoteAddress不一样

    4.4 查询节点区块同步情况

    节点都加入集群后,在所有的节点查看区块情况,应该所有的节点都是一样的,或者由于延迟相差几个区块。

    > eth.blockNumber
    282
    

    这样我们的私有链集群都搭建成功了,所有的节点区块保持一致。

    4.5 监控节点区块同步情况

    接下来,不要关闭上面的3个节点终端,留待备用。
    我们重新打开一个宿主机的终端,尝试通过接口监控节点的区块同步情况,这里我们仅演示下nodejs。
    为了运行nodejs 程序,我们需要安装nodejs软件包

    # yum install -y nodejs
    # node -v
    v6.14.0
    

    本监控脚步我们引用了 node-json-rpc 库,需要提前安装

    # npm install  node-json-rpc
    npm: relocation error: npm: symbol SSL_set_cert_cb, version libssl.so.10 not defined in file libssl.so.10 with link time reference
    

    发现报错了,这里是由于openssl库的问题导致的,重新安装openssl库

    # yum install -y openssl
    

    然后再次执行 npm 安装程序

    # npm install node-json-rpc
    /home/ethereum
    └── node-json-rpc@0.0.1 
    

    OK,安装成功。
    默认库文件就在当前目录下

    # tree node_modules/
    node_modules/
    └── node-json-rpc
        ├── lib
        │   ├── index.js
        │   ├── rpcauth.js
        │   ├── rpcclient.js
        │   └── rpcserver.js
        ├── LICENSE
        ├── package.json
        ├── README.md
        └── test
            ├── etc
            │   ├── optsclient.js
            │   └── optsserver.js
            └── rpc.js
    
    4 directories, 10 files
    

    运行命令行NodeJS 的监控同步程序

    # node  monitor.js 
    ETH_NODE_2 coinbase: 0xa849dc84dc2500ac4abb872b125c7e67012f6ec2
    ETH_NODE_3 block number: 556
    ETH_NODE_1 coinbase: 0x5edd8d6c2ea0328f54f91cd7714d1f9bbcb9911d
    ETH_NODE_1 block number: 556
    ETH_NODE_2 block number: 556
    ETH_NODE_3 coinbase: 0x26214c499202e2e6021f820e58c1eff2b1a252f9
    ETH_NODE_1 coinbase: 0x5edd8d6c2ea0328f54f91cd7714d1f9bbcb9911d
    ETH_NODE_3 coinbase: 0x26214c499202e2e6021f820e58c1eff2b1a252f9
    ETH_NODE_2 coinbase: 0xa849dc84dc2500ac4abb872b125c7e67012f6ec2
    ETH_NODE_1 block number: 557
    ETH_NODE_2 block number: 557
    ETH_NODE_3 block number: 557
    ......
    

    上面我们看见3个节点的区块都保持一致,私链集群运行成功。
    由于是单服务器上面做的演示,这时候宿主机的负载就有点大了,区块的产生也不会很快。

    5 钱包转账

    5.1 查询初始钱包余额

    接下来开始演示钱包的相关操作
    进入节点一(eth_node_1)终端,查看钱包余额

    >  eth.accounts
    ["0x5edd8d6c2ea0328f54f91cd7714d1f9bbcb9911d", "0xef41fbddb7fd8cc42076c7613f98c533d55e8d99"]
    > node1_w1 = eth.accounts[0]
    >  eth.getBalance(node1_w1)
    3.013125e+21
    > web3.fromWei(eth.getBalance(node1_w1))
    3037.03125
    

    web3.fromWei 函数将余额转换为易读的单位wei,便于后续演示。
    其他节点基本相同。

    5.2 查询用户钱包余额

    现在查看用户的钱包的余额情况

    >  eth.accounts
    ["0x5edd8d6c2ea0328f54f91cd7714d1f9bbcb9911d", "0xef41fbddb7fd8cc42076c7613f98c533d55e8d99"]
    > node1_w2 = eth.accounts[1]
    > web3.fromWei(eth.getBalance(node1_w2))
    0
    

    现在用户钱包还是空的,没有余额

    5.3 初始钱包->用户钱包转账

    我们尝试做一笔转账操作
    在节点一(eth_node_1),从初始钱包->用户钱包,转账40个Wei

    >  eth.sendTransaction({from: node1_w1, to: node1_w2, value: web3.toWei(40, "ether")})
    Error: authentication needed: password or unlock
        at web3.js:3143:20
        at web3.js:6347:15
        at web3.js:5081:36
        at <anonymous>:1:1
    

    我们发现报错了,密码不对或者需要解锁
    我们先把初始账号进行解锁操作

    > personal.unlockAccount(node1_w1, "123456", 300)   
    true
    

    解锁钱包,需要执行我们生成钱包的时候指定的密码,这里是123456,解锁时间300秒。
    返回true 说明解锁成功,继续我们的转账操作

    > eth.sendTransaction({from: node1_w1, to: node1_w2, value: web3.toWei(40, "ether")})
    "0x844c074011fc256e8c3ae8e0a50f7a149c5785afcb78e750e091be90bbede22e"
    

    返回的是交易号

    5.4 查询挂起的交易
    > eth.pendingTransactions
    []
    这里如果区块同步的慢,可以看到挂起的交易
    出现[],说明我们的交易已经同步了
    > eth.getTransaction('0x844c074011fc256e8c3ae8e0a50f7a149c5785afcb78e750e091be90bbede22e')
    {
      blockHash: "0x54c455696b0321e58cee3e4ea9d50da98ee5842d6d2db5bcdd774de56d5f688d",
      blockNumber: 851,
      from: "0x5edd8d6c2ea0328f54f91cd7714d1f9bbcb9911d",
      gas: 90000,
      gasPrice: 18000000000,
      hash: "0x844c074011fc256e8c3ae8e0a50f7a149c5785afcb78e750e091be90bbede22e",
      input: "0x",
      nonce: 0,
      r: "0xf17019be2e3d55ede4a5fde3333a4f1a8164b34004764143d74565db77985df1",
      s: "0x48f5ad381e776de128cc9a137a3979b3f3de94cf6412e3a35213d1e7592c0315",
      to: "0xef41fbddb7fd8cc42076c7613f98c533d55e8d99",
      transactionIndex: 0,
      v: "0x41",
      value: 40000000000000000000
    }
    

    由于我们3个节点是同步的,所以在所有的区块,都查看到交易

    5.5 查询用户钱包余额

    交易确认后,我们就可以在用户钱包查看到余额

    >  web3.fromWei(eth.getBalance(node1_w2))
    40
    

    这样我们在同一个节点的转账操作就成功了。

    5.6 跨节点钱包转账

    现在我们进行在不同节点之间的钱包转账操作
    上一步操作中,我们节点一的用户钱包,已经有40wei,这一步我们转移到节点二用户钱包 15wei,转移到节点三用户钱包 10wei。
    节点一(eth_node_1)

    > eth.accounts
    ["0x5edd8d6c2ea0328f54f91cd7714d1f9bbcb9911d", "0xef41fbddb7fd8cc42076c7613f98c533d55e8d99"]
    

    节点二(eth_node_2)

    > eth.accounts
    ["0xa849dc84dc2500ac4abb872b125c7e67012f6ec2", "0xbe38275ed6414268698294a9a1c06909e8b96a5a"]
    

    节点三(eth_node_3)

    > eth.accounts
    ["0x26214c499202e2e6021f820e58c1eff2b1a252f9", "0x48aa3d8e793e8d2da00b23ecea1555c3b82e3262"]
    

    重新登录节点一(eth_node_1)
    先确认用户钱包余额

    > web3.fromWei(eth.getBalance('0xef41fbddb7fd8cc42076c7613f98c533d55e8d99'))
    40
    >  web3.fromWei(eth.getBalance('0xbe38275ed6414268698294a9a1c06909e8b96a5a'))
    0
    > web3.fromWei(eth.getBalance('0x48aa3d8e793e8d2da00b23ecea1555c3b82e3262'))
    0
    

    开始转账操作,同样转账前,我们需要先解锁节点一的用户钱包

    > personal.unlockAccount('0xef41fbddb7fd8cc42076c7613f98c533d55e8d99', "123456", 300)
    true
    > eth.sendTransaction({from: '0xef41fbddb7fd8cc42076c7613f98c533d55e8d99', to: '0xbe38275ed6414268698294a9a1c06909e8b96a5a', value: web3.toWei(15, "ether")})
    "0x6a60647492fcaea93a721ab7dc761a8fade6fd44d7f0283ee38fb5d146773c4e"
    > eth.sendTransaction({from: '0xef41fbddb7fd8cc42076c7613f98c533d55e8d99', to: '0x48aa3d8e793e8d2da00b23ecea1555c3b82e3262', value: web3.toWei(10, "ether")})
    "0xd95914eeccc1ec05fd22c15f29e0d3515b904268074124aa9f669fd529efcae1"
    
    > eth.pendingTransactions
    []
    没有挂起的交易
    > eth.sendTransaction({from: '0xef41fbddb7fd8cc42076c7613f98c533d55e8d99', to: '0xbe38275ed6414268698294a9a1c06909e8b96a5a', value: web3.toWei(15, "ether")})
    "0x6a60647492fcaea93a721ab7dc761a8fade6fd44d7f0283ee38fb5d146773c4e"
    > eth.sendTransaction({from: '0xef41fbddb7fd8cc42076c7613f98c533d55e8d99', to: '0x48aa3d8e793e8d2da00b23ecea1555c3b82e3262', value: web3.toWei(10, "ether")})
    "0xd95914eeccc1ec05fd22c15f29e0d3515b904268074124aa9f669fd529efcae1"
    > eth.pendingTransactions
    [{
        blockHash: null,
        blockNumber: null,
        from: "0xef41fbddb7fd8cc42076c7613f98c533d55e8d99",
        gas: 90000,
        gasPrice: 18000000000,
        hash: "0x6a60647492fcaea93a721ab7dc761a8fade6fd44d7f0283ee38fb5d146773c4e",
        input: "0x",
        nonce: 0,
        r: "0x703aef116bf4226cf5d4ae14c16ab2e365240b951f22c127f0834f4d07f1c91",
        s: "0x5811d14a3ae3abd47eff111648fbdbbef8d13878961981a3d0110e1b6ac43c0",
        to: "0xbe38275ed6414268698294a9a1c06909e8b96a5a",
        transactionIndex: 0,
        v: "0x42",
        value: 15000000000000000000
    }, {
        blockHash: null,
        blockNumber: null,
        from: "0xef41fbddb7fd8cc42076c7613f98c533d55e8d99",
        gas: 90000,
        gasPrice: 18000000000,
        hash: "0xd95914eeccc1ec05fd22c15f29e0d3515b904268074124aa9f669fd529efcae1",
        input: "0x",
        nonce: 1,
        r: "0x31d052be55fe988aa06931b807e59b9bd9dd20fa47c076e2135c40022b48df57",
        s: "0x24faf81fb0b88c50ed2fa5f26d9292f0554eeed41e112bffba0bc374a82ad730",
        to: "0x48aa3d8e793e8d2da00b23ecea1555c3b82e3262",
        transactionIndex: 0,
        v: "0x41",
        value: 10000000000000000000
    }]
    这里可以查到有2笔挂起的交易待确认,延迟一会等交易确认完毕。
    > eth.pendingTransactions
    []
    
    5.7 查询用户钱包余额

    交易全部确认完毕后,再次查询所有钱包的用户钱包余额

    >  web3.fromWei(eth.getBalance('0xbe38275ed6414268698294a9a1c06909e8b96a5a'))
    15
    > web3.fromWei(eth.getBalance('0x48aa3d8e793e8d2da00b23ecea1555c3b82e3262'))
    10
    > web3.fromWei(eth.getBalance('0xef41fbddb7fd8cc42076c7613f98c533d55e8d99'))
    14.999244
    

    这里我们发现,节点二和节点三的用户钱包,都已经有余额了,说明转账成功了。
    节点一的钱包余额不是 40-15-10 = 15,而是比15小一些,这里面主要每一笔交易都需要交易费的,从转出方进行扣除,也就是手续费,手续费支付给参与记账的节点。

    6 监控私有链节点运行情况

    6.1 eth-netstats

    以太坊网络监控,官方网站:https://ethstats.net/
    本小节的目标,就是我们搭建一个自己的私链的监控平台
    首先,新打开一个宿主机终端,下载源码

    # cd  /home/ethereum
    # git clone https://github.com/cubedro/eth-netstats.git
    # cd eth-netstats
    # npm install
    # npm install -g grunt-cli
    这一步需要一段时间
    

    这时候一般会报告一些错误,主要一些依赖库没有自动安装
    我们需要手工安装上

    # npm install debug
    # npm install lodash logger chalk http  express primus primus-emit primus-spark-latency
    # npm install body-parser websockets
    # npm install --save ws
    # npm install d3 geoip-lite 
    # npm install jade
    # npm install
    # npm install -g grunt-cli
    
    

    运行成功后,通过浏览器访问页面,默认监听的宿主机的3000端口
    在我的演示中:


    eth-netstat.png

    现在还没有数据,显示的都是空的,接下来我们需要给页面推送数据过来。

    6.2 eth-net-intelligence-api

    这个是后端服务,与以太坊一起运行并跟踪网络状态,通过JSON-RPC获取信息并通过WebSockets连接到eth-netstats以提供信息。 有关完整安装说明,请前往阅读eth-net-intelligence-api

    另外打开一个宿主机的终端

    # cd /home/ethereum
    # git clone https://github.com/cubedro/eth-net-intelligence-api.git
    

    需要提前安装pm2 软件包

    # npm install -g pm2
    # pm2 --version
    2.10.2
    

    需要复制一份app.json 文件对应的节点

    # cp app.json enode1.json
    文件内容如下
    # cat enode1.json
    [
      {
        "name"              : "enodeAA",
        "script"            : "app.js",
        "log_date_format"   : "YYYY-MM-DD HH:mm Z",
        "merge_logs"        : false,
        "watch"             : false,
        "max_restarts"      : 10,
        "exec_interpreter"  : "node",
        "exec_mode"         : "fork_mode",
        "env":
        {
          "NODE_ENV"        : "production",
          "RPC_HOST"        : "172.17.0.2",
          "RPC_PORT"        : "8545",
          "LISTENING_PORT"  : "30303",
          "INSTANCE_NAME"   : "enodeAA",
          "CONTACT_DETAILS" : "",
          "WS_SERVER"       : "http://192.168.6.116:3000",
          "WS_SECRET"       : "admin",
          "VERBOSITY"       : 2
        }
      }
    ]
    

    同样再次复制一份生成enode2.json 和 enode3.json

    # cat enode2.json
    [
      {
        "name"              : "enodeAB",
        "script"            : "app.js",
        "log_date_format"   : "YYYY-MM-DD HH:mm Z",
        "merge_logs"        : false,
        "watch"             : false,
        "max_restarts"      : 10,
        "exec_interpreter"  : "node",
        "exec_mode"         : "fork_mode",
        "env":
        {
          "NODE_ENV"        : "production",
          "RPC_HOST"        : "172.17.0.3",
          "RPC_PORT"        : "8545",
          "LISTENING_PORT"  : "30303",
          "INSTANCE_NAME"   : "enodeAB",
          "CONTACT_DETAILS" : "",
          "WS_SERVER"       : "http://192.168.6.116:3000",
          "WS_SECRET"       : "admin",
          "VERBOSITY"       : 2
        }
      }
    ]
    # cat enode3.json
    [
      {
        "name"              : "enodeAC",
        "script"            : "app.js",
        "log_date_format"   : "YYYY-MM-DD HH:mm Z",
        "merge_logs"        : false,
        "watch"             : false,
        "max_restarts"      : 10,
        "exec_interpreter"  : "node",
        "exec_mode"         : "fork_mode",
        "env":
        {
          "NODE_ENV"        : "production",
          "RPC_HOST"        : "172.17.0.4",
          "RPC_PORT"        : "8545",
          "LISTENING_PORT"  : "30303",
          "INSTANCE_NAME"   : "enodeAC",
          "CONTACT_DETAILS" : "",
          "WS_SERVER"       : "http://192.168.6.116:3000",
          "WS_SECRET"       : "admin",
          "VERBOSITY"       : 2
        }
      }
    ]
    

    开始运行底层数据收集程序

    # pm2 start enode1.json
    # pm2 start enode2.json
    # pm2 start enode3.json
    # pm2 list
    ┌──────────┬────┬──────┬──────┬────────┬─────────┬────────┬─────┬───────────┬──────┬──────────┐
    │ App name │ id │ mode │ pid  │ status │ restart │ uptime │ cpu │ mem       │ user │ watching │
    ├──────────┼────┼──────┼──────┼────────┼─────────┼────────┼─────┼───────────┼──────┼──────────┤
    │ enodeAA   │ 0  │ fork │ 3365 │ online │ 85      │ 2s     │ 24% │ 21.3 MB   │ root │ disabled │
    │ enodeAB   │ 1  │ fork │ 3351 │ online │ 5       │ 2s     │ 0%  │ 22.2 MB   │ root │ disabled │
    │ enodeAC   │ 2  │ fork │ 3333 │ online │ 1       │ 2s     │ 0%  │ 24.1 MB   │ root │ disabled │
    └──────────┴────┴──────┴──────┴────────┴─────────┴────────┴─────┴───────────┴──────┴──────────┘
     Use `pm2 show <id|name>` to get more details about an app
    

    状态一栏,如果是online说明运行成功,如果有错误,会提示 errored
    可以通过下面的命令找到提示信息,安装需要的依赖库

    # pm2 logs
    

    如果报错,需要提前安装好一些依赖库

    # npm install  chalk util  web3   debounce geth
    
    6.3 浏览器监控

    状态如下图所示


    eth3.png

    7 备注说明

    后续进行持续更新!

    相关文章

      网友评论

      • DeepblueMRday:enode1、2、3.json 的地址是错误的,web3连接不上,需要改成本地的地址,端口号不同,执行命令docker container -ls 查看
      • DeepblueMRday:运行命令行NodeJS 的监控同步程序中的monitor.js内容是啥,文档中没有体现?

      本文标题:Step by Step 搭建私有链集群(Docker版本)

      本文链接:https://www.haomeiwen.com/subject/egbckftx.html