Golang Go语言中请教一个连接 ssh 的问题

首先需求是调用 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"}
百思不得其解,求大佬指教~


Golang Go语言中请教一个连接 ssh 的问题

更多关于Golang Go语言中请教一个连接 ssh 的问题的实战教程也可以访问 https://www.itying.com/category-94-b0.html

9 回复

第一个 err 没有 check ,不知道你这个 err 是谁返回的

更多关于Golang Go语言中请教一个连接 ssh 的问题的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


sessionStart, err = connectStart.NewSession()是这一句,上面的 err 没有报错所以刚刚复制的时候删掉了,怕代码太长哈哈哈

EOF 错误,end of file ,怀疑是第二次连接重用了第一次的连接
第二次连接如果单独调用正常吗,正常的话大概就是这样
没用过 ssh.Dial

是不是对端服务还没 ready 或者 TCP 状态不对。抓包看看,以及日志可以贴一下

如果没有明显问题,调试单步执行一下,看看是哪里报的错误

应该是这个问题,我把调用 ssh 和执行命令封装出来之后再调用就没有这个问题了,但是我第一次执行完正常 close 了呀,很奇怪

考虑过可能没有 id_rsa 文件吗

重启后你要等待 sshd 服务 ready ,不能在能 ping 通后马上去执行。

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

其实你这个问题问 chatgpt 就会给你和我一样的建议。

如果是服务端侧的问题,应该是 connection close/reset/aborted 类错误

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

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

如果觉得奇怪就还原之前的代码,用正确的和错误的来对比,具体哪个细节逻辑不一样

在Golang中连接SSH通常使用golang.org/x/crypto/ssh这个包。这个包提供了丰富的功能来与SSH服务器进行交互。以下是一个基本的示例,展示了如何连接到SSH服务器并执行一个简单的命令:

  1. 首先,确保你已经安装了golang.org/x/crypto/ssh包,可以使用以下命令安装:

    go get golang.org/x/crypto/ssh
    
  2. 然后,你可以使用以下代码来连接到SSH服务器:

    package main
    
    import (
        "fmt"
        "golang.org/x/crypto/ssh"
        "io/ioutil"
        "log"
        "os"
    )
    
    func main() {
        config := &ssh.ClientConfig{
            User: "your_username",
            Auth: []ssh.AuthMethod{
                ssh.Password("your_password"),
            },
            HostKeyCallback: ssh.InsecureIgnoreHostKey(), // 注意:在生产环境中,应使用更安全的方式验证主机密钥
        }
    
        conn, err := ssh.Dial("tcp", "your_server:22", config)
        if err != nil {
            log.Fatalf("Failed to dial: %s", err)
        }
        defer conn.Close()
    
        session, err := conn.NewSession()
        if err != nil {
            log.Fatalf("Failed to create session: %s", err)
        }
        defer session.Close()
    
        output, err := session.Output("your_command")
        if err != nil {
            log.Fatalf("Failed to run: %s", err)
        }
    
        fmt.Println(string(output))
    }
    

这段代码展示了如何设置SSH客户端配置,连接到服务器,并运行一个命令。注意,在生产环境中,应避免使用明文密码,并应使用更安全的方式验证主机密钥。

回到顶部