`
weishiym
  • 浏览: 34177 次
  • 性别: Icon_minigender_1
  • 来自: 广西
社区版块
存档分类
最新评论

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

 
阅读更多

最近收到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的错误:
https://tschottdorf.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

分享到:
评论

相关推荐

    linux下段错误检查sigsegv

    通过理解和应用以上知识点,你可以更有效地在Linux环境中检查和解决段错误问题。使用提供的`sigsegv.c`和`sigsegv.h`文件,你可以编写一个能够捕捉并分析SIGSEGV信号的调试程序,这对于定位和修复这类错误至关重要。

    Obtaining a stack trace in C upon SIGSEGV

    ### 获取 C 语言程序在 ...通过捕获 `SIGSEGV` 信号并打印堆栈轨迹,开发者可以更快地定位和解决问题。尽管存在一些局限性,如对编译选项的要求和平台兼容性问题,但对于很多应用场景来说,这种方法仍然是非常有用的。

    藏经阁-云时代下的 性能优化&运维实践之路.pdf

    2. 移动云架构:使用阿里云提供的云计算服务,实现移动应用程序的架构设计和部署。 3. JBoss、Tomcat、Redis、MySQL、TMCTaobaoAPIDubboJetty存储系统日志系统统计系统告警系统等技术架构:提供了强大的技术架构支撑...

    jvm crash的崩溃日志详细分析及注意点

    `-XX:+ShowMessageBoxOnError`参数在Linux环境下启用后,当JVM崩溃时,系统会自动启动GDB进行分析和调试,这对于测试环境尤其有用。 JVM崩溃日志中的关键部分包括: 1. **错误信息概要**:这部分包含致命错误的...

    Linux Debugging(五): coredump 分析入門1

    在Linux系统中,调试是解决程序异常和错误的关键步骤,特别是在遇到程序崩溃并产生coredump时。coredump是操作系统在程序异常终止时保存的内存映像,包含了程序运行时的状态,如内存布局、堆栈信息、全局变量和...

    解决Oracle 9.2.0.6版本数据库由于ORA-07445宕机问题

    Oracle数据库在运行过程中可能会遇到各种错误,其中,ORA-07445错误是一个与内核相关...此外,保持数据库的及时更新和维护,以及遵循Oracle官方的建议,可以有效预防和解决这类问题,保证数据库服务的稳定性和可靠性。

    JVM Crash,生成hs_err_pid.log文件

    10. **注册表和环境变量**:显示JVM运行时的环境,有时环境变量的配置问题也会导致崩溃。 针对`hs_err_pid.log`文件中的信息,我们可以采取以下步骤来解决问题: 1. **复现问题**:尝试在相同的环境中重现崩溃,...

    coredump问题原理探究-Linux x86版.rar

    本资料"coredump问题原理探究-Linux x86版"聚焦于Linux环境下,特别是x86架构下的核心转储文件分析,旨在帮助开发者深入理解core dump的工作机制,并提供有效的定位和解决问题的方法。 一、core dump的基本概念 1. ...

    Segmentation Fault错误原因总结 _ Sissy 婷婷 Blog1

    Segmentation Fault,通常简称为SIGSEGV,是Linux和其他类Unix操作系统中的一种错误,它发生在程序尝试访问其无权访问的内存区域时。这可能是由于多种原因造成的,如非法地址访问、指针越界、空指针解引用、内存泄漏...

    走下神坛的内存调试器--定位多线程内存越界问题实践总结.pdf

    诊断多线程程序中的内存越界问题通常需要仔细地分析和重建问题发生时的场景。实践总结中可能会包含以下步骤: - **启动时设置**:在程序启动时设置特定的调试选项或环境变量,以便让调试工具如Valgrind或Electric ...

    android2.3.3蓝牙无法打开问题get_adapter_path D-Bus error

    ### Android 2.3.3蓝牙无法打开问题分析与解决 #### 问题描述 在Android 2.3.3系统中遇到蓝牙无法打开的问题,并在尝试打开蓝牙时出现了以下错误日志: ``` 07-03 18:03:55.581: E/BluetoothEventLoop.cpp(7146): ...

    JVM致命错误完全解析:基于现实案例

    该日志文件可以帮助开发者和维护者快速定位和解决问题。 文件头:文件头是错误日志的开头部分,主要描述了问题的简单信息。这部分内容也会打印到标准输出和应用程序的控制台上。文件头包含了致命错误的基本信息,如...

    Android Native Crash分析详解

    2. **SIGBUS(signal 7)**:非法地址访问,但与SIGSEGV不同之处在于访问的地址虽在进程地址空间内,但未按字节对齐(例如,访问未被2或4整除的地址)。另一种情况是在使用mmap映射文件后,文件被截断但仍尝试访问已...

    system_crash_analysis_and_debug.doc

    本文将深入探讨系统崩溃分析和调试的原理,以及在Linux环境下,如何有效地应对和解决这一问题。 首先,我们需要理解什么是系统崩溃。系统崩溃,通常指的是应用程序在操作系统上运行时因非法操作导致的强制退出,这...

    Crash log on target platform

    为了深入理解并解决这个问题,我们需要详细地查看和解析错误日志。 错误日志,通常被称为“crash logs”,是系统或应用程序在发生异常或崩溃时自动生成的记录。这些日志包含了关于错误发生时的详细信息,包括错误...

    死锁与段错误解读分析

    在Linux环境下,程序员在进行多线程或并发编程时经常会遇到死锁和段错误两种不同的问题,它们分别有着各自的概念、成因以及解决方法。 首先,死锁是指两个或两个以上的进程或线程在资源分配过程中,由于竞争资源而...

    UNIX环境高级编程课后习题详细解答

    8. **错误处理和调试技巧**:学会利用stderr输出错误信息,使用gdb进行程序调试,以及分析核心转储文件,是解决实际问题的重要手段。 通过解答这些课后习题,学习者不仅能掌握理论知识,还能提升实际编程和问题解决...

Global site tag (gtag.js) - Google Analytics