ADR 004: Deployment and Packaging Strategy¶
Status¶
Accepted
Context¶
The Jupyter Notebook Validator Operator must be packaged and deployed in a way that:
- Provides excellent user experience for installation and upgrades
- Integrates seamlessly with OpenShift's OperatorHub
- Supports both enterprise (OpenShift) and community (Kubernetes) users
- Enables automated lifecycle management (installation, upgrades, uninstallation)
- Follows cloud-native best practices
Current Requirements¶
- Primary Target: OpenShift 4.18+ with OperatorHub integration
- Secondary Target: Vanilla Kubernetes clusters
- User Personas:
- Platform Admins: Install and manage operator cluster-wide
- Namespace Users: Use operator in their namespaces
- CI/CD Systems: Automate operator deployment in pipelines
Technical Considerations¶
- Operator Lifecycle Manager (OLM): OpenShift's native operator management system
- Helm Charts: Popular packaging format for Kubernetes applications
- Raw Manifests: Simple YAML files for kubectl apply
- Container Registry: Where operator images are stored and distributed
Available Options¶
Option 1: OLM Bundle Only¶
- Pros:
- Native OpenShift integration
- Automatic upgrades via OLM
- Dependency management
- OperatorHub catalog listing
- Cons:
- OLM not standard on vanilla Kubernetes
- Requires OLM installation for K8s users
- More complex packaging
Option 2: Helm Chart Only¶
- Pros:
- Works on any Kubernetes cluster
- Popular and well-understood
- Templating and values customization
- Helm Hub listing
- Cons:
- Not native to OpenShift OperatorHub
- Manual upgrade management
- No automatic dependency resolution
Option 3: Raw Manifests Only¶
- Pros:
- Simplest approach
- No additional tools required
- Easy to understand and customize
- Cons:
- No templating or customization
- Manual upgrade process
- No dependency management
- Poor user experience
Option 4: Hybrid Approach (OLM + Helm + Manifests)¶
- Pros:
- Best experience for each platform
- Maximum flexibility for users
- Supports all deployment scenarios
- Cons:
- Must maintain multiple packaging formats
- Increased testing burden
- More documentation required
Decision¶
We will adopt a Hybrid Packaging Strategy with the following priority:
Primary: OLM Bundle (OpenShift OperatorHub)¶
- Target: OpenShift 4.18+ users
- Distribution: Red Hat Ecosystem Catalog, OperatorHub.io
- Format: OLM bundle with ClusterServiceVersion (CSV)
- Upgrades: Automatic via OLM subscription channels
Secondary: Helm Chart (Kubernetes Community)¶
- Target: Vanilla Kubernetes users, local development
- Distribution: Artifact Hub, GitHub Releases
- Format: Helm chart with customizable values
- Upgrades: Manual via
helm upgrade
Tertiary: Raw Manifests (Advanced Users)¶
- Target: CI/CD pipelines, GitOps workflows, advanced users
- Distribution: GitHub Releases, documentation
- Format: Kustomize-compatible YAML manifests
- Upgrades: Manual via
kubectl apply
Consequences¶
Positive¶
- Best-in-Class UX: Each platform gets optimal deployment experience
- Broad Adoption: Supports OpenShift, Kubernetes, and GitOps workflows
- Automatic Upgrades: OLM users get seamless upgrades
- Flexibility: Users can choose deployment method that fits their workflow
- Discoverability: Listed in OperatorHub and Artifact Hub
Negative¶
- Maintenance Burden: Must maintain three packaging formats
- Testing Complexity: Must test all deployment methods
- Documentation Overhead: Separate guides for each method
- Release Coordination: Must synchronize releases across formats
Neutral¶
- Tooling: Operator SDK supports generating all formats
- CI/CD: Can automate packaging for all formats
Implementation Notes¶
OLM Bundle Structure¶
bundle/
├── manifests/
│ ├── jupyter-notebook-validator-operator.clusterserviceversion.yaml
│ ├── mlops.dev_notebookvalidationjobs.yaml # CRD
│ └── operator_rbac.yaml
├── metadata/
│ └── annotations.yaml
├── tests/
│ └── scorecard/
└── Dockerfile # Bundle image
ClusterServiceVersion (CSV) Example¶
apiVersion: operators.coreos.com/v1alpha1
kind: ClusterServiceVersion
metadata:
name: jupyter-notebook-validator-operator.v0.1.0
namespace: placeholder
spec:
displayName: Jupyter Notebook Validator Operator
description: |
Kubernetes-native operator for validating Jupyter Notebooks in MLOps workflows.
version: 0.1.0
maturity: alpha
provider:
name: Your Organization
maintainers:
- name: Platform Team
email: platform@example.com
links:
- name: Documentation
url: https://github.com/your-org/jupyter-notebook-validator-operator
icon:
- base64data: <base64-encoded-icon>
mediatype: image/png
keywords:
- jupyter
- notebook
- validation
- mlops
installModes:
- type: OwnNamespace
supported: true
- type: SingleNamespace
supported: true
- type: MultiNamespace
supported: false
- type: AllNamespaces
supported: true
install:
strategy: deployment
spec:
permissions: []
clusterPermissions:
- serviceAccountName: jupyter-notebook-validator-operator
rules:
- apiGroups: ["mlops.dev"]
resources: ["notebookvalidationjobs"]
verbs: ["*"]
- apiGroups: [""]
resources: ["pods", "configmaps", "secrets"]
verbs: ["get", "list", "watch", "create", "update", "delete"]
deployments:
- name: jupyter-notebook-validator-operator
spec:
replicas: 1
selector:
matchLabels:
name: jupyter-notebook-validator-operator
template:
metadata:
labels:
name: jupyter-notebook-validator-operator
spec:
serviceAccountName: jupyter-notebook-validator-operator
containers:
- name: operator
image: quay.io/your-org/jupyter-notebook-validator-operator:v0.1.0
command:
- /manager
env:
- name: WATCH_NAMESPACE
valueFrom:
fieldRef:
fieldPath: metadata.annotations['olm.targetNamespaces']
customresourcedefinitions:
owned:
- name: notebookvalidationjobs.mlops.dev
version: v1alpha1
kind: NotebookValidationJob
displayName: Notebook Validation Job
description: Defines a notebook validation job
Helm Chart Structure¶
charts/jupyter-notebook-validator-operator/
├── Chart.yaml
├── values.yaml
├── templates/
│ ├── deployment.yaml
│ ├── service.yaml
│ ├── serviceaccount.yaml
│ ├── role.yaml
│ ├── rolebinding.yaml
│ ├── crd.yaml
│ └── NOTES.txt
└── README.md
Helm Values Example¶
# values.yaml
replicaCount: 1
image:
repository: quay.io/your-org/jupyter-notebook-validator-operator
tag: v0.1.0
pullPolicy: IfNotPresent
serviceAccount:
create: true
name: jupyter-notebook-validator-operator
rbac:
create: true
resources:
limits:
cpu: 200m
memory: 256Mi
requests:
cpu: 100m
memory: 128Mi
nodeSelector: {}
tolerations: []
affinity: {}
Kustomize Structure¶
config/
├── default/
│ ├── kustomization.yaml
│ └── manager_auth_proxy_patch.yaml
├── manager/
│ ├── kustomization.yaml
│ └── manager.yaml
├── rbac/
│ ├── kustomization.yaml
│ ├── role.yaml
│ ├── role_binding.yaml
│ └── service_account.yaml
├── crd/
│ ├── kustomization.yaml
│ └── bases/
│ └── mlops.dev_notebookvalidationjobs.yaml
└── samples/
└── mlops_v1alpha1_notebookvalidationjob.yaml
Distribution Channels¶
OLM Channels¶
# bundle/metadata/annotations.yaml
annotations:
operators.operatorframework.io.bundle.channels.v1: alpha,beta,stable
operators.operatorframework.io.bundle.channel.default.v1: stable
- alpha: Experimental features, may have breaking changes
- beta: Stable API, release candidates
- stable: Production-ready, long-term support
CI/CD Pipeline¶
# .github/workflows/release.yml
name: Release
on:
push:
tags:
- 'v*'
jobs:
build-and-publish:
runs-on: ubuntu-latest
steps:
- name: Build operator image
run: make docker-build docker-push IMG=${{ env.IMAGE }}
- name: Build OLM bundle
run: make bundle bundle-build bundle-push
- name: Package Helm chart
run: helm package charts/jupyter-notebook-validator-operator
- name: Publish to Artifact Hub
run: helm push jupyter-notebook-validator-operator-*.tgz oci://registry.example.com/charts
- name: Generate raw manifests
run: kustomize build config/default > release/install.yaml
- name: Create GitHub Release
uses: actions/create-release@v1
with:
files: |
release/install.yaml
jupyter-notebook-validator-operator-*.tgz
Installation Documentation¶
OpenShift (OLM)¶
# Install via OperatorHub UI or CLI
oc create -f - <<EOF
apiVersion: operators.coreos.com/v1alpha1
kind: Subscription
metadata:
name: jupyter-notebook-validator-operator
namespace: openshift-operators
spec:
channel: stable
name: jupyter-notebook-validator-operator
source: community-operators
sourceNamespace: openshift-marketplace
EOF
Kubernetes (Helm)¶
# Add Helm repository
helm repo add jupyter-validator https://your-org.github.io/jupyter-notebook-validator-operator
# Install operator
helm install jupyter-validator jupyter-validator/jupyter-notebook-validator-operator \
--namespace jupyter-validator-system \
--create-namespace
Kubernetes (Kustomize)¶
# Install via kustomize
kubectl apply -k github.com/your-org/jupyter-notebook-validator-operator/config/default?ref=v0.1.0
References¶
Related ADRs¶
- ADR 001: Operator Framework and SDK Version
- ADR 002: Platform Version Support Strategy
- ADR 008: CI/CD Pipeline Integration
Revision History¶
| Date | Author | Description |
|---|---|---|
| 2025-11-07 | Team | Initial decision |