Skip to content

Commit b162c55

Browse files
committed
feat: OpenShift deployment fixes - enable pgbouncer (session mode), nginx proxy, fix NFS persistence, add amd64 nginx Dockerfile, add deployment guide
Signed-off-by: Yosief Eyob <yosiefogbazion@gmail.com>
1 parent 0fca4e5 commit b162c55

3 files changed

Lines changed: 148 additions & 1 deletion

File tree

Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,131 @@
1+
# OpenShift Deployment Guide for ContextForge MCP Gateway
2+
3+
## Prerequisites
4+
5+
- OpenShift cluster with `oc` and `helm` CLI tools
6+
- NFS PersistentVolume with `ReadWriteMany` access mode
7+
- Docker Hub account (for pulling rate-limited images)
8+
9+
## Quick Deploy
10+
```bash
11+
# 1. Log in to OpenShift
12+
oc login <cluster-api-url> --token=<your-token>
13+
oc project <your-namespace>
14+
15+
# 2. Ensure NFS PV is available
16+
oc get pv | grep nfsx
17+
18+
# If PV is in Released state, clear it:
19+
oc patch pv nfsx-pv --type=json -p '[{"op":"remove","path":"/spec/claimRef"}]'
20+
21+
# 3. Create Docker Hub pull secret (required to avoid rate limits)
22+
oc create secret docker-registry dockerhub-secret \
23+
--docker-server=docker.io \
24+
--docker-username=<your-dockerhub-username> \
25+
--docker-password=<your-dockerhub-password> \
26+
-n <your-namespace>
27+
oc secrets link default dockerhub-secret --for=pull -n <your-namespace>
28+
29+
# 4. Install with Helm
30+
helm install contextforge ./charts/mcp-stack -n <your-namespace> \
31+
-f <your-values-file> \
32+
--set mcpContextForge.replicaCount=1
33+
34+
# 5. Wait for pods (Postgres must start first, then migration, then gateway)
35+
oc get pods -n <your-namespace> -w
36+
37+
# 6. Fix PgBouncer seccomp issue (OpenShift rejects RuntimeDefault)
38+
oc get deployment contextforge-mcp-stack-pgbouncer -n <your-namespace> -o yaml > /tmp/pgbouncer-deploy.yaml
39+
sed -i.bak '/seccompProfile/,/type: RuntimeDefault/d' /tmp/pgbouncer-deploy.yaml
40+
oc replace -f /tmp/pgbouncer-deploy.yaml --force
41+
42+
# Add pull secret to PgBouncer
43+
oc patch deployment contextforge-mcp-stack-pgbouncer -n <your-namespace> \
44+
--type=strategic -p '{"spec":{"template":{"spec":{"imagePullSecrets":[{"name":"dockerhub-secret"}]}}}}'
45+
46+
# 7. Create the route (points to nginx proxy, not directly to gateway)
47+
oc create route edge contextforge \
48+
--service=contextforge-mcp-stack-nginx \
49+
--port=80 \
50+
--insecure-policy=Redirect \
51+
-n <your-namespace>
52+
53+
# 8. Scale gateway to 2 replicas after migration completes
54+
oc scale deployment contextforge-mcp-stack-mcpgateway -n <your-namespace> --replicas=2
55+
56+
# 9. Access the app
57+
oc get route -n <your-namespace>
58+
# Open the URL with https:// and log in with admin@example.com / admin123
59+
```
60+
61+
## values.yaml Changes Required for OpenShift + NFS
62+
63+
The following changes were made to the default `values.yaml` for OpenShift on IBM Fyre with NFS storage:
64+
65+
| Setting | Default | Changed To | Reason |
66+
|---------|---------|------------|--------|
67+
| `pgbouncer.enabled` | `false` | `true` | Enables connection pooling |
68+
| `pgbouncer.pool.mode` | `transaction` | `session` | Alembic migrations require advisory locks which only work in session mode |
69+
| `postgres.persistence.useReadWriteOncePod` | `true` | `false` | NFS volumes only support ReadWriteMany |
70+
| `postgres.persistence.accessModes` | `[ReadWriteOnce]` | `[ReadWriteMany]` | Must match the NFS PV access mode |
71+
| `nginxProxy.enabled` | `false` | `true` | Enables nginx reverse proxy in front of gateway |
72+
| `nginxProxy.persistence.enabled` | `true` | `false` | Only one NFS PV available on test clusters |
73+
| `nginxProxy.image.repository` | `mcpgateway/nginx-cache` | `yosiefeyob1/nginx-cache` | Original image not published; built from infra/nginx/Dockerfile.amd64 |
74+
75+
## Post-Deploy Manual Fixes (Should Be Automated in Chart)
76+
77+
### 1. PgBouncer seccomp Profile
78+
OpenShift's Security Context Constraints (SCC) reject `seccompProfile: RuntimeDefault` for regular service accounts. The PgBouncer deployment template includes this setting, causing pods to fail with `FailedCreate`. Fix by exporting the deployment YAML, removing the seccomp block, and replacing.
79+
80+
**Recommended chart fix:** Make the seccomp profile conditional or remove it for OpenShift compatibility.
81+
82+
### 2. Docker Hub Pull Secret
83+
Docker Hub rate-limits unauthenticated pulls. The PgBouncer (`edoburu/pgbouncer`) and nginx images require authentication. A pull secret must be created and linked to the default service account, then patched into deployments.
84+
85+
**Recommended chart fix:** Support `global.imagePullSecrets` properly so all deployments inherit the pull secret.
86+
87+
### 3. Route Creation
88+
The Helm chart does not create an OpenShift Route. The route must be created manually and should point to the nginx proxy service (not directly to the gateway).
89+
90+
**Recommended chart fix:** Add an optional OpenShift Route template in the chart, gated by a values flag like `route.enabled: true`.
91+
92+
## Nginx Proxy Image
93+
94+
The `mcpgateway/nginx-cache` image referenced in the chart does not exist on Docker Hub. It must be built from the Dockerfile in this repo.
95+
96+
- `infra/nginx/Dockerfile` — Original Red Hat UBI 10 base (requires x86 build environment)
97+
- `infra/nginx/Dockerfile.amd64` — Alpine-based alternative (can be cross-compiled from ARM Macs using `docker buildx`)
98+
99+
To build from an ARM Mac:
100+
```bash
101+
docker buildx create --name mybuilder --use
102+
docker buildx build --platform linux/amd64 -t <your-registry>/nginx-cache:latest -f infra/nginx/Dockerfile.amd64 infra/nginx/ --push
103+
```
104+
105+
## Traffic Flow
106+
```
107+
Browser → OpenShift Route → Nginx Proxy (cache + reverse proxy) → Gateway Pods → PgBouncer → PostgreSQL
108+
```
109+
110+
## Troubleshooting
111+
```bash
112+
# Check all pods
113+
oc get pods -n <your-namespace>
114+
115+
# Check pod logs
116+
oc logs <pod-name> -n <your-namespace> --tail=50
117+
118+
# Check PVC binding
119+
oc get pvc -n <your-namespace>
120+
oc get pv
121+
122+
# Check route
123+
oc get route -n <your-namespace>
124+
125+
# Check secrets
126+
oc get secret -n <your-namespace>
127+
128+
# Full teardown and redeploy
129+
helm uninstall contextforge -n <your-namespace>
130+
oc patch pv nfsx-pv --type=json -p '[{"op":"remove","path":"/spec/claimRef"}]'
131+
```

