Polyglot Runbooks: Managing Mixed Technology Stacks
Real infrastructure isn’t monolingual. You’ve got Bash scripts, Python tools, SQL queries, and API calls—often in the same procedure. Your runbooks should handle all of them.
This guide covers building polyglot runbooks for mixed tech stacks. For tool comparisons, see our interactive runbook tools comparison.
The Polyglot Reality
A typical operational task might involve:
- Shell commands to check Kubernetes
- SQL query to verify database state
- Python script for data transformation
- cURL for API calls
- More shell to apply fixes
Each technology has its place. Your runbook needs to accommodate all of them.
Polyglot Runbook Structure
Using Multiple Languages in Markdown
Markdown’s fenced code blocks support language hints:
# Database Migration Runbook
## 1. Check current schema version
```sql
SELECT version FROM schema_migrations ORDER BY version DESC LIMIT 1;
```
## 2. Backup current data
```bash
pg_dump -h db.internal production > backup-$(date +%Y%m%d).sql
```
## 3. Run migration script
```python
#!/usr/bin/env python3
from migration_runner import run_migrations
run_migrations(target_version='2024.01')
```
## 4. Verify migration
```sql
SELECT version FROM schema_migrations ORDER BY version DESC LIMIT 1;
```
## 5. Smoke test API
```bash
curl -s http://api.internal/health | jq '.database_version'
```
Language-Specific Patterns
Bash: The Glue Language
Bash connects everything. Use it for:
- Running CLI tools (kubectl, aws, gcloud)
- Piping between commands
- Quick checks and verifications
# Check and respond
if kubectl get pods -l app=api | grep -q CrashLoopBackOff; then
echo "Pod crash detected"
kubectl describe pods -l app=api | tail -20
else
echo "Pods healthy"
fi
SQL: Direct Database Operations
For database checks and modifications:
-- Check connection count
SELECT count(*), state
FROM pg_stat_activity
GROUP BY state
ORDER BY count DESC;
-- Check long-running queries
SELECT pid, now() - query_start as duration, query
FROM pg_stat_activity
WHERE state = 'active'
AND query_start < now() - interval '1 minute'
ORDER BY duration DESC;
Python: Complex Logic
When Bash gets unwieldy:
#!/usr/bin/env python3
"""Analyze logs for error patterns."""
import json
import subprocess
from collections import Counter
# Get logs
result = subprocess.run(
['kubectl', 'logs', '-l', 'app=api', '--tail=1000'],
capture_output=True, text=True
)
# Parse and count errors
errors = Counter()
for line in result.stdout.split('\n'):
if 'ERROR' in line:
try:
log = json.loads(line)
errors[log.get('error_type', 'unknown')] += 1
except json.JSONDecodeError:
errors['parse_error'] += 1
# Report
for error_type, count in errors.most_common(10):
print(f"{error_type}: {count}")
cURL/HTTP: API Operations
For REST API interactions:
# Health check with detailed timing
curl -w "\nDNS: %{time_namelookup}s\nConnect: %{time_connect}s\nTTFB: %{time_starttransfer}s\nTotal: %{time_total}s\n" \
-s http://api.internal/health | jq '.'
# POST with JSON body
curl -X POST http://api.internal/admin/cache/clear \
-H "Content-Type: application/json" \
-H "Authorization: Bearer $API_TOKEN" \
-d '{"scope": "all"}'
JQ: JSON Processing
For parsing JSON output:
# Extract specific fields
kubectl get pods -o json | jq '.items[] | {name: .metadata.name, status: .status.phase}'
# Filter and transform
curl -s http://api.internal/metrics | jq '.services[] | select(.healthy == false) | .name'
# Complex aggregation
kubectl get events -o json | jq '[.items[] | select(.type == "Warning")] | group_by(.reason) | map({reason: .[0].reason, count: length})'
Connecting Different Languages
Passing Data Between Steps
Sometimes you need output from one language as input to another.
Bash to SQL
# Get user ID from API
USER_ID=$(curl -s http://api.internal/users/search?email=user@example.com | jq -r '.id')
# Use in SQL query
psql -h db.internal -c "SELECT * FROM orders WHERE user_id = '$USER_ID';"
SQL to Bash
# Get value from database
COUNT=$(psql -h db.internal -t -c "SELECT count(*) FROM pending_jobs;")
# Use in conditional
if [ "$COUNT" -gt 100 ]; then
echo "Job queue backing up: $COUNT pending"
kubectl scale deployment/worker --replicas=5
fi
Python to Bash
# Run Python analysis, use result
THRESHOLD=$(python3 -c "
import statistics
data = [10, 20, 15, 25, 30]
print(int(statistics.mean(data) * 1.5))
")
echo "Using threshold: $THRESHOLD"
Runbook for a Real Mixed Stack
Here’s a complete polyglot runbook for a typical microservices environment:
# API Performance Investigation
## Overview
Use this runbook when API latency alerts fire.
## 1. Quick Status Check
### API Pods
```bash
kubectl get pods -l app=api -o wide
kubectl top pods -l app=api
```
### Error Rate
```bash
curl -s http://prometheus:9090/api/v1/query?query=rate(http_errors_total[5m]) | jq '.data.result[0].value[1]'
```
## 2. Database Investigation
### Connection Count
```sql
SELECT count(*), state FROM pg_stat_activity GROUP BY state;
```
### Slow Queries
```sql
SELECT pid, now() - query_start as duration, substring(query, 1, 100)
FROM pg_stat_activity
WHERE state = 'active' AND query_start < now() - interval '5 seconds'
ORDER BY duration DESC
LIMIT 10;
```
## 3. Log Analysis
### Recent Errors (Bash)
```bash
kubectl logs -l app=api --tail=500 | grep -i error | tail -20
```
### Error Distribution (Python)
```python
#!/usr/bin/env python3
import subprocess
import json
from collections import Counter
result = subprocess.run(
['kubectl', 'logs', '-l', 'app=api', '--tail=1000'],
capture_output=True, text=True
)
errors = Counter()
for line in result.stdout.split('\n'):
if '"level":"error"' in line:
try:
log = json.loads(line)
errors[log.get('msg', 'unknown')[:50]] += 1
except:
pass
for msg, count in errors.most_common(5):
print(f"{count:4d} | {msg}")
```
## 4. External Dependencies
### Check Third-Party APIs
```bash
for endpoint in payment.api.com inventory.api.com shipping.api.com; do
echo -n "$endpoint: "
curl -s -o /dev/null -w "%{http_code} %{time_total}s" "https://$endpoint/health"
echo
done
```
## 5. Remediation Options
### If Database Slow
```sql
-- Kill long-running query
SELECT pg_terminate_backend(PID);
```
### If External Dependency Down
```bash
# Enable circuit breaker
kubectl set env deployment/api CIRCUIT_BREAKER_ENABLED=true
```
### If Resource Constrained
```bash
kubectl scale deployment/api --replicas=5
```
Tool Support for Polyglot Runbooks
Different tools handle multiple languages differently:
| Tool | Bash | Python | SQL | HTTP |
|---|---|---|---|---|
| Jupyter | Via subprocess | Native | Via library | Via requests |
| Org-mode | Native | Native | Via babel | Via curl |
| Runme | Native | Via shebang | Via psql | Via curl |
| Stew | Native | Via shebang | Via clients | Native |
Stew’s Polyglot Approach
Stew handles mixed technology stacks naturally:
- Bash: Run directly
- Python/Ruby/etc: Via shebang (
#!/usr/bin/env python3) - SQL: Via database CLIs (
psql,mysql) - HTTP: Built-in REST support
Your runbook reflects how you actually work—across languages and tools.
# Works in Stew as-is
```bash
kubectl get pods
```
```python
#!/usr/bin/env python3
print("Analysis complete")
```
```bash
psql -c "SELECT count(*) FROM users;"
```
Join the waitlist and write runbooks that match your stack.