阿里云环境部署Hyperledger Fabric之SIGSEGV问题分析和解决经验分享-阿里云开发者社区

开发者社区> 余珊> 正文

阿里云环境部署Hyperledger Fabric之SIGSEGV问题分析和解决经验分享

简介: 引言 最近收到Hyperledger社区的一些朋友反馈在阿里云环境上部署开源区块链项目Hyperledger Fabric的过程中遇到了和SIGSEV相关的fatal error,正好我此前也遇到并解决过类似的问题,因此这里分享一下当时问题的分析过程和解决的经验,希望能带来一点启发和帮助。
+关注继续查看

最近收到Hyperledger社区的一些朋友反馈在阿里云环境上部署开源区块链项目Hyperledger Fabric的过程中遇到了和SIGSEV相关的fatal error,正好笔者此前也遇到并解决过类似的问题,因此这里分享一下当时问题的分析过程和解决的经验,希望能为大家带来一点启发和帮助。


问题描述

在部署Hyperledger Fabric过程中,peer、orderer服务启动失败,同时cli容器上执行cli-test.sh测试时也报错。错误类型均是signal SIGSEGV: segmentation violation。错误日志示例如下:

2017-11-01 02:44:04.247 UTC [peer] updateTrustedRoots -> DEBU 2a0 Updating trusted root authorities for channel mychannel
fatal error: unexpected signal during runtime execution
[signal SIGSEGV: segmentation violation code=0x1 addr=0x63 pc=0x7f9d15ded259]
runtime stack:
runtime.throw(0xdc37a7, 0x2a)
        /opt/go/src/runtime/panic.go:566 +0x95
runtime.sigpanic()
        /opt/go/src/runtime/sigpanic_unix.go:12 +0x2cc
goroutine 64 [syscall, locked to thread]:
runtime.cgocall(0xb08d50, 0xc4203bcdf8, 0xc400000000)
        /opt/go/src/runtime/cgocall.go:131 +0x110 fp=0xc4203bcdb0 sp=0xc4203bcd70
net._C2func_getaddrinfo(0x7f9d000008c0, 0x0, 0xc420323110, 0xc4201a01e8, 0x0, 0x0, 0x0)

分析过程

我们进行了深入分析和试验,在Hyperledger Fabric这个bug https://jira.hyperledger.org/browse/FAB-5822的启发下,采用了如下workaround可以解决这个问题:

  • 在docker compose yaml里对peer、orderer、cli的环境变量加入GODEBUG=netdns=go

这个设置的作用是不采用cgo resolver (从错误日志里可看到是cgo resolver抛出的错误)而采用pure go resolver。

进一步分析golang在什么情况下会在cgo resolver和pure go resolver之间切换:

Name Resolution
The method for resolving domain names, whether indirectly with functions like Dial or directly with functions like LookupHost and LookupAddr, varies by operating system.
On Unix systems, the resolver has two options for resolving names. It can use a pure Go resolver that sends DNS requests directly to the servers listed in /etc/resolv.conf, or it can use a cgo-based resolver that calls C library routines such as getaddrinfo and getnameinfo.
By default the pure Go resolver is used, because a blocked DNS request consumes only a goroutine, while a blocked C call consumes an operating system thread. When cgo is available, the cgo-based resolver is used instead under a variety of conditions: on systems that do not let programs make direct DNS requests (OS X), when the LOCALDOMAIN environment variable is present (even if empty), when the RES_OPTIONS or HOSTALIASES environment variable is non-empty, when the ASR_CONFIG environment variable is non-empty (OpenBSD only), when /etc/resolv.conf or /etc/nsswitch.conf specify the use of features that the Go resolver does not implement, and when the name being looked up ends in .local or is an mDNS name.
The resolver decision can be overridden by setting the netdns value of the GODEBUG environment variable (see package runtime) to go or cgo, as in:
export GODEBUG=netdns=go    # force pure Go resolver
export GODEBUG=netdns=cgo   # force cgo resolver*

根据这一线索,我们对比了此前部署成功环境和最近部署失败环境各自的底层配置文件,最终找到了不同之处:

  • 在老环境(区块链部署成功)上的容器里,查看

    # cat /etc/resolv.conf 
    nameserver 127.0.0.11
    options ndots:0
  • 在新环境(区块链部署失败)上的容器里,查看

    # cat /etc/resolv.conf 
    nameserver 127.0.0.11
    options timeout:2 attempts:3 rotate single-request-reopen ndots:0

这个差异导致了老的成功环境是采用pure Go resolver的,而在新的失败环境被切换到cgo resolver, 这是因为含有pure Go resolver不支持的options single-request-reopen。

注:Pure Go resolver目前仅支持ndots, timeout, attempts, rotate
https://github.com/golang/go/blob/964639cc338db650ccadeafb7424bc8ebb2c0f6c/src/net/dnsconfig_unix.go

       case "options": // magic options
            for _, s := range f[1:] {
                switch {
                case hasPrefix(s, "ndots:"):
                    n, _, _ := dtoi(s[6:])
                    if n < 0 {
                        n = 0
                    } else if n > 15 {
                        n = 15
                    }
                    conf.ndots = n
                case hasPrefix(s, "timeout:"):
                    n, _, _ := dtoi(s[8:])
                    if n < 1 {
                        n = 1
                    }
                    conf.timeout = time.Duration(n) * time.Second
                case hasPrefix(s, "attempts:"):
                    n, _, _ := dtoi(s[9:])
                    if n < 1 {
                        n = 1
                    }
                    conf.attempts = n
                case s == "rotate":
                    conf.rotate = true
                default:
                    conf.unknownOpt = true
                }
            }

