阿里云Jindo CLI是管理OSS-HDFS服务的命令行工具。本文通过Java Spring Boot封装Jindo CLI命令为API接口,实现OSS-HDFS的程序化管理。支持集中部署和分离式部署两种架构,适用于运维管控系统、自动化脚本、CI/CD工具等场景。
方案选型
方式一:集中部署
业务应用与Jindo CLI工具部署在同一台服务器,通过本地调用方式实现集成。如图所示,业务应用在业务服务器上直接调用Jindo CLI,再访问OSS-HDFS服务。
优点:实现简单,无网络开销。
缺点:CLI执行会占用主业务资源。
适用场景:快速验证、内部自动化脚本、单一管理平台等场景。
方式二:分离式部署
业务应用(服务器 A)与 Jindo CLI(服务器 B)分离,CLI 封装为独立的 HTTP Agent 服务,通过 Restful API 通信。如图所示,业务应用远程调用 HTTP Agent,由 Agent 调用 Jindo CLI,最终访问 OSS-HDFS 服务。
优点:AccessKey隔离在工具服务器;业务与工具解耦,支持独立升级与跨语言调用;可同时为多个业务平台提供服务。
缺点:需额外维护Agent服务,存在一定网络开销;RESTful API调用默认无鉴权,需要客户自行实现安全校验(如使用Auth Token等机制)。
适用场景:生产环境,尤其是对扩展性要求高的系统,以及需要为多个业务平台提供服务的场景。
前置准备
在开始部署前,请确保已完成以下准备工作:
OSS-HDFS服务:已开通OSS-HDFS服务的Bucket。参见开通OSS-HDFS服务。
权限准备:RAM用户拥有访问OSS-HDFS权限。建议遵循最小权限原则,例如,若仅需读取,则不授予写或删除权限 。参见授权访问OSS-HDFS服务。
服务器环境:至少一台服务器(ECS 或自建),并满足以下条件:与 Bucket 同地域、能通过内网访问 OSS、已安装 JDK。
Jindo SDK:已安装并配置 Jindo SDK。
集中部署:在业务服务器上安装。
分离式部署:仅在 Agent 服务器(服务器 B)上安装。
方式一:集中部署
业务应用与Jindo CLI 在同一服务器,直接在业务应用内部调用 Jindo CLI 命令。适用于快速验证。
步骤一:开发后端API接口
创建 Spring Boot API 接口,封装 jindo fs -ls
命令。
不同版本的 Jindo SDK 主命令可能存在差异。部分新版本使用 jindo,而一些早期版本使用 jindofs。
在复制代码前,建议您先在服务器的命令行中手动执行jindo -v
或jindofs -v
,以确认您环境中正确的命令。本文所有示例均以 jindo 为例。
package com.example.jindotest;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.IOException;
@RestController
public class JindoController {
@PostMapping("/jindo-ls")
public ResponseEntity<String> executeJindoLs(@RequestParam("path") String path) {
// 校验路径格式
if (path == null || !path.startsWith("oss://")) {
return ResponseEntity.badRequest().body("Invalid OSS path");
}
try {
// 构建 Jindo CLI 命令
ProcessBuilder processBuilder = new ProcessBuilder("jindo", "fs", "-ls", path);
processBuilder.redirectErrorStream(true);
// 启动进程
Process process = processBuilder.start();
// 读取命令输出
BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()));
StringBuilder result = new StringBuilder();
String line;
while ((line = reader.readLine()) != null) {
result.append(line).append("\n");
}
// 等待进程结束并检查退出码
int exitCode = process.waitFor();
if (exitCode == 0) {
// 返回命令执行结果
return ResponseEntity.ok(result.toString());
} else {
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
.body("Command failed with exit code: " + exitCode);
}
} catch (IOException | InterruptedException e) {
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
.body("Error executing command: " + e.getMessage());
}
}
}
步骤二:结果验证
完成部署后,可通过以下方式验证集成效果。
web界面验证:
参考附录Web界面示例代码,将代码中的请求路径修改为
/jindo-ls
。访问http://<业务服务器公网IP>:8080
(需要打开业务服务器入方向的8080端口)进行测试。输入文件路径:如:oss://my-bucket.cn-hangzhou.oss-dls.aliyuncs.com/
,单击"List Files"按钮。如果界面成功展示了OSS的文件列表,则集成成功。API接口验证(备选)
如果暂不方便部署前端,可使用 curl 直接测试后端接口是否工作正常。
curl -X POST "http://localhost:8080/jindo-ls?path=oss://bucket.endpoint/"
返回文件列表即为集成成功。
验证成功后,可参考附录中的常用Jindo CLI命令,将其他命令集成到您的系统中。
方式二:分离式部署
生产环境推荐。此方案将Jindo CLI封装为独立的Agent服务,实现责任分离。适用于内网环境或安全要求相对较低的场景,如需高安全要求,请自行实现API访问校验机制。
步骤一:在服务器 B 启动 Agent 服务
创建Agent脚本
创建
JindoCliHttpAgent.py
文件,脚本使用Python3实现。其他技术栈可参考相同思路进行实现。#!/usr/bin/env python3 # -*- coding: utf-8 -*- import http.server import json import subprocess import socketserver import shlex class CommandHandler(http.server.SimpleHTTPRequestHandler): def do_POST(self): if self.path == '/execute': # 读取请求体 content_length = int(self.headers.get('Content-Length', 0)) post_data = self.rfile.read(content_length) try: # 解析 JSON 请求 data = json.loads(post_data) command = data.get('command', '') # 验证命令参数 if not command: self.send_response(400) self.send_header('Content-Type', 'application/json') self.end_headers() self.wfile.write(json.dumps({'error': 'Missing "command" parameter'}).encode('utf-8')) return # 安全执行命令 command_list = shlex.split(command) output = subprocess.check_output(command_list, stderr=subprocess.STDOUT) output_str = output.decode('utf-8').strip() # 返回执行结果 self.send_response(200) self.send_header('Content-Type', 'application/json') self.end_headers() self.wfile.write(json.dumps({'output': output_str}).encode('utf-8')) except Exception as e: # 返回错误信息 self.send_response(500) self.send_header('Content-Type', 'application/json') self.end_headers() self.wfile.write(json.dumps({'error': str(e)}).encode('utf-8')) else: self.send_error(404, 'Not Found') # 多线程 HTTP 服务器 class ThreadedHTTPServer(socketserver.ThreadingMixIn, http.server.HTTPServer): pass if __name__ == '__main__': # 启动 HTTP Agent 服务 server_address = ('0.0.0.0', 8000) httpd = ThreadedHTTPServer(server_address, CommandHandler) print('Starting secure HTTP Agent on port 8000...') httpd.serve_forever()
配置防火墙
为服务器B开放入方向8000端口,仅允许业务服务器A的私网IP访问,禁止公网访问。
添加执行权限
chmod +x JindoCliHttpAgent.py
启动HTTP Agent服务
python3 JindoCliHttpAgent.py
服务启动后会监听8000端口请求,启动效果如下图所示。
验证Agent服务
新开终端,测试Agent是否正常工作。替换<Bucketname>和<EndPoint>为实际值,例如:
oss://my-bucket.cn-hangzhou.oss-dls.aliyuncs.com/
curl -X POST http://localhost:8000/execute \ -H "Content-Type: application/json" \ -d '{"command": "jindo fs -ls oss://<Bucketname>.<EndPoint>/"}'
如果返回 JSON 格式的文件列表,如下图示,则证明Agent已部署成功。
步骤二:在服务器A上集成业务系统
不同版本的 Jindo SDK 主命令可能存在差异。部分新版本使用 jindo,而一些早期版本使用 jindofs。
在复制代码前,建议您先在服务器的命令行中手动执行jindo -v
或jindofs -v
,以确认您环境中正确的命令。本文所有示例均以 jindo 为例。
创建Java API接口调用Agent服务。请将代码中的<服务器B的私网IP>替换为实际的服务器B私网IP地址。
package com.example.jindotest;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.IOException;
@RestController
public class JindoAgentController {
@PostMapping("/jindo-agent-ls")
public ResponseEntity<String> executeJindoLs(@RequestParam String path) {
// 校验路径格式
if (path == null || !path.startsWith("oss://")) {
return ResponseEntity.badRequest().body("Invalid OSS path");
}
try {
// 构造 JSON 请求体
String requestBody = String.format("{\"command\": \"jindo fs -ls %s\"}", path);
// 通过 curl 调用 Agent 服务
ProcessBuilder processBuilder = new ProcessBuilder(
"curl", "-X", "POST", "http://<服务器B的私网IP>:8000/execute",
"-H", "Content-Type: application/json",
"-d", requestBody
);
// 启动进程
Process process = processBuilder.start();
// 读取输出流
BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()));
StringBuilder result = new StringBuilder();
String line;
while ((line = reader.readLine()) != null) {
result.append(line).append("\n");
}
// 检查执行结果
int exitCode = process.waitFor();
if (exitCode == 0) {
return ResponseEntity.ok(result.toString());
} else {
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body("Command failed with exit code: " + exitCode);
}
} catch (IOException | InterruptedException e) {
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body("Error executing command: " + e.getMessage());
}
}
}
生产环境建议使用RestTemplate或WebClient等HTTP客户端库替代curl,获得更好的性能和错误处理能力。
步骤三:结果验证
网络连通性检查
如果采用分离式部署,需要确保服务器间网络连通。在服务器A上执行
ping <服务器B私网IP>
。若稳定收到ping响应,无丢包。则证明两台服务器连通。web界面验证
参考附录Web界面示例代码,将代码中的请求路径修改为
/jindo-agent-ls
。访问http://<服务器A公网IP>:8080
(需要打开服务器A入方向的8080端口)进行测试。输入文件路径:如:oss://my-bucket.cn-hangzhou.oss-dls.aliyuncs.com/
,单击"List Files"按钮。如果界面成功展示了OSS的文件列表,则集成成功。API接口验证(备选方式)
如果暂不方便部署前端,可使用 curl 直接测试后端接口是否工作正常。
curl -X POST "http://localhost:8080/jindo-agent-ls?path=oss://<bucket>.<Endpoint>/"
返回文件列表即为集成成功。
验证成功后,可参考附录中的常用Jindo CLI命令,将其他命令集成到您的系统中。
安全建议
Jindo CLI 具有 OSS-HDFS 服务配置管理和数据删除能力,使用不当可能造成严重后果。在生产环境部署前,务必在测试环境充分验证。
核心原则
最小权限:仅授予完成任务所需的最小权限,避免过度授权。
访问控制:在调用端实施严格的身份验证和授权机制。
并发控制:限制并发操作数量,防止配置冲突和数据管理混乱。
网络安全
分离式部署:Agent 服务器仅允许业务服务器私网 IP 访问,禁止公网暴露。
端口管理:通过安全组或防火墙精确控制访问端口。
相关文档
附录
常用Jindo CLI命令
命令 | 功能说明 | 命令示例 |
stat | 显示文件状态。 |
|
ls | 列出目录下文件。可选参数-R,表示递归显示。 |
|
du | 显示目录中所有文件的大小。可选参数如下:
|
|
count | 显示文件大小以及文件数量。可选参数-h,显示文件大小单位。 |
|
listUserGroupsMappings | 列出所有用户和组的关系。 |
|
dumpInventory | 导出文件元数据。 |
|
putConfig | 服务特性设置(如设置目录保护) |
|
getConfig | 获取配置信息(如目录保护信息) |
|
Web界面示例代码
将以下代码保存为index.html
,放置在Spring Boot项目的src/main/resources/static/
目录下:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Jindo CLI Management Interface</title>
<style>
body {
font-family: Arial, sans-serif;
max-width: 1000px;
margin: 50px auto;
padding: 20px;
background-color: #f5f5f5;
}
.container {
background-color: white;
padding: 30px;
border-radius: 8px;
box-shadow: 0 2px 10px rgba(0,0,0,0.1);
}
h1 {
color: #333;
text-align: center;
border-bottom: 2px solid #007bff;
padding-bottom: 10px;
}
.form-group {
margin-bottom: 20px;
}
label {
display: block;
margin-bottom: 5px;
font-weight: bold;
color: #555;
}
input[type="text"] {
width: 100%;
padding: 10px;
border: 1px solid #ddd;
border-radius: 4px;
font-size: 16px;
box-sizing: border-box;
}
button {
background-color: #007bff;
color: white;
padding: 12px 24px;
border: none;
border-radius: 4px;
cursor: pointer;
font-size: 16px;
margin-right: 10px;
}
button:hover {
background-color: #0056b3;
}
button:disabled {
background-color: #6c757d;
cursor: not-allowed;
}
.result {
margin-top: 20px;
padding: 15px;
border: 1px solid #ddd;
border-radius: 4px;
background-color: #f8f9fa;
min-height: 100px;
font-family: monospace;
white-space: pre-wrap;
font-size: 14px;
}
.loading {
color: #007bff;
font-style: italic;
}
.error {
color: #dc3545;
background-color: #f8d7da;
border-color: #f5c6cb;
}
.success {
color: #155724;
background-color: #d4edda;
border-color: #c3e6cb;
}
</style>
</head>
<body>
<div class="container">
<h1> Jindo CLI Management Interface</h1>
<div class="form-group">
<label for="ossPath">OSS Path:</label>
<input type="text" id="ossPath"
value="oss://your-bucket-name.cn-hangzhou.oss-dls.aliyuncs.com/"
placeholder="Enter OSS path (e.g., oss://bucket-name.endpoint/)">
</div>
<div class="form-group">
<button onclick="listFiles()">List Files (ls)</button>
<button onclick="clearResult()">Clear Result</button>
</div>
<div id="result" class="result">
Ready to execute Jindo CLI commands...
</div>
</div>
<script>
function listFiles() {
const path = document.getElementById('ossPath').value;
const resultDiv = document.getElementById('result');
if (!path || !path.startsWith('oss://')) {
resultDiv.innerHTML = 'Error: Please enter a valid OSS path starting with "oss://"';
resultDiv.className = 'result error';
return;
}
// Show loading
resultDiv.innerHTML = 'Loading... Please wait';
resultDiv.className = 'result loading';
// Call backend API
fetch('/jindo-ls', {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
},
body: 'path=' + encodeURIComponent(path)
})
.then(response => {
if (response.ok) {
return response.text();
} else {
throw new Error('HTTP ' + response.status + ': ' + response.statusText);
}
})
.then(data => {
resultDiv.innerHTML = 'Success:\n\n' + data;
resultDiv.className = 'result success';
})
.catch(error => {
resultDiv.innerHTML = 'Error:\n\n' + error.message;
resultDiv.className = 'result error';
});
}
function clearResult() {
document.getElementById('result').innerHTML = 'Ready to execute Jindo CLI commands...';
document.getElementById('result').className = 'result';
}
// Allow Enter key to trigger listFiles
document.getElementById('ossPath').addEventListener('keypress', function(e) {
if (e.key === 'Enter') {
listFiles();
}
});
</script>
</body>
</html>