• -------------------------------------------------------------
  • ====================================

fabric部署填坑记

区块链 dewbay 4年前 (2020-09-29) 2024次浏览 已收录 0个评论 扫描二维码

fabric 版本:release-1.3

机器环境:

  1. cat /etc/os-release
  2. NAME="CentOS Linux"
  3. VERSION="7 (Core)"
  4. ID="centos"
  5. ID_LIKE="rhel fedora"
  6. VERSION_ID="7"
  7. PRETTY_NAME="CentOS Linux 7 (Core)"
  8. ANSI_COLOR="0;31"
  9. CPE_NAME="cpe:/o:centos:centos:7"
  10. HOME_URL="https://www.centos.org/"
  11. BUG_REPORT_URL="https://bugs.centos.org/"
  12. CENTOS_MANTISBT_PROJECT="CentOS-7"
  13. CENTOS_MANTISBT_PROJECT_VERSION="7"
  14. REDHAT_SUPPORT_PRODUCT="centos"
  15. REDHAT_SUPPORT_PRODUCT_VERSION="7"
  16. #内核版本
  17. uname -sr
  18. Linux 3.10.0-514.26.2.el7.x86_64
  19. #官方自带 Docker 版本
  20. rpm -qa | grep docker
  21. docker-common-1.13.1-84.git07f3374.el7.centos.x86_64
  22. docker-client-1.13.1-84.git07f3374.el7.centos.x86_64
  23. docker-1.13.1-84.git07f3374.el7.centos.x86_64

初步申请 5 台机器来搭建集群网络:

名称 ip 地址 职责
orderer.example.com xx.xx.xx.x1 共识节点
peer0.org1.example.com xx.xx.xx.x2 公司 1 peer0
peer1.org1.example.com xx.xx.xx.x3 公司 1 peer1
peer0.org2.example.com xx.xx.xx.x4 公司 2 peer0
peer1.org2.example.com xx.xx.xx.x5 公司 2 peer1

1.初始化所有机器环境,安装依赖包,配置环境变量

  1. yum install docker git -y
  2. pip install docker-compose
  3. mkdir -p /data/packages/
  4. cd /data/packages
  5. curl https://dl.google.com/go/go1.11.2.linux-amd64.tar.gz -o go1.11.2.linux-amd64.tar.gz
  6. tar -C /usr/local/ -xzf go1.11.2.linux-amd64.tar.gz
  7. echo "PATH=\\$PATH:/usr/local/go/bin" >> /etc/bashrc
  8. echo "export PATH" >> /etc/bashrc
  9. echo "export GOPATH=/data/go" >> /etc/bashrc
  10. source /etc/bashrc
  11. # 在 peer 机器执行
  12. mkdir -p $GOPATH/src/github.com/hyperledger/fabric/multipeer

2. 部署orderer

  1. # 拉取 fabric 源码
  2. mkdir -p $GOPATH/src/github.com/hyperledger/
  3. git clone https://github.com/hyperledger/fabric.git && cd fabric
  4. git checkout release-1.3
  5. make release
  6. mkdir -p multipeer/channel-artifacts && cd multipeer
  7. cp ../examples/e2e_cli/crypto-config.yaml ../examples/e2e_cli/configtx.yaml ../examples/e2e_cli/generateArtifacts.sh .
  8. # 调整 generateArtifacts.sh 脚本
  9. # 调整路径 export FABRIC_ROOT=$PWD/..
  10. # 注释掉该方法 #replacePrivateKey
  11. # 生成证书相关的文件
  12. ./generateArtifacts.sh
  13. cp ../examples/e2e_cli/docker-compose-cli.yaml docker-compose-order.yaml
  14. cp -r ../examples/e2e_cli/base/ .
  15. # 修改 docker-compose-cli.yaml 将 peer 和 cli 的配置删除
  16. # 启动 orderer service
  17. docker-compose -f docker-compose-orderer.yaml up -d 2>&1
  18. # scp 文件到 peer 节点 前提是 peer 节点已创建好接收目录
  19. cp -r ../examples/chaincode/ .
  20. # 删除 chaincode/go/目录下除 examples02 目录外的其他目录,只保留 examples02
  21. scp -r channel-artifacts/ crypto-config/ chaincode/ root@xx.xx.xx.x2:$GOPATH/src/github.com/hyperledger/fabric/multipeer
  22. scp -r channel-artifacts/ crypto-config/ chaincode/ root@xx.xx.xx.x3:$GOPATH/src/github.com/hyperledger/fabric/multipeer
  23. scp -r channel-artifacts/ crypto-config/ chaincode/ root@xx.xx.xx.x4:$GOPATH/src/github.com/hyperledger/fabric/multipeer
  24. scp -r channel-artifacts/ crypto-config/ chaincode/ root@xx.xx.xx.x5:$GOPATH/src/github.com/hyperledger/fabric/multipeer

