V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
The Go Programming Language
http://golang.org/
Go Playground
Go Projects
Revel Web Framework
xuqiccr
V2EX  ›  Go 编程语言

请教一个 golang 连接 ssh 的问题

  •  
  •   xuqiccr · 235 天前 · 1370 次点击
    这是一个创建于 235 天前的主题,其中的信息可能已经有所发展或是发生改变。

    首先需求是调用 ssh 访问腾讯云云实例执行 A 脚本,然后对此云实例进行重启,开机完成后再执行 B 脚本。从执行 A ,重启到开机都没有问题,但是在执行 B 脚本前新建 session 时,调用 client.NewSession()方法一直返回 error:EOF ,实在是定位不到问题,求大佬指教。

    我的 ssh 连接逻辑抽取的方法:

    func SSHConnect(host string) (*ssh.Client, error) {
        var (
           addr         string
           clientConfig *ssh.ClientConfig
           client       *ssh.Client
           err          error
        )
    
        homePath, err := os.UserHomeDir()
        if err != nil {
           return nil, err
        }
        key, err := ioutil.ReadFile(path.Join(homePath, ".ssh", "id_rsa"))
        zap.L().Info("key", zap.String("path", path.Join(homePath, ".ssh", "id_rsa")))
        if err != nil {
           return nil, err
        }
        signer, err := ssh.ParsePrivateKey(key)
        if err != nil {
           return nil, err
        }
    
        clientConfig = &ssh.ClientConfig{
           User: "root",
           Auth: []ssh.AuthMethod{
              ssh.PublicKeys(signer),
           },
           Timeout:         30 * time.Second,
           HostKeyCallback: ssh.InsecureIgnoreHostKey(),
        }
    
        // connect to ssh
        addr = fmt.Sprintf("%s:%d", host, 22)
    
        if client, err = ssh.Dial("tcp", addr, clientConfig); err != nil {
           //err = errors.Wrapf(err, "")
           return nil, err
        }
    
        return client, nil
    }
    

    调用 ssh 执行脚本 A:

    var connectShut *ssh.Client
    var sessionShut *ssh.Session
    connectShut, err = util.SSHConnect(*ip[0])
    if err != nil {
        zap.L().Error("mysqlToggleWorker SSHConnect error", zap.Error(err), zap.Any("ip", *ip[0]))
        return err
    }
    sessionShut, err = connectShut.NewSession()
    if err != nil {
        zap.L().Error("mysqlToggleWorker NewSession error", zap.Error(err), zap.Any("ip", *ip[0]))
        return err
    }
    shCommand := "sh " + shPath
    output, err := sessionShut.CombinedOutput(shCommand)
    if err != nil {
        zap.L().Error("mysqlToggleWorker Run error", zap.Error(err), zap.Any("ip", *ip[0]), 
        zap.Any("shCommand", shCommand), zap.String("result", string(output)))
    }
    err = sessionShut.Close()
    if err != nil || err != io.EOF {
        zap.L().Error("mysqlToggleWorker sessionShut.Close() error", zap.Error(err), zap.Any("ip", *ip[0]))
    }
    err = connectShut.Close()
    if err != nil {
        zap.L().Error("mysqlToggleWorker connectShut.Close() error", zap.Error(err), zap.Any("ip", *ip[0]))
    }
    

    省略重启的过程,下面开始调用脚本 B:

    var connectStart *ssh.Client
    var sessionStart *ssh.Session
    connectStart, err = util.SSHConnect(*ip[0])
    sessionStart, err = connectStart.NewSession()
    if err != nil {
        zap.L().Error("mysqlToggleWorker DescribeInstancesStatus error", zap.Error(err), zap.Any("ip", *ip[0]))
        return err
    }
    

    此时返回的 err 为:{"error": "EOF", "ip": "10.1.0.1"}
    百思不得其解,求大佬指教~

    8 条回复    2024-03-28 17:59:50 +08:00
    xhd2015
        1
    xhd2015  
       235 天前 via iPhone   ❤️ 1
    第一个 err 没有 check ,不知道你这个 err 是谁返回的
    xuqiccr
        2
    xuqiccr  
    OP
       235 天前
    @xhd2015 sessionStart, err = connectStart.NewSession()是这一句,上面的 err 没有报错所以刚刚复制的时候删掉了,怕代码太长哈哈哈
    zhuisui
        3
    zhuisui  
       235 天前   ❤️ 1
    EOF 错误,end of file ,怀疑是第二次连接重用了第一次的连接
    第二次连接如果单独调用正常吗,正常的话大概就是这样
    没用过 ssh.Dial
    CEBBCAT
        4
    CEBBCAT  
       235 天前   ❤️ 1
    是不是对端服务还没 ready 或者 TCP 状态不对。抓包看看,以及日志可以贴一下

    如果没有明显问题,调试单步执行一下,看看是哪里报的错误
    xuqiccr
        5
    xuqiccr  
    OP
       235 天前
    @zhuisui 应该是这个问题,我把调用 ssh 和执行命令封装出来之后再调用就没有这个问题了,但是我第一次执行完正常 close 了呀,很奇怪
    yann123
        6
    yann123  
       235 天前   ❤️ 1
    考虑过可能没有 id_rsa 文件吗
    swulling
        7
    swulling  
       235 天前 via iPhone   ❤️ 1
    重启后你要等待 sshd 服务 ready ,不能在能 ping 通后马上去执行。

    写一个 loop 吧,循环尝试 N 次执行一个命令比如 time 来探活,成功后再执行业务脚本。

    其实你这个问题问 chatgpt 就会给你和我一样的建议。
    zhuisui
        8
    zhuisui  
       235 天前   ❤️ 1
    如果是服务端侧的问题,应该是 connection close/reset/aborted 类错误

    倒不如直接抓包,看看第二次调用是否真的建立了 tcp 连接

    ------------

    如果觉得奇怪就还原之前的代码,用正确的和错误的来对比,具体哪个细节逻辑不一样
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   1802 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 27ms · UTC 16:43 · PVG 00:43 · LAX 08:43 · JFK 11:43
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.