增加主机信息功能
This commit is contained in:
@@ -8,14 +8,28 @@ import java.io.Serializable;
|
|||||||
* Docker 容器信息
|
* Docker 容器信息
|
||||||
*/
|
*/
|
||||||
@Data
|
@Data
|
||||||
public class ContainerInfo implements Serializable {
|
public class ContainerInfo implements Serializable {
|
||||||
|
|
||||||
private String containerId;
|
private String containerId;
|
||||||
private String image;
|
private String image;
|
||||||
private String command;
|
private String command;
|
||||||
private String status;
|
|
||||||
private String created;
|
private String created;
|
||||||
private String names;
|
private String status;
|
||||||
private String ports;
|
private String ports;
|
||||||
|
private String names;
|
||||||
private String accountId;
|
private String accountId;
|
||||||
|
|
||||||
|
public ContainerInfo() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public ContainerInfo(String containerId, String image, String command, String created, String status, String ports, String names, String accountId) {
|
||||||
|
this.containerId = containerId;
|
||||||
|
this.image = image;
|
||||||
|
this.command = command;
|
||||||
|
this.created = created;
|
||||||
|
this.status = status;
|
||||||
|
this.ports = ports;
|
||||||
|
this.names = names;
|
||||||
|
this.accountId = accountId;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,28 +5,31 @@ import com.jeesite.modules.apps.Module.ContainerInfo;
|
|||||||
import com.jeesite.modules.apps.Module.DockerResult;
|
import com.jeesite.modules.apps.Module.DockerResult;
|
||||||
import com.jeesite.modules.apps.Module.SystemInfo;
|
import com.jeesite.modules.apps.Module.SystemInfo;
|
||||||
import com.jeesite.modules.biz.entity.MySftpAccounts;
|
import com.jeesite.modules.biz.entity.MySftpAccounts;
|
||||||
|
import io.micrometer.common.util.StringUtils;
|
||||||
|
|
||||||
import java.io.ByteArrayOutputStream;
|
import java.io.ByteArrayOutputStream;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.nio.charset.StandardCharsets;
|
import java.nio.charset.StandardCharsets;
|
||||||
import java.util.ArrayList;
|
import java.util.*;
|
||||||
import java.util.Hashtable;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Docker 工具类(连接池版本)
|
* Docker 工具类(连接池版本)
|
||||||
*/
|
*/
|
||||||
public class DockerUtil {
|
public class DockerUtil {
|
||||||
|
|
||||||
private static final int SSH_TIMEOUT = 5000;
|
private static final int SSH_TIMEOUT = 3000;
|
||||||
private static final int MAX_POOL_SIZE = 50;
|
private static final int MAX_POOL_SIZE = 50;
|
||||||
|
|
||||||
/** 连接池:accountId -> Session */
|
/**
|
||||||
|
* 连接池:accountId -> Session
|
||||||
|
*/
|
||||||
private static final Map<String, PooledSession> sessionPool = new ConcurrentHashMap<>();
|
private static final Map<String, PooledSession> sessionPool = new ConcurrentHashMap<>();
|
||||||
|
|
||||||
/** 被池化的 Session,包含创建时间和活跃标记 */
|
/**
|
||||||
|
* 被池化的 Session,包含创建时间和活跃标记
|
||||||
|
*/
|
||||||
private static class PooledSession {
|
private static class PooledSession {
|
||||||
Session session;
|
Session session;
|
||||||
long createdAt;
|
long createdAt;
|
||||||
@@ -44,11 +47,8 @@ public class DockerUtil {
|
|||||||
*/
|
*/
|
||||||
private static PooledSession getSession(MySftpAccounts account) throws JSchException {
|
private static PooledSession getSession(MySftpAccounts account) throws JSchException {
|
||||||
String key = account.getAccountId();
|
String key = account.getAccountId();
|
||||||
|
|
||||||
// 1. 检查现有连接:未使用 + 未断开 → 直接复用
|
|
||||||
PooledSession pooled = sessionPool.get(key);
|
PooledSession pooled = sessionPool.get(key);
|
||||||
if (pooled != null && !pooled.inUse && pooled.session.isConnected()) {
|
if (pooled != null && !pooled.inUse && pooled.session.isConnected()) {
|
||||||
// 超过30分钟没用的连接,视为过期,关闭重建
|
|
||||||
if (System.currentTimeMillis() - pooled.createdAt > 30 * 60 * 1000) {
|
if (System.currentTimeMillis() - pooled.createdAt > 30 * 60 * 1000) {
|
||||||
pooled.session.disconnect();
|
pooled.session.disconnect();
|
||||||
pooled.session = null;
|
pooled.session = null;
|
||||||
@@ -76,7 +76,7 @@ public class DockerUtil {
|
|||||||
|
|
||||||
if ("key".equalsIgnoreCase(account.getAuthType()) && account.getPrivateKey() != null) {
|
if ("key".equalsIgnoreCase(account.getAuthType()) && account.getPrivateKey() != null) {
|
||||||
jsch.addIdentity("key_" + key,
|
jsch.addIdentity("key_" + key,
|
||||||
account.getPrivateKey().getBytes(StandardCharsets.UTF_8), null, null);
|
account.getPrivateKey().getBytes(StandardCharsets.UTF_8), null, null);
|
||||||
} else {
|
} else {
|
||||||
session.setPassword(account.getPassword());
|
session.setPassword(account.getPassword());
|
||||||
}
|
}
|
||||||
@@ -105,9 +105,8 @@ public class DockerUtil {
|
|||||||
channel = (ChannelExec) session.openChannel("exec");
|
channel = (ChannelExec) session.openChannel("exec");
|
||||||
|
|
||||||
String command = account.getRootPath() != null && !account.getRootPath().isEmpty()
|
String command = account.getRootPath() != null && !account.getRootPath().isEmpty()
|
||||||
? "cd " + account.getRootPath() + " && " + cmd
|
? "cd " + account.getRootPath() + " && " + cmd
|
||||||
: cmd;
|
: cmd;
|
||||||
|
|
||||||
channel.setCommand(command);
|
channel.setCommand(command);
|
||||||
InputStream in = channel.getInputStream();
|
InputStream in = channel.getInputStream();
|
||||||
channel.connect(SSH_TIMEOUT);
|
channel.connect(SSH_TIMEOUT);
|
||||||
@@ -121,7 +120,6 @@ public class DockerUtil {
|
|||||||
return out.toString(StandardCharsets.UTF_8).trim();
|
return out.toString(StandardCharsets.UTF_8).trim();
|
||||||
|
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
// 连接异常,移除缓存,强制下次重建
|
|
||||||
if (pooled != null) {
|
if (pooled != null) {
|
||||||
sessionPool.remove(account.getAccountId());
|
sessionPool.remove(account.getAccountId());
|
||||||
if (pooled.session.isConnected()) {
|
if (pooled.session.isConnected()) {
|
||||||
@@ -135,36 +133,32 @@ public class DockerUtil {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ──────────────────────────────────────────────
|
|
||||||
// 对外方法
|
|
||||||
// ──────────────────────────────────────────────
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 列出容器(一次 SSH 连接获取所有信息)
|
* 列出容器(一次 SSH 连接获取所有信息)
|
||||||
*/
|
*/
|
||||||
public static List<ContainerInfo> listContainers(MySftpAccounts accounts, boolean all) {
|
public static List<ContainerInfo> listContainers(MySftpAccounts accounts, boolean all) {
|
||||||
List<ContainerInfo> list = new ArrayList<>();
|
String FORMAT = "{{.ID}}|{{.Image}}|{{.Command}}|{{.CreatedAt}}|{{.Status}}|{{.Ports}}|{{.Names}}";
|
||||||
String format = "\"{{.ID}}|{{.Image}}|{{.Command}}|{{.CreatedAt}}|{{.Status}}|{{.Ports}}|{{.Names}}\"";
|
String cmd = (all ? "docker ps -a" : "docker ps") + " --format \"" + FORMAT + "\"";
|
||||||
String cmd = (all ? "docker ps -a" : "docker ps") + " --format " + format;
|
|
||||||
|
|
||||||
String output = runCommand(accounts, cmd);
|
String output = runCommand(accounts, cmd);
|
||||||
if (output == null || output.isBlank()) return list;
|
if (output == null || output.isBlank()) {
|
||||||
|
return Collections.emptyList();
|
||||||
for (String line : output.split("\\R")) {
|
|
||||||
if (line.isBlank()) continue;
|
|
||||||
String[] arr = line.split("\\|");
|
|
||||||
ContainerInfo info = new ContainerInfo();
|
|
||||||
info.setContainerId(arr.length > 0 ? arr[0].trim() : "");
|
|
||||||
info.setImage(arr.length > 1 ? arr[1].trim() : "");
|
|
||||||
info.setCommand(arr.length > 2 ? arr[2].trim() : "");
|
|
||||||
info.setCreated(arr.length > 3 ? arr[3].trim() : "");
|
|
||||||
info.setStatus(arr.length > 4 ? arr[4].trim() : "");
|
|
||||||
info.setPorts(arr.length > 5 ? arr[5].trim() : "");
|
|
||||||
info.setNames(arr.length > 6 ? arr[6].trim() : "");
|
|
||||||
info.setAccountId(accounts.getAccountId());
|
|
||||||
list.add(info);
|
|
||||||
}
|
}
|
||||||
return list;
|
return Arrays.stream(output.split("\\R"))
|
||||||
|
.filter(StringUtils::isNotBlank)
|
||||||
|
.map(line -> {
|
||||||
|
String[] arr = line.split("\\|", 8);
|
||||||
|
return new ContainerInfo(
|
||||||
|
arr.length > 0 ? arr[0].trim() : "",
|
||||||
|
arr.length > 1 ? arr[1].trim() : "",
|
||||||
|
arr.length > 2 ? arr[2].trim() : "",
|
||||||
|
arr.length > 3 ? arr[3].trim() : "",
|
||||||
|
arr.length > 4 ? arr[4].trim() : "",
|
||||||
|
arr.length > 5 ? arr[5].trim() : "",
|
||||||
|
arr.length > 6 ? arr[6].trim() : "",
|
||||||
|
accounts.getAccountId()
|
||||||
|
);
|
||||||
|
})
|
||||||
|
.collect(Collectors.toList());
|
||||||
}
|
}
|
||||||
|
|
||||||
public static DockerResult start(MySftpAccounts accounts, String containerId) {
|
public static DockerResult start(MySftpAccounts accounts, String containerId) {
|
||||||
@@ -203,9 +197,9 @@ public class DockerUtil {
|
|||||||
*/
|
*/
|
||||||
public static SystemInfo systemInfo(MySftpAccounts accounts) {
|
public static SystemInfo systemInfo(MySftpAccounts accounts) {
|
||||||
String cmd =
|
String cmd =
|
||||||
"echo CPU:$(top -bn1 2>/dev/null | grep 'Cpu(s)' | awk '{printf \"%.1f\", 100-$8}') && " +
|
"echo CPU:$(top -bn1 2>/dev/null | grep 'Cpu(s)' | awk '{printf \"%.1f\", 100-$8}') && " +
|
||||||
"echo MEM:$(free 2>/dev/null | grep Mem | awk '{printf \"%.1f\", $3/$2*100}') && " +
|
"echo MEM:$(free 2>/dev/null | grep Mem | awk '{printf \"%.1f\", $3/$2*100}') && " +
|
||||||
"echo DISK:$(df -h / 2>/dev/null | grep / | awk '{gsub(/%/,\"\"); print $5}')";
|
"echo DISK:$(df -h / 2>/dev/null | grep / | awk '{gsub(/%/,\"\"); print $5}')";
|
||||||
|
|
||||||
String output = runCommand(accounts, cmd);
|
String output = runCommand(accounts, cmd);
|
||||||
SystemInfo info = new SystemInfo();
|
SystemInfo info = new SystemInfo();
|
||||||
@@ -224,10 +218,6 @@ public class DockerUtil {
|
|||||||
return info;
|
return info;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ──────────────────────────────────────────────
|
|
||||||
// 连接管理
|
|
||||||
// ──────────────────────────────────────────────
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 关闭指定账号的连接(账号删除或更新密码时调用)
|
* 关闭指定账号的连接(账号删除或更新密码时调用)
|
||||||
*/
|
*/
|
||||||
|
|||||||
Reference in New Issue
Block a user