Zurück zum Hub

cargo.ayedo.cloud/ayedo/k8s/envoy

Registry-Block

polycrate block pull cargo.ayedo.cloud/ayedo/k8s/envoy:0.2.9

Envoy Gateway

Base Envoy Gateway infrastructure for platform / Loadbalancer regions. This block deploys the envoy-gateway control plane, Gateway API CRDs (optional), two GatewayClasses, matching EnvoyProxy pools, and optionally a bootstrap Gateway that brings up the shared data-plane LoadBalancer VIP immediately.

Per-tenant routes (HTTPRoute / TLSRoute / Backend / policies) are not owned here — they are created by consumers (Managed Controlplane k3s-server, polycrate-api LBI, or hand-written manifests).

Wert
Block cargo.ayedo.cloud/ayedo/k8s/envoy
Version 0.2.0
Kind / Type / Flavor k8sapp / lb / envoy
Chart OCI oci://docker.io/envoyproxy/gateway-helm v1.8.2 (app 1.8.2)
Icon https://icons.ayedo.de/png/envoy.png
Upstream https://gateway.envoyproxy.io

Beispiele: polycrate block examples envoy

Related specs (polycrate-api): polycrate spec inspect 486, polycrate spec inspect 385.


What it deploys

  1. Helm gateway-helm into config.namespace (default envoy-gateway-system)
  2. EnvoyProxy CRs: eg-shared (mergeGateways: true) + eg-dedicated (mergeGateways: false)
  3. GatewayClass CRs with the same names (contract to API / MCP)
  4. Optional default Gateway (config.default_gateway) — bootstrap listener(s), creates the shared LB
  5. Optional ClientTrafficPolicy / BackendTrafficPolicy (Proxy Protocol, timeouts)
  6. Optional WAF Coraza path via ext_proc (Service/Deployment; policies are user/API-owned)
  7. Optional VMPodScrape for data-plane Prometheus stats (:19001)
Host cluster
├── NS envoy-gateway-system
│   ├── Deployment envoy-gateway          (controller)
│   ├── EnvoyProxy eg-shared|eg-dedicated
│   ├── Gateway eg-shared                 (optional default_gateway)
│   ├── DaemonSet/Deployment envoy-…      (data plane, after Gateway attaches)
│   └── Service LoadBalancer              (shared VIP when mergeGateways)
└── cluster-scoped GatewayClass eg-shared|eg-dedicated

GatewayClasses (contract)

Class mergeGateways Use
eg-shared true One fleet + one VIP; SNI/hostname multiplex. Default for MCP edge / dense LBI.
eg-dedicated false Own fleet/service/IP per Gateway (raw TCP / dedicated VIP).

Class names are configurable under config.gatewayclass.*.name but should stay stable once polycrate-api / workspaces depend on them.


Shared VIP + default Gateway

With mergeGateways: true, Envoy creates the LoadBalancer only after a Gateway attaches. For platform edges that need a stable VIP (parallel to nginx), enable:

service:
  type: LoadBalancer
  external_traffic_policy: Local   # needs DaemonSet for true client-IP
  loadbalancer_ip: "203.0.113.20"  # Cilium lbipam + MetalLB annotations + loadBalancerIP
default_gateway:
  enabled: true
  name: eg-shared
  listeners:
    - name: tls
      protocol: TLS
      port: 443
      tls_mode: Passthrough
      allowed_routes_namespaces: All
  • loadbalancer_ip is applied only to the shared EnvoyProxy (dedicated IPs stay per-Gateway / API).
  • Bootstrap listener tls (Passthrough) + allowedRoutes=All lets other namespaces attach TLSRoute via parentRefs (sectionName: tls).
  • Listener http (:80) accepts HTTPRoute (ACME HTTP-01 / plaintext).

Passthrough vs TLS termination

Goal Listener Route kind
SNI passthrough (MCP kube-apiserver terminates TLS) protocol: TLS, tls_mode: Passthrough TLSRoute
Edge terminates TLS, then HTTP routing / WAF protocol: HTTPS, tls_mode: Terminate + certificate_refs HTTPRoute

The shared bootstrap Gateway keeps Passthrough on :443 on purpose (MCP + SNI multiplex). That does not block termination elsewhere: with mergeGateways=true on eg-shared, the API or operator can create additional Gateways on the same class (same VIP) that add an HTTPS/Terminate listener + cert Secret refs; HTTPRoute attaches to that listener (sectionName: https or whatever you name it).

Do not put a catch-all HTTPS/Terminate listener on the bootstrap Gateway without certs — Gateway API requires certificateRefs for Terminate.


Parallel to nginx

nginx keeps classic Ingress + its own VIP. Envoy is an additional edge:

Edge Class / IngressClass Typical VIP
nginx IngressClass nginx existing platform LB
envoy GatewayClass eg-shared service.loadbalancer_ip

Consumers choose deliberately (e.g. k3s-server ingress.type: gateway).