3. 部署peer xx.xx.xx.x1

touch docker-compose-peer.yaml
文件内容如下(特别注意黄色高亮的部分,不同 peer 可能会有不同):
fabric部署填坑记

  1. # 启动 peer
  2. docker-compose -f docker-compose-peer.yaml up -d
  3. docker exec -it cli bash
  4. # 设置变量
  5. ORDERER_CA=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem
  6. # 创建 channel
  7. peer channel create -o orderer.example.com:7050 -c mychannel -f ./channel-artifacts/channel.tx --tls --cafile $ORDERER_CA
  8. # 加入 channel
  9. peer channel join -b mychannel.block
  10. # 安装 chaincode
  11. peer chaincode install -n mycc -p github.com/hyperledger/fabric/examples/chaincode/go/example02/cmd/ -v 1.0
  12. # 初始化 chaincode
  13. peer chaincode instantiate -o orderer.example.com:7050 --tls --cafile $ORDERER_CA -C mychannel -n mycc -v 1.0 -c '{"Args":["init","a","100","b","200"]}' -P "OR ('Org1MSP.peer','Org2MSP.peer')"
  14. # chaincode 查询 a
  15. peer chaincode query -C mychannel -n mycc -c '{"Args":["query","a"]}'
  16. # chaincode a 向 b 转 10
  17. peer chaincode invoke --tls --cafile $ORDERER_CA -C mychannel -n mycc -c '{"Args":["invoke","a","b","10"]}'
  18. # chaincode 查询 b
  19. peer chaincode query -C mychannel -n mycc -c '{"Args":["query","b"]}'
  20. # 将 docker 中生成的 mychannel.block 文件拷贝出来
  21. docker cp [docker_container_id]:/opt/gopath/src/github.com/hyperledger/fabric/peer .
  22. # 复制到其他 peer 节点
  23. scp mychannel.block oot@xx.xx.xx.x3:$GOPATH/src/github.com/hyperledger/fabric/multipeer
  24. scp mychannel.block oot@xx.xx.xx.x4:$GOPATH/src/github.com/hyperledger/fabric/multipeer
  25. scp mychannel.block oot@xx.xx.xx.x5:$GOPATH/src/github.com/hyperledger/fabric/multipeer

4. peer xx.xx.xx.x3 4 5 节点

touch docker-compose-peer.yaml
内容和上个 peer 只有黄色高亮部分需调整,详情见附件

  1. docker-compose -f docker-compose-peer.yaml up -d
  2. # 查看 docker cli container id
  3. docker ps
  4. docker cp mychannel.block [docker_container_id]:/opt/gopath/src/github.com/hyperledger/fabric/peer
  5. docker exec -it cli bash
  6. peer channel join -b mychannel.block
  7. peer chaincode install -n mycc -p github.com/hyperledger/fabric/examples/chaincode/go/example02/cmd/ -v 1.0
  8. peer chaincode query -C mychannel -n mycc -c '{"Args":["query","a"]}'

顺利的话,到这里就结束了,但实操过程中遇到了几个问题,列出如下:

a. 启动 peer 节点,执行 docker exec -it cli bash 时,抛出 runtime error

  1. Cannot ssh into a running pod/container -- rpc error: code = 2 desc = oci runtime error: exec failed: container_linux.go:247: starting container process caused "process_linux.go:110: decoding init error from pipe caused "read parent: connection reset by peer"" command terminated with exit code 126

原因是 centOS7 官方 Docker 发行版 1.13.1-84 中有 bug 导致的,可以通过如下方式解决(bug 详情参见引用文章 1),降级到 1.13.1-75 版本即可,执行降级 docker 版本命令:

  1. service docker stop
  2. yum downgrade docker-1.13.1-75.git8633870.el7.centos.x86_64 docker-client-1.13.1-75.git8633870.el7.centos.x86_64 docker-common-1.13.1-75.git8633870.el7.centos.x86_64
  3. service docker start

b. 在 peer 节点上执行 peer channel create …、peer channel list 均抛出类似下面的错误:

  1. Error: failed to create deliver client: orderer client failed to connect to orderer.example.com:7050: failed to create new connection: context deadline exceeded

