介绍

前段时间接了一个比较特殊的需求,需要做一个用于部署服务的服务。主要是将一个k8s服务集群部署到远端的服务器上,具体服务器的连接信息会通过接口传入。

本来部署是人工来完成的,无非是将一些必须的文件scp到目标服务器上,然后ssh远程登录,执行一些安装的操作,齐活。安装的流程没什么问题,主要是这些步骤需要使用代码来实现,也就是需要一个支持SSH的client库来执行这些操作

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.

实现

为了完成部署服务的任务,需要解决几个问题:

SSH连接到远端的服务器

在服务器上执行指令

使用scp命令传输文件

编辑服务器上的文件,主要是为了修改一些配置文件

这里介绍下几个主要的工具方法

远程ssh连接

先定义一个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 remoteExecute(Session session, String command) throws JSchException {

log.debug(">> {}", command);

List 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.

执行结果令人满意,这些常见的命令都成功了

再次好评~

scp操作

scp操作官方给了很详细的示例scpTo+scpFrom,再次好评~

scpTo:

public static long scpTo(String source, Session session, String destination) {

FileInputStream fileInputStream = null;

try {

ChannelExec channel = (ChannelExec) session.openChannel("exec");

OutputStream out = channel.getOutputStream();

InputStream in = channel.getInputStream();

boolean ptimestamp = false;

String command = "scp";

if (ptimestamp) {

command += " -p";

}

command += " -t " + destination;

channel.setCommand(command);

channel.connect(CONNECT_TIMEOUT);

if (checkAck(in) != 0) {

return -1;

}

File _lfile = new File(source);

if (ptimestamp) {

command = "T " + (_lfile.lastModified() / 1000) + " 0";

// The access time should be sent here,

// but it is not accessible with JavaAPI ;-<

command += (" " + (_lfile.lastModified() / 1000) + " 0\n");

out.write(command.getBytes());

out.flush();

if (checkAck(in) != 0) {

return -1;

}

}

//send "C0644 filesize filename", where filename should not include '/'

long fileSize = _lfile.length();

command = "C0644 " + fileSize + " ";

if (source.lastIndexOf('/') > 0) {

command += source.substring(source.lastIndexOf('/') + 1);

} else {

command += source;

}

command += "\n";

out.write(command.getBytes());

out.flush();

if (checkAck(in) != 0) {

return -1;

}

//send content of file

fileInputStream = new FileInputStream(source);

byte[] buf = new byte[1024];

long sum = 0;

while (true) {

int len = fileInputStream.read(buf, 0, buf.length);

if (len <= 0) {

break;

}

out.write(buf, 0, len);

sum += len;

}

//send '\0'

buf[0] = 0;

out.write(buf, 0, 1);

out.flush();

if (checkAck(in) != 0) {

return -1;

}

return sum;

} catch(JSchException e) {

log.error("scp to catched jsch exception, ", e);

} catch(IOException e) {

log.error("scp to catched io exception, ", e);

} catch(Exception e) {

log.error("scp to error, ", e);

} finally {

if (fileInputStream != null) {

try {

fileInputStream.close();

} catch (Exception e) {

log.error("File input stream close error, ", e);

}

}

}

return -1;

}

scpFrom:

public static long scpFrom(Session session, String source, String destination) {

FileOutputStream fileOutputStream = null;

try {

ChannelExec channel = (ChannelExec) session.openChannel("exec");

channel.setCommand("scp -f " + source);

OutputStream out = channel.getOutputStream();

InputStream in = channel.getInputStream();

channel.connect();

byte[] buf = new byte[1024];

//send '\0'

buf[0] = 0;

out.write(buf, 0, 1);

out.flush();

while(true) {

if (checkAck(in) != 'C') {

break;

}

}

//read '644 '

in.read(buf, 0, 4);

long fileSize = 0;

while (true) {

if (in.read(buf, 0, 1) < 0) {

break;

}

if (buf[0] == ' ') {

break;

}

fileSize = fileSize * 10L + (long)(buf[0] - '0');

}

String file = null;

for (int i = 0; ; i++) {

in.read(buf, i, 1);

if (buf[i] == (byte) 0x0a) {

file = new String(buf, 0, i);

break;

}

}

// send '\0'

buf[0] = 0;

out.write(buf, 0, 1);

out.flush();

// read a content of lfile

if (Files.isDirectory(Paths.get(destination))) {

fileOutputStream = new FileOutputStream(destination + File.separator +file);

} else {

fileOutputStream = new FileOutputStream(destination);

}

long sum = 0;

while (true) {

int len = in.read(buf, 0 , buf.length);

if (len <= 0) {

break;

}

sum += len;

if (len >= fileSize) {

fileOutputStream.write(buf, 0, (int)fileSize);

break;

}

fileOutputStream.write(buf, 0, len);

fileSize -= len;

}

return sum;

} catch(JSchException e) {

log.error("scp to catched jsch exception, ", e);

} catch(IOException e) {

log.error("scp to catched io exception, ", e);

} catch(Exception e) {

log.error("scp to error, ", e);

} finally {

if (fileOutputStream != null) {

try {

fileOutputStream.close();

} catch (Exception e) {

log.error("File output stream close error, ", e);

}

}

}

return -1;

}

另外还有一个公用的方法checkAck:

private static int checkAck(InputStream in) throws IOException {

int b=in.read();

// b may be 0 for success,

// 1 for error,

// 2 for fatal error,

// -1

if(b==0) return b;

if(b==-1) return b;

if(b==1 || b==2){

StringBuffer sb=new StringBuffer();

int c;

do {

c=in.read();

sb.append((char)c);

}

while(c!='\n');

if(b==1){ // error

log.debug(sb.toString());

}

if(b==2){ // fatal error

log.debug(sb.toString());

}

}

return b;

}

测试一下:

我们在项目根目录下新建一个文件test.txt

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()) {

log.debug("Host({}) connected.", remote.getHost());

}

