在上一篇文章中,我们介绍了在 Kubernetes 上运行 Fabric 的机制和架构。 这篇文章会详细讲解安装的具体步骤。
准备环境
CMD 客户机
- 自己的 Mac(10.4.249.231)
- 可以通过 kubectl 操作远程的 Kubernetes 集群
- 安装 Python3,部署脚本是用 Python 写的
集群环境
- CentOS7
- Kubernetes v1.11.0
- Docker 18.03.1-ce
- Fabric 1.2.0
NFS
- 给集群做共享存储,挂载证书和一些 channel 文件。
代码
本文用到的代码在 https://github.com/batizhao/fabric-on-kubernetes
1 | fabric-on-kubernetes |
配置文件
A. crypto-config.yaml
cryptogen 工具根据 crypto-config.yaml 来生成 Fabric 成员的证书,一个简单的例子如下:
1 | OrdererOrgs: |
其中 OrdererOrgs 和 PeerOrgs 关键字区分 organization 的类型,两种组织的内部结构如下:
1) OrdererOrgs 中定义了一个名字为 Orderer ,域名为 orgorderer1 的 org 。
2) PeerOrgs 中定义了两个 org ,分别为 Org1 和 Org2 ,对应的域名为 org1、 org2 与 orderer 类似,每个 org 生成了两个 peers ,虽然 org1 中 peer0 和 org2 中 peer0 的 ID 重复,但是他不属于同一个 org ,通过域名很容易就能区分出它们。
需要注意的是,由于 K8S 中的 namespace 不支持 ‘.’ 和大写字母,因此各个组织的域名不能包含这些字符。
更多关于 crypto-config.yaml 的配置方式,请参考 Fabric 源码中的关于 cryptogen 的描述 ( fabric/common/tools/cryptogen/main.go)
cryptogen 工具会生成 crypto-config 目录,该目录的结构如下:
1 | crypto-config |
可以看出,每个 org 都包含了 msp、 ca、 tlsca 和 users 目录,然后根据 org 类型的不同,还分别有 peers 和 orderers 目录,里面存放着 org 中每个成员的 msp 和 tls 文件。
B. configtx.yaml
configtxgen 工具根据该文件生成 Orderer 初始化的时候要使用的 genesis.block(创世块),获知 organization 的各种信息。因此,用户要根据 crypto-config.yaml 中关于 organization 的定义来修改 configtx.yaml 以生成合适的 genesis.block 。例如,用户在 crypto-config.yaml 中增加了一个 Org3 ,并且要创建一个包含 Org1, Org2, Org3 的集群,则应该通过以下两步修改 configtx.yaml :
在 profile 中增加 Org3:
在 Organization 中增加 Org3 的 MSPDir
注意的是每个 organization 中的 MSPDir 的值必须是这种形式:
crypto-config/{OrgType}/{OrgName}/msp
模板文件
在 Kubernetes 中部署 Fabric 时,需要为每个节点编写相应的配置文件。由于节点数可能很多,这是既复杂又易错的重复劳动。为提高效率,可通过模板自动生成配置文件。本文使用了 5 个模板文件,可用脚本替换其中的变量,均在笔者给出示例代码中的 templates 目录中,这些模板的作用如下:
A. namespace.yaml
定义 Fabric 集群在 K8s 中的 namespace ,它对应着 organization 的域名。为了在多节点共享证书等文件,使用了 NFS 服务器作为存储。在 K8s 中通过相应的 PV 和 PVC ,namespace 下的 Pod 可以通过 PVC 来获取与之相应的文件。
B. cli.yaml
CLI pod 模板,每个 organization 中都配备了一个 CLI pod,目的是提供命令行界面,可统一管理组织内的所有 peer ,其中包括 channel 的创建, chaincode 的安装等。CLI Pod 的 CORE_PEER_ADDRESS 环境变量默认值为 org 中的第一个 peer,可以通过修改该环境变量来连接不同的 peer 。
yaml 文件中的 command 是为了防止 CLI pod 自动退出,CLI 的默认工作目录为 /opt/gopath/src/github.com/hyperledger/fabric/peer 。由于该目录下的 channel-artifacts 挂载了 NFS 上 /opt/share/channel-artifacts,因此把创建 channel 时返回的 xxx.block 文件放在该目录下供所有 CLI Pod共享。
C. ca.yaml
Fabric 的 CA 服务的 pod 定义模板,用于 organization 中的证书管理,其 yaml 文件除了定义 deployment 外,还定义了 service 。service 通过 selector 与 deployment 绑定,其中 deployment 中的 label 是 selector 与其绑定的根据。
D. orderer.yaml
Orderer 的 pod 定义模板,需要注意的是,cryptogen 并不会生成 genesis.block ,然而缺少该文件时,orderer 会启动失败,因此在启动 orderer 之前需要预先生成 genesis.block ,并将其放在相应的 org 目录下。
E. peer.yaml
每个 peer pod 的定义模板。在该 yaml 中分别定义了 peer 和 couchDB 两个 container 。在实例化 chaincode (cc) 时,peer 需要连接 Docker 引擎来创建 cc 容器,因此要把 worker 宿主机的 var/run/docker.sock 映射到 peer 容器内部。
DNS
在 Fabric 设计中, chaincode 目前是以 Docker 容器的方式运行在 peer 容器所在的宿主机上,peer 容器需要调用 Docker 引擎的接口来构建和创建 chaincode 容器,调用接口是通过这个连接:
unix:///var/run/docker.sock
通过 docker.sock 创建的容器脱离在 Kubernetes 的体系之外,虽然它仍在 Flannel 的网络上,但却无法获得 peer 节点的 IP 地址。这是因为创建该容器的 Docker 引擎使用宿主机默认的 DNS 解析来 peer 的域名,所以无法找到。
为了解决解析域名的问题,需要在每个 worker 的 DOCKER_OPTS 中加入相关参数,我的 kube-dns 的 IP 为10.68.0.2,宿主机网络 DNS 的 IP 地址假设为 10.4.246.1,为使得 chaincode 的容器可以解析到 peer 节点,在每个 Docker 节点,修改步骤如下:
1 | echo 'DOCKER_OPTS="--dns=10.68.0.2 --dns=10.4.246.1 --dns-search default.svc.cluster.local --dns-search svc.cluster.local --dns-opt ndots:2 --dns-opt timeout:2 --dns-opt attempts:2"' >> /etc/default/docker |
部署
以下操作都在 CMD 客户机上进行,NFS 的共享目录为 /opt/share。
在 CMD 中挂载 NFS 目录
在 nfs server 上创建 /data 和 /opt/share 两个目录,保证 CMD 和集群可以有权限读写。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18 cat << EOF >> /etc/exports
/opt/share 10.4.249.231(rw,insecure,no_root_squash)
/data 10.4.249.231(rw,insecure,no_root_squash)
/data 172.31.21.0/24(rw,insecure,no_root_squash)
/opt/share 172.31.21.0/24(rw,insecure,no_root_squash)
EOF
exportfs -rv
exporting 10.4.249.231:/data
exporting 10.4.249.231:/opt/share
exporting 172.31.21.0/24:/opt/share
exporting 172.31.21.0/24:/data
showmount -e 172.31.21.208
Exports list on 172.31.21.208:
/data 172.31.21.0/24 10.4.249.231
/opt/share 172.31.21.0/24 10.4.249.231
在 CMD 上创建 /opt/share 和 /opt/data 目录并 mount
1 | mkdir -p /opt/share && sudo chmod 777 /opt/share |
下载源码和 Fabric 脚本
1 | git clone https://github.com/batizhao/fabric-on-kubernetes.git |
这段会在当前目录下生成一个 bin 目录,包含了运行 Fabric 的二进制脚本和证书、创世块生成工具。
在部署之前,还需要修改 cli 和 namespace 中的 nfs 地址。
部署
1 | ./generateALL.sh |
这段调用 configtxgen 生成各组织的证书,调用 python 生成 Kubernetes 的 yaml 文件。
1 | python3 transform/run.py |
这段会通过 python 脚本调用 kubectl 在 Kubernetes 上创建 Fabric 集群。
验证
1 | kubectl get pod --all-namespaces |
几分钟以后,可以看到所有 Pod 都 Running 了。
测试 Fabric 集群
当所有 Pod Running 之后,可以进入开发、部署 chaincode 的阶段。
进入 CLI 容器
A. 查找 org1 下边的容器
1 | kubectl get pod -n org1 |
登录 org1 cli 容器
1 | kubectl exec -it cli-59d46f884-p5grk bash -n org1 |
创建channel
1 | peer channel create -o orderer0.orgorderer1:7050 -c mychannel -f ./channel-artifacts/channel.tx |
拷贝 mychannel.block 到 channel-artifacts 目录
1 | cp mychannel.block channel-artifacts |
加入 mychannel
1 | peer channel join -b channel-artifacts/mychannel.block |
更新 anchor peer,每个 org 只需执行一次
1 | peer channel update -o orderer0.orgorderer1:7050 -c mychannel -f./channel-artifacts/Org1MSPanchors.tx |
B. 查找 org2 下边的容器
1 | kubectl get pod -n org2 |
登录 org2 cli 容器
1 | kubectl exec -it cli-7798868bf9-zmq8d bash -n org2 |
加入 mychannel
1 | peer channel join -b channel-artifacts/mychannel.block |
更新 anchor peer,每个 org 只需执行一次
1 | peer channel update -o orderer0.orgorderer1:7050 -c mychannel -f./channel-artifacts/Org2MSPanchors.tx |
安装 chaincode
分别登录 org1 cli ,org2 cli 容器
1 | kubectl exec -it cli-59d46f884-p5grk bash -n org1 |
安装 chaincode
1 | peer chaincode install -n mycc -v 1.0 -p github.com/hyperledger/fabric/peer/channel-artifacts/chaincode |
实例化 chaincode(只要在任意 org 执行一次)
1 | peer chaincode instantiate -o orderer0.orgorderer1:7050 \ |
查询账本
1 | peer chaincode query -C mychannel -n mycc -c '{"Args":["query","a"]}' |
a to b 转帐 10
1 | peer chaincode invoke -o orderer0.orgorderer1:7050\ |
查询账本
1 | peer chaincode query -C mychannel -n mycc -c '{"Args":["query","a"]}' |
Chaincode Java Client
这里 用 Java 实现了一个 Chaincode API 调用,效果同上边的命令行。
清除集群
当需要删除集群的时候,可以通过 transform 目录下的 delete.py 脚本来清理环境,该脚本会遍历 crypto-config 目录,找出所有的 yaml 文件,并通过 kuberclt delete -f xxx.yaml 的方式将资源逐个删除。
1 | python3 transform/delete.py |
总结
本文介绍的部署方法,是基于 Kubernetes 容器云平台实现 BaaS 的基础步骤。先介绍了 Fabric 的架构、详细部署过程,之后介绍了 chaincode 的部署和调用。在此之上,可以增加更多的区块链层管理功能,图形化运维界面,使得开发人员投入更多的精力到应用的业务逻辑上。
在此之前,有试用过 Heperledger Cello,发现这个项目还没法用。在网上找到 How to Deploy Hyperledger Fabric on Kubernetes 这篇博文,和国内这篇 用Kubernetes部署超级账本Fabric的区块链即服务 做参考,应该是同一个人写的。只不过写于 2017 年 Fabric v1.0 的时候,现在已经是 v1.2,所以对项目做了些修改才能运行。这应该也是目前唯一的 Fabric v1.2 on Kubernetes 部署指引。后续会通过实现一些 chaincode 来更深入的理解 Fabric 原理。