fix: Web Widget SDK + Auto-Reply AgentBot sender + LLM 真实模型对接
## 核心修复 ### 1. Auto-Reply Sender 修复(所有渠道) - AutoReplyListener.sendAutoReply() 通过 botInboxRepo 查询 inbox 关联的 AgentBot - 使用正确的 SenderType="AgentBot"(非小写 agent_bot)传递真实 AgentBot ID - bootstrap 注入 agentBotInboxRepo/agentBotRepo 依赖 ### 2. 事件数据 BUG 修复(影响所有 Webhook 渠道) - incoming_persister.dispatch(): 补全 sender_type/content 到 event.Data - channel/webhook.go: HandleWebhook 同步分发也补全 sender_type/content - 未补全前 AutoReplyListener 找不到字段直接跳过 ### 3. Web Widget SDK 生产验证修复 - cookie → localStorage token 同步(frontend/index.html) - 路由双注册修复(router.go) - Vite SPA 模式 + /widget 重写(vite.config.ts) - WidgetService 注入 Dispatcher 触发事件分发 ### 4. LLM 真实模型对接 - 配置 deepseek-v4-flash @ http://10.58.144.6:2014/v1 - LLM-mode auto-reply 规则创建并验证通过 - Prompt 文档落地: docs/captain-ai-auto-replay-prompt.md ### 5. 新增基础设施 - Helm chart (deploy/helm/) - Widget SDK 生产测试页面 - QA 报告 Closes: BUG-W2 (auth sync), BUG-W3 (route double-reg), BUG-WEBHOOK-EVENT (missing event data fields)
This commit is contained in:
@@ -0,0 +1,19 @@
|
||||
apiVersion: v2
|
||||
name: gochat
|
||||
description: GoChat — Open-source customer engagement platform (Go port of Chatwoot)
|
||||
type: application
|
||||
version: 0.1.0
|
||||
appVersion: "1.0.0"
|
||||
home: https://github.com/gochat/gochat
|
||||
icon: https://gochat.io/logo.png
|
||||
maintainers:
|
||||
- name: gochat-team
|
||||
email: team@gochat.io
|
||||
sources:
|
||||
- https://github.com/gochat/gochat
|
||||
keywords:
|
||||
- chat
|
||||
- customer-engagement
|
||||
- live-chat
|
||||
- omnichannel
|
||||
- chatwoot
|
||||
@@ -0,0 +1,49 @@
|
||||
{{/*
|
||||
Expand the name of the chart.
|
||||
*/}}
|
||||
{{- define "gochat.name" -}}
|
||||
{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }}
|
||||
{{- end }}
|
||||
|
||||
{{/*
|
||||
Create a default fully qualified app name.
|
||||
*/}}
|
||||
{{- define "gochat.fullname" -}}
|
||||
{{- if .Values.fullnameOverride }}
|
||||
{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" }}
|
||||
{{- else }}
|
||||
{{- $name := default .Chart.Name .Values.nameOverride }}
|
||||
{{- if contains $name .Release.Name }}
|
||||
{{- .Release.Name | trunc 63 | trimSuffix "-" }}
|
||||
{{- else }}
|
||||
{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
|
||||
{{/*
|
||||
Create chart name and version as used by the chart label.
|
||||
*/}}
|
||||
{{- define "gochat.chart" -}}
|
||||
{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }}
|
||||
{{- end }}
|
||||
|
||||
{{/*
|
||||
Common labels
|
||||
*/}}
|
||||
{{- define "gochat.labels" -}}
|
||||
helm.sh/chart: {{ include "gochat.chart" . }}
|
||||
{{ include "gochat.selectorLabels" . }}
|
||||
{{- if .Chart.AppVersion }}
|
||||
app.kubernetes.io/version: {{ .Chart.AppVersion | quote }}
|
||||
{{- end }}
|
||||
app.kubernetes.io/managed-by: {{ .Release.Service }}
|
||||
{{- end }}
|
||||
|
||||
{{/*
|
||||
Selector labels
|
||||
*/}}
|
||||
{{- define "gochat.selectorLabels" -}}
|
||||
app.kubernetes.io/name: {{ include "gochat.name" . }}
|
||||
app.kubernetes.io/instance: {{ .Release.Name }}
|
||||
{{- end }}
|
||||
@@ -0,0 +1,15 @@
|
||||
apiVersion: v1
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
name: {{ include "gochat.fullname" . }}-config
|
||||
labels:
|
||||
{{- include "gochat.labels" . | nindent 4 }}
|
||||
data:
|
||||
{{- range $key, $value := .Values.configMap.data }}
|
||||
{{ $key }}: {{ $value | quote }}
|
||||
{{- end }}
|
||||
POSTGRES_HOST: {{ if .Values.postgresql.enabled }}{{ include "gochat.fullname" . }}-postgresql{{ else }}{{ .Values.configMap.data.POSTGRES_HOST | default "localhost" }}{{ end }}
|
||||
POSTGRES_PORT: "5432"
|
||||
POSTGRES_DATABASE: {{ .Values.postgresql.auth.database | quote }}
|
||||
REDIS_HOST: {{ if .Values.redis.enabled }}{{ include "gochat.fullname" . }}-redis-master{{ else }}{{ .Values.configMap.data.REDIS_HOST | default "localhost" }}{{ end }}
|
||||
REDIS_PORT: "6379"
|
||||
@@ -0,0 +1,82 @@
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: {{ include "gochat.fullname" . }}
|
||||
labels:
|
||||
{{- include "gochat.labels" . | nindent 4 }}
|
||||
spec:
|
||||
replicas: {{ .Values.app.replicaCount }}
|
||||
selector:
|
||||
matchLabels:
|
||||
{{- include "gochat.selectorLabels" . | nindent 6 }}
|
||||
strategy:
|
||||
{{- toYaml .Values.app.strategy | nindent 4 }}
|
||||
template:
|
||||
metadata:
|
||||
annotations:
|
||||
{{- toYaml .Values.app.podAnnotations | nindent 8 }}
|
||||
labels:
|
||||
{{- include "gochat.selectorLabels" . | nindent 8 }}
|
||||
spec:
|
||||
terminationGracePeriodSeconds: {{ .Values.app.terminationGracePeriodSeconds }}
|
||||
containers:
|
||||
- name: gochat
|
||||
image: "{{ .Values.app.image.repository }}:{{ .Values.app.image.tag }}"
|
||||
imagePullPolicy: {{ .Values.app.image.pullPolicy }}
|
||||
ports:
|
||||
- name: http
|
||||
containerPort: 3000
|
||||
protocol: TCP
|
||||
{{- if .Values.metrics.enabled }}
|
||||
- name: metrics
|
||||
containerPort: {{ .Values.metrics.service.port }}
|
||||
protocol: TCP
|
||||
{{- end }}
|
||||
envFrom:
|
||||
- configMapRef:
|
||||
name: {{ include "gochat.fullname" . }}-config
|
||||
- secretRef:
|
||||
name: {{ include "gochat.fullname" . }}-secret
|
||||
{{- if .Values.tracing.enabled }}
|
||||
- configMapRef:
|
||||
name: {{ include "gochat.fullname" . }}-otel-config
|
||||
{{- end }}
|
||||
{{- range $key, $value := .Values.app.extraEnv }}
|
||||
env:
|
||||
- name: {{ $key }}
|
||||
value: {{ $value | quote }}
|
||||
{{- end }}
|
||||
lifecycle:
|
||||
preStop:
|
||||
exec:
|
||||
command: ["sh", "-c", "sleep {{ .Values.app.preStopDelaySeconds }}"]
|
||||
livenessProbe:
|
||||
{{- toYaml .Values.app.livenessProbe | nindent 12 }}
|
||||
readinessProbe:
|
||||
{{- toYaml .Values.app.readinessProbe | nindent 12 }}
|
||||
resources:
|
||||
{{- toYaml .Values.app.resources | nindent 12 }}
|
||||
volumeMounts:
|
||||
- name: configs
|
||||
mountPath: /app/configs
|
||||
- name: migrations
|
||||
mountPath: /app/migrations
|
||||
volumes:
|
||||
- name: configs
|
||||
configMap:
|
||||
name: {{ include "gochat.fullname" . }}-configs
|
||||
- name: migrations
|
||||
configMap:
|
||||
name: {{ include "gochat.fullname" . }}-migrations
|
||||
{{- with .Values.app.nodeSelector }}
|
||||
nodeSelector:
|
||||
{{- toYaml . | nindent 8 }}
|
||||
{{- end }}
|
||||
{{- with .Values.app.affinity }}
|
||||
affinity:
|
||||
{{- toYaml . | nindent 8 }}
|
||||
{{- end }}
|
||||
{{- with .Values.app.tolerations }}
|
||||
tolerations:
|
||||
{{- toYaml . | nindent 8 }}
|
||||
{{- end }}
|
||||
@@ -0,0 +1,27 @@
|
||||
{{- if and .Values.sealedSecrets.enabled .Values.sealedSecrets.externalSecret.enabled }}
|
||||
apiVersion: external-secrets.io/v1beta1
|
||||
kind: ExternalSecret
|
||||
metadata:
|
||||
name: {{ include "gochat.fullname" . }}-external-secret
|
||||
labels:
|
||||
{{- include "gochat.labels" . | nindent 4 }}
|
||||
spec:
|
||||
refreshInterval: {{ .Values.sealedSecrets.externalSecret.refreshInterval }}
|
||||
secretStoreRef:
|
||||
name: {{ .Values.sealedSecrets.externalSecret.secretStoreRef.name }}
|
||||
kind: {{ .Values.sealedSecrets.externalSecret.secretStoreRef.kind }}
|
||||
target:
|
||||
name: {{ include "gochat.fullname" . }}-secret
|
||||
template:
|
||||
type: Opaque
|
||||
data:
|
||||
{{- range $key, $remoteKey := .Values.sealedSecrets.externalSecret.mapping }}
|
||||
{{ $key }}: "{{ `{{ .` }}{{ $remoteKey }}{{ ` }}` }}"
|
||||
{{- end }}
|
||||
data:
|
||||
{{- range $key, $remoteKey := .Values.sealedSecrets.externalSecret.mapping }}
|
||||
- secretKey: {{ $key }}
|
||||
remoteRef:
|
||||
key: {{ $remoteKey }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
@@ -0,0 +1,49 @@
|
||||
{{- if .Values.autoscaling.enabled }}
|
||||
apiVersion: autoscaling/v2
|
||||
kind: HorizontalPodAutoscaler
|
||||
metadata:
|
||||
name: {{ include "gochat.fullname" . }}
|
||||
labels:
|
||||
{{- include "gochat.labels" . | nindent 4 }}
|
||||
spec:
|
||||
scaleTargetRef:
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
name: {{ include "gochat.fullname" . }}
|
||||
minReplicas: {{ .Values.autoscaling.minReplicas }}
|
||||
maxReplicas: {{ .Values.autoscaling.maxReplicas }}
|
||||
metrics:
|
||||
{{- if .Values.autoscaling.targetCPUUtilizationPercentage }}
|
||||
- type: Resource
|
||||
resource:
|
||||
name: cpu
|
||||
target:
|
||||
type: Utilization
|
||||
averageUtilization: {{ .Values.autoscaling.targetCPUUtilizationPercentage }}
|
||||
{{- end }}
|
||||
{{- if .Values.autoscaling.targetMemoryUtilizationPercentage }}
|
||||
- type: Resource
|
||||
resource:
|
||||
name: memory
|
||||
target:
|
||||
type: Utilization
|
||||
averageUtilization: {{ .Values.autoscaling.targetMemoryUtilizationPercentage }}
|
||||
{{- end }}
|
||||
behavior:
|
||||
scaleDown:
|
||||
stabilizationWindowSeconds: {{ .Values.autoscaling.behavior.scaleDown.stabilizationWindowSeconds }}
|
||||
policies:
|
||||
- type: Percent
|
||||
value: {{ .Values.autoscaling.behavior.scaleDown.percent }}
|
||||
periodSeconds: {{ .Values.autoscaling.behavior.scaleDown.periodSeconds }}
|
||||
scaleUp:
|
||||
stabilizationWindowSeconds: {{ .Values.autoscaling.behavior.scaleUp.stabilizationWindowSeconds }}
|
||||
policies:
|
||||
- type: Percent
|
||||
value: {{ .Values.autoscaling.behavior.scaleUp.percent }}
|
||||
periodSeconds: {{ .Values.autoscaling.behavior.scaleUp.periodSeconds }}
|
||||
- type: Pods
|
||||
value: {{ .Values.autoscaling.behavior.scaleUp.pods }}
|
||||
periodSeconds: {{ .Values.autoscaling.behavior.scaleUp.podsPeriodSeconds }}
|
||||
selectPolicy: Max
|
||||
{{- end }}
|
||||
@@ -0,0 +1,37 @@
|
||||
{{- if .Values.ingress.enabled }}
|
||||
apiVersion: networking.k8s.io/v1
|
||||
kind: Ingress
|
||||
metadata:
|
||||
name: {{ include "gochat.fullname" . }}
|
||||
labels:
|
||||
{{- include "gochat.labels" . | nindent 4 }}
|
||||
annotations:
|
||||
{{- toYaml .Values.ingress.annotations | nindent 4 }}
|
||||
spec:
|
||||
ingressClassName: {{ .Values.ingress.className }}
|
||||
{{- 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 }}
|
||||
pathType: {{ .pathType }}
|
||||
backend:
|
||||
service:
|
||||
name: {{ include "gochat.fullname" $ }}
|
||||
port:
|
||||
number: {{ $.Values.app.service.port }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
@@ -0,0 +1,83 @@
|
||||
{{- if .Values.tracing.enabled }}
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: {{ include "gochat.fullname" . }}-jaeger
|
||||
labels:
|
||||
{{- include "gochat.labels" . | nindent 4 }}
|
||||
app.kubernetes.io/component: jaeger
|
||||
spec:
|
||||
replicas: 1
|
||||
selector:
|
||||
matchLabels:
|
||||
{{- include "gochat.selectorLabels" . | nindent 6 }}
|
||||
app.kubernetes.io/component: jaeger
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
{{- include "gochat.selectorLabels" . | nindent 8 }}
|
||||
app.kubernetes.io/component: jaeger
|
||||
spec:
|
||||
containers:
|
||||
- name: jaeger
|
||||
image: "{{ .Values.tracing.jaeger.image.repository }}:{{ .Values.tracing.jaeger.image.tag }}"
|
||||
imagePullPolicy: {{ .Values.tracing.jaeger.image.pullPolicy }}
|
||||
ports:
|
||||
- name: otlp-grpc
|
||||
containerPort: 4317
|
||||
protocol: TCP
|
||||
- name: otlp-http
|
||||
containerPort: 4318
|
||||
protocol: TCP
|
||||
- name: jaeger-query
|
||||
containerPort: 16686
|
||||
protocol: TCP
|
||||
- name: jaeger-admin
|
||||
containerPort: 14269
|
||||
protocol: TCP
|
||||
env:
|
||||
- name: COLLECTOR_OTLP_ENABLED
|
||||
value: "true"
|
||||
- name: LOG_LEVEL
|
||||
value: {{ .Values.tracing.jaeger.logLevel | quote }}
|
||||
resources:
|
||||
{{- toYaml .Values.tracing.jaeger.resources | nindent 12 }}
|
||||
livenessProbe:
|
||||
httpGet:
|
||||
path: /
|
||||
port: 14269
|
||||
initialDelaySeconds: 5
|
||||
periodSeconds: 15
|
||||
readinessProbe:
|
||||
httpGet:
|
||||
path: /
|
||||
port: 14269
|
||||
initialDelaySeconds: 5
|
||||
periodSeconds: 15
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: {{ include "gochat.fullname" . }}-jaeger
|
||||
labels:
|
||||
{{- include "gochat.labels" . | nindent 4 }}
|
||||
app.kubernetes.io/component: jaeger
|
||||
spec:
|
||||
type: ClusterIP
|
||||
ports:
|
||||
- name: otlp-grpc
|
||||
port: 4317
|
||||
targetPort: otlp-grpc
|
||||
protocol: TCP
|
||||
- name: otlp-http
|
||||
port: 4318
|
||||
targetPort: otlp-http
|
||||
protocol: TCP
|
||||
- name: query
|
||||
port: 16686
|
||||
targetPort: jaeger-query
|
||||
protocol: TCP
|
||||
selector:
|
||||
{{- include "gochat.selectorLabels" . | nindent 4 }}
|
||||
app.kubernetes.io/component: jaeger
|
||||
{{- end }}
|
||||
@@ -0,0 +1,17 @@
|
||||
{{- if .Values.metrics.enabled }}
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: {{ include "gochat.fullname" . }}-metrics
|
||||
labels:
|
||||
{{- include "gochat.labels" . | nindent 4 }}
|
||||
spec:
|
||||
type: {{ .Values.metrics.service.type }}
|
||||
ports:
|
||||
- port: {{ .Values.metrics.service.port }}
|
||||
targetPort: metrics
|
||||
protocol: TCP
|
||||
name: metrics
|
||||
selector:
|
||||
{{- include "gochat.selectorLabels" . | nindent 4 }}
|
||||
{{- end }}
|
||||
@@ -0,0 +1,97 @@
|
||||
{{- if .Values.networkPolicy.enabled }}
|
||||
apiVersion: networking.k8s.io/v1
|
||||
kind: NetworkPolicy
|
||||
metadata:
|
||||
name: {{ include "gochat.fullname" . }}-allow-ingress
|
||||
labels:
|
||||
{{- include "gochat.labels" . | nindent 4 }}
|
||||
spec:
|
||||
podSelector:
|
||||
matchLabels:
|
||||
{{- include "gochat.selectorLabels" . | nindent 6 }}
|
||||
policyTypes:
|
||||
- Ingress
|
||||
- Egress
|
||||
ingress:
|
||||
# Allow traffic from Ingress controller (nginx)
|
||||
- from:
|
||||
- namespaceSelector:
|
||||
matchLabels:
|
||||
{{- toYaml .Values.networkPolicy.ingressNamespaceLabels | nindent 12 }}
|
||||
ports:
|
||||
- protocol: TCP
|
||||
port: {{ .Values.app.service.port }}
|
||||
# Allow Prometheus scraping for metrics
|
||||
- from:
|
||||
- namespaceSelector:
|
||||
matchLabels:
|
||||
{{- toYaml .Values.networkPolicy.monitoringNamespaceLabels | nindent 12 }}
|
||||
ports:
|
||||
- protocol: TCP
|
||||
port: {{ .Values.metrics.service.port }}
|
||||
# Allow internal pod-to-pod communication (app ↔ worker)
|
||||
- from:
|
||||
- podSelector:
|
||||
matchLabels:
|
||||
{{- include "gochat.selectorLabels" . | nindent 12 }}
|
||||
ports:
|
||||
- protocol: TCP
|
||||
port: {{ .Values.app.service.port }}
|
||||
---
|
||||
# Egress policy: allow DNS, PostgreSQL, Redis, and outbound HTTPS
|
||||
apiVersion: networking.k8s.io/v1
|
||||
kind: NetworkPolicy
|
||||
metadata:
|
||||
name: {{ include "gochat.fullname" . }}-allow-egress
|
||||
labels:
|
||||
{{- include "gochat.labels" . | nindent 4 }}
|
||||
spec:
|
||||
podSelector:
|
||||
matchLabels:
|
||||
{{- include "gochat.selectorLabels" . | nindent 6 }}
|
||||
policyTypes:
|
||||
- Egress
|
||||
egress:
|
||||
# Allow DNS resolution (kube-dns)
|
||||
- to:
|
||||
- namespaceSelector:
|
||||
matchLabels:
|
||||
kubernetes.io/metadata.name: kube-system
|
||||
- podSelector:
|
||||
matchLabels:
|
||||
k8s-app: kube-dns
|
||||
ports:
|
||||
- protocol: UDP
|
||||
port: 53
|
||||
- protocol: TCP
|
||||
port: 53
|
||||
# Allow PostgreSQL connection
|
||||
- to:
|
||||
- podSelector:
|
||||
matchLabels:
|
||||
app.kubernetes.io/name: postgresql
|
||||
ports:
|
||||
- protocol: TCP
|
||||
port: 5432
|
||||
# Allow Redis connection
|
||||
- to:
|
||||
- podSelector:
|
||||
matchLabels:
|
||||
app.kubernetes.io/name: redis
|
||||
ports:
|
||||
- protocol: TCP
|
||||
port: 6379
|
||||
# Allow outbound HTTPS (LLM APIs, webhook callbacks, SMTP)
|
||||
- to:
|
||||
- ipBlock:
|
||||
cidr: 0.0.0.0/0
|
||||
except:
|
||||
- 10.0.0.0/8
|
||||
- 172.16.0.0/12
|
||||
- 192.168.0.0/16
|
||||
ports:
|
||||
- protocol: TCP
|
||||
port: 443
|
||||
- protocol: TCP
|
||||
port: 587
|
||||
{{- end }}
|
||||
@@ -0,0 +1,21 @@
|
||||
{{- if .Values.tracing.enabled }}
|
||||
apiVersion: v1
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
name: {{ include "gochat.fullname" . }}-otel-config
|
||||
labels:
|
||||
{{- include "gochat.labels" . | nindent 4 }}
|
||||
data:
|
||||
OTEL_SERVICE_NAME: "{{ include "gochat.fullname" . }}"
|
||||
OTEL_EXPORTER_OTLP_ENDPOINT: "{{ .Values.tracing.otlp.endpoint }}"
|
||||
OTEL_EXPORTER_OTLP_PROTOCOL: "{{ .Values.tracing.otlp.protocol }}"
|
||||
OTEL_TRACES_SAMPLER: "{{ .Values.tracing.sampler.type }}"
|
||||
OTEL_TRACES_SAMPLER_ARG: "{{ .Values.tracing.sampler.arg }}"
|
||||
OTEL_PROPAGATORS: "{{ .Values.tracing.propagators }}"
|
||||
OTEL_RESOURCE_ATTRIBUTES: "service.name={{ include "gochat.fullname" . }},service.version={{ .Values.app.image.tag }},deployment.environment={{ .Values.global.environment }}"
|
||||
OTEL_LOG_LEVEL: "{{ .Values.tracing.logLevel }}"
|
||||
OTEL_EXPORTER_OTLP_TIMEOUT: "{{ .Values.tracing.otlp.timeout }}"
|
||||
OTEL_BSP_SCHEDULE_DELAY: "5000"
|
||||
OTEL_BSP_MAX_QUEUE_SIZE: "2048"
|
||||
OTEL_BSP_MAX_EXPORT_BATCH_SIZE: "512"
|
||||
{{- end }}
|
||||
@@ -0,0 +1,18 @@
|
||||
{{- if .Values.podDisruptionBudget.enabled }}
|
||||
apiVersion: policy/v1
|
||||
kind: PodDisruptionBudget
|
||||
metadata:
|
||||
name: {{ include "gochat.fullname" . }}
|
||||
labels:
|
||||
{{- include "gochat.labels" . | nindent 4 }}
|
||||
spec:
|
||||
{{- if .Values.podDisruptionBudget.minAvailable }}
|
||||
minAvailable: {{ .Values.podDisruptionBudget.minAvailable }}
|
||||
{{- end }}
|
||||
{{- if .Values.podDisruptionBudget.maxUnavailable }}
|
||||
maxUnavailable: {{ .Values.podDisruptionBudget.maxUnavailable }}
|
||||
{{- end }}
|
||||
selector:
|
||||
matchLabels:
|
||||
{{- include "gochat.selectorLabels" . | nindent 6 }}
|
||||
{{- end }}
|
||||
@@ -0,0 +1,23 @@
|
||||
{{- if .Values.sealedSecrets.enabled }}
|
||||
apiVersion: bitnami.com/v1alpha1
|
||||
kind: SealedSecret
|
||||
metadata:
|
||||
name: {{ include "gochat.fullname" . }}-sealed-secret
|
||||
labels:
|
||||
{{- include "gochat.labels" . | nindent 4 }}
|
||||
annotations:
|
||||
# Sealed Secrets are encrypted with the cluster's public key
|
||||
# Use kubeseal to encrypt: kubeseal --format yaml < secret.yaml > sealed-secret.yaml
|
||||
sealedsecrets.bitnami.com/cluster-wide: "true"
|
||||
spec:
|
||||
encryptedData:
|
||||
{{- range $key, $value := .Values.sealedSecrets.encryptedData }}
|
||||
{{ $key }}: {{ $value }}
|
||||
{{- end }}
|
||||
template:
|
||||
metadata:
|
||||
name: {{ include "gochat.fullname" . }}-secret
|
||||
labels:
|
||||
{{- include "gochat.labels" . | nindent 8 }}
|
||||
type: Opaque
|
||||
{{- end }}
|
||||
@@ -0,0 +1,11 @@
|
||||
apiVersion: v1
|
||||
kind: Secret
|
||||
metadata:
|
||||
name: {{ include "gochat.fullname" . }}-secret
|
||||
labels:
|
||||
{{- include "gochat.labels" . | nindent 4 }}
|
||||
type: Opaque
|
||||
data:
|
||||
{{- range $key, $value := .Values.secrets.data }}
|
||||
{{ $key }}: {{ $value | b64enc }}
|
||||
{{- end }}
|
||||
@@ -0,0 +1,15 @@
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: {{ include "gochat.fullname" . }}
|
||||
labels:
|
||||
{{- include "gochat.labels" . | nindent 4 }}
|
||||
spec:
|
||||
type: {{ .Values.app.service.type }}
|
||||
ports:
|
||||
- port: {{ .Values.app.service.port }}
|
||||
targetPort: http
|
||||
protocol: TCP
|
||||
name: http
|
||||
selector:
|
||||
{{- include "gochat.selectorLabels" . | nindent 4 }}
|
||||
@@ -0,0 +1,16 @@
|
||||
{{- if and .Values.metrics.enabled .Values.metrics.serviceMonitor.enabled }}
|
||||
apiVersion: monitoring.coreos.com/v1
|
||||
kind: ServiceMonitor
|
||||
metadata:
|
||||
name: {{ include "gochat.fullname" . }}-metrics
|
||||
labels:
|
||||
{{- include "gochat.labels" . | nindent 4 }}
|
||||
spec:
|
||||
selector:
|
||||
matchLabels:
|
||||
{{- include "gochat.selectorLabels" . | nindent 6 }}
|
||||
endpoints:
|
||||
- port: metrics
|
||||
interval: {{ .Values.metrics.serviceMonitor.interval }}
|
||||
path: {{ .Values.metrics.serviceMonitor.path }}
|
||||
{{- end }}
|
||||
@@ -0,0 +1,35 @@
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: {{ include "gochat.fullname" . }}-worker
|
||||
labels:
|
||||
{{- include "gochat.labels" . | nindent 4 }}
|
||||
app.kubernetes.io/component: worker
|
||||
spec:
|
||||
replicas: {{ .Values.worker.replicaCount }}
|
||||
selector:
|
||||
matchLabels:
|
||||
{{- include "gochat.selectorLabels" . | nindent 6 }}
|
||||
app.kubernetes.io/component: worker
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
{{- include "gochat.selectorLabels" . | nindent 8 }}
|
||||
app.kubernetes.io/component: worker
|
||||
spec:
|
||||
containers:
|
||||
- name: worker
|
||||
image: "{{ .Values.worker.image.repository }}:{{ .Values.worker.image.tag }}"
|
||||
imagePullPolicy: {{ .Values.worker.image.pullPolicy }}
|
||||
command: {{- toYaml .Values.worker.command | nindent 12 }}
|
||||
envFrom:
|
||||
- configMapRef:
|
||||
name: {{ include "gochat.fullname" . }}-config
|
||||
- secretRef:
|
||||
name: {{ include "gochat.fullname" . }}-secret
|
||||
resources:
|
||||
{{- toYaml .Values.worker.resources | nindent 12 }}
|
||||
{{- with .Values.app.nodeSelector }}
|
||||
nodeSelector:
|
||||
{{- toYaml . | nindent 8 }}
|
||||
{{- end }}
|
||||
@@ -0,0 +1,89 @@
|
||||
# GoChat Production Values Override
|
||||
global:
|
||||
environment: production
|
||||
|
||||
app:
|
||||
replicaCount: 3
|
||||
resources:
|
||||
limits:
|
||||
cpu: 2000m
|
||||
memory: 1Gi
|
||||
requests:
|
||||
cpu: 500m
|
||||
memory: 512Mi
|
||||
|
||||
worker:
|
||||
replicaCount: 3
|
||||
resources:
|
||||
limits:
|
||||
cpu: 2000m
|
||||
memory: 1Gi
|
||||
|
||||
postgresql:
|
||||
primary:
|
||||
persistence:
|
||||
size: 50Gi
|
||||
resources:
|
||||
limits:
|
||||
cpu: 2000m
|
||||
memory: 2Gi
|
||||
|
||||
redis:
|
||||
master:
|
||||
persistence:
|
||||
size: 10Gi
|
||||
configuration: |
|
||||
maxmemory 512mb
|
||||
maxmemory-policy allkeys-lru
|
||||
appendonly yes
|
||||
appendfsync everysec
|
||||
replica:
|
||||
replicaCount: 2
|
||||
|
||||
ingress:
|
||||
hosts:
|
||||
- host: gochat.example.com
|
||||
paths:
|
||||
- path: /
|
||||
pathType: Prefix
|
||||
tls:
|
||||
- secretName: gochat-tls
|
||||
hosts:
|
||||
- gochat.example.com
|
||||
|
||||
# ---- Autoscaling (HPA) — enabled in production ----
|
||||
autoscaling:
|
||||
enabled: true
|
||||
minReplicas: 3
|
||||
maxReplicas: 15
|
||||
targetCPUUtilizationPercentage: 70
|
||||
targetMemoryUtilizationPercentage: 80
|
||||
|
||||
# ---- NetworkPolicy — enabled in production ----
|
||||
networkPolicy:
|
||||
enabled: true
|
||||
|
||||
# ---- PodDisruptionBudget — enabled in production ----
|
||||
podDisruptionBudget:
|
||||
enabled: true
|
||||
minAvailable: 1
|
||||
|
||||
# ---- Sealed Secrets — use in production ----
|
||||
sealedSecrets:
|
||||
enabled: true
|
||||
encryptedData: {}
|
||||
# Generate encrypted data with: kubeseal --format yaml < secret.yaml
|
||||
|
||||
# ---- Distributed Tracing — enabled in production ----
|
||||
tracing:
|
||||
enabled: true
|
||||
sampler:
|
||||
type: parentbased_traceidratio
|
||||
arg: "0.1" # 10% sampling in production
|
||||
|
||||
configMap:
|
||||
data:
|
||||
GOCHAT_ENV: "production"
|
||||
GOCHAT_SERVER_MODE: "release"
|
||||
GOCHAT_LOG_LEVEL: "info"
|
||||
GOCHAT_WORKER_CONCURRENCY: "10"
|
||||
@@ -0,0 +1,54 @@
|
||||
# GoChat Staging Values Override
|
||||
global:
|
||||
environment: staging
|
||||
|
||||
app:
|
||||
replicaCount: 1
|
||||
image:
|
||||
tag: "develop"
|
||||
resources:
|
||||
limits:
|
||||
cpu: 500m
|
||||
memory: 256Mi
|
||||
requests:
|
||||
cpu: 250m
|
||||
memory: 128Mi
|
||||
|
||||
worker:
|
||||
replicaCount: 1
|
||||
resources:
|
||||
limits:
|
||||
cpu: 500m
|
||||
memory: 256Mi
|
||||
|
||||
postgresql:
|
||||
primary:
|
||||
persistence:
|
||||
size: 5Gi
|
||||
|
||||
redis:
|
||||
master:
|
||||
persistence:
|
||||
size: 2Gi
|
||||
configuration: |
|
||||
maxmemory 128mb
|
||||
maxmemory-policy allkeys-lru
|
||||
appendonly yes
|
||||
|
||||
ingress:
|
||||
hosts:
|
||||
- host: gochat-staging.example.com
|
||||
paths:
|
||||
- path: /
|
||||
pathType: Prefix
|
||||
tls:
|
||||
- secretName: gochat-staging-tls
|
||||
hosts:
|
||||
- gochat-staging.example.com
|
||||
|
||||
configMap:
|
||||
data:
|
||||
GOCHAT_ENV: "staging"
|
||||
GOCHAT_SERVER_MODE: "debug"
|
||||
GOCHAT_LOG_LEVEL: "debug"
|
||||
FRONTEND_URL: "https://gochat-staging.example.com"
|
||||
@@ -0,0 +1,278 @@
|
||||
# GoChat Helm Chart Values
|
||||
# Reference: Chatwoot Helm chart pattern — app + worker + postgres + redis
|
||||
# Adjust values per environment (dev/staging/prod)
|
||||
|
||||
# ---- Global ----
|
||||
global:
|
||||
environment: production
|
||||
|
||||
# ---- Application ----
|
||||
app:
|
||||
replicaCount: 2
|
||||
image:
|
||||
repository: gochat/gochat
|
||||
tag: "1.0.0"
|
||||
pullPolicy: IfNotPresent
|
||||
service:
|
||||
type: ClusterIP
|
||||
port: 3000
|
||||
resources:
|
||||
limits:
|
||||
cpu: 1000m
|
||||
memory: 512Mi
|
||||
requests:
|
||||
cpu: 500m
|
||||
memory: 256Mi
|
||||
# Health probes — references the health endpoints we created
|
||||
livenessProbe:
|
||||
httpGet:
|
||||
path: /live
|
||||
port: 3000
|
||||
initialDelaySeconds: 15
|
||||
periodSeconds: 30
|
||||
timeoutSeconds: 5
|
||||
failureThreshold: 3
|
||||
readinessProbe:
|
||||
httpGet:
|
||||
path: /ready
|
||||
port: 3000
|
||||
initialDelaySeconds: 10
|
||||
periodSeconds: 10
|
||||
timeoutSeconds: 5
|
||||
failureThreshold: 3
|
||||
# Rolling update strategy
|
||||
strategy:
|
||||
type: RollingUpdate
|
||||
rollingUpdate:
|
||||
maxSurge: 1
|
||||
maxUnavailable: 0
|
||||
# Graceful shutdown configuration
|
||||
terminationGracePeriodSeconds: 30
|
||||
preStopDelaySeconds: 5 # Delay before SIGTERM to allow load balancer deregistration
|
||||
# Environment variables from ConfigMap + Secrets
|
||||
envFrom:
|
||||
configMapRef: gochat-config
|
||||
secretRef: gochat-secret
|
||||
# Additional env vars
|
||||
extraEnv: {}
|
||||
# Pod annotations for monitoring
|
||||
podAnnotations:
|
||||
prometheus.io/scrape: "true"
|
||||
prometheus.io/port: "9090"
|
||||
prometheus.io/path: "/metrics"
|
||||
# Affinity for multi-AZ deployment
|
||||
affinity:
|
||||
podAntiAffinity:
|
||||
preferredDuringSchedulingIgnoredDuringExecution:
|
||||
- weight: 100
|
||||
podAffinityTerm:
|
||||
labelSelector:
|
||||
matchExpressions:
|
||||
- key: app.kubernetes.io/name
|
||||
operator: In
|
||||
values:
|
||||
- gochat
|
||||
topologyKey: kubernetes.io/hostname
|
||||
# Node selector
|
||||
nodeSelector: {}
|
||||
# Tolerations
|
||||
tolerations: []
|
||||
|
||||
# ---- Worker ----
|
||||
worker:
|
||||
replicaCount: 2
|
||||
image:
|
||||
repository: gochat/gochat
|
||||
tag: "1.0.0"
|
||||
pullPolicy: IfNotPresent
|
||||
command: ["serve", "--worker-only"]
|
||||
resources:
|
||||
limits:
|
||||
cpu: 1000m
|
||||
memory: 512Mi
|
||||
requests:
|
||||
cpu: 250m
|
||||
memory: 256Mi
|
||||
envFrom:
|
||||
configMapRef: gochat-config
|
||||
secretRef: gochat-secret
|
||||
|
||||
# ---- Metrics sidecar ----
|
||||
metrics:
|
||||
enabled: true
|
||||
service:
|
||||
type: ClusterIP
|
||||
port: 9090
|
||||
serviceMonitor:
|
||||
enabled: true
|
||||
interval: 15s
|
||||
path: /metrics
|
||||
|
||||
# ---- PostgreSQL ----
|
||||
postgresql:
|
||||
enabled: true # Set false to use external PostgreSQL
|
||||
image:
|
||||
repository: pgvector/pgvector
|
||||
tag: pg16
|
||||
auth:
|
||||
database: gochat_production
|
||||
username: gochat
|
||||
password: "" # Set via --set or secrets
|
||||
existingSecret: gochat-postgres-secret
|
||||
primary:
|
||||
persistence:
|
||||
enabled: true
|
||||
size: 10Gi
|
||||
storageClass: ""
|
||||
resources:
|
||||
limits:
|
||||
cpu: 1000m
|
||||
memory: 1Gi
|
||||
requests:
|
||||
cpu: 500m
|
||||
memory: 512Mi
|
||||
service:
|
||||
port: 5432
|
||||
|
||||
# ---- Redis ----
|
||||
redis:
|
||||
enabled: true # Set false to use external Redis
|
||||
auth:
|
||||
password: "" # Set via --set or secrets
|
||||
existingSecret: gochat-redis-secret
|
||||
master:
|
||||
persistence:
|
||||
enabled: true
|
||||
size: 5Gi
|
||||
storageClass: ""
|
||||
resources:
|
||||
limits:
|
||||
cpu: 500m
|
||||
memory: 512Mi
|
||||
requests:
|
||||
cpu: 250m
|
||||
memory: 256Mi
|
||||
configuration: |
|
||||
maxmemory 512mb
|
||||
maxmemory-policy allkeys-lru
|
||||
appendonly yes
|
||||
appendfsync everysec
|
||||
replica:
|
||||
replicaCount: 1
|
||||
persistence:
|
||||
enabled: true
|
||||
size: 5Gi
|
||||
|
||||
# ---- Ingress ----
|
||||
ingress:
|
||||
enabled: true
|
||||
className: "nginx"
|
||||
annotations:
|
||||
cert-manager.io/cluster-issuer: letsencrypt-prod
|
||||
nginx.ingress.kubernetes.io/proxy-body-size: "50m"
|
||||
nginx.ingress.kubernetes.io/ssl-redirect: "true"
|
||||
hosts:
|
||||
- host: gochat.example.com
|
||||
paths:
|
||||
- path: /
|
||||
pathType: Prefix
|
||||
tls:
|
||||
- secretName: gochat-tls
|
||||
hosts:
|
||||
- gochat.example.com
|
||||
|
||||
# ---- ConfigMap data ----
|
||||
configMap:
|
||||
data:
|
||||
GOCHAT_ENV: "production"
|
||||
GOCHAT_SERVER_HOST: "0.0.0.0"
|
||||
GOCHAT_SERVER_PORT: "3000"
|
||||
GOCHAT_SERVER_MODE: "release"
|
||||
GOCHAT_LOG_LEVEL: "info"
|
||||
GOCHAT_LOG_FORMAT: "json"
|
||||
GOCHAT_METRICS_ENABLED: "true"
|
||||
GOCHAT_METRICS_PORT: "9090"
|
||||
GOCHAT_WORKER_CONCURRENCY: "10"
|
||||
GOCHAT_FEATURE_CAPTAIN_AI: "false"
|
||||
GOCHAT_FEATURE_CSAT: "true"
|
||||
FRONTEND_URL: "https://gochat.example.com"
|
||||
|
||||
# ---- Autoscaling (HPA) ----
|
||||
autoscaling:
|
||||
enabled: false # Enable for production
|
||||
minReplicas: 2
|
||||
maxReplicas: 10
|
||||
targetCPUUtilizationPercentage: 70
|
||||
targetMemoryUtilizationPercentage: 80
|
||||
behavior:
|
||||
scaleDown:
|
||||
stabilizationWindowSeconds: 300
|
||||
percent: 10
|
||||
periodSeconds: 60
|
||||
scaleUp:
|
||||
stabilizationWindowSeconds: 60
|
||||
percent: 50
|
||||
periodSeconds: 60
|
||||
pods: 2
|
||||
podsPeriodSeconds: 60
|
||||
|
||||
# ---- NetworkPolicy ----
|
||||
networkPolicy:
|
||||
enabled: false # Enable for production
|
||||
ingressNamespaceLabels:
|
||||
kubernetes.io/metadata.name: ingress-nginx
|
||||
monitoringNamespaceLabels:
|
||||
kubernetes.io/metadata.name: monitoring
|
||||
|
||||
# ---- PodDisruptionBudget ----
|
||||
podDisruptionBudget:
|
||||
enabled: false # Enable for production (requires >= 2 replicas)
|
||||
minAvailable: 1 # Keep at least 1 pod available during disruptions
|
||||
# maxUnavailable: 1 # Alternative: allow max 1 pod unavailable
|
||||
|
||||
# ---- Secrets (placeholder — use --set or sealed-secrets) ----
|
||||
secrets:
|
||||
data: {}
|
||||
# POSTGRES_PASSWORD, REDIS_PASSWORD, JWT_SECRET, SMTP_PASSWORD, etc.
|
||||
# MUST be set via --set or external secret management
|
||||
|
||||
# ---- Sealed Secrets / External Secret Management ----
|
||||
sealedSecrets:
|
||||
enabled: false # Enable for production
|
||||
encryptedData: {}
|
||||
externalSecret:
|
||||
enabled: false # Enable for production with External Secrets Operator
|
||||
refreshInterval: 1h
|
||||
secretStoreRef:
|
||||
name: aws-secrets-manager
|
||||
kind: ClusterSecretStore
|
||||
mapping: {}
|
||||
# POSTGRES_PASSWORD: postgres-password
|
||||
# REDIS_PASSWORD: redis-password
|
||||
# JWT_SECRET: jwt-secret
|
||||
|
||||
# ---- Distributed Tracing (OpenTelemetry + Jaeger) ----
|
||||
tracing:
|
||||
enabled: false # Enable for production/staging
|
||||
sampler:
|
||||
type: parentbased_traceidratio # Sampling strategy: always_on, always_off, parentbased_traceidratio
|
||||
arg: "0.1" # Sample 10% of traces in production (adjust per environment)
|
||||
propagators: "tracecontext,baggage" # W3C Trace Context propagation
|
||||
logLevel: info
|
||||
otlp:
|
||||
endpoint: "gochat-jaeger:4317" # OTLP gRPC endpoint (in-cluster Jaeger)
|
||||
protocol: grpc
|
||||
timeout: "10s"
|
||||
jaeger:
|
||||
image:
|
||||
repository: jaegertracing/all-in-one
|
||||
tag: "1.55"
|
||||
pullPolicy: IfNotPresent
|
||||
logLevel: info
|
||||
resources:
|
||||
limits:
|
||||
cpu: 500m
|
||||
memory: 512Mi
|
||||
requests:
|
||||
cpu: 100m
|
||||
memory: 128Mi
|
||||
Reference in New Issue
Block a user