Skip to content

Commit 346496e

Browse files
committed
implement K8sDatabaseManager
1 parent 8fa0d24 commit 346496e

File tree

5 files changed

+405
-12
lines changed

5 files changed

+405
-12
lines changed

CLAUDE.md

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ This document contains development notes, architecture decisions, and lessons le
66

77
## Project Structure
88

9-
- `src/jupyter_scheduler_k8s/` - Main Python package with K8sExecutionManager
9+
- `src/jupyter_scheduler_k8s/` - Main Python package with K8sExecutionManager and K8sDatabaseManager
1010
- `image/` - Docker image with Pixi-based Python environment and notebook executor
1111
- `local-dev/` - Local development configuration (Kind cluster)
1212
- `Makefile` - Build and development automation with auto-detection
@@ -29,7 +29,7 @@ This document contains development notes, architecture decisions, and lessons le
2929

3030
## Key Design Principles
3131

32-
1. **Minimal Extension**: Only override ExecutionManager, reuse everything else from jupyter-scheduler
32+
1. **Minimal Extension**: Only override ExecutionManager and DatabaseManager, reuse everything else from jupyter-scheduler
3333
2. **Container Simplicity**: Container just executes notebooks, unaware of K8s or scheduler
3434
3. **No Circular Dependencies**: Container doesn't depend on jupyter-scheduler package
3535
4. **Staging Compatibility**: Work with jupyter-scheduler's existing file staging mechanism
@@ -56,6 +56,13 @@ This document contains development notes, architecture decisions, and lessons le
5656

5757
### Phase 2: K8s Backend Implementation ✅
5858

59+
#### K8s Database Backend (Production-Ready)
60+
- **Storage**: K8s Jobs as database records using labels and annotations
61+
- **SQLAlchemy Interface**: K8sSession and K8sQuery mimic SQLAlchemy patterns
62+
- **DatabaseManager Plugin**: Clean integration via jupyter-scheduler's Type system
63+
- **Zero SQL Dependencies**: Complete replacement for SQLite/PostgreSQL
64+
- **Usage**: `jupyter lab --SchedulerApp.db_url="k8s://default" --SchedulerApp.database_manager_class="jupyter_scheduler_k8s.K8sDatabaseManager"`
65+
5966
### Pre-Populated PVC Architecture (Production-Ready)
6067
- **Storage**: PVC (PersistentVolumeClaim) for production-ready file handling
6168
- Works with all standard K8s clusters (Kind, minikube, EKS, GKE, AKS)
@@ -187,11 +194,12 @@ jupyter lab --Scheduler.execution_manager_class="jupyter_scheduler_k8s.K8sExecut
187194

188195
## Current Implementation Status
189196

190-
### Latest Architecture: S3 Storage (Production Ready ✅)
191-
1. **Upload inputs** - AWS CLI sync to S3 bucket
192-
2. **Container execution** - Job downloads from S3, executes notebook, uploads outputs
193-
3. **Download outputs** - AWS CLI sync from S3 to staging directory
194-
4. **Durability** - Files survive cluster failures, can be retrieved later
197+
### Latest Architecture: K8s Database + S3 Storage (Production Ready ✅)
198+
1. **Database operations** - Job metadata stored in K8s Jobs using labels/annotations
199+
2. **Upload inputs** - AWS CLI sync to S3 bucket
200+
3. **Container execution** - Job downloads from S3, executes notebook, uploads outputs
201+
4. **Download outputs** - AWS CLI sync from S3 to staging directory
202+
5. **Durability** - Both metadata and files survive cluster failures
195203

196204
**Key Implementation Details:**
197205
- **AWS credentials passed at runtime**: K8sExecutionManager passes host AWS credentials to containers via environment variables