charts/mcp-stack/values.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1506,7 +1506,7 @@ nginxProxy:
15061506
enabled: true
15071507

15081508
image:
1509-
repository: mcpgateway/nginx-cache
1509+
repository: yosiefeyob1/nginx-cache
15101510
tag: latest
15111511
pullPolicy: IfNotPresent
15121512

infra/nginx/Dockerfile.amd64

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
FROM nginx:1.26-alpine
2+
RUN apk add --no-cache curl ca-certificates
3+
RUN mkdir -p /var/cache/nginx/static \
4+
/var/cache/nginx/api \
5+
/var/cache/nginx/schema \
6+
/var/log/nginx \
7+
/run/nginx && \
8+
chown -R nginx:nginx /var/cache/nginx /var/log/nginx /run/nginx && \
9+
chmod -R 755 /var/cache/nginx
10+
COPY nginx.conf /etc/nginx/nginx.conf
11+
COPY docker-entrypoint.sh /docker-entrypoint.sh
12+
RUN chmod +x /docker-entrypoint.sh
13+
EXPOSE 80 443
14+
HEALTHCHECK --interval=30s --timeout=5s --start-period=10s --retries=3 \
15+
CMD curl -f http://localhost/health || curl -fk https://localhost/health || exit 1
16+
ENTRYPOINT ["/docker-entrypoint.sh"]

0 commit comments

Comments
 (0)