141 lines
6.3 KiB
YAML
141 lines
6.3 KiB
YAML
# Kong Gateway — cluster-internal API gateway (namespace `api`).
|
|
#
|
|
# Chart: kong/kong 3.4.1 (appVersion 3.9). Only overrides are listed; every key
|
|
# here was checked against `helm show values kong/kong --version 3.4.1`, because
|
|
# Helm silently ignores unknown keys — a typo is a no-op, not an error.
|
|
#
|
|
# ── Topology ────────────────────────────────────────────────────────────────
|
|
# external: client -> nginx (TLS, wildcard *.riotpiao.com) -> kong-proxy:80
|
|
# internal: pod -> kong-proxy.api.svc.cluster.local:80
|
|
#
|
|
# nginx stays the single edge and the only LoadBalancer (192.168.1.160). Kong is
|
|
# the policy/routing layer behind it, so it needs no LB IP and no TLS of its own
|
|
# — hence ClusterIP and proxy.tls disabled. Giving Kong its own IP from
|
|
# homelab-pool would mean duplicating cert-manager wiring and diverging from the
|
|
# CoreDNS convention that sends every *.riotpiao.com host to nginx.
|
|
#
|
|
# ── Routing model ───────────────────────────────────────────────────────────
|
|
# Consumers publish an Ingress with `ingressClassName: kong`; the controller
|
|
# turns it into a Kong route. `nginx` remains the default IngressClass, so this
|
|
# is strictly opt-in and no existing Ingress changes behaviour.
|
|
|
|
# Without this the release name is prefixed onto everything (`kong-kong-proxy`).
|
|
# Pinning it keeps the Service name stable and independent of the release name,
|
|
# which matters because the nginx Ingress in k8s/bootstrap/ingress/ingress.yaml
|
|
# references it by name.
|
|
fullnameOverride: kong
|
|
|
|
# Two replicas so a node drain or rollout doesn't take the gateway down. Kong is
|
|
# stateless in DB-less mode, so replicas are pure redundancy.
|
|
replicaCount: 2
|
|
|
|
# Opt in to the `llm-serving-default-deny` NetworkPolicy, which admits port 8080
|
|
# only from pods carrying this label. That policy is a compensating control, not
|
|
# hygiene: vLLM v0.11.0 is frozen on Volta and will never receive patches for
|
|
# several remote/unauthenticated advisories, so it must not be broadly reachable.
|
|
#
|
|
# Without this label Cilium DROPS the packets rather than refusing them, so the
|
|
# symptom is a request that hangs until the client's timeout — not a connection
|
|
# error. /v1/models still worked while this was missing, because
|
|
# request-termination answers inside Kong and never touches an upstream.
|
|
podLabels:
|
|
llm-client: "true"
|
|
|
|
env:
|
|
# DB-less. Config comes from Kubernetes objects via the ingress controller, so
|
|
# git stays the source of truth. A Postgres-backed Kong would put live routing
|
|
# config in a database mutated through the Admin API — state outside git, plus
|
|
# migration Jobs on every upgrade.
|
|
database: "off"
|
|
# `nginx_proxy_<directive>` injects a directive into the proxy location block;
|
|
# this renders `proxy_buffering off;`.
|
|
#
|
|
# Required for LLM streaming. With buffering on (the default) nginx accumulates
|
|
# the upstream response before forwarding, so an SSE stream from
|
|
# `"stream": true` arrives in lumps or stalls until the generation finishes —
|
|
# which defeats the point of streaming. The matching setting is already on the
|
|
# nginx Ingress in ingress.yaml; both hops have to be unbuffered or the
|
|
# buffered one dominates.
|
|
nginx_proxy_proxy_buffering: "off"
|
|
# Any plugin that rewrites the request body — request-transformer on the
|
|
# llm-chat-* routes — reads it through `kong.request.get_body()`, and that
|
|
# returns nothing once nginx has spilled the body past
|
|
# client_body_buffer_size into a temp file. The plugin then re-serializes a
|
|
# body with no `messages`, and the upstream answers
|
|
# HTTP 400 {"error":{"message":"[] is too short - 'messages'"}}
|
|
# Measured on /v1/ornith/chat/completions: 10588 B -> 200, 11088 B -> 400.
|
|
# An agent request carrying tool schemas clears that in one turn, so the
|
|
# buffer has to hold a whole conversation, not a chat message.
|
|
nginx_http_client_body_buffer_size: "16m"
|
|
nginx_http_client_max_body_size: "16m"
|
|
|
|
ingressController:
|
|
enabled: true
|
|
ingressClass: kong
|
|
# The chart's ingress-class template is gated on
|
|
# `.Capabilities.APIVersions.Has "networking.k8s.io/v1/IngressClass"`, so a
|
|
# bare `helm template` renders nothing. ArgoCD passes --api-versions from the
|
|
# live cluster, so it does render there — verify `kubectl get ingressclass
|
|
# kong` after the first sync rather than assuming it.
|
|
createIngressClass: true
|
|
# Deliberately empty: setting is-default-class here would hijack every Ingress
|
|
# in the cluster that omits ingressClassName. nginx keeps that role.
|
|
ingressClassAnnotations: {}
|
|
|
|
proxy:
|
|
enabled: true
|
|
# Chart default is LoadBalancer, which would claim an IP from homelab-pool.
|
|
type: ClusterIP
|
|
http:
|
|
enabled: true
|
|
servicePort: 80
|
|
containerPort: 8000
|
|
# nginx already terminated TLS; a second handshake to the same cluster buys
|
|
# nothing and would need Kong to hold its own certificate.
|
|
tls:
|
|
enabled: false
|
|
|
|
# No Service for the Admin API. The controller reaches it over localhost inside
|
|
# the pod, so exposing it would only create an unauthenticated write path to the
|
|
# gateway's entire configuration.
|
|
admin:
|
|
enabled: false
|
|
|
|
# Kong Manager UI — chart default is `enabled: true` with type NodePort, which
|
|
# would open a port on every node. Not wanted.
|
|
manager:
|
|
enabled: false
|
|
|
|
resources:
|
|
requests:
|
|
cpu: 200m
|
|
memory: 256Mi
|
|
limits:
|
|
cpu: "2"
|
|
memory: 1Gi
|
|
|
|
podDisruptionBudget:
|
|
enabled: true
|
|
minAvailable: 1
|
|
|
|
# Status listener (metrics/health) is on by default at :8100 (chart default,
|
|
# verified via `helm show values`). This just wires the ServiceMonitor the
|
|
# chart already knows how to generate for it, so kong_http_requests_total /
|
|
# kong_latency_* / kong_bandwidth_bytes land in Prometheus. Paired with the
|
|
# cluster-wide `prometheus` KongClusterPlugin in kong-metrics.yaml.
|
|
serviceMonitor:
|
|
enabled: true
|
|
labels:
|
|
release: kube-prometheus-stack
|
|
|
|
# Spread the two replicas across nodes; `ScheduleAnyway` so a single-node
|
|
# situation degrades to co-location instead of leaving a pod Pending.
|
|
topologySpreadConstraints:
|
|
- maxSkew: 1
|
|
topologyKey: kubernetes.io/hostname
|
|
whenUnsatisfiable: ScheduleAnyway
|
|
labelSelector:
|
|
matchLabels:
|
|
app.kubernetes.io/name: kong
|
|
app.kubernetes.io/instance: kong
|