Consumer: Managed Controlplane (k3s-server)

# envoy (platform)
config:
  service:
    loadbalancer_ip: "203.0.113.20"
  default_gateway:
    enabled: true

# k3s-server (MCP)
config:
  loadbalancer:
    enabled: false          # mutual exclusive with ingress
  ingress:
    enabled: true
    type: gateway           # TLSRoute → platform Gateway (defaults)
  k3s:
    cluster_domain: "my-ws.k8s.example.com"   # API host = api.<cluster_domain>
    token: "…"

Defaults in k3s-server: parent_gateway_name=eg-shared, namespace envoy-gateway-system, section tls. See polycrate block examples k3s-server → managed-controlplane-gateway.


HTTP:80 / ACME

Add an HTTP listener for cert-manager HTTP-01 (block does not create issuers/certificates):

default_gateway:
  enabled: true
  listeners:
    - name: http
      protocol: HTTP
      port: 80
      allowed_routes_namespaces: All
    - name: tls
      protocol: TLS
      port: 443
      tls_mode: Passthrough
      allowed_routes_namespaces: All

Proxy Protocol

client_traffic:
  enabled: true                 # requires default_gateway.enabled or target_gateway_name
  proxy_protocol:
    enabled: true               # optional: false (strict)

WAF (Coraza via ext_proc)

Block provisions an optional regional gRPC service. Toggle per route/gateway with EnvoyExtensionPolicy (extProc). Wasm / Dynamic Modules are not supported.

Image build: see waf/BUILD.md. Default image (public): cargo.ayedo.cloud/library/envoy-waf:0.1.0. Build only from waf/ (never the block root).

waf:
  enabled: true                 # uses block default image when repository/tag omitted
  default_policy:
    enabled: false              # opt-in bootstrap policy on the Gateway

Chart OCI mirror / images

  • Chart: chart.repo.url (e.g. oci://cargo.ayedo.cloud/...) + chart.auth
  • Controller: image.controller.{registry,repository,tag}
  • Data-plane Envoy: image.proxy.{registry,repository,tag}

Configuration reference

Key Default Notes
namespace envoy-gateway-system Controller + proxies
manage_crds true Chart owns Gateway API CRDs (single cluster owner)
enable_backend true Backend CRD for out-of-cluster origins
workload_kind daemonset daemonset | deployment
replicas 2 Only for deployment
service.type LoadBalancer
service.external_traffic_policy Local Prefer DaemonSet
service.loadbalancer_ip "" Shared pool VIP
service.annotations {} Merged onto envoyService
service.openstack off Octavia convenience annotations
default_gateway.enabled false Bootstrap Gateway
default_gateway.name eg-shared
default_gateway.listeners TLS:443 Passthrough, All NS Add HTTP:80 for ACME
client_traffic.* off Proxy Protocol / timeouts / buffer
backend_traffic.* off Upstream timeouts
waf.* off Coraza ext_proc path
image.controller / image.proxy chart defaults Optional overrides
tls.cluster_issuer_name letsencrypt-production Reference only — not created
metrics.* / vmpodscrape on Port 19001; optional relabel for polycrate_lbi_*
access_log on, JSON Independent of metrics.enabled

Actions

Action Description
install Helm + EnvoyProxies + GatewayClasses + optional Gateway/policies/WAF + VMPodScrape
uninstall Remove WAF/policies/Gateway/EnvoyProxies/GatewayClasses/VMPodScrape + Helm (CRDs kept)
polycrate run envoy install
polycrate run envoy uninstall
polycrate block examples envoy

TLS

The block does not create a ClusterIssuer or Certificate.

  • Passthrough (default shared listener): backend terminates TLS; attach TLSRoute.
  • Terminate: add an HTTPS listener (tls_mode: Terminate, certificate_refs) on a Gateway of class eg-shared (merged VIP) or eg-dedicated; attach HTTPRoute. Prefer an explicit cert-manager Certificate → Secret, then reference that Secret.

Observability

  • Metrics: Envoy admin Prometheus on :19001, scraped via VMPodScrape when enabled
  • Access logs: JSON to stdout → Vector → VictoriaLogs (polycrate_log_source=app on pods)

Prerequisites

  • Cilium LB-IPAM and/or MetalLB (or cloud LB) for service.type=LoadBalancer
  • Optional: cert-manager ClusterIssuer named in tls.cluster_issuer_name
  • Optional: VictoriaMetrics Operator for metrics.vmpodscrape

Troubleshooting

Symptom Fix
Gateway Programmed=False / NoResources Wait for data-plane DaemonSet pull; check EnvoyProxy
No EXTERNAL-IP Enable default_gateway or create any Gateway on eg-shared; check loadbalancer_ip in pool
TLSRoute not Accepted parentRefs name/namespace/sectionName must match default Gateway listener
Conflict with nginx Different VIP + GatewayClass; do not share the same LB IP

Changelog

See CHANGELOG.poly.