feat(helm): add Neo 4.x Helm chart with microservice architecture#9
feat(helm): add Neo 4.x Helm chart with microservice architecture#9
Conversation
New chart at charts/netapp-neo/ for the v4.x multi-service deployment: 6 independently scalable services (api, worker, extractor, ner, ui, postgres) with shared secrets, per-service configmaps, health probes, init containers, GPU support for NER, and optional ingress.
|
@romdalf - could you review this PR for 4.x.x code line support. Thanks in advance. |
There was a problem hiding this comment.
Pull request overview
Adds a new Helm chart (charts/netapp-neo/) to deploy NetApp NEO v4.x as a multi-service (microservice-style) application with optional in-cluster PostgreSQL and shared configuration/secrets.
Changes:
- Introduces a new Helm chart definition and default values for API/worker/extractor/NER/UI/PostgreSQL.
- Adds Kubernetes manifests (Deployments/Services/Ingresses/ConfigMaps/Secrets) for each service plus optional PostgreSQL StatefulSet.
- Adds helper templates for inter-service URLs, DATABASE_URL construction, and a reusable wait-for-db initContainer.
Reviewed changes
Copilot reviewed 29 out of 29 changed files in this pull request and generated 9 comments.
Show a summary per file
| File | Description |
|---|---|
| charts/netapp-neo/Chart.yaml | New chart metadata for netapp-neo v4.x (appVersion/version). |
| charts/netapp-neo/values.yaml | Default configuration for all services, ingress, GPU toggles, and PostgreSQL settings. |
| charts/netapp-neo/templates/_helpers.tpl | Template helpers for naming/labels, inter-service URLs, DATABASE_URL, image tags, and DB wait initContainer. |
| charts/netapp-neo/templates/NOTES.txt | Post-install guidance and endpoint summary for deployed services/ingresses. |
| charts/netapp-neo/templates/api-deployment.yaml | API Deployment wiring env/config/secrets, probes, and optional DB init container. |
| charts/netapp-neo/templates/api-service.yaml | API Service definition (incl. sessionAffinity). |
| charts/netapp-neo/templates/api-ingress.yaml | Optional API Ingress template. |
| charts/netapp-neo/templates/configmap-api.yaml | API ConfigMap for non-secret runtime settings and internal URLs. |
| charts/netapp-neo/templates/worker-deployment.yaml | Worker Deployment wiring env/config/secrets, probes, and optional DB init container. |
| charts/netapp-neo/templates/worker-service.yaml | Worker Service definition for internal API→worker communication. |
| charts/netapp-neo/templates/configmap-worker.yaml | Worker ConfigMap for concurrency and internal service URLs. |
| charts/netapp-neo/templates/extractor-deployment.yaml | Extractor Deployment wiring env/config/secrets, probes, privileged securityContext, and optional DB init container. |
| charts/netapp-neo/templates/extractor-service.yaml | Extractor Service definition for internal worker→extractor communication. |
| charts/netapp-neo/templates/configmap-extractor.yaml | Extractor ConfigMap for extractor/VLM/OpenAI non-secret settings. |
| charts/netapp-neo/templates/ner-deployment.yaml | NER Deployment wiring env/config, probes, and optional GPU resource limits. |
| charts/netapp-neo/templates/ner-service.yaml | NER Service definition for internal calls. |
| charts/netapp-neo/templates/configmap-ner.yaml | NER ConfigMap for model/runtime settings. |
| charts/netapp-neo/templates/ui-deployment.yaml | UI Deployment wiring NEO_API env var and probes. |
| charts/netapp-neo/templates/ui-service.yaml | UI Service definition. |
| charts/netapp-neo/templates/ui-ingress.yaml | Optional UI Ingress template. |
| charts/netapp-neo/templates/postgres-statefulset.yaml | Optional PostgreSQL StatefulSet with probes and persistence configuration. |
| charts/netapp-neo/templates/postgres-service.yaml | Optional PostgreSQL Service for in-cluster DB access. |
| charts/netapp-neo/templates/secret-db.yaml | Secret for DATABASE_URL and PostgreSQL credentials. |
| charts/netapp-neo/templates/secret-encryption.yaml | Shared encryption key Secret. |
| charts/netapp-neo/templates/secret-jwt.yaml | API JWT Secret. |
| charts/netapp-neo/templates/secret-license.yaml | Shared license/connector id Secret. |
| charts/netapp-neo/templates/secret-graph.yaml | Microsoft Graph credential Secret. |
| charts/netapp-neo/templates/secret-mcp.yaml | MCP OAuth/API key Secret for API integration. |
| charts/netapp-neo/templates/secret-openai.yaml | OpenAI API key Secret for extractor. |
| postgresql: | ||
| enabled: true | ||
| name: neo-postgres | ||
| image: | ||
| repository: docker.io/library/postgres | ||
| tag: "17-alpine" | ||
| pullPolicy: IfNotPresent | ||
| auth: | ||
| username: neo | ||
| password: neo_password | ||
| database: neo_connector |
There was a problem hiding this comment.
PostgreSQL is enabled by default and ships with a well-known default password (neo_password). This can lead to insecure by-default installs if users don’t override values. Consider defaulting postgresql.enabled to false and/or requiring the password to be provided (or generating it and persisting it via lookup on upgrade) instead of hard-coding a default.
| DATABASE_URL: {{ include "netapp-neo.databaseUrl" . | quote }} | ||
| POSTGRES_USER: {{ .Values.postgresql.auth.username | quote }} | ||
| POSTGRES_PASSWORD: {{ .Values.postgresql.auth.password | quote }} | ||
| POSTGRES_DB: {{ .Values.postgresql.auth.database | quote }} |
There was a problem hiding this comment.
This Secret always includes POSTGRES_USER/POSTGRES_PASSWORD/POSTGRES_DB even when postgresql.enabled=false (external DB mode). That stores potentially unused credentials in-cluster and can mislead users about which values are in effect. Consider only emitting these keys when postgresql.enabled is true (and relying on DATABASE_URL alone for external mode).
| DATABASE_URL: {{ include "netapp-neo.databaseUrl" . | quote }} | |
| POSTGRES_USER: {{ .Values.postgresql.auth.username | quote }} | |
| POSTGRES_PASSWORD: {{ .Values.postgresql.auth.password | quote }} | |
| POSTGRES_DB: {{ .Values.postgresql.auth.database | quote }} | |
| DATABASE_URL: {{ include "netapp-neo.databaseUrl" . | quote }} | |
| {{- if .Values.postgresql.enabled }} | |
| POSTGRES_USER: {{ .Values.postgresql.auth.username | quote }} | |
| POSTGRES_PASSWORD: {{ .Values.postgresql.auth.password | quote }} | |
| POSTGRES_DB: {{ .Values.postgresql.auth.database | quote }} | |
| {{- end }} |
| {{- if .Values.postgresql.persistence.enabled }} | ||
| volumeClaimTemplates: | ||
| - metadata: | ||
| name: data | ||
| {{- with .Values.postgresql.persistence.annotations }} | ||
| annotations: | ||
| {{- toYaml . | nindent 10 }} | ||
| {{- end }} | ||
| spec: | ||
| accessModes: | ||
| - {{ .Values.postgresql.persistence.accessMode }} | ||
| {{- if .Values.postgresql.persistence.storageClass }} | ||
| storageClassName: {{ .Values.postgresql.persistence.storageClass }} | ||
| {{- end }} | ||
| resources: | ||
| requests: | ||
| storage: {{ .Values.postgresql.persistence.size }} | ||
| {{- else }} | ||
| volumeClaimTemplates: | ||
| - metadata: | ||
| name: data | ||
| spec: | ||
| accessModes: | ||
| - ReadWriteOnce | ||
| resources: | ||
| requests: | ||
| storage: 1Gi | ||
| {{- end }} |
There was a problem hiding this comment.
When postgresql.persistence.enabled is false, this StatefulSet still defines volumeClaimTemplates (and will still request storage). This makes the persistence toggle ineffective and can cause unexpected PVC provisioning/failures. If persistence is disabled, use an emptyDir volume (or omit the PVC template entirely) instead of creating a PVC.
| {{- if .Values.ner.gpu.enabled }} | ||
| resources: | ||
| limits: | ||
| nvidia.com/gpu: {{ .Values.ner.gpu.count }} | ||
| {{- else if .Values.ner.resources }} | ||
| resources: | ||
| {{- toYaml .Values.ner.resources | nindent 12 }} | ||
| {{- end }} |
There was a problem hiding this comment.
With ner.gpu.enabled=true, the template hard-codes resources.limits.nvidia.com/gpu and ignores any CPU/memory requests/limits users might set in ner.resources. This makes it hard to run reliably on GPU nodes (OOM/evictions). Consider merging GPU limits into ner.resources (or supporting separate ner.gpu.resources overrides) instead of replacing the entire resources block.
| # Encryption | ||
| ENCRYPTION_KEY: "" | ||
| # License | ||
| NETAPP_CONNECTOR_LICENSE: "" | ||
| CONNECTOR_ID: "netappneo" |
There was a problem hiding this comment.
These worker.env settings (e.g., ENCRYPTION_KEY, NETAPP_CONNECTOR_LICENSE, CONNECTOR_ID) are not referenced by the templates: the worker Deployment sources these values from the shared -encryption / -license Secrets which are populated from api.env.*. Keeping unused values here is confusing and makes it unclear which values actually take effect; consider removing them or wiring the templates to use worker.env.* consistently.
| # Encryption | |
| ENCRYPTION_KEY: "" | |
| # License | |
| NETAPP_CONNECTOR_LICENSE: "" | |
| CONNECTOR_ID: "netappneo" |
| # Encryption | ||
| ENCRYPTION_KEY: "" |
There was a problem hiding this comment.
extractor.env.ENCRYPTION_KEY is defined in values, but the extractor Deployment reads ENCRYPTION_KEY from the shared -encryption Secret (populated from api.env.ENCRYPTION_KEY). This value currently has no effect; consider removing it from extractor.env or updating the templates so the extractor’s encryption key is sourced from extractor.env (or a clearly named shared section).
| # Encryption | |
| ENCRYPTION_KEY: "" |
| */}} | ||
| {{- define "netapp-neo.databaseUrl" -}} | ||
| {{- if .Values.postgresql.enabled -}} | ||
| postgresql://{{ .Values.postgresql.auth.username }}:{{ .Values.postgresql.auth.password }}@{{ include "netapp-neo.fullname" . }}-postgres:{{ .Values.postgresql.service.port }}/{{ .Values.postgresql.auth.database }} |
There was a problem hiding this comment.
netapp-neo.databaseUrl interpolates username/password/database directly into the URL. If any of these contain reserved URL characters (e.g., @, :, /, #), the resulting DATABASE_URL will be invalid. Consider URL-encoding the components (e.g., via sprig functions) before constructing the connection string.
| postgresql://{{ .Values.postgresql.auth.username }}:{{ .Values.postgresql.auth.password }}@{{ include "netapp-neo.fullname" . }}-postgres:{{ .Values.postgresql.service.port }}/{{ .Values.postgresql.auth.database }} | |
| postgresql://{{ .Values.postgresql.auth.username | urlquery }}:{{ .Values.postgresql.auth.password | urlquery }}@{{ include "netapp-neo.fullname" . }}-postgres:{{ .Values.postgresql.service.port }}/{{ .Values.postgresql.auth.database | urlquery }} |
| # Microsoft Graph | ||
| MS_GRAPH_TENANT_ID: "" | ||
| MS_GRAPH_CLIENT_ID: "" | ||
| MS_GRAPH_CLIENT_SECRET: "" | ||
| MS_GRAPH_CONNECTOR_ID: "netappneo" |
There was a problem hiding this comment.
These worker.env Microsoft Graph credentials are also not used by the templates: worker consumes Graph settings via the shared -graph Secret populated from api.env.*. Either remove these unused values or update the Secret/template wiring so the worker’s configuration is sourced from worker.env.* as the values file suggests.
| # Microsoft Graph | |
| MS_GRAPH_TENANT_ID: "" | |
| MS_GRAPH_CLIENT_ID: "" | |
| MS_GRAPH_CLIENT_SECRET: "" | |
| MS_GRAPH_CONNECTOR_ID: "netappneo" |
| # ============================================================================= | ||
| postgresql: | ||
| enabled: true | ||
| name: neo-postgres |
There was a problem hiding this comment.
postgresql.name is defined here but the templates always name PostgreSQL resources as {{ include "netapp-neo.fullname" . }}-postgres and never reference this value. Consider removing postgresql.name to avoid confusion, or update the templates to honor it.
| name: neo-postgres |
|
With a helm lint, was good... just the nit: While deploying, I got the following warning: The chart was deployed, and I got the extractor in a crash backoff loop, which I am 100% sure I am doing something wrong. I am guessing I might need to point to a directory to ingest/chunk documents: Other than that, just from a chart/yaml perspective... this looks good to me. |
Summary
charts/netapp-neo/for the v4.x multi-service architectureDesign highlights
postgresql.auth.*when built-in postgres enabled, or passthrough viapostgresql.externalDatabaseUrlSYS_ADMIN+DAC_READ_SEARCHcaps (not privileged), extractor getsprivileged: true, api/worker run as UID 1000ner.gpu.enabled, addsnvidia.com/gpuresource limits + nodeSelector/tolerationswait-for-db) on api, worker, extractor — conditional onpostgresql.enabledsessionAffinity: ClientIPTest plan
helm lint charts/netapp-neo— passed (0 failures)helm template test charts/netapp-neo— all 26 manifests render correctlyprivileged: truener.gpu.enabled=truepostgresql.enabled=falseomits StatefulSet, Service, and init containers