Skip to content

Commit b5b6701

Browse files
authored
Merge branch 'main' into ci-add-area-labels
2 parents 8bfbf47 + 04d683f commit b5b6701

25 files changed

Lines changed: 960 additions & 99 deletions

.github/workflows/ci-lint.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ jobs:
6262
uses: actions/checkout@v7
6363

6464
- name: Set up Go
65-
uses: actions/setup-go@v6
65+
uses: actions/setup-go@v7
6666
with:
6767
go-version-file: go.mod
6868
cache: true

.github/workflows/ci-pr-checks.yaml

Lines changed: 3 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# Matrix jobs (e2e-image-common, e2e-image-router, e2e-router) are each
1+
# Matrix jobs (e2e-image-common, e2e-image-router) are each
22
# followed by a *-status job that aggregates their result, since GitHub
33
# required status checks cannot reference individual matrix legs.
44
name: CI - Test
@@ -64,7 +64,7 @@ jobs:
6464
uses: actions/checkout@v7
6565

6666
- name: Set up Go
67-
uses: actions/setup-go@v6
67+
uses: actions/setup-go@v7
6868
with:
6969
go-version-file: go.mod
7070

@@ -348,29 +348,6 @@ jobs:
348348
if: ${{ needs.check-changes.outputs.src == 'true' }}
349349
runs-on: ubuntu-latest
350350
timeout-minutes: 45
351-
strategy:
352-
fail-fast: false
353-
matrix:
354-
suite:
355-
- name: pd
356-
label-filter: "!Disruptive && !Extended && !SharedStorage && !Metrics"
357-
needs-renderer: "false"
358-
- name: pd-shared-storage-deprecated
359-
label-filter: "SharedStorage && DeprecatedPD"
360-
needs-renderer: "false"
361-
- name: pd-shared-storage-disagg
362-
label-filter: "SharedStorage && Disagg"
363-
needs-renderer: "false"
364-
- name: pd-metrics
365-
label-filter: "Metrics"
366-
needs-renderer: "false"
367-
- name: extended
368-
label-filter: "Extended"
369-
needs-renderer: "true"
370-
- name: disruption
371-
label-filter: "Disruptive"
372-
needs-renderer: "false"
373-
name: e2e-router (${{ matrix.suite.name }})
374351
steps:
375352
- name: Checkout source
376353
uses: actions/checkout@v7
@@ -404,7 +381,6 @@ jobs:
404381
path: ${{ runner.temp }}/e2e-images
405382

406383
- name: Download renderer image
407-
if: ${{ matrix.suite.needs-renderer == 'true' }}
408384
uses: actions/download-artifact@v8
409385
with:
410386
name: e2e-image-renderer
@@ -417,26 +393,11 @@ jobs:
417393
docker load --input "$image"
418394
done
419395
420-
- name: Run e2e-router (${{ matrix.suite.name }})
396+
- name: Run e2e-router
421397
shell: bash
422398
env:
423399
GO_MOD_CACHE_VOL: ${{ steps.e2e-setup.outputs.go-mod-cache }}
424400
GO_BUILD_CACHE_VOL: ${{ steps.e2e-setup.outputs.go-build-cache }}
425-
E2E_LABEL_FILTER: ${{ matrix.suite.label-filter }}
426-
LOAD_VLLM_RENDER_IMAGE: ${{ matrix.suite.needs-renderer }}
427401
PULL_VLLM_RENDER_IMAGE: "false"
428402
HF_TOKEN: ${{ secrets.HF_TOKEN }}
429403
run: make test-e2e-run
430-
431-
e2e-router-status:
432-
needs: e2e-router
433-
if: always()
434-
runs-on: ubuntu-latest
435-
steps:
436-
- name: Check e2e-router result
437-
run: |
438-
result="${{ needs.e2e-router.result }}"
439-
if [[ "$result" != "success" && "$result" != "skipped" ]]; then
440-
echo "::error::e2e-router failed for at least one matrix suite"
441-
exit 1
442-
fi

