Skip to content

feat(deploy): add Kustomize deployment option - #245

Closed
mkoushni wants to merge 7 commits into
llm-d:mainfrom
mkoushni:feat/kustomize-deployment
Closed

feat(deploy): add Kustomize deployment option#245
mkoushni wants to merge 7 commits into
llm-d:mainfrom
mkoushni:feat/kustomize-deployment

Conversation

@mkoushni

@mkoushni mkoushni commented Jul 16, 2026

Copy link
Copy Markdown
Contributor

[Feature] Enable deployment using Kustomize

Adds a Kustomize deployment path for the Inference Payload Processor (IPP),
aligning with the llm-d community deployment standard and enabling IPP to be
consumed as a step in llm-d-benchmark
and GitOps workflows that prefer Kustomize over Helm.

The existing Helm chart (config/charts/payload-processor/) is untouched;
Kustomize is an additive alternative.


Fixes #125


Changes

New: config/kustomize/

config/kustomize/
├── README.md                              ← full usage guide
├── base/                                  ← provider-agnostic core resources
│   ├── kustomization.yaml
│   ├── deployment.yaml                    ← Deployment (1 replica, port 9004/9005)
│   ├── service.yaml                       ← ClusterIP Service (HTTP2, port 9004)
│   ├── serviceaccount.yaml
│   ├── rbac.yaml                          ← Role + RoleBinding (single-namespace)
│   └── configmap.yaml                     ← default PayloadProcessorConfig
└── overlays/
    ├── default/kustomization.yaml         ← no provider (base only)
    ├── istio/                             ← + EnvoyFilter + DestinationRule
    │   ├── kustomization.yaml
    │   ├── envoyfilter.yaml
    │   └── destinationrule.yaml
    └── gke/                               ← + GCPRoutingExtension + HealthCheckPolicy
        ├── kustomization.yaml
        ├── gcproutingextension.yaml
        └── healthcheckpolicy.yaml

Each overlay exposes a single namespace: field as the sole namespace control
point — the base layer intentionally omits metadata.namespace so that the
overlay drives it.

Modified: Makefile

New ##@ Deployment section:

Target Description
make kustomize Install the kustomize binary to bin/ if absent (via go install, cross-platform)
make kustomize-build [KUSTOMIZE_OVERLAY=default|istio|gke] Render manifests to stdout
make kustomize-deploy [KUSTOMIZE_OVERLAY=default|istio|gke] Apply to cluster
make kustomize-undeploy [KUSTOMIZE_OVERLAY=default|istio|gke] Remove from cluster

Modified: README.md

Added a Deployment section with side-by-side Helm and Kustomize quick-start
commands.

Fixed: pre-existing goconst lint issues

Extracted repeated string literals into named constants to clear golangci-lint
(4 issues, pre-existing):

File Constants added
pkg/config/loader/defaults.go configAPIVersion, configKind
test/integration/util.go headerContentLength, bodyFieldPrompt

Review Feedback Addressed

Thanks @senanz for the review — two issues are fixed in follow-up commits:

  1. Platform-specific kustomize install (comment) —
    the kustomize Makefile target hardcoded a linux_amd64 release tarball
    download, which broke on macOS/ARM. Switched to
    go install sigs.k8s.io/kustomize/kustomize/v5@$(KUSTOMIZE_VERSION) via the
    repo's existing go-install-tool helper (same pattern already used for
    helm, yq, controller-gen), which is cross-platform by construction.

  2. Namespace drift risk in the Istio overlay (comment) —
    the EnvoyFilter cluster_name and DestinationRule host embedded the
    namespace as a hardcoded FQDN segment that had to be manually kept in sync
    with namespace: in kustomization.yaml. Implemented the suggested
    "ideal fix": a Kustomize replacements block that derives the namespace
    from the Service resource and injects it into both fields at build time,
    so namespace: is now the single source of truth and can't drift.
    Verified by rendering the overlay with a custom namespace and confirming
    both fields update automatically.


Usage

# No provider (Deployment + Service only)
kubectl kustomize config/kustomize/overlays/default | kubectl apply -f -

# Istio (adds EnvoyFilter + DestinationRule)
make kustomize-deploy KUSTOMIZE_OVERLAY=istio

# GKE (adds GCPRoutingExtension + HealthCheckPolicy)
make kustomize-deploy KUSTOMIZE_OVERLAY=gke

# Custom namespace
cd config/kustomize/overlays/default
kustomize edit set namespace my-namespace

