简介:本资源为Kettle(Pentaho Data Integration)的Web化版本——webKettle完整部署包,面向ETL工程师、数据集成开发者及需要远程协作处理数据流程的技术人员。它将传统桌面端Spoon工具迁移至浏览器环境,支持跨设备在线设计、调试与执行转换/作业,特别适用于分布式团队、云环境部署及实时数据治理场景。压缩包共1002个文件,含284个JAR(核心运行依赖)、482个PNG/SVG/GIF(Web界面资源)、112个CLASS(编译类文件)及配套脚本(如Spoon.bat、Kitchen.bat、Carte.bat等),整体157.7MB,结构完整覆盖服务启动、配置管理与前端交互所需全部组件。目前已有1557人学习下载,用户可直接解压部署至Tomcat,快速获得开箱即用的Web版ETL平台,包含完整服务脚本、默认配置模板与图形化界面资源,大幅降低Web化部署门槛与调试成本。
1. Kettle 的 Web 版不是“网页版 Spoon”,而是可嵌入、可调度、可审计的生产级 ETL 服务入口
你点开kettle-web.zip,双击start.bat,浏览器打开http://localhost:8080,看到一个带登录页、项目树、作业列表、执行日志的界面——这不是把 Spoon 拖进浏览器的“伪 Web 化”,而是基于 Pentaho Data Integration(PDI)核心引擎构建的轻量级 Web 服务层。它不替代 Spoon 的图形化开发,但彻底解决了三个一线痛点:团队多人无法共用同一台开发机、ETL 任务不能按 Cron 触发、历史执行记录查不到谁在什么时间改了哪一步。这个包本质是Carte 服务 + 自定义 Web 控制台 + REST API 封装的最小可行组合,体积小(解压后 <25MB)、无数据库依赖(默认 HSQLDB)、启动即用。适合中小数据团队做过渡期统一调度入口,也适合嵌入到已有 OA 或 BI 平台中作为数据流水线模块。如果你还在用本地 Spoon 手动双击.kjb文件跑批、靠 Excel 记录执行时间、靠截图存档报错信息——这个 zip 就是你今天该停下手头活、花 12 分钟部署验证的“后悔药”。
2. 部署结构与核心组件:为什么它能脱离 Spoon 独立运行?
这个kettle-web.zip不是“Kettle 官方 Web 版”(Pentaho Server 已停止维护),而是社区长期演进出的轻量化替代方案。它绕开了重量级 Java EE 容器(如 Tomcat + Pentaho WAR 包),采用内嵌 Jetty + Carte 微服务架构。理解其组成,才能不被“Web 版”字面迷惑。
2.1 目录结构即运行逻辑:每个文件夹都在承担明确角色
解压后你会看到如下关键目录:
kettle-web/ ├── carteserver/ # Carte 服务本体:接收 .kjb/.ktr 远程执行请求,返回 JSON 状态 ├── webapp/ # Web 控制台前端:Vue.js 单页应用(已编译),静态资源 + API 代理 ├── config/ # 全局配置中心:carte-config.xml(端口/日志/安全)、web-config.json(菜单/权限) ├── jobs/ # 作业存储区:所有上传的 .kjb 文件实际存放位置(非数据库) ├── logs/ # 执行日志落盘目录:每个 job 执行生成独立 timestamp.log,供 Web 界面读取 └── start.bat / start.sh # 启动脚本:顺序启动 Carte + Jetty,自动重试失败服务提示:
jobs/目录是唯一需要你手动管理的持久化路径。所有通过 Web 界面上传的作业、修改的参数,最终都写入此目录下的.kjb文件。这意味着——备份jobs/就等于备份全部业务流程,无需导出数据库。
2.2 Carte 服务:Kettle 的“远程执行引擎”,不是“Web 服务器”
Carte 是 PDI 内置的轻量级 HTTP 服务,早在 Kettle 4.x 时代就存在。kettle-web.zip中的carteserver/是对其的定制增强版(添加了 Basic Auth、JSON 响应封装、执行队列控制)。它监听8081端口(默认),提供以下关键 REST 接口:
| 接口路径 | 方法 | 用途 | 关键参数 |
|---|---|---|---|
/kettle/status | GET | 查询 Carte 服务健康状态 | — |
/kettle/executeJob | POST | 远程触发作业 | name: 作业名(不含.kjb)、params: JSON 字符串(如{"INPUT_PATH":"/data/in"}) |
/kettle/getJobStatus | GET | 查询作业执行状态 | id: Carte 返回的执行 ID |
/kettle/stopJob | POST | 强制终止运行中作业 | id: 执行 ID |
# 示例:用 curl 触发一个名为 'daily_clean' 的作业,传入日期参数 curl -X POST "http://localhost:8081/kettle/executeJob" \ -H "Content-Type: application/json" \ -d '{ "name": "daily_clean", "params": {"RUN_DATE": "20240520", "SOURCE_DB": "mysql_prod"} }' # 返回:{"id":"carte_20240520_142311_789","status":"running"}这段命令背后,Carte 会从jobs/daily_clean.kjb加载作业,注入RUN_DATE和SOURCE_DB参数,调用 PDI 核心引擎执行,并将日志实时写入logs/daily_clean_20240520_142311.log。Web 控制台的所有“运行”按钮,底层都是调用这些接口。你完全可以用 Python 脚本、Airflow Operator、甚至钉钉机器人来驱动它——这才是它作为“服务”的价值。
2.3 Web 控制台:Vue 前端 + API 代理,不碰 Kettle 任何 Java 类
webapp/目录下是纯前端代码(index.html,js/app.js,css/style.css),没有 JSP、没有 Servlet。它通过config/web-config.json中配置的carteUrl(默认http://localhost:8081)与 Carte 通信。关键设计点:
- 无状态登录:登录时向 Carte 发送
POST /kettle/login(Basic Auth),Carte 返回临时 Token,后续所有请求在 Header 中携带Authorization: Bearer <token>; - 作业树动态加载:访问
/kettle/listJobs获取jobs/下所有.kjb文件名,渲染为左侧导航树; - 日志流式读取:对
/logs/{jobname}_{timestamp}.log发起长轮询(Long Polling),模拟实时日志输出,避免 WebSocket 复杂度; - 参数模板化:在
jobs/下同名.json文件(如daily_clean.json)定义参数默认值和类型(string/date/list),Web 界面据此渲染表单。
// jobs/daily_clean.json 示例:定义 Web 界面参数输入框 { "params": [ { "name": "RUN_DATE", "type": "date", "default": "2024-05-20", "label": "执行日期" }, { "name": "SOURCE_DB", "type": "list", "options": ["mysql_prod", "pgsql_staging", "oracle_backup"], "default": "mysql_prod", "label": "源数据库" } ] }这个 JSON 文件不存在?Web 界面就只显示一个“无参数”运行按钮。参数配置权完全交还给开发者,而非前端硬编码——这是它比某些“魔改版 Web Kettle”更可控的根本原因。
3. 从本地 Spoon 开发到 Web 部署:四步完成作业迁移与参数化
你手头已有.kjb作业,想让它在 Web 界面可调度、可传参、可监控。这不是简单复制粘贴,而是要补全 Kettle 生产环境必需的“元数据契约”。以下是某高校实验室数据清洗项目的实操路径,全程无删改原作业逻辑。
3.1 步骤一:校验作业兼容性——禁用绝对路径与 GUI 依赖
Kettle 作业在 Web 环境下运行,必须满足两个前提:
- 所有文件路径必须相对化或参数化:Spoon 中写的
C:\data\input.csv在 Linux 服务器上必然失败。需改为${INPUT_DIR}/input.csv; - 禁用任何 GUI 组件:如
Show message box步骤、Get variables from dialog等交互式步骤,在无桌面环境的 Carte 中会直接抛HeadlessException。
<!-- 错误示范:Spoon 中直接写的绝对路径 --> <entry> <name>Text file input</name> <type>TextFileInput</type> <filename>C:\data\sales_2024.csv</filename> </entry> <!-- 正确做法:替换为参数占位符 --> <entry> <name>Text file input</name> <type>TextFileInput</type> <filename>${INPUT_DIR}/sales_${RUN_DATE}.csv</filename> </entry>提示:在 Spoon 中按
Ctrl+Shift+P打开“设置变量”对话框,批量定义INPUT_DIR、OUTPUT_DIR、RUN_DATE等全局变量。这些变量在 Carte 执行时由 Web 界面传入,或在carte-config.xml中设默认值。
3.2 步骤二:上传作业与参数模板——让 Web 知道怎么跟你对话
将本地开发好的daily_clean.kjb复制到kettle-web/jobs/目录。切勿用 Web 界面的“上传”按钮——它会尝试解析并可能破坏复杂作业结构。直接文件系统操作最可靠。
接着,创建同名参数模板daily_clean.json:
{ "params": [ { "name": "RUN_DATE", "type": "date", "default": "2024-05-20", "label": "清洗日期", "required": true }, { "name": "INPUT_DIR", "type": "string", "default": "/opt/kettle-data/in", "label": "输入目录", "description": "需确保 Carte 进程有读取权限" }, { "name": "OUTPUT_DIR", "type": "string", "default": "/opt/kettle-data/out", "label": "输出目录" } ] }注意:
description字段会在 Web 界面鼠标悬停时显示,是给运维同事的救命提示。required: true表示该参数必填,否则“运行”按钮置灰。
3.3 步骤三:配置 Carte 安全与日志——生产环境不可跳过的两道锁
编辑config/carte-config.xml,重点修改三处:
<carte> <!-- 1. 绑定地址与端口:生产环境务必改掉默认 0.0.0.0 --> <host>127.0.0.1</host> <port>8081</port> <!-- 2. 启用 Basic Auth:用户名密码明文写在这里(简单场景够用)--> <users> <user> <name>etl-admin</name> <password>SecurePass2024!</password> <role>admin</role> </user> </users> <!-- 3. 日志级别调为 INFO,避免 DEBUG 刷爆磁盘 --> <logging> <level>INFO</level> </logging> </carte>同时检查config/web-config.json中的carteUrl是否匹配:
{ "carteUrl": "http://127.0.0.1:8081", "auth": { "username": "etl-admin", "password": "SecurePass2024!" } }提示:若 Carte 与 Web 控制台部署在不同机器,
carteUrl必须是可路由的 IP(如http://192.168.1.100:8081),且防火墙放行8081端口。host仍建议设为0.0.0.0以监听所有网卡。
3.4 步骤四:启动并验证——用一条命令确认全流程打通
在kettle-web/目录下执行:
# Windows start.bat # Linux/macOS chmod +x start.sh && ./start.sh观察控制台输出:
- 第一行出现
Carte server started on port 8081→ Carte 启动成功; - 第二行出现
Jetty server started on port 8080→ Web 控制台启动成功; - 浏览器访问
http://localhost:8080,输入etl-admin/SecurePass2024!登录; - 左侧树出现
daily_clean,点击进入,填写RUN_DATE=2024-05-20,点“运行”; - 右侧日志窗口开始滚动,末尾出现
Finished job [daily_clean]且状态变绿。
此时你已完成从本地开发到 Web 调度的闭环。下一步就是把它接入你的定时任务系统。
4. 避坑指南:五个血泪经验换来的“别踩我刚踩过的坑”
这个包看似简单,但因 Kettle 引擎的黑匣子特性,新手常在看似无关的环节翻车。以下是某公司数据平台组三个月高频问题归总,每条都附带复现方式和根因定位法。
4.1 现象:Web 界面点击“运行”无反应,浏览器控制台报500 Internal Server Error
原因:Carte 服务未启动,或carte-config.xml中<host>配置为localhost导致 Web 控制台(运行在 Jetty)无法反向连接 Carte。
解决:
- 检查
start.bat输出,确认Carte server started...行存在; - 查看
logs/carte.log,搜索ERROR,常见为端口被占用(改config/carte-config.xml中<port>); - 将
<host>改为127.0.0.1(非localhost),因部分系统localhost解析慢或失败; - 重启整个服务。
4.2 现象:作业在 Web 运行时报Unable to load database connection definition,但在 Spoon 里正常
原因:Spoon 中配置的数据库连接(如 MySQL)使用了 JDBC 驱动的绝对路径(如D:\drivers\mysql-connector-java-8.0.33.jar),而 Carte 运行在 Linux 服务器,路径无效。
解决:
- 将 JDBC 驱动 JAR 包复制到
carteserver/lib/目录下; - 在 Spoon 中重新编辑数据库连接,取消勾选“Use custom driver class path”,改用 Carte 自带的类路径;
- 保存
.kjb后重新上传。
4.3 现象:日志窗口显示Starting entry [xxx]后停滞,无后续输出,作业状态一直running
原因:作业中存在死循环或阻塞步骤(如Wait for file等待不存在的文件),Carte 默认无超时机制,进程卡死。
解决:
- 进入
logs/目录,找到对应时间戳的日志文件,用tail -f实时查看; - 在作业开头插入
Write to log步骤,输出当前时间戳,确认卡在哪一步; - 对
Wait for file等步骤,显式设置Timeout in seconds(如300); - 在
carte-config.xml中添加<timeout>600</timeout>(单位秒),强制 Carte 终止超时作业。
4.4 现象:Web 界面参数输入框为空,或提交后参数未注入到作业中
原因:.json参数模板文件名与.kjb作业名不完全一致(如大小写、空格、下划线差异),或 JSON 格式错误(多逗号、少引号)。
解决:
- 进入
jobs/目录,执行ls -la,确认daily_clean.kjb与daily_clean.json文件名逐字符相同(Linux 区分大小写); - 用
jq工具校验 JSON:jq . jobs/daily_clean.json,报错则修复语法; - 检查
.kjb文件中是否真的引用了该参数:在 Spoon 中右键作业 → “编辑作业”,在“参数”选项卡确认RUN_DATE已声明。
4.5 现象:作业执行成功,但logs/下无对应日志文件,Web 界面日志为空
原因:Carte 进程对logs/目录无写入权限(尤其在 Linux 下以root启动但logs/属主为other)。
解决:
- 查看
start.sh中启动 Carte 的用户(如sudo -u kettle java -jar ...); - 执行
ls -ld logs/,确认目录属主与 Carte 进程用户一致; - 若不一致,执行
chown -R kettle:kettle logs/(假设用户为kettle); - 重启服务。
5. 进阶技巧:用 REST API 实现跨系统调度与异常自动告警
Web 界面只是入口,真正的生产力在于把它变成你现有技术栈的“ETL 插件”。下面以某电商公司订单同步场景为例,展示如何用几行代码打通 Airflow 与 Kettle Web。
5.1 场景还原:每天 2:00 AM 同步昨日订单,失败时发钉钉告警
该公司原有 Airflow DAG 负责调度各类数据任务,现在希望将 Kettle 作业sync_orders_daily.kjb纳入统一调度。要求:
- Airflow 调用 Kettle Web API 触发作业;
- 每 30 秒轮询执行状态,超时 10 分钟则标记失败;
- 失败时调用钉钉机器人 Webhook 发送告警。
5.2 Airflow Operator 封装:复用即插即用
创建自定义 OperatorKettleWebOperator(Python 文件operators/kettle_web_operator.py):
from airflow.providers.http.hooks.http import HttpHook from airflow.exceptions import AirflowException import json import time class KettleWebOperator(BaseOperator): template_fields = ('job_name', 'params') def __init__( self, job_name: str, params: dict = None, carte_url: str = "http://localhost:8081", username: str = "etl-admin", password: str = "SecurePass2024!", timeout_seconds: int = 600, poll_interval: int = 30, **kwargs ): super().__init__(**kwargs) self.job_name = job_name self.params = params or {} self.carte_url = carte_url self.timeout_seconds = timeout_seconds self.poll_interval = poll_interval self.auth = (username, password) def execute(self, context): # Step 1: Trigger job hook = HttpHook(method='POST', http_conn_id='kettle_web') response = hook.run( endpoint='/kettle/executeJob', data=json.dumps({ "name": self.job_name, "params": self.params }), headers={"Content-Type": "application/json"}, auth=self.auth ) execution = response.json() execution_id = execution['id'] self.log.info(f"Triggered job {self.job_name} with ID {execution_id}") # Step 2: Poll status until finish or timeout start_time = time.time() while time.time() - start_time < self.timeout_seconds: status_resp = hook.run( endpoint=f'/kettle/getJobStatus?id={execution_id}', auth=self.auth ) status_data = status_resp.json() if status_data['status'] == 'finished': self.log.info(f"Job {self.job_name} completed successfully") return elif status_data['status'] == 'error': raise AirflowException(f"Job {self.job_name} failed: {status_data.get('error', 'Unknown error')}") time.sleep(self.poll_interval) raise AirflowException(f"Job {self.job_name} timed out after {self.timeout_seconds}s") # 在 DAG 中使用 sync_orders_task = KettleWebOperator( task_id='sync_orders_daily', job_name='sync_orders_daily', params={ "RUN_DATE": "{{ ds_nodash }}", # Airflow 模板变量:20240520 "SOURCE_DB": "mysql_orders_prod" }, carte_url="http://192.168.1.100:8081", # 生产服务器 IP username="etl-prod", password="ProdPass2024!", dag=dag )说明:
http_conn_id='kettle_web'需在 Airflow UI 的 Admin → Connections 中预先配置,Type 选HTTP,Host 填http://192.168.1.100:8081,Login/Password 填 Carte 用户凭证。
5.3 钉钉告警集成:失败时自动推送结构化消息
在 Operator 的execute方法末尾添加告警逻辑(接续上文):
except AirflowException as e: # 构造钉钉消息 dingtalk_msg = { "msgtype": "text", "text": { "content": f"[Kettle Web 告警]\n任务:{self.job_name}\n时间:{context['ts']}\n错误:{str(e)}\n执行ID:{execution_id}" } } # 调用钉钉 Webhook(需提前在钉钉群获取) requests.post( url="https://oapi.dingtalk.com/robot/send?access_token=xxx", json=dingtalk_msg, headers={"Content-Type": "application/json"} ) raise e5.4 验证与监控:三步确认链路健壮
- 本地测试:在 Airflow CLI 中执行
airflow tasks test my_dag sync_orders_daily 2024-05-20,观察日志是否打印Triggered job...和completed successfully; - 日志交叉验证:检查
kettle-web/logs/sync_orders_daily_20240520_*.log是否生成,内容是否与 Airflow 日志一致; - 故障注入测试:临时停掉 Carte 服务,触发任务,确认钉钉收到告警,且 Airflow Task 显示
failed状态。
从那以后我每次上线新 Kettle 作业,都强制走一遍这个 Airflow Operator 封装流程——哪怕只是临时调试,也要先配好告警再点运行。因为线上数据任务的尊严,不在于它跑得多快,而在于它出问题时,你能比业务方更快收到消息。希望帮到你。
本文还有配套的精品资源,点击获取