.github/workflows/ci-release.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ jobs:
6262
uses: actions/checkout@v7
6363

6464
- name: Set up Go
65-
uses: actions/setup-go@v6
65+
uses: actions/setup-go@v7
6666
with:
6767
go-version-file: go.mod
6868

.github/workflows/nightly-router-perf-test-optimized-baseline-10k-1k.yaml

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -67,21 +67,21 @@ jobs:
6767
HF_TOKEN: ${{ secrets.HF_TOKEN }}
6868
steps:
6969
- name: Checkout llm-d/llm-d-router
70-
uses: actions/checkout@v6
70+
uses: actions/checkout@v7
7171

7272
- name: Checkout kubernetes-sigs/inference-perf
73-
uses: actions/checkout@v6
73+
uses: actions/checkout@v7
7474
with:
7575
repository: kubernetes-sigs/inference-perf
7676
path: inference-perf
7777

7878
- name: Authenticate to Google Cloud
79-
uses: google-github-actions/auth@v2
79+
uses: google-github-actions/auth@v3
8080
with:
8181
credentials_json: ${{ secrets.GKE_SA_KEY }}
8282

8383
- name: Set up gcloud CLI and kubectl
84-
uses: google-github-actions/setup-gcloud@v2
84+
uses: google-github-actions/setup-gcloud@v3
8585
with:
8686
project_id: ${{ env.GCP_PROJECT_ID }}
8787
install_components: 'kubectl,gke-gcloud-auth-plugin'
@@ -97,7 +97,7 @@ jobs:
9797
curl https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-3 | bash
9898
9999
- name: Set up Python
100-
uses: actions/setup-python@v5
100+
uses: actions/setup-python@v7
101101
with:
102102
python-version: "3.10"
103103

@@ -125,7 +125,7 @@ jobs:
125125
--gcs-bucket ${{ env.GCS_BUCKET }}
126126
127127
- name: Upload Performance Results Artifact
128-
uses: actions/upload-artifact@v4
128+
uses: actions/upload-artifact@v7
129129
if: always()
130130
with:
131131
name: epp-perf-results

Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -126,7 +126,7 @@ E2E_NUM_PROCS ?= 5
126126
# Should we pass ALL env vars here?
127127
E2E_ENV_VARS = EPP_IMAGE VLLM_IMAGE SIDECAR_IMAGE VLLM_RENDER_IMAGE \
128128
E2E_KEEP_CLUSTER_ON_FAILURE E2E_PORT E2E_METRICS_PORT K8S_CONTEXT READY_TIMEOUT \
129-
E2E_NUM_PROCS E2E_LABEL_FILTER LOAD_VLLM_RENDER_IMAGE HF_TOKEN
129+
E2E_NUM_PROCS LOAD_VLLM_RENDER_IMAGE HF_TOKEN
130130
BUILDER_E2E_ENV_FLAGS = $(foreach v,$(E2E_ENV_VARS),$(if $($(v)),-e '$(v)=$($(v))'))
131131
ifneq ($(filter command line environment,$(origin NAMESPACE)),)
132132
BUILDER_E2E_ENV_FLAGS += -e NAMESPACE=$(NAMESPACE)

