# GETH 本地多节点私链docker搭建手册

By [Web3踩坑日记](https://paragraph.com/@st4rball) · 2022-05-04

---

最近NFT比较火，背后的智能合约逐渐成熟，Web3似乎到了即将快速发展时期。为了能够研究一下底层的技术，在本地搭建私链并进行调试最容易接触到整个智能合约运作的原理。我这里并没有选择直接本地安装`GETH`，为了更加真实的模拟多个节点的情况，使用`docker`来搭建多个容器部署`GETH`节点会更加的有意思些。同时除了建立本地私链，还可以接入本地的区块浏览器和监控进行观察。编写合约的流程就放到下一篇文章(才不是因为懒)吧。

前期准备
----

### 环境

    - Mac OS Big Sur 11.4
    - Python 3.9
    
    - Docker Client:
       - Cloud integration: v1.0.22
       - Version:           20.10.13
    
    - Docker Server: Docker Desktop 4.6.1 (76265)
      - Engine:
      - Version:          20.10.13
        - Go version:       go1.16.15
      - containerd:
        - Version:          1.5.10
      - runc:
        - Version:          1.0.3
      - docker-init:
        - Version:          0.19.0
    

### Docker image

*   ethereum/client-go:latest
    
*   kamael/eth-netstats:latest
    
*   alethio/ethereum-lite-explorer:latest
    

### Git repo

*   eth-net-intelligence-api
    

搭建流程
----

### 节点初始化

首先需要起2+个docker容器，其中一个作为`Main`节点，其他的作为`Slave`节点，将`Slave`节点连接到`Main`节点即可完成多peers的私链状态。

*   启动`Main`节点容器
    

    docker run --entrypoint /bin/sh -p 30303:30303 -p 30303:30303/udp -p 8545:8545 -it -v $(PATH):/root --privileged=true ethereum/client-go
    

其中，`-p 30303:30303/udp`是不可或缺的，节点的发现与连接是通过UDP进行的通信，不加上这一条就会导致无法`addPeers`。`8545`则作为RPC通信端口。挂载的目的是为了之后方便修改配置和查看log。这里容器的启动，都没有选择容器自带的entrypoint来启动`GETH`，主要是为了定制化的启动想要的`GETH`。

*   启动`Slave`节点容器
    

    docker run --entrypoint /bin/sh -p -it -v $(PATH):/root --privileged=true ethereum/client-go
    

`Slave`节点则不需要额外暴露端口，通过docker内部的网桥可以直接互联，当然也可能建立新的`docker network`。对了，这两个容器的挂载文件夹最好的不同的。

*   启动`GETH`节点
    

为了创建自定义的本地Private Network，需要创建`genesis.json`。

    {
            "config": {
                    "chainId": 2333,
                    "homesteadBlock": 0,
                    "eip150Block": 0,
                    "eip155Block": 0,
                    "eip158Block": 0,
                    "byzantiumBlock": 0,
                    "constantinopleBlock": 0
            },
            "coinbase" : "0x0000000000000000000000000000000000000000",
            "difficulty" : "10",
            "extraData" : "",
            "gasLimit" : "0xffffffff",
            "nonce" : "0x0000000000000042",
            "mixhash" : "0x0000000000000000000000000000000000000000000000000000000000000000",
            "parentHash" : "0x0000000000000000000000000000000000000000000000000000000000000000",
            "timestamp" : "0x00",
            "alloc": { 
                    "a65cf03166298503d8e81053167bb8d97213f4a2": {"balance": "100000000"}
            }
    }
    

这里面主要需要修改的是`chainId`，每个网络都有唯一的id，节点之间的发现也是通过这个完成。其他`config`里的参数尽量保持，之前漏配上一个参数会导致无法进行创世块的初始化。`difficulty`则决定了挖矿(PoW)的难度，因为是本地测试，可以小一点。`gasLimit`可以设置大一点，方便跑复杂的合约。`coinbase`是挖矿获得收益存入的钱包地址。`alloc`是可以通过设定一些初始钱包的余额。

再完成了`genesis.json`的编辑后，需要进行初始化。这一步在`Main`和`Slave`里都需要操作。需要把同一份配置文件放到每个容器挂载的目录里，保证初始化的信息是一致，不然之后节点之间因为hash不同无法互联。

    geth --datadir /root/data init genesis.json
    

### 启动节点

> 启动 `Main` 节点

    geth --networkid 2333 --http --http.addr=0.0.0.0 --http.port=8545 --http.api "web3,eth,debug,personal,net,admin,txpool" --http.corsdomain "*" --allow-insecure-unlock --datadir data --nodiscover --nat extip:172.17.0.2 -verbosity 10 --ipcdisable --vmdebug --ws --ws.addr=0.0.0.0 --ws.origins=* --graphql --graphql.corsdomain=* --graphql.vhosts=* --txpool.lifetime 0h5m0s --rpc.allow-unprotected-txs --identity "Main" console 2>> main.log
    

> 启动 `Slave` 节点

    geth --networkid 2333 --http --http.addr=0.0.0.0 --http.port=8545 --http.api "web3,eth,debug,personal,net" --http.corsdomain "*" --allow-insecure-unlock --datadir data --nodiscover --nat extip:172.17.0.3 -verbosity 10 --ipcdisable --vmdebug --ws --ws.addr=0.0.0.0 --ws.origins=* --graphql --graphql.corsdomain=* --graphql.vhosts=* --miner.threads 1 --txpool.lifetime 0h5m0s --rpc.allow-unprotected-txs --identity "Slave" console 2>> slave.log
    

![启动多节点](https://storage.googleapis.com/papyrus_images/cdf3e4cca11c0ec85946bd33c944119c47b134f0cd1f79230d13deb7927576fa.png)

启动多节点

这两个启动命令真的是又臭又长，主要是增加了很多配置，可以将其保存下来写到`bash脚本`里进行启动。接下来解释一下相关参数的作用：

1.  \--networkid: 和`genesis.json`里的保持一致
    
2.  \--http: 支持http接口调用，后面的相关参数都是为了配置http接口
    
3.  \--allow-insecure-unlock: 允许不安全的解锁账户，方便调试
    
4.  \--datadir: 之前初始化的节点数据位置
    
5.  \--nodiscover: 如果不加上这个，在节点启动后，会有大量外部节点进行探测
    
6.  \--nat: 开启NAT模式，不然容器节点无法互联，后面的地址是docker容器的地址。在容器启动这里非常关键，很多情况的节点无法`addPeers`就是这个原因
    
7.  \--verbosity: 日志等级，方便排查问题
    
8.  \--ipcdisable: 节点之间的互联没有通过ipc的方式，这里使用网络通信的方式
    
9.  \--ws: 启用websocket方便之后搭建节点监控，--graphql也是一样的作用
    
10.  \--rpc.allow-unprotected-txs: 常规情况不会需要开启这个，由于ERC1820协议需要注册固定地址，需要开启这个配置，之后在部署合约的时候会详细讲解相关内容
    
11.  \--identity: 定义节点名称
    
12.  \--txpool.lifetime: 对于一些交易，如果长时间没有回应，自动终结，避免一些tx卡住
    
13.  \--miner.threads: `Slave`节点用来控制miner线程
    

*   AddPeers
    

刚才虽然启动了节点，但是由于开启了`--nodiscover`，节点无法直接通过发现的方式自动互联，因此需要手动进行添加节点。通过查询node的信息，然后使用`admin.addPeers()`添加节点。

实际上，即使`admin.addPeers()`返回`True`也不代表真的就添加节点成功，需要通过`admin.peers`进行检查。

通过以下的`curl`也可以验证节点是否成功添加，以及http接口是否正常。

    curl --location --request POST 'localhost:8545' --header 'Content-Type: application/json' --data-raw '{    "jsonrpc": "2.0",    "id": 1,   "method": "admin_peers",    "params": []}'
    

### 挖矿

首先确保要有account，不然需要先去创建。然后选择其中一个节点作为挖矿收益汇入的钱包。

    > miner.setEtherbase(eth.accounts[0])
    

通过这种方式，可以挖到一个`block`就停止。这里是保持每一笔交易都需要手动确认挖矿。另一种也可以将`Slave`节点启动

    > miner.start();admin.sleepBlocks(1),miner.stop()
    

监控
--

### 本地区块浏览器

    docker run -d -p 8000:80 -e APP_NODE_URL="http://0.0.0.0:8545" alethio/ethereum-lite-explorer
    

![本地简易区块浏览器](https://storage.googleapis.com/papyrus_images/dabd5f824551e84dfb3628689151aef42f1a0cf2e0438002dad45f3fdb9d52f8.png)

本地简易区块浏览器

### 节点状态Dashboard

启动监控

    $ docker run -d -p 3000:3000 -e WS_SECRET=mysecret --name eth-netstats kamael/eth-netstats
    
    $ git clone https://github.com/cubedro/eth-net-intelligence-api
    

![节点Dashboard](https://storage.googleapis.com/papyrus_images/82f0a7252146d6e2dd682a39639dd6290089192b78f7b957701682308512642a.png)

节点Dashboard

常见问题
----

Q1: 无法成功addPeers

A1: 网络配置问题，或者启动参数没有配置正确

Q2: tx交易一直pending

A2: 退出`Geth`节点终端，进入`data/geth`删去`transactions.rlp`然后重新启动节点即可，每个节点都需要删去这个文件

一些玩法
----

至此已经可以成功的在本地搭建起ETH Private Network了，之后就可以在上面随心所欲的玩耍了。结合`remix`在本地进行编写合约以及运行合约会更加的容易些。之后再来补上关于本地跑合约，创建自己的Token，以及部署ERC1820在本地私链。

---

*Originally published on [Web3踩坑日记](https://paragraph.com/@st4rball/geth-docker)*