README.md

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,8 @@ Kubernetes backend for [jupyter-scheduler](https://github.com/jupyter-server/jup
1010
4. Results uploaded back to S3, then downloaded to JupyterLab and accessible through the UI
1111

1212
**Key features:**
13-
- **S3 storage** - files survive Kubernetes cluster or Jupyter Server failures. Supports any S3-compatible storage like AWS S3, MinIO, GCS with S3 API, and so on
13+
- **K8s database** - store job metadata in Kubernetes Jobs (zero SQL dependencies)
14+
- **S3 storage** - files survive Kubernetes cluster or Jupyter Server failures
1415
- Parameter injection for notebook customization
1516
- Multiple output formats (HTML, PDF, etc.)
1617
- Works with any Kubernetes cluster (Kind, minikube, EKS, GKE, AKS)
@@ -146,9 +147,12 @@ export S3_BUCKET="<your-test-bucket>"
146147
export AWS_ACCESS_KEY_ID="<your-access-key>"
147148
export AWS_SECRET_ACCESS_KEY="<your-secret-key>"
148149

149-
# Launch and test through JupyterLab UI
150+
# Launch with K8s execution only
150151
jupyter lab --Scheduler.execution_manager_class="jupyter_scheduler_k8s.K8sExecutionManager"
151152

153+
# Launch with K8s database + K8s execution
154+
jupyter lab --SchedulerApp.db_url="k8s://default" --SchedulerApp.database_manager_class="jupyter_scheduler_k8s.K8sDatabaseManager" --Scheduler.execution_manager_class="jupyter_scheduler_k8s.K8sExecutionManager"
155+
152156
# Cleanup
153157
make clean
154158
```
@@ -197,12 +201,13 @@ make clean # Remove cluster and cleanup
197201
## Implementation Status
198202

199203
### Working Features ✅
200-
- Custom `K8sExecutionManager` that extends `jupyter-scheduler.ExecutionManager` and runs notebook jobs in Kubernetes pods
204+
- **K8s Database**: `K8sDatabaseManager` stores job metadata in K8s Jobs (zero SQL dependencies)
205+
- **K8s Execution**: `K8sExecutionManager` runs notebook jobs in Kubernetes pods
201206
- Parameter injection and multiple output formats
202207
- File handling for any notebook size with proven S3 operations
203208
- Configurable CPU/memory limits
204209
- Event-driven job monitoring with Watch API
205-
- S3 storage: Files persist beyond kubernetes cluster or jupyter server failures using AWS CLI for reliable transfers
210+
- S3 storage: Files persist beyond kubernetes cluster or jupyter server failures
206211

207212
### Planned 🚧
208213
- GPU resource configuration for k8s jobs from UI

src/jupyter_scheduler_k8s/__init__.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
"""Kubernetes backend for jupyter-scheduler."""
22

33
from .executors import K8sExecutionManager
4+
from .database_manager import K8sDatabaseManager
45

56
__version__ = "0.1.0"
6-
__all__ = ["K8sExecutionManager"]
7+
__all__ = ["K8sExecutionManager", "K8sDatabaseManager"]
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
from kubernetes import client, config
2+
from jupyter_scheduler.managers import DatabaseManager
3+
4+
from .k8s_orm import K8sSession
5+
6+
7+
class K8sDatabaseManager(DatabaseManager):
8+
"""Database manager that uses Kubernetes Jobs for storage."""
9+
10+
def create_session(self, db_url: str):
11+
"""Create K8s session factory."""
12+
if not db_url.startswith("k8s://"):
13+
raise ValueError(f"K8sDatabaseManager only supports k8s:// URLs, got: {db_url}")
14+
15+
namespace = db_url[6:] or "default"
16+
17+
def session_factory():
18+
return K8sSession(namespace=namespace)
19+
return session_factory
20+
21+
def create_tables(self, db_url: str, drop_tables: bool = False):
22+
"""Ensure K8s namespace exists."""
23+
if not db_url.startswith("k8s://"):
24+
return
25+
26+
namespace = db_url[6:] or "default"
27+
28+
try:
29+
config.load_incluster_config()
30+
except config.ConfigException:
31+
config.load_kube_config()
32+
33+
v1 = client.CoreV1Api()
34+
35+
try:
36+
v1.read_namespace(name=namespace)
37+
except client.ApiException as e:
38+
if e.status == 404:
39+
namespace_body = client.V1Namespace(
40+
metadata=client.V1ObjectMeta(name=namespace)
41+
)
42+
v1.create_namespace(body=namespace_body)

0 commit comments

Comments
 (0)