feat(k8s): deploy postgres chromadb backend (backend-only) - #9
Conversation
There was a problem hiding this comment.
Pull request overview
This PR introduces Kubernetes deployment configurations for a backend application with PostgreSQL and ChromaDB dependencies. The deployment enables the backend service to operate in a Kubernetes cluster with persistent storage for both the database and vector store.
Changes:
- Added Kubernetes manifests for PostgreSQL database with persistent storage
- Added Kubernetes manifests for ChromaDB vector database with persistent storage
- Added backend deployment with health probes, resource limits, and service configuration
- Updated ConfigMap to align ChromaDB data path with deployment configuration
Reviewed changes
Copilot reviewed 4 out of 4 changed files in this pull request and generated 13 comments.
| File | Description |
|---|---|
| infra/k8s/application/05-backend.yaml | Backend deployment with 2 replicas, resource limits, health probes, and ClusterIP service exposing port 8001 |
| infra/k8s/application/04-postgres.yaml | PostgreSQL database deployment with PersistentVolumeClaim, health checks, and ClusterIP service on port 5432 |
| infra/k8s/application/03-chromadb.yaml | ChromaDB vector database deployment with PersistentVolumeClaim and ClusterIP service on port 8000 |
| infra/k8s/application/02-configmap.yaml | Updated CHROMA_DB_PATH from '/data' to '/chroma_data' to match deployment configuration |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| - name: PERSIST_DIRECTORY | ||
| value: "/chroma_data" | ||
| - name: ANONYMIZED_TELEMETRY | ||
| value: "false" |
There was a problem hiding this comment.
The ChromaDB deployment is missing health probes (readinessProbe and livenessProbe). Without these probes, Kubernetes cannot determine if the ChromaDB container is healthy and ready to accept traffic, which could lead to the backend attempting to connect to an unhealthy ChromaDB instance. Consider adding HTTP probes similar to the backend deployment.
| value: "false" | |
| value: "false" | |
| livenessProbe: | |
| httpGet: | |
| path: /api/v1/heartbeat | |
| port: 8000 | |
| initialDelaySeconds: 30 | |
| periodSeconds: 10 | |
| timeoutSeconds: 5 | |
| failureThreshold: 3 | |
| readinessProbe: | |
| httpGet: | |
| path: /api/v1/heartbeat | |
| port: 8000 | |
| initialDelaySeconds: 10 | |
| periodSeconds: 5 | |
| timeoutSeconds: 3 | |
| failureThreshold: 3 |
| - name: postgres | ||
| image: postgres:16-alpine | ||
| ports: | ||
| - containerPort: 5432 | ||
| env: | ||
| # 공개 설정(ConfigMap) | ||
| - name: POSTGRES_USER | ||
| valueFrom: | ||
| configMapKeyRef: | ||
| name: app-config | ||
| key: DB_USER | ||
| - name: POSTGRES_DB | ||
| valueFrom: | ||
| configMapKeyRef: | ||
| name: app-config | ||
| key: DB_NAME | ||
|
|
||
| # 민감 설정(Secret) | ||
| - name: POSTGRES_PASSWORD | ||
| valueFrom: | ||
| secretKeyRef: | ||
| name: app-secret | ||
| key: DB_PASSWORD | ||
|
|
||
| volumeMounts: | ||
| - name: postgres-data | ||
| mountPath: /var/lib/postgresql/data |
There was a problem hiding this comment.
The PostgreSQL deployment is missing resource requests and limits. Database workloads can be resource-intensive, and without these constraints, the pod could consume unbounded CPU and memory, potentially impacting cluster stability. Consider adding appropriate resource specifications.
| type: RollingUpdate | ||
| rollingUpdate: | ||
| maxUnavailable: 0 | ||
| maxSurge: 2 |
There was a problem hiding this comment.
The deployment strategy allows maxUnavailable: 0 with 2 replicas, which is good for zero-downtime deployments. However, maxSurge: 2 means during an update, up to 4 backend pods could be running simultaneously (2 existing + 2 new). This could lead to resource contention. Consider setting maxSurge: 1 to be more conservative with resource usage while still maintaining zero downtime.
| maxSurge: 2 | |
| maxSurge: 1 |
| - name: PERSIST_DIRECTORY | ||
| value: "/chroma_data" | ||
| - name: ANONYMIZED_TELEMETRY | ||
| value: "false" |
There was a problem hiding this comment.
The ChromaDB deployment is missing resource requests and limits. Without these, the pod could consume unbounded CPU and memory, potentially impacting other services in the cluster. Consider adding resource constraints similar to the backend deployment.
| value: "false" | |
| value: "false" | |
| resources: | |
| requests: | |
| cpu: "100m" | |
| memory: "256Mi" | |
| limits: | |
| cpu: "500m" | |
| memory: "512Mi" |
| # ✅ docker-compose와 동일한 의미로 서비스명으로 연결 | ||
| # (ConfigMap/Secret에 DATABASE_URL이 없더라도 여기서 강제 세팅) |
There was a problem hiding this comment.
The comment states 'docker-compose와 동일한 의미로 서비스명으로 연결' but then mentions forcing DATABASE_URL even if not in ConfigMap/Secret. However, this is misleading because DB_USER, DB_PASSWORD, and DB_NAME are referenced from the ConfigMap/Secret (loaded via envFrom), so they should exist. The comment should clarify that this env block is explicitly constructing the DATABASE_URL from those existing variables rather than suggesting they might not exist.
| # ✅ docker-compose와 동일한 의미로 서비스명으로 연결 | |
| # (ConfigMap/Secret에 DATABASE_URL이 없더라도 여기서 강제 세팅) | |
| # ✅ docker-compose와 동일한 의미로 서비스명(postgres)으로 연결 | |
| # envFrom으로 로드된 DB_USER/DB_PASSWORD/DB_NAME을 이용해 DATABASE_URL을 명시적으로 구성 |
| apiVersion: apps/v1 | ||
| kind: Deployment | ||
| metadata: | ||
| name: postgres | ||
| namespace: tva | ||
| spec: | ||
| replicas: 1 | ||
| selector: | ||
| matchLabels: | ||
| app: postgres |
There was a problem hiding this comment.
PostgreSQL is deployed as a Deployment with a PersistentVolumeClaim using ReadWriteOnce access mode. This configuration is not ideal for databases because during updates or node failures, the new pod may fail to start if the PVC is still attached to the old pod on a different node. Consider using a StatefulSet instead, which provides better guarantees for stateful applications and handles persistent storage more reliably.
| apiVersion: apps/v1 | ||
| kind: Deployment | ||
| metadata: | ||
| name: chromadb | ||
| namespace: tva | ||
| spec: | ||
| replicas: 1 | ||
| selector: | ||
| matchLabels: | ||
| app: chromadb |
There was a problem hiding this comment.
ChromaDB is deployed as a Deployment with a PersistentVolumeClaim using ReadWriteOnce access mode. This configuration can cause issues during updates or node failures, as the new pod may fail to start if the PVC is still attached to the old pod on a different node. Consider using a StatefulSet instead for better handling of stateful storage and more reliable updates.
| - name: CHROMADB_HOST | ||
| value: "chromadb" | ||
| - name: CHROMADB_PORT | ||
| value: "8000" | ||
| - name: CHROMA_DB_PATH | ||
| value: "/chroma_data" |
There was a problem hiding this comment.
The environment variables CHROMADB_HOST, CHROMADB_PORT, and CHROMA_DB_PATH are being set here but they are already defined in the ConfigMap (app-config) which is loaded via envFrom. This creates redundancy and potential for configuration drift. Since these values are already in the ConfigMap with the same values, these explicit env entries should be removed to maintain a single source of truth for configuration.
| - name: CHROMADB_HOST | |
| value: "chromadb" | |
| - name: CHROMADB_PORT | |
| value: "8000" | |
| - name: CHROMA_DB_PATH | |
| value: "/chroma_data" |
| - secretRef: | ||
| name: app-secret |
There was a problem hiding this comment.
The backend deployment references 'app-secret' which does not exist in the repository. A Secret resource needs to be created (e.g., 01-secret.yaml) that defines at minimum the DB_PASSWORD key, otherwise the postgres and backend deployments will fail to start due to missing required environment variables.
| # 민감 설정(Secret) | ||
| - name: POSTGRES_PASSWORD | ||
| valueFrom: | ||
| secretKeyRef: | ||
| name: app-secret | ||
| key: DB_PASSWORD |
There was a problem hiding this comment.
The postgres deployment references 'app-secret' which does not exist in the repository. A Secret resource needs to be created that defines the DB_PASSWORD key, otherwise this deployment will fail to start.
No description provided.