用Docker构建一个区块链工作和开发环境

预读先知

区块链(Blockchain)是一种安全的在线交易方式。区块链是一种分布式数字账目,在全球数以千计的计算机上记录交易,使得注册的交易不能被追溯改变。他们通过集体自利的大规模协作进行认证。结果是产生一个以参与者的数据安全的不确定性为边际的健壮的工作流。区块链的使用消除了来自数字资产的无限再现性的特性。它确认每个单位的数字现金只花了一次,解决了长期存在的双重支出问题。区块链被描述为价值交换协议。这种价值交换可以更快,更安全,更便宜地通过区块链完成。区块链可以分配所有权,因为它提供了强制提供和接受的记录。

以太坊(Ethereum)是一个运行智能合约的分布式平台:保证应用程序完全按照程序运行,没有任何宕机,审查,欺诈或第三方干扰的可能性。
这些应用程序运行在一个定制的区块链上。这使开发者能够创建市场,存储债务或承诺的记录,根据很久以前给出的指令转移资金(如遗嘱或期货合约)以及很多尚未出现的未来应用场景,所有的这些都没有中间人或者交易对手风险。

在传统的服务器体系结构上,每个应用程序都必须设置自己的服务器,在独立的孤岛中运行自己的代码,使数据共享变得困难。如果单个应用遭到入侵或离线,许多用户和其他应用都会受到影响。

在区块链上,任何人都可以设置一个节点,复制所有节点的必要数据以达成协议,并由用户和应用程序开发人员进行补偿。这允许用户数据保持私有和应用程序分散像互联网应该工作。

以太坊Go(语言)团队构建一个“geth”节点的Docker镜像作为其持续构建链的一部分。 我们可以使用这些镜像在我们的本地环境中快速运行以太坊节点。本文中我们将利用Docker构建一个以太坊工作和开发环境。

一个完整的以太坊节点

第一个测试是下载当前Ethereum go-client(“geth”)映像,并启动连接到以太坊生产网络的客户端节点。

20161210145743

现在,我们启动一个简单的节点,如在Ethereum文档中所描述的。一旦区块同步开始,请使用CTRL + C停止节点。我们不打算使用这个容器,所以现在不需要下载整个链数据。

20161210145756

20161210145804

上面,我们使用RUN命令启动了Docker容器。 RUN拿一个叫做“ethereum / client-go”的镜像创建了一个新容器,并启动这个镜像定义的入口点(entry point)“/ usr / bin / geth”。

以下部分需要重点理解:

1 RUN总是创建一个新容器。这意味着,每次通过使用RUN来启动一个容器,我们最终将产生很多无用的容器。一旦创建了容器,重新启动它的正确方法是用START命令。

2 Docker RUN命令后的命令行参数 -it -p 30303:30303。等价于“-i -t”的“-it”代表“交互式”和“终端设备(tty)”。没有这些说明,容器将在后台运行,不会给我们的终端反馈。 “-p 30303:30303”指示Docker将端口30303从容器内部暴露给主机和其他容器,端口号为30303。容器是一个隔离的环境,并且不会隐含的暴露这个端口,以太坊客户端在容器内部将不能够接触外部世界和区块链。 30303是默认的以太坊点对点网络端口。镜像名称后面的其他参数用于镜像定义为启动时的起点的命令。本例中未设置。

3 另一个比较重要的是这个测试中存储区块链数据的地方。默认情况下,“geth”使用“$ userhome / .etherum”作为默认数据目录。容器内部运行时,如果没有指定,则为“root”:“/root/.ethereum”。然而,这个地方在其“虚拟盘”上的容器的“内部”。使数据位于容器内保持它与主机和其他容器隔离,这不一定是我们想要的。

在这个例子中,节点需要下载整个区块链数据。这需要大量的时间,带宽和存储空间,并且在各种容器和主机之间共享这些文件可能变得非常困难。

共享数据库在运行较大的应用程序时显然是一个典型的问题,Docker在这方面提供了多种选择。在以前的Docker版本中,人们经常使用所谓的“数据容器”。这些是经典和专用容器,独立于专用于存储数据的应用程序运行时实例。当前的Docker版本通过所谓的VOLUMES取代了这个想法。在本文中,我们将使用不同的解决方案:我们将区块链和帐户数据存储在主机的磁盘上,并将目录挂载到容器中。这对于以太坊有一些优势,我们将在后面介绍。

在继续之前,让我们看看我们目前在我们的安装中有什么镜像:

20161210145813

接下来,让我们列出我们的容器实例是否在运行。必须通过添加“-a”命令指定活动容器:

20161210145821

INSPECT命令非常方便,它显示容器的整个配置和情况。

20161210145829

我们当前的容器以前是使用RUN命令创建的,现在我们将使用START命令重新启动实例:

20161210145836

“-i”代表将容器输出到我们的终端,“d8”表示容器ID,它也可以是镜像名称,不需要输入完整的ID,只要它是唯一的。

或者,我们可以在后台启动容器,并稍后连接终端。