进一步的,我们尝试分析是什么原因导致了新老容器内的resolv.conf的内容变化,发现了原来是最近宿主机ECS的配置文件发生了变化:

  • 失败的环境 - 新创建的ECS:

    # cat /etc/resolv.conf
    # Dynamic resolv.conf(5) file for glibc resolver(3) generated by resolvconf(8)
    #     DO NOT EDIT THIS FILE BY HAND -- YOUR CHANGES WILL BE OVERWRITTEN
    nameserver 100.100.2.138
    nameserver 100.100.2.136
    options timeout:2 attempts:3 rotate single-request-reopen
  • 成功的环境 - 原来的ECS:

    # cat /etc/resolv.conf
    # Dynamic resolv.conf(5) file for glibc resolver(3) generated by resolvconf(8)
    #     DO NOT EDIT THIS FILE BY HAND -- YOUR CHANGES WILL BE OVERWRITTEN
    nameserver 100.100.2.136
    nameserver 100.100.2.138

另一方面,我们也尝试分析为什么切换到cgo resolver之后会产生SIGSEGV的错误,以下这篇文章解释了static link cgo会导致SIGSEGV的错误:
http://tbg.github.io/golang-static-linking-bug

而这个Hyperledger Fabric的bug则指出了Hyperledger Fabric的build(尤其是和getaddrinfo相关方法)正是static link的:
https://jira.hyperledger.org/browse/FAB-6403

至此,我们找到了问题的根源和复盘了整个问题发生的逻辑:

  • 近期新创建的ECS主机中的resolv.conf内容发生了变化 -> 导致Hyperledger Fabric的容器内域名解析从pure Go resolver切换至cgo resolver -> 触发了一个已知的由静态链接cgo导致的SIGSEGV错误 -> 导致Hyperledger Fabric部署失败。

解决方法建议

更新Hyperledger Fabric的docker compose yaml模板,为所有Hyperledger Fabric的节点(如orderer, peer, ca, cli等)添加环境变量GODEBUG=netdns=go以强制使用pure Go resolver。

版权声明:本文内容由阿里云实名注册用户自发贡献,版权归原作者所有,阿里云开发者社区不拥有其著作权,亦不承担相应法律责任。具体规则请查看《阿里云开发者社区用户服务协议》和《阿里云开发者社区知识产权保护指引》。如果您发现本社区中有涉嫌抄袭的内容,填写侵权投诉表单进行举报,一经查实,本社区将立刻删除涉嫌侵权内容。

相关文章
MapReduce框架Mapper和Reducer类源码分析
一:Mapper类 在Hadoop的mapper类中,有4个主要的函数,分别是:setup,cleanup,map,run。代码如下: protected void setup(Context context) throws IOException, InterruptedEx...
678 0
源码分析 RocketMQ DLedger 多副本之 Leader 选主
本文将按照《RocketMQ 多副本前置篇:初探raft协议》的思路来学习RocketMQ选主逻辑。首先先回顾一下关于Leader的一些思考: 节点状态需要引入3种节点状态:Follower(跟随者)、Candidate(候选者),该状态下的节点会发起投票请求,Leader(主节点)。
2206 0
源码分析 RocketMQ DLedger 多副本存储实现
详细介绍了RocketMQ DLedger 多副本存储实现部分,主要参考RocketMQ 存储部分的设计理念,并重点阐述 DLedger 消除存储格式、索引文件存储格式,诸如内存映射、刷盘、过期文件删除、文件加载与恢复由于在《RocketMQ技术内幕》中详细介绍,故本文并未重复介绍。
2086 0
阿里云物联网平台服务端订阅AMQP典型问题——Client failed to authenticate using SASL: PLAIN
服务端可以直接订阅产品下多种类型的消息:设备上报消息、设备状态变化通知、设备生命周期变更、网关发现子设备上报、设备拓扑关系变更等。配置服务端订阅后,物联网平台会将产品下所有设备的已订阅类型的消息转发至您的服务器。
1127 0
只需4个步骤,分析解决在生产环境下JVM内存泄露问题
只需4个步骤,分析解决在生产环境下JVM内存泄露问题
4800 0
11.06直播预告|ClickHouse对海量数据分析的经验分享
1.海量日志分析技术选型的思考 2.ClickHouse使用经验及问题解决 3.对ClickHouse未来的期待
1822 0
+关注
余珊
阿里云区块链服务高级技术专家,研究领域包括Blockchain(Hyperledger Fabric、企业以太坊/Quorum),Cloud Messaging, Docker, Kubernetes,以及面向金融、银行、零售、政务等行业客户的解决方案
5
文章
0
问答
来源圈子
更多
容器服务 Kubernetes 版(简称 ACK)提供高性能可伸缩的容器应用管理能力,支持企业级 Kubernetes 容器化应用的全生命周期管理。容器服务 Kubernetes 版简化集群的搭建和扩容等工作,整合阿里云虚拟化、存储、网络和安全能力,打造云端最佳的 Kubernetes 容器化应用运行环境。
+ 订阅
文章排行榜
最热
最新
相关电子书
更多
《2021云上架构与运维峰会演讲合集》
立即下载
《零基础CSS入门教程》
立即下载
《零基础HTML入门教程》
立即下载