orderer service 服务日志没有显示有连接进来,研究半天无果,决定跑一边 fabric 自带的 e2e_cli 示例程序看看是否正常。

启动 e2e_cli 实例流程:

  1. yum install docker git -y
  2. pip install docker-compose
  3. mkdir -p /data/packages/
  4. cd /data/packages
  5. curl https://dl.google.com/go/go1.11.2.linux-amd64.tar.gz -o go1.11.2.linux-amd64.tar.gz
  6. tar -C /usr/local/ -xzf go1.11.2.linux-amd64.tar.gz
  7. echo "PATH=\\$PATH:/usr/local/go/bin" >> /etc/bashrc
  8. echo "export PATH" >> /etc/bashrc
  9. echo "export GOPATH=/data/go" >> /etc/bashrc
  10. source /etc/bashrc
  11. mkdir -p /data/go/src/github.com/hyperledger/
  12. cd /data/go/src/github.com/hyperledger/
  13. git clone https://github.com/hyperledger/fabric.git
  14. cd fabric
  15. git checkout release-1.3
  16. cd examples/e2e_cli
  17. service docker start
  18. ./network.sh up

结果同样没能正常启动,报如下异常:

  1. ____ _____ _ ____ _____ _____ ____ _____
  2. / ___| |_ _| / \ | _ \ |_ _| | ____| |___ \ | ____|
  3. \___ \ | | / _ \ | |_) | | | _____ | _| __) | | _|
  4. ___) | | | / ___ \ | _ < | | |_____| | |___ / __/ | |___
  5. |____/ |_| /_/ \_\ |_| \_\ |_| |_____| |_____| |_____|
  6. Channel name : mychannel
  7. Check orderering service availability...
  8. Attempting to fetch system channel 'e2e-orderer-syschan' ...3 secs
  9. Attempting to fetch system channel 'e2e-orderer-syschan' ...9 secs
  10. Attempting to fetch system channel 'e2e-orderer-syschan' ...15 secs
  11. Attempting to fetch system channel 'e2e-orderer-syschan' ...21 secs
  12. Attempting to fetch system channel 'e2e-orderer-syschan' ...27 secs
  13. Attempting to fetch system channel 'e2e-orderer-syschan' ...33 secs
  14. Attempting to fetch system channel 'e2e-orderer-syschan' ...39 secs
  15. Attempting to fetch system channel 'e2e-orderer-syschan' ...45 secs
  16. Attempting to fetch system channel 'e2e-orderer-syschan' ...51 secs
  17. Attempting to fetch system channel 'e2e-orderer-syschan' ...57 secs
  18. 2018-12-12 09:58:33.981 UTC [main] InitCmd -> WARN 001 CORE_LOGGING_LEVEL is no longer supported, please use the FABRIC_LOGGING_SPEC environment variable
  19. 2018-12-12 09:58:33.995 UTC [main] SetOrdererEnv -> WARN 002 CORE_LOGGING_LEVEL is no longer supported, please use the FABRIC_LOGGING_SPEC environment variable
  20. Error: failed to create deliver client: orderer client failed to connect to orderer.example.com:7050: failed to create new connection: context deadline exceeded
  21. !!!!!!!!!!!!!!! Ordering Service is not available, Please try again ... !!!!!!!!!!!!!!!!
  22. ================== ERROR !!! FAILED to execute End-2-End Scenario ==================

由于是单机不存在机器安全组问题,无法 connect 应该和 dns 有关,通过对比正常跑起来的机器/etc/resolv.conf 文件,发现如下差异:问题机器多了 options 这一行

  1. options timeout:2 attempts:3 rotate single-request-reopen

在阿里云云栖平台搜索相关内容,果不其然,找到了余珊的这篇《阿里云环境部署 Hyperledger Fabric 之 SIGSEGV 问题分析和解决经验分享》 把问题的来龙去脉分析的很清楚了。
golang 支持两种类型的 dns 解析,即 pure go dns 和 cgo dns,其中 pure go 是使用纯 go 语言实现的 dns 解析,cgo 则是通过 c 语言实现,这两种实现的区别如下:

1)纯 GO 语言实现的域名解析,从/etc/resolv.conf 中取出本地 dns server 地址列表, 发送 DNS 请求(UDP 报文)并获得结果
2) 使用 cgo 方式, 最终会调用到 c 标准库的 getaddrinfo 或 getnameinfo 函数(不建议使用对 GO 协程不友好)

而 go 怎么选择使用哪种 DNS 来进行解析呢?

