6aad64903b
Point the OTLP exporter at Tempo instead of Jaeger. Enable the built-in /metrics endpoint by setting METRICS_SECRET: without it LibreChat answers 401 and exposes nothing. Prometheus scrapes it with a Bearer token from a dedicated job, since annotation-based discovery cannot carry credentials. A Kubernetes Secret is namespaced, so the same value must also exist as `librechat-metrics` in the observability namespace — one side verifies the token, the other presents it. secret.example.yaml documents the pairing; the real secret.yaml stays gitignored. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
182 lines
6.3 KiB
YAML
182 lines
6.3 KiB
YAML
apiVersion: apps/v1
|
|
kind: Deployment
|
|
metadata:
|
|
name: librechat
|
|
namespace: librechat
|
|
spec:
|
|
replicas: 1
|
|
selector:
|
|
matchLabels:
|
|
app: librechat
|
|
template:
|
|
metadata:
|
|
labels:
|
|
app: librechat
|
|
annotations:
|
|
# Prometheus' dedicated `librechat` job (bearer token) targets this
|
|
# pod; the annotation is a selection marker, not a scrape config.
|
|
prometheus.io/scrape-with-auth: "true"
|
|
prometheus.io/port: "3080"
|
|
spec:
|
|
# *.sttlab.local are lab-only hostnames (self-signed cert), not real
|
|
# DNS — this pod needs an explicit mapping to each Gateway's stable
|
|
# in-cluster ClusterIP to resolve them at all.
|
|
# catalog-dev: kong-v2 catalog-api Gateway (dataplane-ingress-catalog-dev-v4bkm-xqq6v), REST+MCP share this hostname now.
|
|
# llm-dev: kong-v2's platform-llm Gateway (dataplane-ingress-llm-dev-rlrxf-7fkb7).
|
|
hostAliases:
|
|
- ip: "192.168.194.225"
|
|
hostnames:
|
|
- "catalog-dev.sttlab.local"
|
|
- ip: "192.168.194.153"
|
|
hostnames:
|
|
- "llm-dev.sttlab.local"
|
|
initContainers:
|
|
- name: wait-for-mongodb
|
|
image: busybox:1.36
|
|
command:
|
|
- sh
|
|
- -c
|
|
- |
|
|
until nc -z mongodb 27017; do
|
|
echo "Waiting for MongoDB..."; sleep 3;
|
|
done
|
|
- name: wait-for-meilisearch
|
|
image: busybox:1.36
|
|
command:
|
|
- sh
|
|
- -c
|
|
- |
|
|
until nc -z meilisearch 7700; do
|
|
echo "Waiting for MeiliSearch..."; sleep 3;
|
|
done
|
|
containers:
|
|
- name: librechat
|
|
image: ghcr.io/danny-avila/librechat:latest
|
|
ports:
|
|
- containerPort: 3080
|
|
envFrom:
|
|
- configMapRef:
|
|
name: librechat-config
|
|
env:
|
|
- name: MONGO_URI
|
|
valueFrom:
|
|
secretKeyRef:
|
|
name: librechat-secrets
|
|
key: MONGO_URI
|
|
- name: MEILI_MASTER_KEY
|
|
valueFrom:
|
|
secretKeyRef:
|
|
name: librechat-secrets
|
|
key: MEILI_MASTER_KEY
|
|
- name: JWT_SECRET
|
|
valueFrom:
|
|
secretKeyRef:
|
|
name: librechat-secrets
|
|
key: JWT_SECRET
|
|
- name: JWT_REFRESH_SECRET
|
|
valueFrom:
|
|
secretKeyRef:
|
|
name: librechat-secrets
|
|
key: JWT_REFRESH_SECRET
|
|
- name: CREDS_KEY
|
|
valueFrom:
|
|
secretKeyRef:
|
|
name: librechat-secrets
|
|
key: CREDS_KEY
|
|
- name: CREDS_IV
|
|
valueFrom:
|
|
secretKeyRef:
|
|
name: librechat-secrets
|
|
key: CREDS_IV
|
|
- name: OPENAI_API_KEY
|
|
valueFrom:
|
|
secretKeyRef:
|
|
name: librechat-secrets
|
|
key: OPENAI_API_KEY
|
|
- name: ANTHROPIC_API_KEY
|
|
valueFrom:
|
|
secretKeyRef:
|
|
name: librechat-secrets
|
|
key: ANTHROPIC_API_KEY
|
|
- name: PLATFORM_LLM_LIBRECHAT_APIKEY
|
|
valueFrom:
|
|
secretKeyRef:
|
|
name: librechat-secrets
|
|
key: PLATFORM_LLM_LIBRECHAT_APIKEY
|
|
- name: LIBRECHAT_OIDC_CLIENT_SECRET
|
|
valueFrom:
|
|
secretKeyRef:
|
|
name: librechat-secrets
|
|
key: LIBRECHAT_OIDC_CLIENT_SECRET
|
|
# Same Keycloak client secret as LIBRECHAT_OIDC_CLIENT_SECRET
|
|
# above (one Keycloak client, "librechat", used both for the
|
|
# app's own SSO login and for each MCP server's OAuth flow) —
|
|
# LibreChat's OIDC-login code path reads this specific env var
|
|
# name, so it's duplicated rather than referenced once.
|
|
- name: OPENID_CLIENT_SECRET
|
|
valueFrom:
|
|
secretKeyRef:
|
|
name: librechat-secrets
|
|
key: LIBRECHAT_OIDC_CLIENT_SECRET
|
|
- name: OPENID_SESSION_SECRET
|
|
valueFrom:
|
|
secretKeyRef:
|
|
name: librechat-secrets
|
|
key: OPENID_SESSION_SECRET
|
|
# Enables /metrics (401 without it). Scraped by the Prometheus in
|
|
# the observability namespace using a Bearer token.
|
|
- name: METRICS_SECRET
|
|
valueFrom:
|
|
secretKeyRef:
|
|
name: librechat-secrets
|
|
key: METRICS_SECRET
|
|
# jira-mcp.sttlab.local / konggw.sttlab.local serve a self-signed
|
|
# cert (see hostAliases comment above) — Node rejects it by
|
|
# default; NODE_EXTRA_CA_CERTS trusts just this one CA, unlike
|
|
# NODE_TLS_REJECT_UNAUTHORIZED=0 which would disable TLS
|
|
# verification globally.
|
|
- name: NODE_EXTRA_CA_CERTS
|
|
value: /etc/sttlab-tls/tls.crt
|
|
# 512Mi was too tight for LibreChat under real use (MCP clients,
|
|
# OTel instrumentation) and caused repeated V8 heap-limit crashes.
|
|
# Cap old-space explicitly, leaving headroom under the new
|
|
# container limit for non-heap memory (buffers, native modules).
|
|
- name: NODE_OPTIONS
|
|
value: "--max-old-space-size=1200"
|
|
resources:
|
|
requests:
|
|
cpu: 100m
|
|
memory: 512Mi
|
|
limits:
|
|
cpu: 500m
|
|
memory: 1536Mi
|
|
livenessProbe:
|
|
httpGet:
|
|
path: /health
|
|
port: 3080
|
|
initialDelaySeconds: 30
|
|
periodSeconds: 20
|
|
readinessProbe:
|
|
httpGet:
|
|
path: /health
|
|
port: 3080
|
|
initialDelaySeconds: 20
|
|
periodSeconds: 10
|
|
volumeMounts:
|
|
- name: app-config
|
|
mountPath: /app/librechat.yaml
|
|
subPath: librechat.yaml
|
|
- name: sttlab-tls-cert
|
|
mountPath: /etc/sttlab-tls
|
|
readOnly: true
|
|
volumes:
|
|
- name: app-config
|
|
configMap:
|
|
name: librechat-app-config
|
|
- name: sttlab-tls-cert
|
|
secret:
|
|
secretName: sttlab-tls
|
|
items:
|
|
- key: tls.crt
|
|
path: tls.crt
|