remoteExecute(session, "ls /root/jsch-demo/");

scpTo("test.txt", session, "/root/jsch-demo/");

remoteExecute(session, "ls /root/jsch-demo/");

remoteExecute(session, "echo ' append text.' >> /root/jsch-demo/test.txt");

scpFrom(session, "/root/jsch-demo/test.txt", "file-from-remote.txt");

session.disconnect();

}

日志输出如下:

而且可以看到项目目录下出现了一个文件file-from-remote.txt。里面的内容比原先的test.txt多了 append text

Host(192.168.124.20) connected.

>> ls /root/jsch-demo/

test1

test2

test-file

>> ls /root/jsch-demo/

test1

test2

test-file

test.txt

>> echo ' append text.' >> /root/jsch-demo/test.txt

远程编辑

我们平时在服务器上编辑文件一般使用vi,非常方便,但是在这里操作vi就有点复杂了

最后采用的方案是,先将源文件备份,然后scp拉到本地,编辑完后scp回原位置

remoteEdit方法:

private static boolean remoteEdit(Session session, String source, Function, List> process) {

InputStream in = null;

OutputStream out = null;

try {

String fileName = source;

int index = source.lastIndexOf('/');

if (index >= 0) {

fileName = source.substring(index + 1);

}

//backup source

remoteExecute(session, String.format("cp %s %s", source, source + ".bak." +System.currentTimeMillis()));

//scp from remote

String tmpSource = System.getProperty("java.io.tmpdir") + session.getHost() +"-" + fileName;

scpFrom(session, source, tmpSource);

in = new FileInputStream(tmpSource);

//edit file according function process

String tmpDestination = tmpSource + ".des";

out = new FileOutputStream(tmpDestination);

List inputLines = new ArrayList<>();

BufferedReader reader = new BufferedReader(new InputStreamReader(in));

String inputLine = null;

while ((inputLine = reader.readLine()) != null) {

inputLines.add(inputLine);

}

List outputLines = process.apply(inputLines);

for (String outputLine : outputLines) {

out.write((outputLine + "\n").getBytes());

out.flush();

}

//scp to remote

scpTo(tmpDestination, session, source);

return true;

} catch (Exception e) {

log.error("remote edit error, ", e);

return false;

} finally {

if (in != null) {

try {

in.close();

} catch (Exception e) {

log.error("input stream close error", e);

}

}

if (out != null) {

try {

out.close();

} catch (Exception e) {

log.error("output stream close error", e);

}

}

}

}

测试一下:

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()) {

log.debug("Host({}) connected.", remote.getHost());

}

remoteExecute(session, "echo 'It a test file.' > /root/jsch-demo/test");

remoteExecute(session, "cat /root/jsch-demo/test");

remoteEdit(session, "/root/jsch-demo/test", (inputLines) -> {

List outputLines = new ArrayList<>();

for (String inputLine : inputLines) {

outputLines.add(inputLine.toUpperCase());

}

return outputLines;

});

remoteExecute(session, "cat /root/jsch-demo/test");

session.disconnect();

}

执行后日志输出:

Host(192.168.124.20) connected.

>> echo 'It a test file.' > /root/jsch-demo/test

>> cat /root/jsch-demo/test

It a test file.

>> cp /root/jsch-demo/test /root/jsch-demo/test.bak.1564556060191

>> cat /root/jsch-demo/test

IT A TEST FILE.

可以看到字母已经都是大写了

总结

上面这些方法,基本上覆盖了我们日常在服务器上进行操作的场景了,那么不管部署服务,还是运维服务器都不成问题了