See config/kustomize/README.md for customization
options (namespace, image tag, custom IPP config, multi-namespace RBAC).


Testing

Kustomize build validation

All three overlays render without errors and produce the expected resource kinds:

Overlay Resources
default ConfigMap, Deployment, Role, RoleBinding, Service, ServiceAccount
istio + DestinationRule, EnvoyFilter
gke + GCPRoutingExtension, HealthCheckPolicy

Validated with:

kubectl kustomize config/kustomize/overlays/default
kubectl kustomize config/kustomize/overlays/istio
kubectl kustomize config/kustomize/overlays/gke

Go test suite

Re-ran the full suite after the review fixes above (make test-coverage, i.e.
go test -race -coverprofile=coverage.out -covermode=atomic ./...). All
tested packages still pass:

Package Result Coverage
pkg/common ✅ PASS 81.8%
pkg/common/envoy ✅ PASS 87.5%
pkg/common/envoy/test ✅ PASS 100.0%
pkg/common/error ✅ PASS 100.0%
pkg/common/observability/logging ✅ PASS 69.4%
pkg/config/loader ✅ PASS 78.6%
pkg/datalayer ✅ PASS 68.0%
pkg/datastore/inmemory ✅ PASS 100.0%
pkg/framework/interface/datalayer ✅ PASS 94.9%
pkg/framework/interface/datalayer/pricing ✅ PASS 100.0%
pkg/framework/interface/requesthandling ✅ PASS 59.3%
pkg/framework/plugins/datalayer/modelconfigcollector ✅ PASS 82.7%
pkg/framework/plugins/datalayer/requestcostmetadata ✅ PASS 87.3%
pkg/framework/plugins/datalayer/requestmetadata ✅ PASS 92.3%
pkg/framework/plugins/modelselector/filter/modelgroup ✅ PASS 100.0%
pkg/framework/plugins/modelselector/filter/modelname ✅ PASS 100.0%
pkg/framework/plugins/modelselector/picker/maxscore ✅ PASS 73.3%
pkg/framework/plugins/modelselector/picker/random ✅ PASS 88.9%
pkg/framework/plugins/modelselector/picker/weightedrandom ✅ PASS 66.7%
pkg/framework/plugins/modelselector/scorer/costaware ✅ PASS 95.7%
pkg/framework/plugins/modelselector/scorer/sessionaffinity ✅ PASS 100.0%
pkg/framework/plugins/requesthandling/basemodelextractor ✅ PASS 81.4%
pkg/framework/plugins/requesthandling/bodyfieldtoheader ✅ PASS 100.0%
pkg/framework/plugins/requesthandling/modelselector ✅ PASS 94.7%
pkg/framework/plugins/requesthandling/profilepicker/single ✅ PASS 100.0%
pkg/framework/plugins/responsehandling/modelnametoheader ✅ PASS 100.0%
pkg/handlers ✅ PASS 66.0%
pkg/metrics ✅ PASS 50.0%
pkg/modelselector ✅ PASS 80.1%
pkg/server ✅ PASS 59.5%
test/integration ✅ PASS 97.2%

Total coverage: 60.4% (statements)

Kustomize namespace-drift regression check

Rendered the istio overlay with a temporarily patched namespace: my-namespace
and confirmed both the EnvoyFilter cluster_name and DestinationRule host
updated automatically to payload-processor.my-namespace.svc.cluster.local.

Linting

golangci-lint run  →  0 issues.
go vet ./...       →  clean

Release note (write NONE if no user-facing change):

Add Kustomize deployment support with base manifests and overlays for default,
Istio, and GKE providers. New Makefile targets: kustomize-build, kustomize-deploy,
kustomize-undeploy (KUSTOMIZE_OVERLAY=default|istio|gke).

@github-actions github-actions Bot added the size/XL Denotes a PR that changes 500-999 lines, ignoring generated files. label Jul 16, 2026
mkoushni added 3 commits July 16, 2026 13:17
Signed-off-by: mkoushni <mkoushni@redhat.com>
v9 moves the action's runtime from Node 20 to Node 24, per the
upstream release notes.

Signed-off-by: mkoushni <mkoushni@redhat.com>
golangci-lint flagged plugin_test.go for improper gofmt formatting
and an unused waitForTokenPrices helper with no call sites.

Signed-off-by: mkoushni <mkoushni@redhat.com>
@mkoushni
mkoushni force-pushed the feat/kustomize-deployment branch from f54b42e to af5b45a Compare July 16, 2026 10:21

