前段时间接了一个比较特殊的需求,需要做一个用于部署服务的服务。主要是将一个k8s服务集群部署到远端的服务器上,具体服务器的连接信息会通过接口传入。
本来部署是人工来完成的,无非是将一些必须的文件scp到目标服务器上,然后ssh远程登录,执行一些安装的操作,齐活。安装的流程没什么问题,主要是这些步骤需要使用代码来实现,也就是需要一个支持SSH的client库来执行这些操作
最终选用了 JSch(Java Secure Channel) ,官网是这么介绍的:
JSch is a pure Java implementation of SSH2. JSch allows you to connect to an sshd server and use port forwarding, X11 forwarding, file transfer, etc., and you can integrate its functionality into your own Java programs. JSch is licensed under BSD style license.
为了完成部署服务的任务,需要解决几个问题:
这里介绍下几个主要的工具方法
先定义一个Remote类,用于记录服务器登录信息
@Data public class Remote { private String user = "root"; private String host = "127.0.0.1"; private int port = 22; private String password = ""; private String identity = "~/.ssh/id_rsa"; private String passphrase = ""; } 复制代码
这里填充了一些默认值,平时用的时候方便一些
JSch使用Session来定义一个远程节点:
public static Session getSession(Remote remote) throws JSchException { JSch jSch = new JSch(); if (Files.exists(Paths.get(remote.getIdentity()))) { jSch.addIdentity(remote.getIdentity(), remote.getPassphrase()); } Session session = jSch.getSession(remote.getUser(), remote.getHost(),remote.getPort()); session.setPassword(remote.getPassword()); session.setConfig("StrictHostKeyChecking", "no"); return session; } 复制代码
测试一下:
public static void main(String[] args) throws Exception { Remote remote = new Remote(); remote.setHost("192.168.124.20"); remote.setPassword("123456"); Session session = getSession(remote); session.connect(CONNECT_TIMEOUT); if (session.isConnected()) { System.out.println("Host({}) connected.", remote.getHost); } session.disconnect(); } 复制代码
正确的输入了服务器地址和密码后,连接成功。
这里要提一下,JSch会优先使用填入的ssh_key去尝试登录,尝试失败后才会使用password登录,这点和平时使用ssh命令的交互是一致的,好评~
接下来就是编写一个通用的方法,用于在Session上执行命令
public static List<String> remoteExecute(Session session, String command) throws JSchException { log.debug(">> {}", command); List<String> resultLines = new ArrayList<>(); ChannelExec channel = null; try{ channel = (ChannelExec) session.openChannel("exec"); channel.setCommand(command); InputStream input = channel.getInputStream(); channel.connect(CONNECT_TIMEOUT); try { BufferedReader inputReader = new BufferedReader(newInputStreamReader(input)); String inputLine = null; while((inputLine = inputReader.readLine()) != null) { log.debug(" {}", inputLine); resultLines.add(inputLine); } } finally { if (input != null) { try { input.close(); } catch (Exception e) { log.error("JSch inputStream close error:", e); } } } } catch (IOException e) { log.error("IOcxecption:", e); } finally { if (channel != null) { try { channel.disconnect(); } catch (Exception e) { log.error("JSch channel disconnect error:", e); } } } return resultLines; } 复制代码
测试一下:
public static void main(String[] args) throws Exception { Remote remote = new Remote(); remote.setHost("192.168.124.20"); remote.setPassword("123456"); Session session = getSession(remote); session.connect(CONNECT_TIMEOUT); if (session.isConnected()) { System.out.println("Host({}) connected.", remote.getHost()); } remoteExecute(session, "pwd"); remoteExecute(session, "mkdir /root/jsch-demo"); remoteExecute(session, "ls /root/jsch-demo"); remoteExecute(session, "touch /root/jsch-demo/test1; touch /root/jsch-demo/test2"); remoteExecute(session, "echo 'It a test file.' > /root/jsch-demo/test-file"); remoteExecute(session, "ls -all /root/jsch-demo"); remoteExecute(session, "ls -all /root/jsch-demo | grep test"); remoteExecute(session, "cat /root/jsch-demo/test-file"); session.disconnect(); } 复制代码
执行后,日志输出如下内容:
Host(192.168.124.20) connected. >> pwd /root >> mkdir /root/jsch-demo >> ls /root/jsch-demo >> touch /root/jsch-demo/test1; touch /root/jsch-demo/test2 >> echo 'It a test file.' > /root/jsch-demo/test-file >> ls -all /root/jsch-demo total 12 drwxr-xr-x 2 root root 4096 Jul 30 03:05 . drwx------ 6 root root 4096 Jul 30 03:05 .. -rw-r--r-- 1 root root 0 Jul 30 03:05 test1 -rw-r--r-- 1 root root 0 Jul 30 03:05 test2 -rw-r--r-- 1 root root 16 Jul 30 03:05 test-file >> ls -all /root/jsch-demo | grep test -rw-r--r-- 1 root root 0 Jul 30 03:05 test1 -rw-r--r-- 1 root root 0 Jul 30 03:05 test2 -rw-r--r-- 1 root root 16 Jul 30 03:05 test-file >> cat /root/jsch-demo/test-file It a test file. 复制代码
执行结果令人满意,这些常见的命令都成功了
再次好评~