前言
最近在做云原生运维相关系统开发,需要api操作helm,也是调研找了许久,找到个不错的开源项目,分享一下叫helm-java
链接:https://github.com/manusa/helm-java
这个项目是之前分享过的文章Fabric8io团队24年初开源的,目前找了网上各个博客还都没有,都是操作helm比较麻烦的。这个Helm-Java使用简单,和Fabirc8差不多,该项目也有详细api使用讲解。
注:有兴趣可以研究研究,这里和大家简单分享一下这个库
补充:对于开源项目还未实现的命令,如helm search repo [repo_name]
,大家可以先用java远程连接主机,再远程执行命令实现,拿到执行后打印的数据,就能对数据处理返回,代码在第二节也给出
24/4/22
helm search repo
命令我在它github提了个issue,没想到官方快速回复并在0.0.7
版本完善,真有开源精神,大家若有问题,也可以去提issue,官方有时间应该也会快速完善。
Helm-Java Demo使用
根据官网下方对每个命令对应api的讲解,做了demo,亲测有效
<dependency>
<groupId>com.marcnuri.helm-java</groupId>
<artifactId>helm-java</artifactId>
<version>0.0.7</version>
</dependency>
个人demo代码,这里不做详细讲解,因为开源讲解已经很详细,自己可以去试着运行一下,就懂了,链式编程填参数也好理解
package com.yx;
import com.marcnuri.helm.*;
import java.nio.file.Paths;
import java.util.List;
import java.util.UUID;
import java.util.stream.Collectors;
/**
* Author:yx5411
* Date:2024-03-27
* Description: helm-java-api测试
*/
public class Main {
public static void main(String[] args) {
// 集群中的~/.kube/config连接文件下载放到本地
String kubeConfigPath = "D:\\IDEAProject\\helm_java_api\\src\\main\\resources\\conf\\config";
String namespace = "kubeapps";
String uuid = UUID.randomUUID().toString();
String name = "nginx-test-" + uuid.substring(0, 4);
System.out.println("name: " + name);
// findNameSpaceAllChart(namespace, kubeConfigPath);
installHelm(namespace, kubeConfigPath, name);
// createChartDir();
// lintHelm();
// uninstallReleaseByName(namespace, kubeConfigPath);
}
// helm create:在指定位置创建一个包含chart包公共文件的目录,即初始化创建
private static void createChartDir() {
Helm.create()
// Name of the chart to create
.withName("test")
// Path to the directory where the new chart directory will be created
.withDir(Paths.get("D:\\IDEAProject\\helm_java_api\\src\\main\\resources\\tmp"))
.call();
}
// helm list:查询某个命名空间下的所有已发布的chart
private static void findNameSpaceAllChart(String namespace, String kubeConfigPath) {
List<Release> releases = Helm.list()
.withNamespace(namespace)
.withKubeConfig(Paths.get(kubeConfigPath))
.all()
// .allNamespaces()
.deployed()
.failed()
.pending()
.superseded()
.call();
for (Release release : releases) {
System.out.println("Name: " + release.getName());
System.out.println("Namespace: " + release.getNamespace());
System.out.println("Revision: " + release.getRevision());
System.out.println("Status: " + release.getStatus());
System.out.println("Chart: " + release.getChart());
System.out.println("App Version: " + release.getAppVersion());
System.out.println("-----------------------------");
}
}
// helm lint:检查图表是否存在可能的问题,即验证
public static void lintHelm() {
String chatPath = "D:\\IDEAProject\\helm_java_api\\src\\main\\resources\\conf\\nginx-0.1.0.tgz";
LintResult result = new Helm(Paths.get(chatPath))
.lint()
.strict()
.quiet()
.call();
System.out.println(result.isFailed());
System.out.println(result.getMessages());
}
// helm uninstall:删除某个命名空间下的release,可helm list -n [NAMESPACE]查看已发布的chart
private static void uninstallReleaseByName(String namespace, String kubeConfigPath){
// 假设先查询helm list
List<Release> releases = Helm.list()
.withNamespace(namespace)
.withKubeConfig(Paths.get(kubeConfigPath))
.all()
// 下面是筛选查各种状态的release
// .deployed()
// .failed()
// .pending()
// .superseded()
// .uninstalled()
.call();
// 删除状态为失败和挂起的release
List<String> list = releases.stream().map(Release::getName).collect(Collectors.toList());
for (String name: list){
System.out.println("helm uninstall " + name + " -n " + namespace);
String res = Helm.uninstall(name)
.dryRun() // 模拟卸载,不真实卸载,真卸载需将其注释
.noHooks()
.ignoreNotFound()
.keepHistory()
.withCascade(UninstallCommand.Cascade.BACKGROUND)
.withNamespace(namespace)
.withKubeConfig(Paths.get(kubeConfigPath))
.debug()
.call();
System.out.println(res);
}
}
// helm install:在某个命名空间下安装部署chart
public static void installHelm(String namespace, String kubeConfigPath, String name){
// 通过下面方式nginx.tgz安装正常running,mysql.tgz安装却是pending
// 原因是mysql和redis均默认要1个master和3个pod进行部署,本地只部署1master和2node导致,资源问题
String desc = "helm install " + name;
String chatPath = "D:\\IDEAProject\\helm_java_api\\src\\main\\resources\\conf\\nginx-0.1.0.tgz";
InstallCommand installCommand = new Helm(Paths.get(chatPath)).install();
Release result = installCommand
.withName(name)
.withNamespace(namespace)
.withKubeConfig(Paths.get(kubeConfigPath))
.createNamespace()
.withDescription(desc)
.dependencyUpdate()
.waitReady()
.plainHttp()
.debug()
.call();
System.out.println("Success");
}
}
另外,关于安装部署chart引用
(1) 通过chart引用进行install,这里需要本地有repositories.yaml
文件,我们远程主机用helm --help
可以看到windows默认存的位置,是在%APPDATA%
环境变量下
在cmd窗口
echo %APPDATA%
,可以看到如下,这个变量对应的位置是C:\Users\yx5411\AppData\Roaming
,所以需要在这目录下面加一个helm目录,然后放入yaml文件
(2) %TEMP%
也echo打印,会发现路径C:\Users\yx5411\AppData\Local\Temp
,如下
这里就对应cache中helm目录,需要将远程.cache目录中helm放%TEMP%对应路径,然后本地windows环境就能通过chart引用install了
补充方法:远程连接,执行Linux命令
这是参考一个博主的,自己测了下,能用,能拿到命令执行后,控制台打印的数据
导入Maven依赖,远程连接相关的ssh2
<dependency>
<groupId>ch.ethz.ganymed</groupId>
<artifactId>ganymed-ssh2</artifactId>
<version>build210</version>
</dependency>
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.UnsupportedEncodingException;
import ch.ethz.ssh2.Connection;
import ch.ethz.ssh2.Session;
import ch.ethz.ssh2.StreamGobbler;
/**
* 远程执行Linux命令
*/
public class RemoteExecuteCmd {
//字符编码默认是utf-8
private static String DEFAULT_CHART = "UTF-8";
private Connection conn;
private String ip;
private String userName;
private String userPwd;
public RemoteExecuteCmd(String ip, String userName, String userPwd) {
this.ip = ip;
this.userName = userName;
this.userPwd = userPwd;
}
/**
* 远程登录linux的主机
* @author Ickes
* @since V0.1
* @return
* 登录成功返回true,否则返回false
*/
public Boolean login(){
boolean flag=false;
try {
conn = new Connection(ip);
conn.connect();//连接
flag = conn.authenticateWithPassword(userName, userPwd);//认证
} catch (IOException e) {
e.printStackTrace();
}
return flag;
}
/**
* @author Ickes
* 远程执行shll脚本或者命令
* @param cmd
* 即将执行的命令
* @return
* 命令执行完后返回的结果值
* @since V0.1
*/
public String execute(String cmd){
String result="";
try {
if(login()){
Session session= conn.openSession();//打开一个会话
session.execCommand(cmd);//执行命令
result=processStdout(session.getStdout(), DEFAULT_CHART);
//如果为得到标准输出为空,说明脚本执行出错了
if(result.isEmpty()){
result=processStdout(session.getStderr(),DEFAULT_CHART);
}
conn.close();
session.close();
}
} catch (IOException e) {
e.printStackTrace();
}
return result;
}
/**
* @author Ickes
* 远程执行shell脚本或者命令
* @param cmd
* 即将执行的命令
* @return
* 命令执行成功后返回的结果值,如果命令执行失败,返回空字符串,不是null
* @since V0.1
*/
public String executeSuccess(String cmd){
String result="";
try {
if(login()){
Session session= conn.openSession();//打开一个会话
session.execCommand(cmd);//执行命令
result=processStdout(session.getStdout(),DEFAULT_CHART);
conn.close();
session.close();
}
} catch (IOException e) {
e.printStackTrace();
}
return result;
}
/**
* 解析脚本执行返回的结果集
* @author Ickes
* @param in 输入流对象
* @param charset 编码
* @since V0.1
* @return
* 以纯文本的格式返回
*/
private String processStdout(InputStream in, String charset){
InputStream stdout = new StreamGobbler(in);
StringBuffer buffer = new StringBuffer();;
try {
BufferedReader br = new BufferedReader(new InputStreamReader(stdout,charset));
String line = null;
while((line = br.readLine()) != null){
buffer.append(line+"\n");
}
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return buffer.toString();
}
}
OperateMain.java主类测试
public class OperateMain {
public static void main(String[] args) {
// 得到命令执行后,打印出的数据
String res = demoCmd1();
System.out.println(res);
}
private static String demoCmd1() {
// 或者其他命令ls /opt
String cmd = "helm repo list";
String res = executeCmd(cmd);
return res;
}
private static String executeCmd(String cmd){
String usr = "root";
String password = "root";
String serverIP = "192.168.20.xxx";// 替换
RemoteExecuteCmd executeCommand = new RemoteExecuteCmd(serverIP, usr, password);
String res = executeCommand.execute(cmd);
return res;
}
}