Apache Zeppelin is the web-based notebook server for interactive data analytics across Spark, JDBC, Python, Hive, and dozens of other interpreters — but when the Zeppelin server process hangs due to a full heap, a Spark interpreter process dies and leaves analysts staring at a frozen paragraph, or the interpreter binding loses its session and all running queries silently discard results, there is no built-in alerting. Zeppelin exposes REST endpoints for interpreter and job status, but they require active polling and give no outbound notifications when things break.
Vigilmon gives you external visibility into Zeppelin availability through HTTP monitors on its REST API and a thin health sidecar that polls interpreter state, plus heartbeat monitors that confirm scheduled notebooks are completing on time. This tutorial covers both.
Why Zeppelin Needs External Monitoring
Zeppelin's built-in tooling (the web UI, interpreter process logs, and YARN application tracking for Spark interpreters) requires a human operator actively watching dashboards. External monitoring with Vigilmon adds:
- Process availability alerting when the Zeppelin server becomes unreachable before analysts start their morning work session
- Interpreter health checks detecting dead or stuck Spark, JDBC, or Python interpreter processes before users attempt to run paragraphs
- Notebook execution heartbeats confirming scheduled analytical notebooks are completing successfully
- Response time monitoring catching Zeppelin server performance degradation from GC pressure or resource contention
- API liveness checks verifying the REST API responds — not just that the port is open
These layers matter because Zeppelin failures are often partial: the web UI may serve the HTML shell while the interpreter backend is crashed, making analysts think Zeppelin is healthy until they try to run a cell and it hangs indefinitely.
Step 1: Probe Zeppelin's Built-in REST API
Zeppelin exposes a REST API for version, interpreter, and notebook management. The /api/version endpoint is a lightweight liveness check:
# Check Zeppelin server liveness
curl -s http://zeppelin.example.com:8080/api/version
# Expected response:
# {"status":"OK","message":"","body":{"git-commit-id":"...","version":"0.11.x"}}
Point a Vigilmon monitor directly at http://zeppelin.example.com:8080/api/version for immediate server availability alerting.
Check interpreter status via the REST API:
# List all interpreter settings and their status
curl -s http://zeppelin.example.com:8080/api/interpreter/setting | python3 -m json.tool
# Check a specific interpreter (get interpreter ID from the list above)
curl -s http://zeppelin.example.com:8080/api/interpreter/setting/<interpreter-id>
Step 2: Build a Zeppelin Health Sidecar
For comprehensive health checks — interpreter process status, running job counts, and notebook execution freshness — build a sidecar that interprets Zeppelin's REST API.
Python Health Sidecar
# zeppelin_health.py
from flask import Flask, jsonify
import requests
import os
app = Flask(__name__)
ZEPPELIN_URL = os.environ.get('ZEPPELIN_URL', 'http://localhost:8080')
ZEPPELIN_USER = os.environ.get('ZEPPELIN_USER', '')
ZEPPELIN_PASS = os.environ.get('ZEPPELIN_PASS', '')
CRITICAL_INTERPRETERS = os.environ.get('CRITICAL_INTERPRETERS', 'spark,jdbc').split(',')
def zeppelin_session():
s = requests.Session()
if ZEPPELIN_USER:
s.post(f'{ZEPPELIN_URL}/api/login',
data={'userName': ZEPPELIN_USER, 'password': ZEPPELIN_PASS},
timeout=10)
return s
@app.route('/health/zeppelin')
def zeppelin_health():
try:
s = zeppelin_session()
r = s.get(f'{ZEPPELIN_URL}/api/version', timeout=5)
if r.status_code != 200:
return jsonify({'status': 'down', 'error': f'api_http_{r.status_code}'}), 503
except Exception as e:
return jsonify({'status': 'down', 'error': str(e)}), 503
return jsonify({'status': 'ok', 'version': r.json().get('body', {}).get('version', 'unknown')}), 200
@app.route('/health/zeppelin/interpreters')
def zeppelin_interpreters():
errors = []
warnings = []
try:
s = zeppelin_session()
r = s.get(f'{ZEPPELIN_URL}/api/interpreter/setting', timeout=10)
r.raise_for_status()
interpreters = r.json().get('body', [])
except Exception as e:
return jsonify({'status': 'down', 'error': str(e)}), 503
for interp in interpreters:
name = interp.get('name', 'unknown')
status = interp.get('status', 'unknown')
# Zeppelin interpreter statuses: READY, DOWNLOADING_DEPENDENCIES, ERROR
if name in CRITICAL_INTERPRETERS and status not in ('READY',):
errors.append(f'{name}_status_{status.lower()}')
if errors:
return jsonify({'status': 'down', 'errors': errors}), 503
if warnings:
return jsonify({'status': 'degraded', 'warnings': warnings}), 200
return jsonify({'status': 'ok', 'interpreter_count': len(interpreters)}), 200
@app.route('/health/zeppelin/notebooks')
def zeppelin_notebooks():
"""Check that notebooks are accessible and the API is responsive."""
try:
s = zeppelin_session()
r = s.get(f'{ZEPPELIN_URL}/api/notebook', timeout=10)
r.raise_for_status()
notebooks = r.json().get('body', [])
except Exception as e:
return jsonify({'status': 'down', 'error': str(e)}), 503
return jsonify({'status': 'ok', 'notebook_count': len(notebooks)}), 200
if __name__ == '__main__':
app.run(port=8098)
Node.js Health Sidecar
// zeppelin-health.js
const express = require('express');
const axios = require('axios');
const app = express();
const ZEPPELIN_URL = process.env.ZEPPELIN_URL || 'http://localhost:8080';
const CRITICAL_INTERPRETERS = (process.env.CRITICAL_INTERPRETERS || 'spark,jdbc').split(',');
app.get('/health/zeppelin', async (req, res) => {
try {
const r = await axios.get(`${ZEPPELIN_URL}/api/version`, { timeout: 5000 });
if (r.status !== 200) return res.status(503).json({ status: 'down', error: `http_${r.status}` });
return res.status(200).json({ status: 'ok', version: r.data?.body?.version });
} catch (e) {
return res.status(503).json({ status: 'down', error: e.message });
}
});
app.get('/health/zeppelin/interpreters', async (req, res) => {
let interpreters;
try {
const r = await axios.get(`${ZEPPELIN_URL}/api/interpreter/setting`, { timeout: 10000 });
interpreters = r.data?.body || [];
} catch (e) {
return res.status(503).json({ status: 'down', error: e.message });
}
const errors = [];
for (const interp of interpreters) {
const name = interp.name || 'unknown';
const status = interp.status || 'unknown';
if (CRITICAL_INTERPRETERS.includes(name) && status !== 'READY') {
errors.push(`${name}_status_${status.toLowerCase()}`);
}
}
if (errors.length) return res.status(503).json({ status: 'down', errors });
return res.status(200).json({ status: 'ok', interpreter_count: interpreters.length });
});
app.listen(3014, () => console.log('zeppelin-health listening on 3014'));
Step 3: Configure Vigilmon HTTP Monitor for Zeppelin
- Log in to vigilmon.online and go to Monitors → New Monitor
- Choose HTTP / HTTPS
- Set the URL to
http://zeppelin.example.com:8080/api/version - Set the check interval to 1 minute
- Under Expected response, configure:
- Status code:
200 - Response body contains:
"status":"OK" - Response time threshold:
5000ms
- Status code:
- Under Alert channels, assign your data engineering Slack channel
- Save the monitor
Add a second monitor for the interpreter health sidecar:
- URL:
https://your-app.example.com/health/zeppelin/interpreters - Expected:
200, body contains"status":"ok" - Interval: 2 minutes
- Alert channel: data platform on-call channel
Add a notebook API monitor:
- URL:
https://your-app.example.com/health/zeppelin/notebooks - Expected:
200 - Interval: 5 minutes
- Alert channel: analytics team Slack channel
If you run multiple Zeppelin instances (dev, staging, production), create a separate monitor set for each and label them clearly.
Step 4: Heartbeat Monitoring for Scheduled Zeppelin Notebooks
Process availability checks tell you Zeppelin and its interpreters are running — but they don't confirm that your scheduled analytical notebooks are completing successfully. A notebook can start running, hang on a Spark job with no output, and never complete while Zeppelin reports everything as healthy.
Vigilmon heartbeat monitors detect these failures: the last paragraph of a scheduled notebook pings Vigilmon after it executes successfully.
Set Up the Heartbeat Monitor
- In Vigilmon, go to Monitors → New Monitor → Heartbeat
- Set the name:
zeppelin-daily-analytics-notebook - Set the expected interval: 24 hours (or your notebook schedule)
- Set the grace period: 2 hours
- Save — copy the heartbeat URL, e.g.
https://vigilmon.online/heartbeat/abc123xyz
Wire It Into a Zeppelin Notebook
Add a final paragraph to your scheduled notebook with the interpreter set to %sh (shell):
%sh
# Last paragraph: report successful completion to Vigilmon
VIGILMON_HB="${VIGILMON_HEARTBEAT_URL}"
curl -s --max-time 10 "${VIGILMON_HB}" > /dev/null && echo "Vigilmon heartbeat sent." || echo "WARNING: Heartbeat ping failed."
Or with the %python interpreter:
%python
import requests, os
VIGILMON_HB = os.environ.get('VIGILMON_HEARTBEAT_URL')
if not VIGILMON_HB:
print("WARNING: VIGILMON_HEARTBEAT_URL not set — heartbeat skipped.")
else:
try:
r = requests.get(VIGILMON_HB, timeout=10)
r.raise_for_status()
print(f"Vigilmon heartbeat sent. HTTP {r.status_code}")
except Exception as e:
print(f"ERROR sending heartbeat: {e}")
raise
Triggering Notebooks via Zeppelin REST API (External Scheduler)
If you trigger Zeppelin notebooks from an external scheduler (Airflow, cron, Jenkins), wire the heartbeat into the calling script:
# trigger_zeppelin_notebook.py — run a Zeppelin notebook via REST and ping heartbeat on success
import requests
import time
import os
ZEPPELIN_URL = os.environ['ZEPPELIN_URL']
NOTEBOOK_ID = os.environ['NOTEBOOK_ID']
VIGILMON_HB = os.environ['VIGILMON_HEARTBEAT_URL']
POLL_INTERVAL = 10 # seconds between status polls
MAX_WAIT = 3600 # 1 hour timeout
session = requests.Session()
# Run the notebook
r = session.post(f'{ZEPPELIN_URL}/api/notebook/job/{NOTEBOOK_ID}', timeout=15)
r.raise_for_status()
print(f"Notebook {NOTEBOOK_ID} execution started.")
# Poll until complete
elapsed = 0
while elapsed < MAX_WAIT:
time.sleep(POLL_INTERVAL)
elapsed += POLL_INTERVAL
status_r = session.get(f'{ZEPPELIN_URL}/api/notebook/job/{NOTEBOOK_ID}', timeout=15)
status_r.raise_for_status()
paragraphs = status_r.json().get('body', [])
all_done = all(p.get('status') in ('FINISHED', 'ABORT', 'ERROR') for p in paragraphs)
any_error = any(p.get('status') in ('ERROR', 'ABORT') for p in paragraphs)
if all_done:
if any_error:
raise RuntimeError(f"Notebook {NOTEBOOK_ID} completed with errors.")
break
else:
raise TimeoutError(f"Notebook {NOTEBOOK_ID} did not complete within {MAX_WAIT}s.")
# All paragraphs succeeded — send heartbeat
requests.get(VIGILMON_HB, timeout=10)
print("Vigilmon heartbeat sent.")
Step 5: Alert Routing for Zeppelin Failures
Configure alert routing in Vigilmon:
| Monitor | Alert Channel | Priority | |---|---|---| | Zeppelin API version endpoint | Slack + PagerDuty | P1 | | Interpreter health sidecar | Slack + PagerDuty | P1 | | Notebook API responsiveness | Slack | P2 | | Heartbeat: scheduled daily notebook | Slack + email | P1 | | Heartbeat: batch report notebook | Email | P2 |
Set response time thresholds for early warning:
- Alert at
3000msfor the Zeppelin REST API (slow responses precede OOM crashes from large interpreter heaps) - Alert at
10000msfor the interpreter health sidecar (slow interpreter listing signals a JVM GC pause in the Zeppelin server)
For production analytics pipelines where notebook completion drives downstream reports or dashboards, configure a tight grace period on heartbeats: a notebook that should run at 06:00 and takes 90 minutes should alert by 08:15, not hours later when business users notice missing data.
Summary
Zeppelin failures span server availability, interpreter process health, and end-to-end notebook execution. External monitoring catches all three:
| Monitor Type | What It Covers |
|---|---|
| HTTP monitor on /api/version | Zeppelin server liveness |
| HTTP monitor on interpreter health sidecar | Spark/JDBC/Python interpreter process state |
| HTTP monitor on notebook API | Notebook access and API responsiveness |
| Heartbeat monitor on notebook completion | Scheduled notebook execution success |
Get started free at vigilmon.online — your first Zeppelin availability monitor is running in under two minutes.