GO 语言默认使用纯 GO 的域名解析,因为这样一个阻塞的 DNS 请求只会消耗一个协程, 使 用 cgo 的方式则会阻塞一个系统线程, 只有某些特定条件下才会使用系统提供的 cgo 方式, 例如:
1) 在 OS X 系统中不允许程序直接发送 DNS 请求;
2) LOCALDOMAINH 环境变量存在,即使为空;
3) ES_OPTIONS 或 HOSTALIASES 或 ASR_CONFIG 环境变量非空;
4)/etc/resolv.conf 或/etc/nsswitch.conf 指定的使用方式 GO 解析器没有实现;
5)当要解析的域名以.local 结束, 或者是一个 mDNS 域名

我们的案例使用了 CGO,因为我们的案例符合第四种情况,见 golang 源码解析 options 的代码段:

  1. case "options": // magic options
  2. for _, s := range f[1:] {
  3. switch {
  4. case hasPrefix(s, "ndots:"):
  5. n, _, _ := dtoi(s[6:])
  6. if n < 0 {
  7. n = 0
  8. } else if n > 15 {
  9. n = 15
  10. }
  11. conf.ndots = n
  12. case hasPrefix(s, "timeout:"):
  13. n, _, _ := dtoi(s[8:])
  14. if n < 1 {
  15. n = 1
  16. }
  17. conf.timeout = time.Duration(n) * time.Second
  18. case hasPrefix(s, "attempts:"):
  19. n, _, _ := dtoi(s[9:])
  20. if n < 1 {
  21. n = 1
  22. }
  23. conf.attempts = n
  24. case s == "rotate":
  25. conf.rotate = true
  26. default:
  27. conf.unknownOpt = true
  28. }
  29. }

问题机中包含了 Go 语言解析器未实现的 single-request-reopen(解决网页加载速度慢问题)。

  1. ingle-request-reopen (since glibc 2.9)
  2. Sets RES_SNGLKUPREOP in _res.options. The resolver
  3. uses the same socket for the A and AAAA requests. Some
  4. hardware mistakenly sends back only one reply. When
  5. that happens the client system will sit and wait for
  6. the second reply. Turning this option on changes this
  7. behavior so that if two requests from the same port are
  8. not handled correctly it will close the socket and open
  9. a new one before sending the second request.

那为何使用了 CGO 的 DNS 解析策略会出问题呢?
这是因为 fabric 采用了静态编译,而 CGO 使用的 getaddrinfo 方法需要用到动态链接库,因此会在执行时出现问题。

  1. make peer-docker orderer-docker
  2. 14:39:29 /usr/lib/gcc/x86_64-linux-gnu/5/../../../x86_64-linux-gnu/libltdl.a(dlopen.o): In function `vm_open':
  3. 14:39:29 (.text+0x5e): warning: Using 'dlopen' in statically linked applications requires at runtime the shared libraries from the glibc version used for linking
  4. 14:39:29 /tmp/go-link-176411200/000000.o: In function `_cgo_b0c710f30cfd_C2func_getaddrinfo':
  5. 14:39:29 /tmp/go-build/net/_obj/cgo-gcc-prolog:46: warning: Using 'getaddrinfo' in statically linked applications requires at runtime the shared libraries from the glibc version used for linking

fabric 官方推荐 set FABRIC_CA_DYNAMIC_LINK=true 来解决。当然我们也可以通过其他手段来使 Go 的 DNS 策略切换到默认的 Pure Go 的方式:

  • 添加环境变量 GODEBUG=netdns=go
  • 调整/etc/resolv.conf 里 options(谨慎操作)

参考文章:

  1. CentOS7 官方 Docker 发行版现重大 Bug 可致 Kubernetes 中的健康检测探针失败并影响容器交互 https://jimmysong.io/posts/docker-exec-bug-on-centos7/
  2. 阿里云环境部署Hyperledger Fabric 之 SIGSEGV 问题分析和解决经验分享 https://yq.aliyun.com/articles/238940
  3. resolv.conf 中的 options single-request-reopen 的详细解 http://www.man7.org/linux/man-pages/man5/resolver.5.html

露水湾 , 版权所有丨如未注明 , 均为原创丨本网站采用BY-NC-SA协议进行授权
转载请注明原文链接:fabric部署填坑记
喜欢 (1)
[]
分享 (0)
关于作者:
发表我的评论
取消评论

表情 贴图 加粗 删除线 居中 斜体 签到

Hi,您需要填写昵称和邮箱!

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址