@senanz senanz left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review Summary

Good work — the Kustomize structure is well-conceived, the base/overlay separation is correct, documentation is thorough, and the commit hygiene (DCO sign-off, logical separation) is solid. This fills a real gap for GitOps and benchmark integration workflows.

However, there are a few issues to address before merge:

Must-Fix

# File Issue
1 Makefile Kustomize download hardcodes linux_amd64 — breaks macOS/ARM

Should-Fix (production readiness)

# File Issue
2 overlays/istio/envoyfilter.yaml, destinationrule.yaml Namespace hardcoded in FQDNs — silently breaks when namespace is changed via the documented method
3 base/deployment.yaml No resource requests/limits
4 base/deployment.yaml No liveness/readiness probes despite exposing health port
5 base/service.yaml Missing port name (needed for Istio protocol detection)

Non-blocking suggestions

  • GKE timeout: 1s seems aggressive for full-body ext-proc processing
  • insecureSkipVerify: true in DestinationRule should be documented as an explicit trade-off
  • Consider using app.kubernetes.io/ standard labels instead of bare app: labels
  • Mixed concerns across commits (CI bump + lint fixes + feature) — not blocking but worth noting for future PRs

What's done well

  • Additive to Helm — no breaking changes to existing path
  • README with customization recipes (multi-namespace RBAC, image override, gateway name)
  • goconst fixes are appropriately scoped
  • Overlay-driven namespace design is the right pattern

Happy to re-review once the critical items are addressed. Nice contribution overall.

Comment thread Makefile Outdated
Comment thread config/kustomize/overlays/istio/envoyfilter.yaml
Comment thread config/kustomize/base/deployment.yaml Outdated
Comment thread config/kustomize/overlays/istio/destinationrule.yaml
Comment thread config/kustomize/overlays/gke/gcproutingextension.yaml
Comment thread config/kustomize/base/service.yaml Outdated
mkoushni added 3 commits July 19, 2026 14:37
…pport

The kustomize target hardcoded a linux_amd64 release tarball download,
which broke on macOS and ARM dev environments. Use the existing
go-install-tool helper (already used for helm, yq, controller-gen) so
installation works on any OS/arch supported by the Go toolchain.

Signed-off-by: mkoushni <mkoushni@redhat.com>
…ements

The EnvoyFilter cluster_name and DestinationRule host embedded the
namespace as a hardcoded FQDN segment, requiring manual updates that
could silently drift out of sync with the namespace: field in
kustomization.yaml. Use Kustomize replacements to derive the namespace
from the Service resource and inject it into both fields at build time,
so kustomization.yaml's namespace: is the single source of truth.

Signed-off-by: mkoushni <mkoushni@redhat.com>
- Add resource requests/limits to the base Deployment so the Pod can't
  be OOMKilled or starve neighbors in a shared cluster.
- Add readiness/liveness probes using the gRPC health protocol served
  on port 9005 (grpc.health.v1, see cmd/runner/health.go) so rolling
  updates and self-healing work correctly.
- Name the Deployment's container ports and the Service port so Istio
  (and other mesh sidecars) detect the protocol instead of treating
  traffic as opaque TCP.
- Document why insecureSkipVerify is set on the Istio DestinationRule
  (IPP serves a self-signed cert with no shared CA) so it isn't
  mistaken for an oversight or copied onto external-facing hosts.
- Raise the GKE GCPRoutingExtension timeout from 1s to 10s, since IPP
  streams full request/response bodies and runs model-selection/cost
  logic that can exceed 1s under load.

Signed-off-by: mkoushni <mkoushni@redhat.com>
@mkoushni
mkoushni force-pushed the feat/kustomize-deployment branch from c4b2a76 to 221f3fb Compare July 19, 2026 11:37
@mkoushni
mkoushni requested a review from senanz July 19, 2026 11:49
Resolves conflicts with upstream's CostGuard epoch-management and
requestcostmetadata window/reset changes (PR llm-d#266), which landed
alongside this branch's kustomize deployment work.
@github-actions

Copy link
Copy Markdown

This PR is marked as stale after 21d of inactivity. After an additional 14d of inactivity (7d to become rotten, then 7d more), it will be closed. To prevent this PR from being closed, add a comment or remove the lifecycle/stale label.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

lifecycle/rotten size/XL Denotes a PR that changes 500-999 lines, ignoring generated files.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Feature]: Enable deployment using Kustomize

2 participants