java ssh jsch_JSch-用java实现服务器远程操作相关推荐

  1. linux远程 java jar包_给Linux服务器远程安装jdk

    给Linux服务器远程安装jdk 1.下载jdk 2.上传jar包到Linux服务器上 ​ 2.1.服务器上新建一个JAVA文件,存放jdk文件 # mkdir /usr/java ​ 2.2.本地上 ...

  2. JSch - Java Secure Channel : java 代码实现服务器远程操作

    一.前言 前些天发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默,忍不住分享一下给大家.点击跳转到教程. JSch是SSH2的纯Java实现 . JSch允许您连接到sshd服务器并使用端口转发, ...

  3. 火狐浏览器中打开java_Ubuntu下通过Firefox Opera Chromium浏览器直接执行java应用程序(打开java jnlp文件)实现在服务器远程虚拟控制台完成远程管理的方法...

    远程虚拟控制台依赖于java运行环境(jre),在通过浏览器打开链接前,系统必须安装jre环境,远程管理控制台其实就是一个java程序,打开相应的网站会下载一个jnlp(java网络加载协议)的文件, ...

  4. java ssh cpu_初学Java ssh之Spring 第四篇

    今天我来学习学习Spring中Bean. 在Spring中原来还有标签啊,它相当于标签的老爸,老爸可以有很多个儿子,但是老爸只有一个哦. 也就是说一个标签内可以有多个标签,每个标签代表一个java实例 ...

  5. java ssh 超时时间,Java Ganymed Trlead Orion ssh会话超时

    我正在使用SSH Ganymed库别名Trilead别名Orion. 我试图理解会话的确切行为,因为我需要保持ssh连接打开很长时间(也许永远),并在我的jvm关闭或类似的事情时关闭它. 所以,我的问 ...

  6. 东莞群控服务器系统,群控服务器远程连接工具

    群控服务器远程连接工具 内容精选 换一换 建议不要在分析任务执行过程中卸载,否则可能出现异常.IDE插件只支持以Web模式卸载工具,不支持以CLI模式卸载工具.没有正在运行中的任务.确认卸载:输入y ...

  7. win7 linux ssh key,Windows下创建SSH Key并登录Linux服务器

    上一篇文章我给大家普及了在macOS下创建SSH Key并登录Linux服务器的操作,这篇文章带大家了解一下在Windows下创建SSH Key并登录Linux服务器的操作流程. 创建SSH Key ...

  8. java使用Jsch实现远程操作linux服务器进行文件上传、下载,删除和显示目录信息...

    1.java使用Jsch实现远程操作linux服务器进行文件上传.下载,删除和显示目录信息. 参考链接:https://www.cnblogs.com/longyg/archive/2012/06/2 ...

  9. java读取文件服务器文件,java读取远程服务器文件

    java读取远程服务器文件 内容精选 换一换 已成功登录Java性能分析.待安装Guardian的服务器已开启sshd.待安装Guardian的服务器已安装JRE,JRE版本要求为Huawei JDK ...

最新文章

  1. Python程序员的“避坑”指南
  2. 查看ie保存的表单_小学信息技术gt;搜索保存网页教师资格证面试模板
  3. python3 csv 读入数组_如何将CSV数据读入NumPy中的记录数组?
  4. 博弈论 ----- Nim游戏
  5. 【转】各种字符串算法大总结
  6. python爬取网易云音乐付费音乐_python爬虫学习教程,爬取网易云音乐!
  7. linux英英词典项目,[开发手记] 一款基于命令行的英英词典 (A CLI-Based EE Dictionary)...
  8. PS中放大图片不失真的方法
  9. 联想台式计算机光驱启动,Lenovo联想笔记本光驱启动设置方法图文介绍
  10. matlab 三维图像 叠加,利用 Matlab构建“波的叠加”图样
  11. Android钢琴滑动代码,如何使用Kotlin构建Android旋转旋钮以帮助儿子练习钢琴
  12. Java实验二 货物进销管理系统【简单易懂】
  13. 计算机桌面底下显示条,详细教您电脑屏幕出现条纹怎么办
  14. 常用的行列式和矩阵的性质
  15. 搜遍全网,终于找到一个适合新手入门的物联网教程
  16. 微信小游戏登陆凭证校验出现{errcode:40029,errmsg:invalid code, hints: [ req_id: weh8ka0297hc58 ]}
  17. 盘一盘 QuantLib 系列 4 - CDS/CDX/iTraxx/中国 CRM 和 CDS
  18. JQuery nodeName、nodeValue 以及 nodeType
  19. Android 百度地图定位工具类
  20. POI解析03版07版Word

热门文章

  1. PYQT + ico图标制作
  2. JavaWeb,HTTP和Tomcat
  3. 使用CGLib实现动态代理
  4. 《青春依然,再见理想——献给学弟学妹》大四学生的万字忏悔书,警示学弟学妹【转】
  5. 关于自动完成功能的一段会话
  6. windows注册表几大键值
  7. C# params 用法简介
  8. 第二十届宁波大学程序设计竞赛(同步赛)
  9. QT工程编译错误:No outputs specified for item “DlgCreateInstance.ui“
  10. python爬取股票大单历史记录_python爬取股票实时数据,python爬虫与股票分析