Skip to content

Commit c0dc264

Browse files
Appendix: Running on OpenShift (CRC) locally + full platform tier (#9)
* docs(plan): OpenShift/CRC appendix plan (mirror modernization appendix) * feat(openshift): data-driven Helm chart + CRC assets for the capstone WIP checkpoint (relay step 1). Mirrors modernization openshift/helm/mea: data-driven app-deployment, Postgres/Kafka StatefulSets under datamesh-infra SA (nonroot-v2), app pods drop runAsUser, Routes, internal-registry images. * docs(appendix): draft 'Running on OpenShift (CRC) locally' + 2 diagrams WIP checkpoint (relay step 2). ~3.3k words; 5 LIVE-EVIDENCE placeholders to be filled from the live CRC run. * fix(openshift): build-and-push printf -- for leading-dashes line Live-run fix: final hint line started with --mirror-infra, which printf parsed as an option (exit 2 after all images pushed successfully). * test(openshift): live CRC verification evidence (all 10 pods 1/1, E2E green) WIP checkpoint (relay steps 3-5). Order placed -> read back through the gateway Route with inventory gRPC stock + Kafka order.placed consumed by notification (Avro/Apicurio). SCC: apps restricted-v2 (UID 1000650000), infra nonroot-v2. * docs(appendix): fold live CRC evidence into chapter; DRA-002..007 Fills the 5 LIVE-EVIDENCE placeholders with the verified run (pods, SCC/UID, health, cross-service GraphQL, Kafka path, what-broke, status footer). Chapter ~4.5k words. Adds decision-log entries + render_with_liquid:false on the plan. * docs(plan): phase 2 full platform tier on OpenShift (approved, OM best-effort) * feat(openshift): platform tier manifests (mesh, obs, keda, prefect, om) WIP checkpoint (phase 2 authoring). platform/ tree: OSSM3 Istio+IstioCNI, canary VS/DR, otel-lgtm (anyuid SA + startup grace after live fix), CMA KedaController + Kafka-lag ScaledObject, Prefect server/worker, OpenMetadata (OpenSearch, best-effort); mesh.enabled/observability.otlp.enabled chart flags (default off); install-platform.sh; DRA-008..014. * fix(openshift): otel-lgtm on anyuid (root, no seccomp) — live CRC fix anyuid SCC rejects seccompProfile/caps; setting them silently dropped the pod back to restricted-v2 (random uid) and Grafana couldn't write /data. Run as uid 0 under the lgtm SA (anyuid) with no seccomp/caps + startup grace probe. * fix(openshift): gateway PERMISSIVE PA so OpenShift Route works under STRICT mesh Namespace STRICT 502'd the non-mesh router -> gateway; scope PERMISSIVE to the edge workload, internal service-to-service stays STRICT (live CRC fix). * feat(openshift): mesh + observability live (canary 90/10, STRICT mTLS, traces) routing.yaml -> mesh-internal VS (OSSM3 has no default ingress gw). Evidence: sidecars 2/2, PeerAuth STRICT + gateway PERMISSIVE edge, canary v1=26/v2=4, Grafana up, Tempo per-service traces (cross-service stitching = documented limit). * test(openshift): KEDA autoscaling verified (scale-to-zero + 0->1 on Kafka lag) * test(openshift): Prefect verified (server+worker Postgres-backed, flow COMPLETED) * test(openshift): OpenMetadata best-effort — OpenSearch green, server blocked by Docker Hub rate limit OpenSearch SCC gate solved (SA openmetadata-opensearch/anyuid, fsGroup+uid ok), cluster green; DB/secrets in place. OM server image blocked by external Docker Hub unauthenticated pull-rate limit (host + VM). om-app-values.yaml repointed at internal-registry mirror for when a docker login is available. * docs(appendix): fold full platform tier into chapter (4 verified + OM best-effort) 'The full platform tier, live' section (mesh/KEDA/observability/Prefect verified, OpenMetadata partial); footer split into live-core + platform-tier; prereqs 32/14/100; DRA-015 (gateway PERMISSIVE under STRICT) + DRA-016 (otel-lgtm anyuid/no-seccomp). * docs(plan): backlog — finish OpenMetadata (Docker Hub login + image mirror) next CRC session
1 parent 580b16a commit c0dc264

62 files changed

Lines changed: 8390 additions & 1 deletion

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

‎_docs/11-running-on-openshift-crc.md‎

Lines changed: 783 additions & 0 deletions
Large diffs are not rendered by default.

‎_plans/decisions.md‎

Lines changed: 687 additions & 1 deletion
Large diffs are not rendered by default.
Lines changed: 136 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,136 @@
1+
---
2+
title: "Plan — OpenShift/CRC appendix"
3+
render_with_liquid: false
4+
---
5+
6+
# Plan — Appendix chapter "Running on OpenShift (CRC) locally" + runnable CRC assets
7+
8+
Status: APPROVED by user 2026-10-07. Built via lgtm-relay (Opus plan → Sonnet build → Opus gate).
9+
Branch: `feat/openshift-crc-appendix`.
10+
11+
## Goal
12+
Add an appendix chapter to the datamesh-reference-arch-python tutorial teaching how to run the
13+
existing capstone (today: minikube via helm) on a local **CRC / OpenShift Local** cluster,
14+
adapting to OpenShift idioms, and **LIVE-VERIFIED** on the local CRC instance. Modeled on the
15+
modernizing-enterprise-applications Part 9 deployment chapters (real runnable assets, applied
16+
live, documenting "dry-run passed / live apply broke" learnings).
17+
18+
## Approach (REVISED 2026-10-07 — mirror the modernization OpenShift appendix)
19+
User direction: "use the modernization project as an example" / "we just did this with the
20+
modernization project." The modernization repo's PR #16 shipped a verified-on-CRC appendix:
21+
`_docs/33-appendix-openshift.md` + a dedicated **`openshift/helm/mea/`** chart (data-driven:
22+
ONE `app-deployment.yaml` ranging a `services:` map in values.yaml), **plain Postgres + Kafka
23+
StatefulSets (NO operators)**, a `mea-infra` ServiceAccount bound to the `nonroot-v2` SCC for the
24+
infra pods, app pods that **drop `runAsUser`** (restricted-v2 assigns a high UID), **Routes**
25+
(edge/Redirect TLS), images via the **internal registry**, plus `openshift/gitops/application.yaml`
26+
and `openshift/evidence/verification.txt`.
27+
28+
**This SUPERSEDES the original "reuse umbrella chart + values-openshift.yaml + OLM operators"
29+
approach.** Instead: build a dedicated **`openshift/helm/datamesh/` chart** mirroring the
30+
modernization structure, adapted to the 7 Python/FastAPI services. Postgres + Kafka as plain
31+
StatefulSets (no CNPG/Strimzi/AMQ-Streams operator installs — avoids OLM complexity on CRC and
32+
matches what the user already did). Apicurio kept in the live core (contracts are central to the
33+
datamesh; light image). Rationale: the user explicitly pointed at this template; it is simpler,
34+
self-contained, and already proven on this exact CRC (2.64 / OCP 4.22.14 / 20GB / 8 vCPU).
35+
36+
OpenShift delta vs the existing minikube helm path: (a) plain StatefulSets vs operator CRs for
37+
Postgres/Kafka; (b) Service NodePort→ClusterIP + Route objects; (c) image refs → internal
38+
registry (`image-registry.openshift-image-registry.svc:5000`); (d) drop `runAsUser` on apps +
39+
`nonroot-v2` infra SA; (e) config/endpoints templated off `{{ .Release.Namespace }}` (project
40+
`datamesh`); (f) anticipate the CRC-can't-reach-Docker-Hub snag → mirror postgres/kafka images
41+
through the host (a real learning from the modernization run).
42+
43+
### Scope tiering (20GB CRC budget; bump to 24GB)
44+
- Core, live-verified: `datamesh` Project, CNPG Postgres, single-broker Kafka, Apicurio, the 7
45+
services, a Route on graphql-gateway. Live check = GraphQL query through the Route hitting
46+
order→inventory→Postgres + an `order.placed` to Kafka.
47+
- Document-only (runnable, unverified): Istio/Service Mesh canary, KEDA/Custom Metrics
48+
Autoscaler, Kiali, LGTM observability. OpenMetadata OMITTED (heaviest, lowest value on CRC).
49+
50+
### Rejected alternatives
51+
- New kustomize overlay — re-expresses chart logic that already renders; values-file reuse = less duplication.
52+
- All operators manual — OpenShift's point is OLM; use Subscriptions, manual only as fallback.
53+
- Istio+KEDA+OpenMetadata live — won't fit 20GB with Postgres+Kafka+7 svcs; document-only keeps honest.
54+
- In-cluster BuildConfig for images — 7 builds slow/fragile; push prebuilt to internal-registry route.
55+
- Project `capstone` — chose `datamesh` (matches deck vocabulary; override hardcoded `.capstone.svc`).
56+
57+
## Steps
58+
0. Pull-secret gate (user) — RESOLVED: user confirms CRC + pull secret already set up on machine.
59+
1. Author runnable assets (Sonnet, no cluster) — mirror modernization `openshift/` tree:
60+
dedicated chart `examples/lgtm-datamesh/openshift/helm/datamesh/` (Chart.yaml, values.yaml with
61+
`services:` map for the 7 services, templates/: app-deployment.yaml [data-driven, drop runAsUser],
62+
app-service.yaml [ClusterIP], configmap-app.yaml, secret-app.yaml, postgres.yaml [StatefulSet],
63+
kafka.yaml [StatefulSet KRaft], apicurio.yaml, route.yaml [graphql-gateway + apicurio],
64+
serviceaccount-infra.yaml [+ nonroot-v2 SCC binding], _helpers.tpl, NOTES.txt); `openshift/README.md`
65+
(crc + build/push loop + image-mirror + helm install + verify commands); `openshift/gitops/application.yaml`;
66+
`openshift/build-and-push.sh`; `openshift/evidence/` placeholder. `helm template` renders clean; `bash -n` script.
67+
2. Draft chapter `_docs/11-running-on-openshift-crc.md` (order 12, ~3-5k words) + paired
68+
`assets/diagrams/18-*.svg`+`.excalidraw`; placeholders for live learnings.
69+
3. Live bring-up (serial, needs crc start): bump memory 24576, crc start, oc login, project,
70+
expose registry route, OLM Subscriptions, oc wait CSVs Succeeded, apply Postgres+Kafka CRs Ready.
71+
4. Images + app deploy + debug (serial): crc-build-push 7 images; helm install -f values-openshift;
72+
apply Routes; fix SCC/ImagePull/injection breakages; record each learning.
73+
5. Live verify + evidence (serial): all core pods Running; curl gateway Route 200 GraphQL; capture
74+
evidence into `examples/lgtm-datamesh/openshift/evidence/`.
75+
6. Fold evidence + learnings into chapter; add `_plans/decisions.md` DRA entries; jekyll build +
76+
check-liquid-collisions.sh + check-cross-references.sh.
77+
78+
## Acceptance criteria
79+
- `_docs/11-running-on-openshift-crc.md` order 12, sorts after summary (order 11); jekyll build green; in nav.
80+
- Chapter ≥2000 words; ≥1 paired `assets/diagrams/18-*.svg`+`.excalidraw` (both parse).
81+
- Explicit dry-run-vs-live scope note + populated "what broke on live apply" section.
82+
- `helm template -f values-openshift.yaml` → ClusterIP + internal-registry refs, `oc apply --dry-run=client` clean.
83+
- `bash -n` clean on crc-up.sh / crc-build-push.sh / crc-teardown.sh.
84+
- Live: all core pods Running/Ready; operator CSVs Succeeded.
85+
- Live: `curl -k https://graphql-gateway-datamesh.apps-crc.testing/<path>` → 200 GraphQL across ≥2 products.
86+
- `openshift/evidence/` populated; `_plans/decisions.md` DRA entries; cross-ref + liquid checks pass.
87+
88+
## Risks
89+
- Pull secret missing → crc start aborts (RESOLVED per user).
90+
- CRC OOM → pods Pending Insufficient memory → bump 24576, heavy tier doc-only, single-broker/1-instance.
91+
- SCC restricted-v2 denials (most likely live break) on apicurio/kafka-ui → scoped anyuid on specific SA only, document.
92+
- Image push auth/registry route → ImagePullBackOff/401 → defaultRoute patch + podman login $(oc whoami -t) --tls-verify=false.
93+
- Istio annotation-vs-label trap (silent, no sidecar) → use pod-template label form; mesh doc-only.
94+
- Hardcoded `.capstone.svc` endpoints leak → grep rendered template before apply.
95+
- CNPG/AMQ Streams CRD drift → oc apply schema rejection → verify apiVersion vs installed operator.
96+
- Operator not in catalogs → Subscription no CSV → verify redhat-operators + community-operators CatalogSources.
97+
98+
---
99+
100+
## PHASE 2 — Full platform tier (added 2026-10-07, user: "full set incl OpenMetadata", 32GB)
101+
APPROVED (OM best-effort). Opus plan returned; CRC resized to 32GB/14cpu/100GB (core green).
102+
Layers: KEDA (Custom Metrics Autoscaler + Kafka-lag ScaledObject on notification), Istio (OSSM3
103+
Sail operator + IstioCNI, pod-label injection on 7 services, canary + STRICT mTLS, edge Route),
104+
observability (grafana/otel-lgtm all-in-one + re-enable OTLP env), Prefect (server+worker, reuse
105+
core Postgres), OpenMetadata (OpenSearch single-node + OM server, anyuid SA, best-effort).
106+
Assets: new examples/lgtm-datamesh/openshift/platform/ tree + install-platform.sh; app-chart
107+
mesh.enabled/observability.otlp.enabled flags (default off); chapter "full platform tier, live"
108+
section + flipped footer; DRA-008..014; per-layer evidence. Order: operators -> mesh CP ->
109+
observability -> meshed rollout (2/2) -> mTLS -> canary -> trace -> KEDA -> Prefect -> OM last.
110+
111+
---
112+
113+
## BACKLOG — finish OpenMetadata next time in CRC (deferred 2026-10-07)
114+
OM is live except the server image, blocked by Docker Hub's unauthenticated pull-rate limit
115+
(`toomanyrequests`) — hit from BOTH the CRC VM and the host. Everything else is in place:
116+
OpenSearch is GREEN under the `openmetadata-opensearch` anyuid SA, the `openmetadata` DB/role +
117+
secrets are provisioned, and `om-app-values.yaml` already points `image.repository` at the internal
118+
registry mirror `image-registry.openshift-image-registry.svc:5000/datamesh/openmetadata-server:1.12.8`.
119+
120+
To complete (next CRC session):
121+
1. `podman login docker.io` with a Docker Hub account (raises the pull-rate limit). A Bitwarden
122+
item may hold creds — `bw-get-secret` is available on this host.
123+
2. Mirror the image via the host into the internal registry:
124+
`REG=default-route-openshift-image-registry.apps-crc.testing`;
125+
`podman login -u kubeadmin -p $(oc whoami -t) --tls-verify=false $REG`;
126+
`podman pull docker.getcollate.io/openmetadata/server:1.12.8`;
127+
`podman tag ... $REG/datamesh/openmetadata-server:1.12.8`;
128+
`podman push --tls-verify=false $REG/datamesh/openmetadata-server:1.12.8`.
129+
3. `helm upgrade --install openmetadata open-metadata/openmetadata -n datamesh --version 1.12.8 -f om-app-values.yaml`
130+
(init container `run-db-migrations` runs the Postgres migrations, then the server starts).
131+
4. `oc apply -f openshift/platform/openmetadata/route.yaml`; wait `deployment/openmetadata` ready.
132+
5. `oc apply -f openshift/platform/openmetadata/ingestion-job.yaml`; confirm ≥1 catalog asset.
133+
6. Reconcile: fix the OpenSearch subchart's serviceAccount values key (the pod ran under `default`
134+
SA and had to be patched to `openmetadata-opensearch` live — find the correct `om-deps-values.yaml`
135+
key so it binds without a patch); then flip OM from "partial/best-effort" to "verified" in
136+
`_docs/11-running-on-openshift-crc.md` (footer + the OpenMetadata subsection) + add a DRA entry.

0 commit comments

Comments
 (0)