最近跟着这本书学习区块链相关入门知识,
区块链技术指南pdf下载:https://legacy.gitbook.com/download/pdf/book/yeasy/blockchain_guide
该pdf已出版纸质书,《区块链原理、设计与应用》:https://item.jd.com/12159265.html
总体来说这本书对想了解区块链技术的初学者非常有价值,可以了解到区块链相关技术的来龙去脉、基本原理、知识体系、相关开源项目等等。看完超级账本项目fabric之后,感觉其整体框架跟同为分布式系统的OpenStack、k8s也比较类似(当然只是从框架来看,从底层实现来讲还是有很大差异的),主要差异在于其数据存储方式上,共识机制方面,OpenStack、k8s都是以传统数据库或etcd作为基础的,类似区块链中共识算法的多个提案者+一个确认者的场景。都是由多个服务构成,比如nova服务有nova-api、nova-compute、nova-scheduler、nova-conductor等服务,k8s有kublet、kube-apiserver、kube-proxy、kube-scheduler等服务,而fabric则有peer、ca、order这几个服务,他们也都有对应的客户端(命令行、sdk)用来发送请求给服务端。
fabric具体能干啥就不提了,还是看书吧,我还是从已知的IaaS、PaaS等云计算技术来对比,总体来说它可以用高级编程语言编写链码,然后上传到fabric区块链网络的某个节点里(具体来说是某个peer节点的docker容器里,我这里还有个疑问没有找到答案,一个链码只能跑在一个peer节点的一个容器里面吗?我理解应该是可以跑在多个peer节点的,从而实现分布式应用,不然就没有意义了),链码可以实现各种各样的功能(实现智能合约、账本管理等),链码用到的持久化数据都在区块链的块里存储,区块相当于是一个分布式数据库(或者专业名词叫分布式账本),从而实现应用的分布式、高可用、高可靠,以及数据的不可篡改,相比较而言,利用IaaS、PaaS等云计算技术也可以实现类似的功能或架构,尤其是k8s的微服务架构,也能支持分布式应用,但其数据却通常还是集中式存储的,并且容易篡改(当然其适用场景也不是为了解决这个问题,我这里只是随意对比下),也可以用分布式数据库来存数据,但仍然不像区块链那样,数据是用链表+区块来存储的。
要说fabric和k8s唯一的关联,我理解就是他们都是把链码或者说应用跑在docker里面的。fabric通过gRPC协议调用链码接口(接口比较固定),而k8s的服务则一般是通过HTTP RestFul API来调用服务(当然也有其他服务使用其他协议,如tcp或者专有协议)。
代码跑在哪里、数据存在哪里其实都不是关键,关键是怎么样让代码跑的愉快,跑的稳定,跑的没压力,还有就是让数据存的可靠,存的准确,存的安全。去中心化的问题,也是相对的,如果你的应用跑在全世界各地,数据也做到分布式存储到世界各地,我理解这也算是去中心化的。
不胡扯了,下面讲下怎么搭fabric测试环境,我这也是现学现卖,跟着官网文档学的,跑了一个example。我这里用的是fabric-1.1版本,操作系统是CentOS-7.2 x86_64。
环境准备:https://hyperledger-fabric.readthedocs.io/en/release-1.1/prereqs.html
主要是两个部分,一个是golang环境,一个是docker环境,docker包含docker daemon和docker-compose两部分。
安装比较简单,yum install golang docker docker-compose,完事儿,其他依赖如curl一般操作系统都是自带有的,当然下面还要用到git clone代码,也可以提前装上。
docker最好配置下国内的镜像源,否则下镜像要等死。修改docker daemon的配置,之后重启docker服务,systemctl restart docker。
1 2 3 4 5 6 7 8 9 10 |
{ "registry-mirrors": ["https://kuamavit.mirror.aliyuncs.com", "https://registry.docker-cn.com", "https://docker.mirrors.ustc.edu.cn"], "max-concurrent-downloads": 10, "log-driver": "json-file", "log-level": "warn", "log-opts": { "max-size": "10m", "max-file": "3" } } |
上面的几个registry-mirrors不保证可用,最好自己找好用的替换。
之后就是下载代码了,需要下载两个项目,一个是fabric本身,一个是fabric-example,当然我是为了跑example示例才下载的第二个,如果你想手工搭fabric环境,可以不用下载第二个。
1 2 3 4 5 6 7 |
git clone https://github.com/hyperledger/fabric.git # 如果默认不是release-1.1分支,需要切一下 git checkout origin/release-1.1 -b release-1.1 git clone https://github.com/hyperledger/fabric-samples.git # 同样切分支,当然也可以git clone -b直接切,我不习惯那么用 git checkout origin/release-1.1 -b release-1.1 |
再之后就是准备fabric可执行文件了,脚本在fabric项目目录下的scripts目录下:
fabric/scripts/bootstrap.sh,直接cd到这个目录下,./bootstrap.sh执行就好了,因为我们已经checkout到1.1版本的release分支了,默认就是跑的1.1版本。
相关可执行文件会下载到scripts目录下的bin目录(get-docker-images.sh是代码库自带的,其他几个二进制文件是新下载的):
1 2 3 4 5 6 7 8 9 |
root@linux scripts [release-1.1] $ ll bin/ total 148968 -rwxrwxr-x 1 1001 1001 23653336 Mar 16 06:13 configtxgen -rwxrwxr-x 1 1001 1001 25113376 Mar 16 06:13 configtxlator -rwxrwxr-x 1 1001 1001 12473976 Mar 16 06:13 cryptogen -rwxrwxr-x 1 1001 1001 20731176 Mar 16 09:33 fabric-ca-client -rwxrwxr-x 1 1001 1001 757 Mar 16 06:14 get-docker-images.sh # 本身代码库就有 -rwxrwxr-x 1 1001 1001 31536304 Mar 16 06:14 orderer -rwxrwxr-x 1 1001 1001 39016824 Mar 16 06:14 peer |
看下bootstrap脚本,可以看到是用curl命令下载的文件,每个文件几十M,国外网站比较慢,建议加上代理或者找国内的镜像(我是挂的代理)。
之后还要把这几个可执行文件所在目录加到PATH环境变量里(后面部署环境跑example要用到,具体来说是byfn.sh里面要用),让它们在任何目录下都可以被调用,当然也可以加上软链接,或者直接copy到/usr/local/bin之类的默认已加入环境变量的路径下,我这里是用的添加环境变量方式:export PATH=$PATH:/root/fabric/scripts/bin/
之后就是参考官方上手文档跑example了:https://hyperledger-fabric.readthedocs.io/en/release-1.1/build_network.html
文档里说是执行3步(文档里面有-m参数,看了下byfn.sh,里面已经没用这个参数了,不过貌似不影响执行,被忽略了):
1 2 3 |
./byfn.sh generate ## 生成配置 ./byfn.sh up ## 启动环境 ./byfn.sh down ## 清理环境 |
每一步都有一堆输出,参考官网文档就行了,这里不贴了。
主要就是启动了一坨docker容器(在执行./byfn.sh up命令过程中通过docker ps -a命令查看):
1 2 3 4 5 6 7 8 |
root@linux fabric [release-1.1] $ docker ps -a | grep hyper CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES dbe4601358e7 hyperledger/fabric-tools:latest "/bin/bash" 3 minutes ago Up 2 minutes cli 51b6bf001365 hyperledger/fabric-orderer:latest "orderer" 3 minutes ago Up 3 minutes 0.0.0.0:7050->7050/tcp orderer.example.com 4aabbc96e3ed hyperledger/fabric-peer:latest "peer node start" 3 minutes ago Up 3 minutes 0.0.0.0:9051->7051/tcp, 0.0.0.0:9053->7053/tcp peer0.org2.example.com 85288ba01bbf hyperledger/fabric-peer:latest "peer node start" 3 minutes ago Up 3 minutes 0.0.0.0:8051->7051/tcp, 0.0.0.0:8053->7053/tcp peer1.org1.example.com c908076115cf hyperledger/fabric-peer:latest "peer node start" 3 minutes ago Up 3 minutes 0.0.0.0:10051->7051/tcp, 0.0.0.0:10053->7053/tcp peer1.org2.example.com c96a1d20dac5 hyperledger/fabric-peer:latest "peer node start" 3 minutes ago Up 3 minutes 0.0.0.0:7051->7051/tcp, 0.0.0.0:7053->7053/tcp peer0.org1.example.com |
通过镜像名就可以看出来容器跑的什么服务。
仔细看下byfn.sh就可以分析出fabric是怎么部署的(至少开发测试环境可以这么部署):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
# Generate the needed certificates, the genesis block and start the network. function networkUp () { checkPrereqs # generate artifacts if they don't exist if [ ! -d "crypto-config" ]; then generateCerts replacePrivateKey generateChannelArtifacts fi if [ "${IF_COUCHDB}" == "couchdb" ]; then IMAGE_TAG=$IMAGETAG docker-compose -f $COMPOSE_FILE -f $COMPOSE_FILE_COUCH up -d 2>&1 else IMAGE_TAG=$IMAGETAG docker-compose -f $COMPOSE_FILE up -d 2>&1 fi if [ $? -ne 0 ]; then echo "ERROR !!!! Unable to start network" exit 1 fi # now run the end to end script docker exec cli scripts/script.sh $CHANNEL_NAME $CLI_DELAY $LANGUAGE $CLI_TIMEOUT if [ $? -ne 0 ]; then echo "ERROR !!!! Test failed" exit 1 fi } |
部署环境主要就是跑了这句: IMAGE_TAG=$IMAGETAG docker-compose -f $COMPOSE_FILE up -d 2>&1
然后就是在cli那个容器里跑example: docker exec cli scripts/script.sh $CHANNEL_NAME $CLI_DELAY $LANGUAGE $CLI_TIMEOUT
跑example过程中会有一坨日志在屏幕上输出。
因此部署环境就是通过docker-compose -f $COMPOSE_FILE up -d来搞定的,IMAGETAG可以通过byfn.sh的-i命令指定,默认是latest。COMPOSE_FILE可以通过-f命令指定,默认是docker-compose-cli.yaml,我们看下这个yaml文件:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 |
# Copyright IBM Corp. All Rights Reserved. # # SPDX-License-Identifier: Apache-2.0 # version: '2' volumes: orderer.example.com: peer0.org1.example.com: peer1.org1.example.com: peer0.org2.example.com: peer1.org2.example.com: networks: byfn: services: orderer.example.com: extends: file: base/docker-compose-base.yaml service: orderer.example.com container_name: orderer.example.com networks: - byfn peer0.org1.example.com: container_name: peer0.org1.example.com extends: file: base/docker-compose-base.yaml service: peer0.org1.example.com networks: - byfn peer1.org1.example.com: container_name: peer1.org1.example.com extends: file: base/docker-compose-base.yaml service: peer1.org1.example.com networks: - byfn peer0.org2.example.com: container_name: peer0.org2.example.com extends: file: base/docker-compose-base.yaml service: peer0.org2.example.com networks: - byfn peer1.org2.example.com: container_name: peer1.org2.example.com extends: file: base/docker-compose-base.yaml service: peer1.org2.example.com networks: - byfn cli: container_name: cli image: hyperledger/fabric-tools:$IMAGE_TAG tty: true stdin_open: true environment: - GOPATH=/opt/gopath - CORE_VM_ENDPOINT=unix:///host/var/run/docker.sock #- CORE_LOGGING_LEVEL=DEBUG - CORE_LOGGING_LEVEL=INFO - CORE_PEER_ID=cli - CORE_PEER_ADDRESS=peer0.org1.example.com:7051 - CORE_PEER_LOCALMSPID=Org1MSP - CORE_PEER_TLS_ENABLED=true - CORE_PEER_TLS_CERT_FILE=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org1.example.com/peers/peer0.org1.example.com/tls/server.crt - CORE_PEER_TLS_KEY_FILE=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org1.example.com/peers/peer0.org1.example.com/tls/server.key - CORE_PEER_TLS_ROOTCERT_FILE=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org1.example.com/peers/peer0.org1.example.com/tls/ca.crt - CORE_PEER_MSPCONFIGPATH=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org1.example.com/users/Admin@org1.example.com/msp working_dir: /opt/gopath/src/github.com/hyperledger/fabric/peer command: /bin/bash volumes: - /var/run/:/host/var/run/ - ./../chaincode/:/opt/gopath/src/github.com/chaincode - ./crypto-config:/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/ - ./scripts:/opt/gopath/src/github.com/hyperledger/fabric/peer/scripts/ - ./channel-artifacts:/opt/gopath/src/github.com/hyperledger/fabric/peer/channel-artifacts depends_on: - orderer.example.com - peer0.org1.example.com - peer1.org1.example.com - peer0.org2.example.com - peer1.org2.example.com networks: - byfn |
可以看出里面定义了一个容器网络,networks: byfn,几个volumes与容器名称相同,在base/docker-compose-base.yaml文件里面有用到,6个service包含1个order节点、4个peer节点、一个cli节点(依赖其他5个service,这个容易理解,客户端必须要等服务端启动才能执行命令),每个service各包含一个容器实例,容器名称与service名称一样(就是上面贴出来的docker ps -a命令的看到的容器列表)。
除了cli这个service之外,其它几个都继续使用了base/docker-compose-base.yaml这个compose配置,这里就不贴了,而这个yaml里的peer服务又依赖了base/peer-base.yaml。总之就是定义了一坨docker容器,然后利用docker-compose编排功能把容器跑起来。
关于docker-compose,可以参考官方文档:
networks、volumes等配置文件中关键字的意义可以参考上面的文档,简单来说部分是给docker实例准备的各种参数,与k8s的service配置文件比较类似,都是为编排服务里各种容器的。这里networks默认就是一个Linux bridge,volumes就是一个临时目录mount到docker容器里给容器用来保存临时数据。
配置文件里端口映射、命令行、环境变量啥的就不说了。
可以看出,整个集群或者说区块链网络就是通过docker compose的编排功能实现的。比手工部署跑起来简单方便多了。