Speaker DaemonSet on CPU-constrained cluster takes >180s to start all 3 pods.
Don't fail the entire pipeline — warn and print speaker pod status instead.
Controller must still be ready (it handles IP assignment) before continuing.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
During rolling updates with the default maxSurge=1, an extra surge pod was
created temporarily (3 pods instead of 2), causing all 3 nodes to report
"Insufficient CPU" and delaying scheduling past the Jenkins rollout timeout.
With maxSurge=0 / maxUnavailable=1, one old pod terminates first before a
new one starts — pod count stays at 2 throughout, no extra CPU needed.
Also increase Jenkins rollout timeout from 300s to 600s as a safety net
for CPU-constrained nodes that may still need extra scheduling time.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Speaker runs on all 3 nodes and needs image pull + startup time per node.
90s was too tight — bumped to 180s to handle slow node startups.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Add MetalLB IPAddressPool (192.168.108.213/32) and L2Advertisement
so the frontend gets a stable external IP on the LAN
- Change frontend service type: NodePort → LoadBalancer
- Add 'Setup MetalLB' stage in Jenkinsfile that installs MetalLB v0.14.8
(idempotent) and applies the IP pool config before each deploy
After deploy: kubectl get svc frontend -n scrum-manager
should show EXTERNAL-IP: 192.168.108.213
App accessible at: http://192.168.108.213
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Root cause: backend deployment.yaml referenced secretKeyRef key: DB_USER and
key: DB_PASSWORD, but the live secret only has MYSQL_USER and MYSQL_PASSWORD.
kubectl apply reported secret/mysql-secret as "unchanged" (last-applied matched
desired) so the drift was never caught — new pods got CreateContainerConfigError.
Changes:
- backend/deployment.yaml: DB_USER → key: MYSQL_USER, DB_PASSWORD → key: MYSQL_PASSWORD
- mysql/deployment.yaml: add MYSQL_USER/MYSQL_PASSWORD env vars so the app user
(scrumapp) is created if MySQL ever reinitializes from a fresh PVC
- mysql/secret.yaml: remove stale commented-out block with old key names
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Increasing the request to 256Mi caused backend pods to be Pending with no
node assignment — the scheduler couldn't fit them alongside MySQL (512Mi
request) and existing pods on the on-premise nodes.
Memory REQUEST drives scheduling (how much the node reserves).
Memory LIMIT drives OOMKill (the actual cap at runtime).
Keep request at 128Mi so pods schedule, limit at 512Mi so Node.js +
Socket.io + MySQL pool don't get OOMKilled on startup.
Also add terminationGracePeriodSeconds: 15 so pods from failed/previous
builds release their node slot quickly instead of blocking new pod scheduling.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Backend was OOMKilled during rolling update startup (Node.js + Socket.io +
MySQL pool exceeds 256Mi). Raised limit to 512Mi and request to 256Mi.
Jenkinsfile: show kubectl get pods immediately after apply so pod state
is visible in build logs. Added full diagnostics (describe + logs) in
post.failure block so the root cause of any future rollout failure is
visible without needing to SSH into the cluster.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
kubectl apply computes a 3-way merge. The base PVC has no storageClassName
(nil), but the already-bound PVC in the cluster has storageClassName=local-path.
This diff caused apply to attempt a mutation on a bound PVC — forbidden by k8s.
Fix: patch the PVC with storageClassName=local-path so desired state matches
live state and apply produces no diff on the PVC.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The mysql-data-pvc was already dynamically provisioned by the cluster's
'local-path' StorageClass. The overlay patch tried to change storageClassName
to 'manual' and volumeName on an already-bound PVC, which Kubernetes forbids:
"spec is immutable after creation except resources.requests"
Fixes:
- Remove mysql-pvc-patch from kustomization.yaml (PVC left as-is)
- Remove mysql-pv.yaml resource (not needed with dynamic provisioner)
- Add comment explaining when manual PV/PVC is needed vs not
Jenkinsfile: add --timeout and FQDN to smoke test curl; add comments
explaining MySQL Recreate strategy startup timing expectations.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Database fixes:
- Add hostPath.type=DirectoryOrCreate so kubelet auto-creates /mnt/data/mysql
- Add fsGroup=999 so MySQL process can write to the hostPath volume
- Add MYSQL_ROOT_HOST=% to allow backend pods to authenticate as root
- Fix liveness/readiness probes to include credentials (-p$MYSQL_ROOT_PASSWORD)
- Increase probe initialDelaySeconds (30/60s) for slow first-run init
- Add 15s grace sleep in backend initContainer after MySQL TCP is up
- Add persistentVolumeReclaimPolicy=Retain to prevent accidental data loss
- Explicit accessModes+resources in PVC patch to avoid list merge ambiguity
- Add nodeAffinity comment in PV for multi-node cluster guidance
Ingress/nginx fixes:
- Remove broken rewrite-target=/ that was rewriting all paths (incl /api) to /
- Route /socket.io directly to backend for WebSocket support
- Add /socket.io/ proxy location to both nginx.conf and K8s ConfigMap
Frontend fix:
- Persist currentUser to localStorage on login so page refresh no longer
clears session and redirects users back to the login page
Tooling:
- Add k8s/overlays/on-premise/deploy.sh for one-command deployment
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Registration form: added 5 new role options to dropdown
- Sidebar: new roles get proper nav access via ALL_ROLES/LEADER_ROLES
- Dashboard: isLeader check expanded to include new leadership roles
- Shared/Pages: role badge colors added for all new roles
- Invite modal: expanded role dropdown
- Add CSS media queries for all sections: sidebar overlay, navbar,
calendar, kanban, dashboard, list view, drawer, modal, reports,
team tasks, members
- Add hamburger menu button (hidden on desktop, visible on mobile)
- Add sidebar slide-in overlay with backdrop for mobile
- Auto-close sidebar on navigation for mobile UX
- Login card, drawer, and modal go full-width on mobile
- Dashboard stats grid collapses to 2-column on mobile
- Report charts stack to single column on mobile
- Login with role-based auth (CTO/Manager/Employee)
- Calendar view (month/week) with task chips and quick-add
- Kanban board with status columns
- Sortable list view with action menus
- Task detail drawer with subtasks, comments, activity
- Add task modal with validation
- Dashboard with stats, workload, priority breakdown
- Team tasks grouped by assignee
- Reports page with recharts (bar, pie, line, horizontal bar)
- Members page with invite modal
- Search and assignee filter across views
- ErrorBoundary for production error handling
- Full dark design system via index.css