20161210145844

或者:

20161210145852

完成这个测试,并了解了上面介绍的基本知识。下面我们将开始区域链之旅了!

我们要做的第一件事是将“geth”节点连接到以太坊生产网络,从而保证我们的本地区块链同步,并为其他工具打开服务端口 – 当然也是在容器中运行。

20161210152439

通过“docker run”命令,启动镜像“ethereum / client-go”。 RUN命令具有以下参数:

“-it”以交互模式启动容器,并将容器的标准输出发送到我们的终端。当以后重新启动容器时,我们可以选择在后台运行该进程,但是现在我们要看看发生了什么。

“ – -name”给容器以逻辑名“ethereum”,我们稍后可以使用它来访问这个实例。
“-p 30303:30303 -p 8545:8545 -p 8546:8546”公开并且将三个端口从容器内部映射到外部。
“-v /opt/docker/ethereum:/root/.ethereum”将主机目录“/ opt / docker / ethereum”(我们要存储区块链数据的位置)挂载到位置“/ root / .ethereum“。后者是“geth”在使用root用户帐户启动时存储所有信息的默认位置。

这个镜像的ENTRYPOINT命令“geth”可以通过INSPECTing可视化来调用,就像我们在主机上直接运行时使用该工具一样。请注意,容器命令行参数不能(容易)过后更改,如果需要不同的命令行,我们将需要创建一个新容器。然而从Docker的角度“容器很便宜”,所以这个约束不是真正的问题,特别是数据不在容器中,不需要重新下载。有一件事要记住:同一时间只有一个“geth”节点可以访问blockchain数据,所以不可能同时运行多个主“geth”节点。

“ – -ws – -rpc”分别激活“geth”的Web接口和HTTP RPC接口。“ – -rpcaddr”0.0.0.0“ – -wsaddr”0.0.0.0“向网络上的所有地址开放这些接口。这样做通常会有点危险,但我们不是在主机的物理网络上运行。这个部分后面会介绍更多。执行上面的命令应该启动一个新的容器,调用“geth”工具,然后开始下载blockchain数据。 (注意:您可以使用“-fast”选项)。

20161210152454

以下行值得重点关注:

20161210152503

1 首先,告警提示“geth”抱怨没有定义“etherbase”。 “etherbase”是成功挖掘区块,执行智能合约并在区块链内返回结果之后用来接收以太奖励的“默认以太坊地址”。这个帐户,在开发合同时也很方便。

2 接下来,我们看到blockchain数据被写入“/root/.ethereum/chaindata”,因为我们已经从我们的主机挂载了这个目录,我们应该可以在本地磁盘上看到出现的数据:

20161210152511

3 最后,HTTP和Web socket端点已经打开,并且创建了默认的IPC(进程间通信)文件句柄“/root/.ethereum/geth.ipc”。这通常会隐藏在容器中,但是我们已经挂载了外部目录,所以该文件可以用于与这个“geth”节点通信。

剩下的就是定义一个默认的以太坊帐户。使用Docker命令允许在正在运行的容器中执行命令,所以很容易。注意,这各操作不会打开另一个不同的容器,它连接到现在运行的容器。

20161210152517

不要忘记记下以太坊地址和密码。为了被“geth”节点识别,容器必须重新启动。新帐户可以在我们挂载的数据目录中找到:

20161210152525

在交互式模式下,容器实例可以使用CTRL + C停止。否则,应该调用docker STOP命令。

关于这一点的最后说明:

为了能与以太坊网络同步,主机时间必须精确匹配以太网网络时间。因此,可能需要使用NTP协议与“世界时间”执行同步:

20161210152532

【容器相关知识】

在当前配置中,我们有一个可以挂载到我们的容器中的以太坊数据目录。这不是因为区块链数据只能在任何情况下由一个进程访问,而是访问可由Ethereum节点用于进程间通信的IPC文件描述符。因此,我们可以在这里继续,而不需要访问网络。

然而,为了充分利用我们的完整容器化以太坊节点,了解Docker如何与网络结合可以大大帮助我们为我们未来的用例找到最佳解决方案。众所周知,网络可以是相当复杂的,所以我们在这里只专注于文章内容相关的部分。

默认情况下,Docker容器无法访问主机的网络。如果这样的话,容器化有什么意义呢?作为替代,Docker创建一个单独的虚拟网络,所有容器和主机都可以访问:“docker0”。可以通过显示主机的网络配置查看:

20161210152541

除了我们的本地网络“eth0”(或类似,NB。eth以太网,而不是以太坊),我们看到名为“docker0”的网络。它是一个不同的子网,172.17.42.1是这个网络上的主机IP地址。为了简单,我们将使用由所有容器共享的默认“docker0”网络。但是,知道Docker也允许创建单独的逻辑网络并将它们分配到特定的容器!

获取容器的IP地址不太直接。默认情况下,在轻量级Linux镜像中未安装“ifconfig”。我们可以使用命令“sudo docker exec apt-get -y install …..”安装一切,但是这个需要在每个容器中一次又一次地执行。有一个更容易的解决方案:

20161210152548

对于这个IP必须要清楚的事情是,它会在在容器重新启动时改变。这个对于将IP用于在创建另一个容器作为命令行参数传递时可能是一个问题,我们将在下面看到。

顺便提一句,INSPECT命令允许查看关于容器,系统和IO配置,启动命令行,文件路径和挂载等等的任何信息。

20161210152632

20161210152648

20161210152658

20161210152658

4 连接JavaScript控制台

接下来,我们想让以太坊主节点与“geth”JavaScript控制台进行交互。这个十分简单…

而棘手的部分是两个容器化的“geth”节点之间的进程间通信。第一个选项是使用安装的数据目录中存在的IPC文件。这是当在同一主机上运行时“geth”节点通信的典型方式。我们需要的是将数据目录安装到第二个节点,在同一个地方,所以“geth”“看到”另一个节点,就好像它只运行一个控制台窗口。两个容器使用相同的IPC描述符进行互连:

20161210152706

替代方案是Web socket或HTTP接口。这需要知道主节点的IP地址,我们知道如何找出.

20161210152713

此方法有一个重要的障碍:我们必须指定主“geth”节点的IP地址作为第二个容器的命令行的一部分。一旦创建,此IP声明不能再更改(除了在配置文件上做手脚),所以这个容器只有在目标容器重新启动和其IP更改之前才有用。

最简单的解决方案是删除此容器,并在每次需要控制台时启动一个新容器。记住 – “容器很便宜”,我们可以用脚本自动化:

20161210152721

5 运行MIX IDE

现在它将要变得非常有趣。到目前为止,我们使用纯命令行“geth”实例在单独的容器中运行,我们使它们进行通信。运行Ethereum Mix IDE增加了一个新的挑战:使用图形用户界面。

Docker不是真正设计为在容器内运行UI,但是我们可以使用各种技巧来解决。目前有三种方式:

1)将整个X11服务器安装到容器中,并使用一些魔法:),如下所述。这个方式很重,但却是完全“Docker方式”让容器保持隔离。

2)简单地将VNC服务器安装到容器中并远程打开UI。很聪明,但由于性能问题用VNC工作却不是真的那么趣。

3)在正确的地方将Linux主机的X11 IPC(进程间通信)socket装入容器,这是相对优雅的,但却打破了容器之间的隔离,因此可能带来安全和稳定问题。

这个问题解决了,我们需要将Mix IDE转换为容器。由于以太坊团队尚未提供预定义的镜像,我们勇敢地使用“Dockerfile”构建自己的镜像。

第一步是创建一个目录来存储Dockerfile,让我们说“ethereum-mix-ide”。接下来,在内部创建名为“Dockerfile”的文件(文件名是必需的),其内容如下:

20161210152731

20161210152739

最后,在目录中,我们调用Docker命令,逐步执行此脚本,并将最终结果保存到我们的新镜像中。请不要忘记在末尾的“。”,因为它是命令行的一部分。

20161210152747

“Dockerfile”脚本是相当自我解释 。我们的镜像是基于最新的官方UBUNTU镜像。首先,它安装各种工具和Mix IDE。为了能够连接到X服务器,正在运行的进程不能是“root”。因此,脚本创建一个名为“mix”的用户并赋予他sudo权限。最后,“mix-ide”设置为自动启动点。
让我们来验证一下结果:

20161210152754

镜像准备好了,我们将创建一个新的容器,并自豪地命名为“ethereumix”

20161210152802

一旦构建,容器可以随时重新启动:

20161210152810

或者,在交互模式下查看所有Beta版的警告消息:

20161210152816

你可能已经注意到这个参数:“-p 18545:8545”。它确实是一个参数,没有typo :)“geth”主节点容器已经将它的端口8545绑定到主机端口8545,所以我们需要选择另一个地方绑定。由于Mix IDE容器启动到“geth”主节点的连接,因此绑定位置不重要。

“-v /tmp/.X11-unix:/tmp/.X11-unix”将我们的本地X11服务器socket挂载到容器,这次使用Docker的VOLUME功能,“-e DISPLAY = $ DISPLAY”设置$ DISPLAY环境变量在容器内的值与我们在主机上的值相同。此变量指定X客户端要显示的地址,这是必须设置的。

Mix IDE不需要访问Blockchain数据,但是我们需要在我们要在区块链上部署Smart Contract时联系主要的以太坊“geth”服务器节点。同样,我们需要服务器节点的IP地址:

20161210152823

然而,这一次,目标地址在UI中指定,并且当服务器IP更改时,容器可以重复使用而不用麻烦。

20161210152830

镜像创建脚本安装了使用Mix IDE所需的内容,但我们可能需要安装其他工具或稍后调整容器。正如我们上面已经看到的,我们可以在容器内执行命令,这甚至可以是一个交互式shell:

20161210152839

成功!

分享到:更多 ()