diff --git a/.sisyphus/evidence/task-25-kustomize-dev.yaml b/.sisyphus/evidence/task-25-kustomize-dev.yaml new file mode 100644 index 0000000..c490889 --- /dev/null +++ b/.sisyphus/evidence/task-25-kustomize-dev.yaml @@ -0,0 +1,495 @@ +apiVersion: v1 +data: + init.sql: | + -- Create keycloak database and user + CREATE DATABASE keycloak; + CREATE USER keycloak WITH PASSWORD 'keycloakpass'; + GRANT ALL PRIVILEGES ON DATABASE keycloak TO keycloak; + + -- Keycloak database permissions + \c keycloak + GRANT ALL PRIVILEGES ON SCHEMA public TO keycloak; + ALTER SCHEMA public OWNER TO keycloak; + + -- Application database permissions + \c workclub + GRANT ALL PRIVILEGES ON SCHEMA public TO app; + ALTER SCHEMA public OWNER TO app; +kind: ConfigMap +metadata: + labels: + app: workclub-postgres + environment: development + name: postgres-init + namespace: workclub-dev +--- +apiVersion: v1 +data: + api-base-url: http://workclub-api + cors-origins: http://localhost:3000 + database-host: workclub-postgres + database-name: workclub + database-port: "5432" + keycloak-realm: workclub + keycloak-url: http://workclub-keycloak + log-level: Information +kind: ConfigMap +metadata: + labels: + app: workclub + environment: development + name: workclub-config + namespace: workclub-dev +--- +apiVersion: v1 +kind: Service +metadata: + labels: + app: workclub-api + component: backend + environment: development + name: workclub-api + namespace: workclub-dev +spec: + ports: + - name: http + port: 80 + protocol: TCP + targetPort: 8080 + selector: + app: workclub-api + environment: development + type: ClusterIP +--- +apiVersion: v1 +kind: Service +metadata: + labels: + app: workclub-frontend + component: frontend + environment: development + name: workclub-frontend + namespace: workclub-dev +spec: + ports: + - name: http + port: 80 + protocol: TCP + targetPort: 3000 + selector: + app: workclub-frontend + environment: development + type: ClusterIP +--- +apiVersion: v1 +kind: Service +metadata: + labels: + app: workclub-keycloak + component: auth + environment: development + name: workclub-keycloak + namespace: workclub-dev +spec: + ports: + - name: http + port: 80 + protocol: TCP + targetPort: 8080 + selector: + app: workclub-keycloak + environment: development + type: ClusterIP +--- +apiVersion: v1 +kind: Service +metadata: + labels: + app: workclub-postgres + component: database + environment: development + name: workclub-postgres + namespace: workclub-dev +spec: + ports: + - name: postgresql + port: 5432 + protocol: TCP + targetPort: 5432 + selector: + app: workclub-postgres + environment: development + type: ClusterIP +--- +apiVersion: v1 +kind: Service +metadata: + labels: + app: workclub-postgres + component: database + environment: development + name: workclub-postgres-headless + namespace: workclub-dev +spec: + clusterIP: None + ports: + - name: postgresql + port: 5432 + protocol: TCP + targetPort: 5432 + publishNotReadyAddresses: true + selector: + app: workclub-postgres + environment: development + type: ClusterIP +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + labels: + app: workclub-api + component: backend + environment: development + name: workclub-api + namespace: workclub-dev +spec: + replicas: 1 + selector: + matchLabels: + app: workclub-api + environment: development + template: + metadata: + labels: + app: workclub-api + component: backend + environment: development + spec: + containers: + - env: + - name: ASPNETCORE_ENVIRONMENT + value: Development + - name: ASPNETCORE_URLS + value: http://+:8080 + - name: ConnectionStrings__DefaultConnection + valueFrom: + secretKeyRef: + key: database-connection-string + name: workclub-secrets + - name: Keycloak__Url + valueFrom: + configMapKeyRef: + key: keycloak-url + name: workclub-config + image: workclub-api:dev + imagePullPolicy: IfNotPresent + livenessProbe: + failureThreshold: 3 + httpGet: + path: /health/live + port: http + initialDelaySeconds: 10 + periodSeconds: 15 + timeoutSeconds: 5 + name: api + ports: + - containerPort: 8080 + name: http + protocol: TCP + readinessProbe: + failureThreshold: 2 + httpGet: + path: /health/ready + port: http + initialDelaySeconds: 5 + periodSeconds: 10 + timeoutSeconds: 5 + resources: + limits: + cpu: 200m + memory: 256Mi + requests: + cpu: 50m + memory: 128Mi + startupProbe: + failureThreshold: 30 + httpGet: + path: /health/startup + port: http + initialDelaySeconds: 5 + periodSeconds: 10 + timeoutSeconds: 5 +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + labels: + app: workclub-frontend + component: frontend + environment: development + name: workclub-frontend + namespace: workclub-dev +spec: + replicas: 1 + selector: + matchLabels: + app: workclub-frontend + environment: development + template: + metadata: + labels: + app: workclub-frontend + component: frontend + environment: development + spec: + containers: + - env: + - name: NODE_ENV + value: production + - name: NEXT_PUBLIC_API_URL + valueFrom: + configMapKeyRef: + key: api-base-url + name: workclub-config + - name: NEXT_PUBLIC_KEYCLOAK_URL + valueFrom: + configMapKeyRef: + key: keycloak-url + name: workclub-config + image: workclub-frontend:dev + imagePullPolicy: IfNotPresent + livenessProbe: + failureThreshold: 3 + httpGet: + path: /api/health + port: http + initialDelaySeconds: 10 + periodSeconds: 15 + timeoutSeconds: 5 + name: frontend + ports: + - containerPort: 3000 + name: http + protocol: TCP + readinessProbe: + failureThreshold: 2 + httpGet: + path: /api/health + port: http + initialDelaySeconds: 5 + periodSeconds: 10 + timeoutSeconds: 5 + resources: + limits: + cpu: 200m + memory: 256Mi + requests: + cpu: 50m + memory: 128Mi +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + labels: + app: workclub-keycloak + component: auth + environment: development + name: workclub-keycloak + namespace: workclub-dev +spec: + replicas: 1 + selector: + matchLabels: + app: workclub-keycloak + environment: development + template: + metadata: + labels: + app: workclub-keycloak + component: auth + environment: development + spec: + containers: + - command: + - start + env: + - name: KC_DB + value: postgres + - name: KC_DB_URL_HOST + value: workclub-postgres + - name: KC_DB_URL_PORT + value: "5432" + - name: KC_DB_URL_DATABASE + value: keycloak + - name: KC_DB_USERNAME + value: keycloak + - name: KC_DB_PASSWORD + valueFrom: + secretKeyRef: + key: keycloak-db-password + name: workclub-secrets + - name: KEYCLOAK_ADMIN + value: admin + - name: KEYCLOAK_ADMIN_PASSWORD + valueFrom: + secretKeyRef: + key: keycloak-admin-password + name: workclub-secrets + - name: KC_HOSTNAME_STRICT + value: "false" + - name: KC_PROXY + value: edge + - name: KC_HTTP_ENABLED + value: "true" + image: quay.io/keycloak/keycloak:26.1 + imagePullPolicy: IfNotPresent + livenessProbe: + failureThreshold: 3 + httpGet: + path: /health/live + port: http + initialDelaySeconds: 20 + periodSeconds: 15 + timeoutSeconds: 5 + name: keycloak + ports: + - containerPort: 8080 + name: http + protocol: TCP + readinessProbe: + failureThreshold: 2 + httpGet: + path: /health/ready + port: http + initialDelaySeconds: 10 + periodSeconds: 10 + timeoutSeconds: 5 + resources: + limits: + cpu: 500m + memory: 512Mi + requests: + cpu: 100m + memory: 256Mi +--- +apiVersion: apps/v1 +kind: StatefulSet +metadata: + labels: + app: workclub-postgres + component: database + environment: development + name: workclub-postgres + namespace: workclub-dev +spec: + replicas: 1 + selector: + matchLabels: + app: workclub-postgres + environment: development + serviceName: workclub-postgres-headless + template: + metadata: + labels: + app: workclub-postgres + component: database + environment: development + spec: + containers: + - env: + - name: POSTGRES_DB + value: workclub + - name: POSTGRES_USER + value: app + - name: POSTGRES_PASSWORD + valueFrom: + secretKeyRef: + key: postgres-password + name: workclub-secrets + image: postgres:16-alpine + imagePullPolicy: IfNotPresent + livenessProbe: + exec: + command: + - /bin/sh + - -c + - pg_isready -U app -d workclub + failureThreshold: 3 + initialDelaySeconds: 10 + periodSeconds: 15 + timeoutSeconds: 5 + name: postgres + ports: + - containerPort: 5432 + name: postgresql + protocol: TCP + readinessProbe: + exec: + command: + - /bin/sh + - -c + - pg_isready -U app -d workclub + failureThreshold: 2 + initialDelaySeconds: 5 + periodSeconds: 10 + timeoutSeconds: 5 + resources: + limits: + cpu: 500m + memory: 512Mi + requests: + cpu: 100m + memory: 256Mi + volumeMounts: + - mountPath: /var/lib/postgresql/data + name: postgres-data + subPath: postgres + - mountPath: /docker-entrypoint-initdb.d + name: postgres-init + volumes: + - configMap: + items: + - key: init.sql + path: init.sql + name: postgres-init + name: postgres-init + volumeClaimTemplates: + - metadata: + labels: + environment: development + name: postgres-data + spec: + accessModes: + - ReadWriteOnce + resources: + requests: + storage: 10Gi + storageClassName: standard +--- +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + labels: + app: workclub + environment: development + name: workclub-ingress + namespace: workclub-dev +spec: + rules: + - host: localhost + http: + paths: + - backend: + service: + name: workclub-frontend + port: + number: 80 + path: / + pathType: Prefix + - backend: + service: + name: workclub-api + port: + number: 80 + path: /api + pathType: Prefix diff --git a/.sisyphus/notepads/club-work-manager/learnings.md b/.sisyphus/notepads/club-work-manager/learnings.md index 2b335af..102672a 100644 --- a/.sisyphus/notepads/club-work-manager/learnings.md +++ b/.sisyphus/notepads/club-work-manager/learnings.md @@ -1822,3 +1822,52 @@ docker run -p 3000:3000 workclub-frontend:dev curl http://localhost:3000 # Should return HTTP 200 ``` + +## [2026-03-03 Task 25] Kustomize Dev Overlay + Resource Limits + Health Checks + +### Files Created +- `infra/k8s/overlays/dev/kustomization.yaml` - Dev overlay configuration +- `infra/k8s/overlays/dev/patches/backend-resources.yaml` - Backend dev resource patch +- `infra/k8s/overlays/dev/patches/frontend-resources.yaml` - Frontend dev resource patch +- `frontend/src/app/api/health/route.ts` - Frontend health endpoint (was missing) + +### Key Decisions +- **Resource Limits**: Dev overlay uses 50% of base resources: + - Requests: cpu=50m (vs base 100m), memory=128Mi (vs base 256Mi) + - Limits: cpu=200m (vs base 500m), memory=256Mi (vs base 512Mi) +- **Image Tags**: Set to `dev` for workclub-api and workclub-frontend +- **Namespace**: `workclub-dev` for isolation +- **Replicas**: All deployments set to 1 for dev environment +- **Frontend Health**: Created missing `/api/health` Next.js route handler + +### Patterns Established +- **Strategic Merge Patches**: Target deployment by name, container name, then patch specific fields +- **Kustomize Overlay Structure**: + ``` + overlays/dev/ + ├── kustomization.yaml (references base, sets namespace, images, replicas, patches) + └── patches/ (strategic merge patches per service) + ``` +- **commonLabels**: Used `environment: development` label (deprecated warning but functional) + +### Issues Encountered +1. **Missing kustomize**: Had to install via Homebrew (`brew install kustomize`) +2. **Missing Frontend Health Endpoint**: `/api/health` declared in base manifest but route didn't exist + - Created `frontend/src/app/api/health/route.ts` with simple `{ status: 'ok' }` response +3. **Deprecation Warning**: `commonLabels` is deprecated in favor of `labels` (non-blocking) + +### Verification Results +✅ `kustomize build` succeeded (exit code 0) +✅ All deployments have `replicas: 1` +✅ Backend resources: cpu=50m-200m, memory=128Mi-256Mi +✅ Frontend resources: cpu=50m-200m, memory=128Mi-256Mi +✅ Image tags: `workclub-api:dev`, `workclub-frontend:dev` +✅ Namespace: `workclub-dev` applied to all resources +✅ Health check endpoints preserved: Backend `/health/*`, Frontend `/api/health` +✅ Evidence saved: `.sisyphus/evidence/task-25-kustomize-dev.yaml` (495 lines) + +### Next Steps for Future Tasks +- Consider creating production overlay with higher resources +- May need to update `commonLabels` to `labels` to avoid deprecation warnings +- Frontend health endpoint is minimal - could enhance with actual health checks + diff --git a/.sisyphus/plans/club-work-manager.md b/.sisyphus/plans/club-work-manager.md index 6add7d2..782603d 100644 --- a/.sisyphus/plans/club-work-manager.md +++ b/.sisyphus/plans/club-work-manager.md @@ -2224,7 +2224,7 @@ Max Concurrent: 6 (Wave 1) - Files: `frontend/Dockerfile`, `frontend/Dockerfile.dev` - Pre-commit: `docker build -t test frontend/` -- [ ] 25. Kustomize Dev Overlay + Resource Limits + Health Checks +- [x] 25. Kustomize Dev Overlay + Resource Limits + Health Checks **What to do**: - Create `/infra/k8s/overlays/dev/kustomization.yaml`: diff --git a/frontend/src/app/api/health/route.ts b/frontend/src/app/api/health/route.ts new file mode 100644 index 0000000..68a734c --- /dev/null +++ b/frontend/src/app/api/health/route.ts @@ -0,0 +1,3 @@ +export async function GET() { + return Response.json({ status: 'ok' }, { status: 200 }); +} diff --git a/infra/k8s/overlays/dev/kustomization.yaml b/infra/k8s/overlays/dev/kustomization.yaml new file mode 100644 index 0000000..4375f0b --- /dev/null +++ b/infra/k8s/overlays/dev/kustomization.yaml @@ -0,0 +1,32 @@ +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization + +resources: + - ../../base + +namespace: workclub-dev + +commonLabels: + environment: development + +images: + - name: workclub-api + newTag: dev + - name: workclub-frontend + newTag: dev + +replicas: + - name: workclub-api + count: 1 + - name: workclub-frontend + count: 1 + +patches: + - path: patches/backend-resources.yaml + target: + kind: Deployment + name: workclub-api + - path: patches/frontend-resources.yaml + target: + kind: Deployment + name: workclub-frontend diff --git a/infra/k8s/overlays/dev/patches/backend-resources.yaml b/infra/k8s/overlays/dev/patches/backend-resources.yaml new file mode 100644 index 0000000..c969074 --- /dev/null +++ b/infra/k8s/overlays/dev/patches/backend-resources.yaml @@ -0,0 +1,16 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: workclub-api +spec: + template: + spec: + containers: + - name: api + resources: + requests: + memory: "128Mi" + cpu: "50m" + limits: + memory: "256Mi" + cpu: "200m" diff --git a/infra/k8s/overlays/dev/patches/frontend-resources.yaml b/infra/k8s/overlays/dev/patches/frontend-resources.yaml new file mode 100644 index 0000000..01bccc1 --- /dev/null +++ b/infra/k8s/overlays/dev/patches/frontend-resources.yaml @@ -0,0 +1,16 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: workclub-frontend +spec: + template: + spec: + containers: + - name: frontend + resources: + requests: + memory: "128Mi" + cpu: "50m" + limits: + memory: "256Mi" + cpu: "200m"