Distributed PDF Structure Extraction on OpenShift (Red Hat AI)
A production-ready system that combines Docling (for PDF understanding) with Apache Spark (for distributed computing) to process thousands of documents in parallel on Red Hat OpenShift Service on AWS (ROSA).
Note: This guide prioritizes the oc command (OpenShift CLI), but kubectl can be used interchangeably.
The docling-spark application serves as a reference architecture for:
- Scale: processing massive document archives using distributed executors.
- Intelligence: extracting deep structure (tables, layout) using the
doclinglibrary. - Security: running safely in restricted enterprise environments (OpenShift
restricted-v2).
- Spark Operator (running on OpenShift) launches a Driver Pod.
- Driver distributes Docling code to Executor Pods.
- Executors process PDFs in parallel (OCR, Layout Analysis, Table Extraction).
- Driver collects results into a single
results.jsonlfile. - Retrieve the results with a single command.
👉 Read the full Architecture & Concepts Guide (Conceptdocs.md)
- OpenShift Cluster.
- Cluster Admin Setup: The Kubeflow Spark Operator must be installed by an admin first (see Installation Guide).
ocCLI configured.- Docker (for building images).
- Quay.io account (or any container registry).
Pre-requisite: This section requires Cluster Admin privileges. You must install the operator once so that users can submit
SparkApplicationCRDs.
# Log in to your ROSA cluster
oc login
# Install Helm (if not already installed)
brew install helm
# Add the Spark Operator Helm repo
helm repo add spark-operator https://kubeflow.github.io/spark-operator
helm repo updateWe need to override some default Helm values to ensure:
- Security: The operator is compatible with OpenShift's
restricted-v2SCC (letting OpenShift assign User IDs automatically). - Functionality: The operator watches all namespaces for SparkApplications (crucial for the
docling-sparknamespace to work).
Create a spark-operator-values.yaml file (or use the one in the repo):
# spark-operator-values.yaml
controller:
podSecurityContext:
fsGroup: null # Override upstream default to let OpenShift SCC assign
webhook:
enable: true
podSecurityContext:
fsGroup: null # Override upstream default to let OpenShift SCC assign
spark:
jobNamespaces: [] # Watch all namespacesImportant: The
spark.jobNamespaces: []setting tells the operator to watch all namespaces. Without this, the operator won't detect jobs submitted to thedocling-sparknamespace.Production Tip: For better encapsulation and control over permissions, specify a list of allowed namespaces instead of watching all:
spark: jobNamespaces: - spark-jobs - analyticsThis limits where Spark jobs can run and provides better security isolation.
Zsh Users: Using this
values.yamlfile is the recommended way to install. It avoids common shell quoting errors (likezsh: no matches found: spark.jobNamespaces[0]="") that occur when passing complex flags directly to Helm.
# Create the namespace
oc new-project kubeflow-spark-operator
# Configure SCC permissions (must be done after namespace creation)
oc apply -f k8s/spark-scc-rolebindings.yaml
# Install via Helm
helm install spark-operator spark-operator/spark-operator \
--namespace kubeflow-spark-operator \
-f spark-operator-values.yaml \
--version 2.2.1Version Note: We use v2.2.1 which includes Spark 3.5.5. Newer versions (v2.3.x+) ship with Spark 4.x which may have breaking changes. See the version matrix for details.
Note: The
k8s/spark-scc-rolebindings.yamlbinds the operator's ServiceAccounts to therestricted-v2SCC, allowing the Spark Operator and its webhooks to run correctly without root privileges. If you use a different namespace, update the namespace references in this file.
oc get pods -n kubeflow-spark-operator
# You should see spark-operator-controller and spark-operator-webhook runningYou have two options depending on your use case:
The repository is pre-configured to use quay.io/rishasin/docling-spark:latest, which contains sample PDFs from the assets/ directory. This allows you to skip the build step entirely and deploy immediately.
Proceed directly to Step 2 (Deploy to ROSA).
Best for: Processing your own documents, customizing the application, or production deployments.
Why you need this: The assets/ directory is copied into the Docker image at build time (see Dockerfile line 47). To process different PDFs, you must rebuild the image with your files.
Steps:
-
Create the assets directory and add your PDFs:
mkdir -p assets cp /path/to/your/pdfs/*.pdf assets/ -
Build the image for ROSA (Linux AMD64):
docker buildx build --platform linux/amd64 \ -t quay.io/YOUR_USERNAME/docling-spark:latest \ --push .Note: The
--platform linux/amd64flag ensures the image runs on ROSA nodes, even if you're building on Apple Silicon (M1/M2/M3 Mac). -
Update the Kubernetes manifest:
Edit
k8s/docling-spark-app.yamland change the image reference:image: quay.io/YOUR_USERNAME/docling-spark:latest # ← Update this line
-
Proceed to Step 2 (Deploy to ROSA).
This script handles Namespace, RBAC, and Job Submission.
chmod +x k8s/deploy.sh
./k8s/deploy.shWait for the job to finish (check logs). As soon as you see this is your terminal
🎉 ALL DONE!
✅ Enhanced processing complete!
😴 Sleeping for 60 minutes to allow file download...
Run: oc cp docling-spark-job-driver:/app/output/results.jsonl ./output/results.jsonl -n docling-spark
Open another terminal and run the below command to save the results.
# Copy results to your local machine
oc cp docling-spark-job-driver:/app/output/results.jsonl ./output/results.jsonl -n docling-spark
# View them
head -n 5 output/results.jsonloc delete sparkapplication docling-spark-job -n docling-sparkscripts/: Python source code.docling_module/: The PDF processing logic.run_spark_job.py: The Spark orchestration script.
k8s/: Kubernetes manifests.docling-spark-app.yaml: The Spark Job definition.deploy.sh: Deployment automation script.
assets/: Place your input PDFs here.requirements.txt: Dependencies for local development (includes PySpark & macOS support).requirements-docker.txt: Dependencies optimized for the Docker container (Linux only).Conceptdocs.md: Deep dive into architecture, decisions, and future roadmap.SparkOperatorOnOpenShift.md: Detailed guide on Spark Operator architecture, installation, and debugging on OpenShift.
See Conceptdocs.md for the detailed roadmap.
