Skip to content

Commit f8126d2

Browse files
committed
feat: add local Kubernetes (Minikube) configurations and architecture documentation
1 parent 6132912 commit f8126d2

13 files changed

Lines changed: 304 additions & 0 deletions

.gitignore

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,3 +37,7 @@ opencode.json
3737
*.tsbuildinfo
3838
.vercel
3939
!.vercel/project.json
40+
41+
# Kubernetes Secrets
42+
kubernetes/secrets.yaml
43+

README.md

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -356,3 +356,62 @@ AI pipeline steps (the LangGraph RAG Agentic Chat, prompt revisions, document gr
356356
**Keeping the free-tier API warm:** Render's free instance sleeps after 15 min idle. GitHub Actions cron is too unreliable for short-interval keep-alive pings, so an external pinger ([cron-job.org](https://cron-job.org)) does a `GET https://api.inferr.xyz/health` every 10 min, restricted to hours 0–18 UTC (~06:00–24:00 IST) to stay within the 750 hr/month budget. The instance is allowed to sleep overnight; the web app's wake overlay (`apps/web/src/lib/server-status.tsx`) covers the first cold request.
357357

358358
**Web (Vercel):** Set `NEXT_PUBLIC_API_URL` to the Render API URL. `vercel.json` at root handles the monorepo build pointing to `apps/web`.
359+
360+
---
361+
362+
## Kubernetes (Local Deployment)
363+
364+
This project contains a complete, local Kubernetes deployment setup located in the [kubernetes/](file:///home/johnvesslyalti/johnvesslyalti_workspace/inferr/kubernetes/) directory. This allows you to run, manage, and test the entire backend infrastructure (NestJS API, Postgres with pgvector, Redis, migrations, and cronjobs) inside a local Kubernetes cluster.
365+
366+
### Architecture
367+
![Kubernetes Architecture Diagram](docs/k8s_architecture.jpg)
368+
369+
### Prerequisites
370+
* **Docker**
371+
* **Minikube**
372+
* **kubectl** & **Helm**
373+
374+
### Launching the Cluster Locally
375+
1. **Start Minikube**:
376+
```bash
377+
minikube start --driver=docker
378+
```
379+
2. **Build the API container inside Minikube**:
380+
Build the NestJS API container image directly in the Minikube registry:
381+
```bash
382+
minikube image build -t inferr-api:latest .
383+
```
384+
3. **Apply the manifests**:
385+
Deploy all resources (Secrets, ConfigMaps, Postgres StatefulSet, Redis, API, and Ingress) to your cluster:
386+
```bash
387+
kubectl apply -f kubernetes/
388+
```
389+
4. **Enable Ingress**:
390+
Enable the Nginx Ingress Controller addon:
391+
```bash
392+
minikube addons enable ingress
393+
```
394+
395+
### Accessing the API
396+
1. Map the domain `api.inferr.local` to your Minikube IP:
397+
```bash
398+
echo "$(minikube ip) api.inferr.local" | sudo tee -a /etc/hosts
399+
```
400+
2. Open `http://api.inferr.local/health` in your browser. You should receive:
401+
```json
402+
{ "status": "ok" }
403+
```
404+
405+
### Managing the Pipeline
406+
* **Trigger the daily scraper manually**:
407+
```bash
408+
kubectl create job --from=cronjob/scraper-cronjob scraper-manual-run
409+
```
410+
* **View the scraper logs**:
411+
```bash
412+
kubectl logs -f -l job-name=scraper-manual-run
413+
```
414+
* **Verify Pod status**:
415+
```bash
416+
kubectl get pods
417+
```

docs/k8s_architecture.jpg

566 KB
Loading

kubernetes/api-deployment.yaml

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
apiVersion: apps/v1
2+
kind: Deployment
3+
metadata:
4+
name: api
5+
namespace: default
6+
labels:
7+
app: api
8+
spec:
9+
replicas: 1
10+
selector:
11+
matchLabels:
12+
app: api
13+
template:
14+
metadata:
15+
labels:
16+
app: api
17+
spec:
18+
initContainers:
19+
- name: wait-for-postgres
20+
image: busybox:1.36
21+
command:
22+
- sh
23+
- -c
24+
- |
25+
until nc -z -w 2 postgres-service 5432; do
26+
echo "Waiting for postgres-service:5432..."
27+
sleep 2
28+
done
29+
containers:
30+
- name: api
31+
image: inferr-api:latest
32+
imagePullPolicy: IfNotPresent
33+
ports:
34+
- containerPort: 3001
35+
name: http
36+
envFrom:
37+
- configMapRef:
38+
name: api-config
39+
- secretRef:
40+
name: api-secrets
41+
livenessProbe:
42+
httpGet:
43+
path: /health
44+
port: 3001
45+
initialDelaySeconds: 15
46+
periodSeconds: 10
47+
readinessProbe:
48+
httpGet:
49+
path: /health
50+
port: 3001
51+
initialDelaySeconds: 15
52+
periodSeconds: 10

kubernetes/api-service.yaml

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
apiVersion: v1
2+
kind: Service
3+
metadata:
4+
name: api-service
5+
namespace: default
6+
spec:
7+
ports:
8+
- port: 3001
9+
targetPort: 3001
10+
name: http
11+
selector:
12+
app: api
13+
type: ClusterIP

kubernetes/configmap.yaml

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
apiVersion: v1
2+
kind: ConfigMap
3+
metadata:
4+
name: api-config
5+
namespace: default
6+
data:
7+
DB_HOST: "postgres-service"
8+
DB_PORT: "5432"
9+
DB_USER: "postgres"
10+
DB_NAME: "ai_feed"
11+
DB_SSL: "false"
12+
API_PORT: "3001"
13+
REDIS_HOST: "redis-service"
14+
REDIS_PORT: "6379"
15+
NODE_ENV: "production"
16+
GOOGLE_CALLBACK_URL: "http://api.inferr.local/auth/google/callback"
17+
GOOGLE_MCP_CALLBACK_URL: "http://api.inferr.local/auth/google/mcp-callback"
18+
API_URL: "https://api.inferr.local"

kubernetes/ingress.yaml

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
apiVersion: networking.k8s.io/v1
2+
kind: Ingress
3+
metadata:
4+
name: api-ingress
5+
namespace: default
6+
spec:
7+
ingressClassName: nginx
8+
rules:
9+
- host: api.inferr.local
10+
http:
11+
paths:
12+
- path: /
13+
pathType: Prefix
14+
backend:
15+
service:
16+
name: api-service
17+
port:
18+
number: 3001

kubernetes/postgres-pvc.yaml

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
apiVersion: v1
2+
kind: PersistentVolumeClaim
3+
metadata:
4+
name: postgres-pvc
5+
namespace: default
6+
spec:
7+
accessModes:
8+
- ReadWriteOnce
9+
resources:
10+
requests:
11+
storage: 1Gi

kubernetes/postgres-service.yaml

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
apiVersion: v1
2+
kind: Service
3+
metadata:
4+
name: postgres-service
5+
namespace: default
6+
spec:
7+
ports:
8+
- port: 5432
9+
targetPort: 5432
10+
selector:
11+
app: postgres
12+
type: ClusterIP
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
apiVersion: apps/v1
2+
kind: StatefulSet
3+
metadata:
4+
name: postgres
5+
namespace: default
6+
spec:
7+
serviceName: postgres-service
8+
replicas: 1
9+
selector:
10+
matchLabels:
11+
app: postgres
12+
template:
13+
metadata:
14+
labels:
15+
app: postgres
16+
spec:
17+
containers:
18+
- name: postgres
19+
image: pgvector/pgvector:pg16
20+
ports:
21+
- containerPort: 5432
22+
name: postgres
23+
env:
24+
- name: POSTGRES_USER
25+
value: "postgres"
26+
- name: POSTGRES_DB
27+
value: "ai_feed"
28+
- name: POSTGRES_PASSWORD
29+
valueFrom:
30+
secretKeyRef:
31+
name: api-secrets
32+
key: DB_PASS
33+
volumeMounts:
34+
- name: postgres-storage
35+
mountPath: /var/lib/postgresql/data
36+
volumes:
37+
- name: postgres-storage
38+
persistentVolumeClaim:
39+
claimName: postgres-pvc

0 commit comments

Comments
 (0)