tutorial

How to Monitor Apache Zeppelin Notebook Availability and Interpreter Health with Vigilmon

Apache Zeppelin interpreter crashes, stalled spark contexts, and unresponsive notebooks silently block data analysts from running queries. Learn how to monitor Zeppelin availability, interpreter health, and notebook execution heartbeats with Vigilmon.

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

  1. Log in to vigilmon.online and go to Monitors → New Monitor
  2. Choose HTTP / HTTPS
  3. Set the URL to http://zeppelin.example.com:8080/api/version
  4. Set the check interval to 1 minute
  5. Under Expected response, configure:
    • Status code: 200
    • Response body contains: "status":"OK"
    • Response time threshold: 5000ms
  6. Under Alert channels, assign your data engineering Slack channel
  7. 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

  1. In Vigilmon, go to Monitors → New Monitor → Heartbeat
  2. Set the name: zeppelin-daily-analytics-notebook
  3. Set the expected interval: 24 hours (or your notebook schedule)
  4. Set the grace period: 2 hours
  5. 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 3000ms for the Zeppelin REST API (slow responses precede OOM crashes from large interpreter heaps)
  • Alert at 10000ms for 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.

Monitor your app with Vigilmon

Free plan — 5 monitors, no credit card required. Up and running in 60 seconds.

Start free →