diff --git a/helm-charts/common/nginx/README.md b/helm-charts/common/nginx/README.md index af1a25309..03d2b72cd 100644 --- a/helm-charts/common/nginx/README.md +++ b/helm-charts/common/nginx/README.md @@ -2,6 +2,77 @@ Helm chart for deploying OPEA nginx service. -## Using nginx chart +## Integration with OPEA Applications -E2E charts for OPEA applications should provide ConfigMap of name `{{ .Release.Name }}-nginx-config` with the required environment variables to configure [OPEA nginx microservice](https://github.com/opea-project/GenAIComps/blob/main/comps/nginx/nginx.conf.template). +When used as a simple proxy within OPEA application charts (ChatQnA, CodeGen, etc.), parent charts should provide a ConfigMap named `{{ .Release.Name }}-nginx-config` containing environment variables for the [OPEA nginx microservice](https://github.com/opea-project/GenAIComps/blob/main/comps/third_parties/nginx/src/nginx.conf.template). + +## Gateway Mode (Central Router) + +The nginx chart can be configured to act as a central gateway/router for OPEA services. This mode provides: + +- Service routing to multiple OPEA microservices +- Custom nginx configuration template support +- Health check endpoints +- Optional ingress and monitoring support + +### How to Use Gateway Mode + +Gateway mode is designed to be used within E2E (end-to-end) charts. The pattern is: + +1. **Create an E2E chart** that includes nginx as a dependency +2. **Define environment variables** in your E2E chart's `{{ .Release.Name }}-nginx-config` ConfigMap +3. **Use custom nginx template** with environment variable substitution + +### Example Implementation + +```yaml +# In your E2E chart's templates/nginx-config.yaml +apiVersion: v1 +kind: ConfigMap +metadata: + name: {{ .Release.Name }}-nginx-config +data: + FRONTEND_SERVICE_IP: "ui.ui.svc.cluster.local" + FRONTEND_SERVICE_PORT: "5173" + CHATQNA_SERVICE_IP: "chatqna.chatqna.svc.cluster.local" + CHATQNA_SERVICE_PORT: "8888" + EMBEDDING_SERVICE_IP: "embedding-usvc.default.svc.cluster.local" + EMBEDDING_SERVICE_PORT: "6000" + DATAPREP_SERVICE_IP: "data-prep.default.svc.cluster.local" + DATAPREP_SERVICE_PORT: "6007" + # ... other service endpoints +``` + +```yaml +# In your E2E chart's values.yaml +nginx: + nginxConfig: + enabled: true + template: | + server { + location / { + proxy_pass http://${FRONTEND_SERVICE_IP}:${FRONTEND_SERVICE_PORT}; + } + location /v1/chatqna { + proxy_pass http://${CHATQNA_SERVICE_IP}:${CHATQNA_SERVICE_PORT}/v1/chatqna; + } + location /v1/embeddings { + proxy_pass http://${EMBEDDING_SERVICE_IP}:${EMBEDDING_SERVICE_PORT}/v1/embeddings; + } + location /v1/dataprep { + proxy_pass http://${DATAPREP_SERVICE_IP}:${DATAPREP_SERVICE_PORT}/v1/dataprep; + } + # ... other routes + } +``` + +See `gateway-values.yaml` for a complete example template. + +## Deployment Modes + +This chart supports two deployment modes: + +- **Simple Proxy Mode** (default): Acts as a reverse proxy within OPEA application charts +- **Gateway Mode**: Central router providing unified access to multiple OPEA services (used within E2E charts) + +All existing deployments continue to work unchanged. Gateway mode requires creating an E2E chart that follows the pattern shown above. diff --git a/helm-charts/common/nginx/templates/configmap.yaml b/helm-charts/common/nginx/templates/configmap.yaml new file mode 100644 index 000000000..7372873a5 --- /dev/null +++ b/helm-charts/common/nginx/templates/configmap.yaml @@ -0,0 +1,14 @@ +# Copyright (C) 2024 Intel Corporation +# SPDX-License-Identifier: Apache-2.0 + +{{- if .Values.nginxConfig.enabled }} +apiVersion: v1 +kind: ConfigMap +metadata: + name: {{ include "nginx.fullname" . }}-template-config + labels: + {{- include "nginx.labels" . | nindent 4 }} +data: + default.conf.template: | +{{- .Values.nginxConfig.template | nindent 4 }} +{{- end }} diff --git a/helm-charts/common/nginx/templates/deployment.yaml b/helm-charts/common/nginx/templates/deployment.yaml index 7d161cf2d..36d1ebf4f 100644 --- a/helm-charts/common/nginx/templates/deployment.yaml +++ b/helm-charts/common/nginx/templates/deployment.yaml @@ -33,6 +33,12 @@ spec: {{- toYaml .Values.podSecurityContext | nindent 8 }} containers: - name: {{ .Chart.Name }} + {{- if .Values.customCommand.enabled }} + command: + {{- toYaml .Values.customCommand.command | nindent 12 }} + args: + {{- toYaml .Values.customCommand.args | nindent 12 }} + {{- end }} envFrom: - configMapRef: name: {{ .Release.Name }}-nginx-config @@ -61,13 +67,29 @@ spec: {{- end }} resources: {{- toYaml .Values.resources | nindent 12 }} - {{- with .Values.volumeMounts }} + {{- if or .Values.volumeMounts .Values.nginxConfig.enabled }} volumeMounts: + {{- if .Values.nginxConfig.enabled }} + - name: nginx-conf + mountPath: /etc/nginx/templates/ + {{- end }} + {{- with .Values.volumeMounts }} {{- toYaml . | nindent 12 }} + {{- end }} {{- end }} - {{- with .Values.volumes }} + {{- if or .Values.volumes .Values.nginxConfig.enabled }} volumes: + {{- if .Values.nginxConfig.enabled }} + - name: nginx-conf + configMap: + name: {{ include "nginx.fullname" . }}-template-config + items: + - key: default.conf.template + path: default.conf.template + {{- end }} + {{- with .Values.volumes }} {{- toYaml . | nindent 8 }} + {{- end }} {{- end }} {{- with .Values.nodeSelector }} nodeSelector: diff --git a/helm-charts/common/nginx/templates/ingress.yaml b/helm-charts/common/nginx/templates/ingress.yaml new file mode 100644 index 000000000..96e1ab2c8 --- /dev/null +++ b/helm-charts/common/nginx/templates/ingress.yaml @@ -0,0 +1,46 @@ +# Copyright (C) 2024 Intel Corporation +# SPDX-License-Identifier: Apache-2.0 + +{{- if .Values.ingress.enabled }} +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + name: {{ include "nginx.fullname" . }} + labels: + {{- include "nginx.labels" . | nindent 4 }} + {{- with .Values.ingress.annotations }} + annotations: + {{- toYaml . | nindent 4 }} + {{- end }} +spec: + {{- with .Values.ingress.className }} + ingressClassName: {{ . }} + {{- end }} + {{- if .Values.ingress.tls }} + tls: + {{- range .Values.ingress.tls }} + - hosts: + {{- range .hosts }} + - {{ . | quote }} + {{- end }} + secretName: {{ .secretName }} + {{- end }} + {{- end }} + rules: + {{- range .Values.ingress.hosts }} + - host: {{ .host | quote }} + http: + paths: + {{- range .paths }} + - path: {{ .path }} + {{- with .pathType }} + pathType: {{ . }} + {{- end }} + backend: + service: + name: {{ include "nginx.fullname" $ }} + port: + number: {{ $.Values.service.port }} + {{- end }} + {{- end }} +{{- end }} diff --git a/helm-charts/common/nginx/templates/servicemonitor.yaml b/helm-charts/common/nginx/templates/servicemonitor.yaml new file mode 100644 index 000000000..f76983199 --- /dev/null +++ b/helm-charts/common/nginx/templates/servicemonitor.yaml @@ -0,0 +1,18 @@ +# Copyright (C) 2024 Intel Corporation +# SPDX-License-Identifier: Apache-2.0 + +{{- if .Values.global.monitoring }} +apiVersion: monitoring.coreos.com/v1 +kind: ServiceMonitor +metadata: + name: {{ include "nginx.fullname" . }} + labels: + release: {{ .Values.global.prometheusRelease }} +spec: + selector: + matchLabels: + {{- include "nginx.selectorLabels" . | nindent 6 }} + endpoints: + - port: nginx + interval: 5s +{{- end }} diff --git a/helm-charts/common/nginx/values.yaml b/helm-charts/common/nginx/values.yaml index d34f8c5ac..56248f26c 100644 --- a/helm-charts/common/nginx/values.yaml +++ b/helm-charts/common/nginx/values.yaml @@ -102,3 +102,25 @@ global: # If set, it will overwrite serviceAccount.name. # If set, and serviceAccount.create is false, it will assume this service account is already created by others. sharedSAName: "" + +# Custom nginx configuration (optional) +# Allows overriding the default nginx configuration template +nginxConfig: + enabled: false + template: "" + +# Custom container startup commands (optional) +# Allows overriding default container startup behavior +customCommand: + enabled: false + command: [] + args: [] + + +# Ingress configuration (optional, disabled by default) +ingress: + enabled: false + className: "" + annotations: {} + hosts: [] + tls: [] diff --git a/helm-charts/common/nginx/variant_gateway-values.yaml b/helm-charts/common/nginx/variant_gateway-values.yaml new file mode 100644 index 000000000..423477560 --- /dev/null +++ b/helm-charts/common/nginx/variant_gateway-values.yaml @@ -0,0 +1,231 @@ +# Copyright (C) 2024 Intel Corporation +# SPDX-License-Identifier: Apache-2.0 + +# Gateway mode example for nginx chart +# This demonstrates how to use nginx as a central gateway/router +# +# IMPORTANT: This is an example only. In practice, you should: +# 1. Create your own E2E chart that includes this nginx chart as a dependency +# 2. Define environment variables in your E2E chart's {{ .Release.Name }}-nginx-config ConfigMap +# 3. Use the nginx template configuration shown below +# +# For a complete example, see how chatqna chart implements this pattern. + + +# Example: Custom container startup for gateway mode +# The E2E chart should provide environment variables via {{ .Release.Name }}-nginx-config ConfigMap +customCommand: + enabled: true + command: ["/bin/sh", "-c"] + args: + - | + # Environment variables are provided by the E2E chart's ConfigMap + # Example variables: FRONTEND_SERVICE_IP, FRONTEND_SERVICE_PORT, CHATQNA_SERVICE_IP, etc. + envsubst '${FRONTEND_SERVICE_IP} ${FRONTEND_SERVICE_PORT} ${CHATQNA_SERVICE_IP} ${CHATQNA_SERVICE_PORT} ${CODEGEN_SERVICE_IP} ${CODEGEN_SERVICE_PORT} ${DOCSUM_SERVICE_IP} ${DOCSUM_SERVICE_PORT} ${DATAPREP_SERVICE_IP} ${DATAPREP_SERVICE_PORT} ${CHATHISTORY_SERVICE_IP} ${CHATHISTORY_SERVICE_PORT} ${PROMPT_SERVICE_IP} ${PROMPT_SERVICE_PORT}' < /etc/nginx/templates/default.conf.template > /etc/nginx/conf.d/default.conf && + nginx -g 'daemon off;' + +# Example: Custom nginx configuration template for gateway mode +# This template uses environment variables that should be provided by the E2E chart +nginxConfig: + enabled: true + template: | + # Default server configuration + server { + listen 80 default_server; + listen [::]:80 default_server; + + # Server name and root settings + server_name _; + root /usr/share/nginx/html; + index index.html index.htm; + + # Default headers + add_header X-Frame-Options SAMEORIGIN; + add_header X-Content-Type-Options nosniff; + add_header X-XSS-Protection "1; mode=block"; + + # Timeouts + client_max_body_size 10G; + proxy_connect_timeout 600; + proxy_send_timeout 600; + proxy_read_timeout 600; + send_timeout 600; + + # Root path goes to UI + location / { + proxy_pass http://${FRONTEND_SERVICE_IP}:${FRONTEND_SERVICE_PORT}; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + } + + # Health check endpoint + location /v1/health_check { + return 200 'healthy\n'; + add_header Content-Type text/plain; + } + + # ChatQnA service + location /v1/chatqna { + proxy_pass http://${CHATQNA_SERVICE_IP}:${CHATQNA_SERVICE_PORT}/v1/chatqna; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + proxy_buffering off; + proxy_cache off; + proxy_request_buffering off; + gzip off; + } + + # CodeGen service + location /v1/codegen { + proxy_pass http://${CODEGEN_SERVICE_IP}:${CODEGEN_SERVICE_PORT}/v1/codegen; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + proxy_buffering off; + proxy_cache off; + proxy_request_buffering off; + gzip off; + } + + # DocSum service + location /v1/docsum { + proxy_pass http://${DOCSUM_SERVICE_IP}:${DOCSUM_SERVICE_PORT}/v1/docsum; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + proxy_buffering off; + proxy_cache off; + proxy_request_buffering off; + gzip off; + } + + # DataPrep service + location /v1/dataprep { + proxy_pass http://${DATAPREP_SERVICE_IP}:${DATAPREP_SERVICE_PORT}/v1/dataprep; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + proxy_connect_timeout 6000; + proxy_send_timeout 6000; + proxy_read_timeout 6000; + send_timeout 6000; + } + + # Chathistory service + location /v1/chathistory { + proxy_pass http://${CHATHISTORY_SERVICE_IP}:${CHATHISTORY_SERVICE_PORT}/v1/chathistory; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + proxy_buffering off; + proxy_cache off; + proxy_request_buffering off; + gzip off; + } + + # Prompt service + location /v1/prompt { + proxy_pass http://${PROMPT_SERVICE_IP}:${PROMPT_SERVICE_PORT}/v1/prompt; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + proxy_buffering off; + proxy_cache off; + proxy_request_buffering off; + gzip off; + } + + # Keycloak service + location /auth { + proxy_pass http://keycloak.keycloak.svc.cluster.local:80; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + proxy_buffering off; + proxy_cache off; + proxy_request_buffering off; + gzip off; + } + + # Error pages + error_page 404 /404.html; + location = /404.html { + root /usr/share/nginx/html; + } + + error_page 500 502 503 504 /50x.html; + location = /50x.html { + root /usr/share/nginx/html; + } + } + +# Optional: Ingress configuration for gateway mode +# Enable this if you want to expose the gateway via an ingress controller +# Replace the hostname with your actual domain name +ingress: + enabled: false + className: "" # e.g., "nginx", "traefik", etc. + annotations: {} + # kubernetes.io/ingress.class: nginx + # kubernetes.io/tls-acme: "true" + # cert-manager.io/cluster-issuer: "letsencrypt-prod" + hosts: + - host: opea-gateway.yourdomain.com # Replace with your actual hostname + paths: + - path: / + pathType: ImplementationSpecific + tls: [] + # - secretName: opea-gateway-tls + # hosts: + # - opea-gateway.yourdomain.com # Replace with your actual hostname + +# Health check probes for gateway mode +readinessProbe: + httpGet: + path: /v1/health_check + port: nginx + initialDelaySeconds: 5 + periodSeconds: 5 + +startupProbe: + httpGet: + path: /v1/health_check + port: nginx + initialDelaySeconds: 5 + periodSeconds: 5 + failureThreshold: 120 + + +# --- +# Example ConfigMap that your E2E chart should create +# This should be in your E2E chart's templates directory, not in the nginx chart +# +# apiVersion: v1 +# kind: ConfigMap +# metadata: +# name: {{ .Release.Name }}-nginx-config +# data: +# FRONTEND_SERVICE_IP: "ui.ui.svc.cluster.local" +# FRONTEND_SERVICE_PORT: "5173" +# CHATQNA_SERVICE_IP: "chatqna.chatqna.svc.cluster.local" +# CHATQNA_SERVICE_PORT: "8888" +# CODEGEN_SERVICE_IP: "codegen.codegen.svc.cluster.local" +# CODEGEN_SERVICE_PORT: "7778" +# DOCSUM_SERVICE_IP: "docsum.docsum.svc.cluster.local" +# DOCSUM_SERVICE_PORT: "8888" +# DATAPREP_SERVICE_IP: "chatqna-data-prep.chatqna.svc.cluster.local" +# DATAPREP_SERVICE_PORT: "6007" +# CHATHISTORY_SERVICE_IP: "chathistory-usvc.chathistory.svc.cluster.local" +# CHATHISTORY_SERVICE_PORT: "6012" +# PROMPT_SERVICE_IP: "prompt-usvc.prompt.svc.cluster.local" +# PROMPT_SERVICE_PORT: "6018"