cmd/epp/runner/runner.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,13 +60,16 @@ import (
6060
fwkdl "github.com/llm-d/llm-d-router/pkg/epp/framework/interface/datalayer"
6161
fwkplugin "github.com/llm-d/llm-d-router/pkg/epp/framework/interface/plugin"
6262
attrconcurrency "github.com/llm-d/llm-d-router/pkg/epp/framework/plugins/datalayer/attribute/concurrency"
63+
attrgpu "github.com/llm-d/llm-d-router/pkg/epp/framework/plugins/datalayer/attribute/gpu"
6364
attrlatency "github.com/llm-d/llm-d-router/pkg/epp/framework/plugins/datalayer/attribute/latency"
6465
attrmodels "github.com/llm-d/llm-d-router/pkg/epp/framework/plugins/datalayer/attribute/models"
6566
attrprefix "github.com/llm-d/llm-d-router/pkg/epp/framework/plugins/datalayer/attribute/prefix"
6667
attrsession "github.com/llm-d/llm-d-router/pkg/epp/framework/plugins/datalayer/attribute/session"
6768
discoveryfile "github.com/llm-d/llm-d-router/pkg/epp/framework/plugins/datalayer/discovery/file"
69+
extdcgm "github.com/llm-d/llm-d-router/pkg/epp/framework/plugins/datalayer/extractor/dcgm"
6870
extractormetrics "github.com/llm-d/llm-d-router/pkg/epp/framework/plugins/datalayer/extractor/metrics"
6971
extmodels "github.com/llm-d/llm-d-router/pkg/epp/framework/plugins/datalayer/extractor/models"
72+
srcdcgm "github.com/llm-d/llm-d-router/pkg/epp/framework/plugins/datalayer/source/dcgm"
7073
sourcemetrics "github.com/llm-d/llm-d-router/pkg/epp/framework/plugins/datalayer/source/metrics"
7174
srcmodels "github.com/llm-d/llm-d-router/pkg/epp/framework/plugins/datalayer/source/models"
7275
sourcenotifications "github.com/llm-d/llm-d-router/pkg/epp/framework/plugins/datalayer/source/notifications"
@@ -564,6 +567,10 @@ func (r *Runner) registerInTreePlugins() {
564567
fwkplugin.Register(srcmodels.ModelsDataSourceType, srcmodels.ModelDataSourceFactory)
565568
fwkplugin.Register(attrmodels.ModelsExtractorType, extmodels.ModelServerExtractorFactory)
566569

570+
// data layer DCGM source/extractor
571+
fwkplugin.Register(srcdcgm.DCGMDataSourceType, srcdcgm.DCGMDataSourceFactory)
572+
fwkplugin.Register(attrgpu.DCGMExtractorType, extdcgm.DCGMExtractorFactory)
573+
567574
fwkplugin.Register(prefix.PrefixCacheScorerPluginType, prefix.PrefixCachePluginFactory)
568575
fwkplugin.Register(maxscore.MaxScorePickerType, maxscore.MaxScorePickerFactory)
569576
fwkplugin.Register(random.RandomPickerType, random.RandomPickerFactory)

pkg/epp/datastore/datastore.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -315,6 +315,7 @@ func (ds *datastore) podUpdateOrAddIfNotExist(ctx context.Context, pod *corev1.P
315315
NamespacedName: createEndpointNamespacedName(pod, idx),
316316
PodName: pod.Name,
317317
Address: pod.Status.PodIP,
318+
NodeAddress: pod.Status.HostIP,
318319
Port: strconv.Itoa(port),
319320
MetricsHost: net.JoinHostPort(pod.Status.PodIP, strconv.Itoa(port)),
320321
Labels: labels,

pkg/epp/datastore/datastore_test.go

Lines changed: 23 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -305,6 +305,10 @@ var (
305305
ObjectMeta: metav1.ObjectMeta{
306306
Name: "pod1",
307307
},
308+
Status: corev1.PodStatus{
309+
PodIP: "10.0.0.1",
310+
HostIP: "192.168.1.10",
311+
},
308312
}
309313
pod1Metrics = &fwkdl.Metrics{
310314
WaitingQueueSize: 0,
@@ -320,6 +324,10 @@ var (
320324
ObjectMeta: metav1.ObjectMeta{
321325
Name: "pod2",
322326
},
327+
Status: corev1.PodStatus{
328+
PodIP: "10.0.0.2",
329+
HostIP: "192.168.1.10",
330+
},
323331
}
324332
pod2Metrics = &fwkdl.Metrics{
325333
WaitingQueueSize: 1,
@@ -506,7 +514,10 @@ func TestPods(t *testing.T) {
506514
for idx, pm := range podList {
507515
gotPods[idx] = &corev1.Pod{
508516
ObjectMeta: metav1.ObjectMeta{Name: pm.GetMetadata().PodName, Namespace: pm.GetMetadata().NamespacedName.Namespace},
509-
Status: corev1.PodStatus{PodIP: pm.GetMetadata().GetIPAddress()},
517+
Status: corev1.PodStatus{
518+
PodIP: pm.GetMetadata().GetIPAddress(),
519+
HostIP: pm.GetMetadata().GetNodeAddress(),
520+
},
510521
}
511522
}
512523
if !cmp.Equal(gotPods, test.wantPods, cmpopts.SortSlices(func(a, b *corev1.Pod) bool { return a.Name < b.Name })) {
@@ -649,6 +660,7 @@ func TestEndpointMetadata(t *testing.T) {
649660

650661
PodName: pod1.Name,
651662
Address: pod1.Status.PodIP,
663+
NodeAddress: pod1.Status.HostIP,
652664
Port: inferencePoolTargetPort,
653665
MetricsHost: net.JoinHostPort(pod1.Status.PodIP, inferencePoolTargetPort),
654666
Labels: map[string]string{},
@@ -671,6 +683,7 @@ func TestEndpointMetadata(t *testing.T) {
671683

672684
PodName: pod1.Name,
673685
Address: pod1.Status.PodIP,
686+
NodeAddress: pod1.Status.HostIP,
674687
Port: inferencePoolMultiTargetPort0,
675688
MetricsHost: net.JoinHostPort(pod1.Status.PodIP, inferencePoolMultiTargetPort0),
676689
Labels: map[string]string{},
@@ -683,6 +696,7 @@ func TestEndpointMetadata(t *testing.T) {
683696

684697
PodName: pod1.Name,
685698
Address: pod1.Status.PodIP,
699+
NodeAddress: pod1.Status.HostIP,
686700
Port: inferencePoolMultiTargetPort1,
687701
MetricsHost: net.JoinHostPort(pod1.Status.PodIP, inferencePoolMultiTargetPort1),
688702
Labels: map[string]string{},
@@ -706,6 +720,7 @@ func TestEndpointMetadata(t *testing.T) {
706720

707721
PodName: pod1.Name,
708722
Address: pod1.Status.PodIP,
723+
NodeAddress: pod1.Status.HostIP,
709724
Port: inferencePoolMultiTargetPort0,
710725
MetricsHost: net.JoinHostPort(pod1.Status.PodIP, inferencePoolMultiTargetPort0),
711726
Labels: map[string]string{},
@@ -718,6 +733,7 @@ func TestEndpointMetadata(t *testing.T) {
718733

719734
PodName: pod1.Name,
720735
Address: pod1.Status.PodIP,
736+
NodeAddress: pod1.Status.HostIP,
721737
Port: inferencePoolMultiTargetPort1,
722738
MetricsHost: net.JoinHostPort(pod1.Status.PodIP, inferencePoolMultiTargetPort1),
723739
Labels: map[string]string{},
@@ -731,8 +747,9 @@ func TestEndpointMetadata(t *testing.T) {
731747

732748
PodName: pod2.Name,
733749
Address: pod2.Status.PodIP,
750+
NodeAddress: pod2.Status.HostIP,
734751
Port: inferencePoolMultiTargetPort0,
735-
MetricsHost: net.JoinHostPort(pod1.Status.PodIP, inferencePoolMultiTargetPort0),
752+
MetricsHost: net.JoinHostPort(pod2.Status.PodIP, inferencePoolMultiTargetPort0),
736753
Labels: map[string]string{},
737754
},
738755
{
@@ -743,8 +760,9 @@ func TestEndpointMetadata(t *testing.T) {
743760

744761
PodName: pod2.Name,
745762
Address: pod2.Status.PodIP,
763+
NodeAddress: pod2.Status.HostIP,
746764
Port: inferencePoolMultiTargetPort1,
747-
MetricsHost: net.JoinHostPort(pod1.Status.PodIP, inferencePoolMultiTargetPort1),
765+
MetricsHost: net.JoinHostPort(pod2.Status.PodIP, inferencePoolMultiTargetPort1),
748766
Labels: map[string]string{},
749767
RankIndex: 1,
750768
},
@@ -766,6 +784,7 @@ func TestEndpointMetadata(t *testing.T) {
766784

767785
PodName: pod1.Name,
768786
Address: pod1.Status.PodIP,
787+
NodeAddress: pod1.Status.HostIP,
769788
Port: inferencePoolMultiTargetPort0,
770789
MetricsHost: net.JoinHostPort(pod1.Status.PodIP, inferencePoolMultiTargetPort0),
771790
Labels: map[string]string{},
@@ -778,6 +797,7 @@ func TestEndpointMetadata(t *testing.T) {
778797

779798
PodName: pod1.Name,
780799
Address: pod1.Status.PodIP,
800+
NodeAddress: pod1.Status.HostIP,
781801
Port: inferencePoolMultiTargetPort1,
782802
MetricsHost: net.JoinHostPort(pod1.Status.PodIP, inferencePoolMultiTargetPort1),
783803
Labels: map[string]string{},

pkg/epp/framework/interface/datalayer/endpoint_metadata.go

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -28,9 +28,12 @@ type EndpointMetadata struct {
2828
NamespacedName types.NamespacedName
2929
PodName string
3030
Address string
31-
Port string
32-
MetricsHost string
33-
Labels map[string]string
31+
// NodeAddress is the node IP hosting this pod (pod.Status.HostIP).
32+
// Empty for non-Kubernetes discovery sources (e.g. file discovery).
33+
NodeAddress string
34+
Port string
35+
MetricsHost string
36+
Labels map[string]string
3437
// RankIndex is this endpoint's position in the pool's TargetPorts,
3538
// identifying the pod-local rank in multi-port deployments.
3639
RankIndex int
@@ -59,6 +62,7 @@ func (epm *EndpointMetadata) Clone() *EndpointMetadata {
5962
},
6063
PodName: epm.PodName,
6164
Address: epm.Address,
65+
NodeAddress: epm.NodeAddress,
6266
Port: epm.Port,
6367
MetricsHost: epm.MetricsHost,
6468
Labels: clonedLabels,
@@ -75,6 +79,7 @@ func (epm *EndpointMetadata) Equal(other *EndpointMetadata) bool {
7579
return epm.NamespacedName == other.NamespacedName &&
7680
epm.PodName == other.PodName &&
7781
epm.Address == other.Address &&
82+
epm.NodeAddress == other.NodeAddress &&
7883
epm.Port == other.Port &&
7984
epm.MetricsHost == other.MetricsHost &&
8085
epm.RankIndex == other.RankIndex &&
@@ -100,6 +105,14 @@ func (epm *EndpointMetadata) GetIPAddress() string {
100105
return epm.Address
101106
}
102107

108+
// GetNodeAddress returns the IP of the node hosting this endpoint.
109+
func (epm *EndpointMetadata) GetNodeAddress() string {
110+
if epm == nil {
111+
return ""
112+
}
113+
return epm.NodeAddress
114+
}
115+
103116
// GetPort returns the Endpoint's inference port.
104117
func (epm *EndpointMetadata) GetPort() string {
105118
return epm.Port
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
# GPU Attributes
2+
3+
This package defines the data structures for GPU hardware metrics collected from NVIDIA DCGM Exporter.
4+
5+
## `GPUUtilization`
6+
7+
A normalized GPU compute utilization value in [0.0, 1.0], derived from `DCGM_FI_DEV_GPU_UTIL` (which reports 0-100). For multi-GPU pods the extractor aggregates across visible devices using `max`.
8+
9+
- **Key**: `GPUUtilizationDataKey` (`GPUUtilization/dcgm-extractor`)
10+
11+
## Producers
12+
13+
The following plugins produce this attribute:
14+
15+
- **`dcgm-extractor`** (Data Layer): Extracts GPU utilization from the DCGM Exporter Prometheus endpoint.

0 commit comments

Comments
 (0)