Skip to content

Commit 8696e23

Browse files
committed
feat(operator): add five-container Bulwark GMS workers
Signed-off-by: mkhadkevich <mkhadkevich@nvidia.com>
1 parent d3220d9 commit 8696e23

16 files changed

Lines changed: 878 additions & 150 deletions

deploy/operator/internal/consts/consts.go

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -131,12 +131,13 @@ const (
131131
KubeAnnotationTopologyClusterTopologyName = "nvidia.com/topology-cluster-topology-name"
132132
KubeLabelDynamoTopologyPrefix = "nvidia.com/dynamo-topology."
133133

134-
DynamoDeploymentConfigEnvVar = "DYN_DEPLOYMENT_CONFIG"
135-
DynamoNamespaceEnvVar = "DYN_NAMESPACE"
136-
DynamoNamespacePrefixEnvVar = "DYN_NAMESPACE_PREFIX"
137-
DynamoNamespaceWorkerSuffixEnvVar = "DYN_NAMESPACE_WORKER_SUFFIX"
138-
DynamoComponentEnvVar = "DYN_COMPONENT"
139-
DynamoDiscoveryBackendEnvVar = "DYN_DISCOVERY_BACKEND"
134+
DynamoDeploymentConfigEnvVar = "DYN_DEPLOYMENT_CONFIG"
135+
DynamoNamespaceEnvVar = "DYN_NAMESPACE"
136+
DynamoNamespacePrefixEnvVar = "DYN_NAMESPACE_PREFIX"
137+
DynamoNamespaceWorkerSuffixEnvVar = "DYN_NAMESPACE_WORKER_SUFFIX"
138+
DynamoComponentEnvVar = "DYN_COMPONENT"
139+
DynamoDiscoveryBackendEnvVar = "DYN_DISCOVERY_BACKEND"
140+
DynamoDiscoveryLogicalInstanceKeyEnvVar = "DYN_DISCOVERY_LOGICAL_INSTANCE_KEY"
140141

141142
// DynamoOperatorAllowGMSSnapshotEnvVar enables the temporary internal
142143
// GMS + Snapshot admission gate when set to "1".

deploy/operator/internal/dynamo/backend_sglang.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,12 @@ func (b *SGLangBackend) UpdateContainer(container *corev1.Container, numberOfNod
3737
"next-steps", "upstream SGLang changes needed")
3838
}
3939

40+
if component.IsInterPodGMSEnabled() {
41+
if !containerHasGMSLoadFormat(container) {
42+
injectFlagsIntoContainerCommand(container, "--load-format gms", false, "sglang")
43+
}
44+
}
45+
4046
// For single node, nothing to do
4147
if numberOfNodes <= 1 {
4248
return

deploy/operator/internal/dynamo/backend_trtllm.go

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,12 @@ func (b *TRTLLMBackend) UpdateContainer(container *corev1.Container, numberOfNod
3030
"next-steps", "upstream TensorRT-LLM changes needed")
3131
}
3232

33+
if component.IsInterPodGMSEnabled() {
34+
if !containerHasGMSLoadFormat(container) {
35+
injectFlagsIntoContainerCommand(container, "--load-format gms", false, "trtllm")
36+
}
37+
}
38+
3339
// For single node, nothing to do
3440
if numberOfNodes <= 1 {
3541
return
@@ -141,7 +147,7 @@ func (b *TRTLLMBackend) setupLeaderContainer(container *corev1.Container, number
141147
"cp /ssh-pk/private.key.pub $HOME/.ssh/authorized_keys",
142148
"chmod 600 $HOME/.ssh/id_rsa $HOME/.ssh/authorized_keys",
143149
"chmod 644 $HOME/.ssh/id_rsa.pub",
144-
fmt.Sprintf("printf 'Host *\\nIdentityFile '$HOME'/.ssh/id_rsa\\nStrictHostKeyChecking no\\nPort %d\\n' > $HOME/.ssh/config", commonconsts.MpiRunSshPort),
150+
fmt.Sprintf("printf 'Host *\\nIdentityFile '$HOME'/.ssh/id_rsa\\nStrictHostKeyChecking no\\nUserKnownHostsFile /dev/null\\nGlobalKnownHostsFile /dev/null\\nPort %d\\n' > $HOME/.ssh/config", commonconsts.MpiRunSshPort),
145151
}
146152

147153
// Calculate total number of GPUs across all nodes. In the normal pod

deploy/operator/internal/dynamo/backend_trtllm_test.go

Lines changed: 10 additions & 10 deletions
Large diffs are not rendered by default.

deploy/operator/internal/dynamo/backend_vllm.go

Lines changed: 9 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ const (
2323
dataParallelSizeLocalFlag = "--data-parallel-size-local"
2424
distributedExecutorFlag = "--distributed-executor-backend"
2525
enableElasticEPFlag = "--enable-elastic-ep"
26+
vllmGMSShadowModeEnvVar = "DYN_VLLM_GMS_SHADOW_MODE"
2627
)
2728

2829
type VLLMBackend struct {
@@ -34,15 +35,21 @@ func (b *VLLMBackend) UpdateContainer(container *corev1.Container, numberOfNodes
3435
// to load weights from the dedicated GMS weight-server pod rather than
3536
// from disk.
3637
if component.IsInterPodGMSEnabled() {
37-
if !containerHasArg(container, "--load-format", "gms") {
38+
if !containerHasGMSLoadFormat(container) {
3839
injectFlagsIntoContainerCommand(container, "--load-format gms", false, "vllm")
3940
}
4041
if component.IsInterPodFailoverEnabled() {
4142
// DYN_VLLM_GMS_SHADOW_MODE activates vLLM's standby/failover behavior
4243
// on top of --load-format gms. Standalone inter-pod GMS should not set
4344
// it: those engines are active clients of the dedicated GMS weight
4445
// server, not shadow engines waiting for failover activation.
45-
container.Env = append(container.Env, corev1.EnvVar{Name: "DYN_VLLM_GMS_SHADOW_MODE", Value: "true"})
46+
//
47+
// This is a vLLM-engine-specific switch (it activates the vLLM-side
48+
// GMS client path for shadow weight loading). It is injected here — in
49+
// the vLLM backend — rather than in the backend-agnostic GMS helpers so
50+
// non-vLLM backends do not inherit a stray, meaningless env var if/when
51+
// inter-pod GMS is extended to them.
52+
container.Env = append(container.Env, corev1.EnvVar{Name: vllmGMSShadowModeEnvVar, Value: "true"})
4653
}
4754
}
4855

@@ -464,16 +471,6 @@ func injectElasticEPRayLaunchFlags(container *corev1.Container, role Role, servi
464471
container.Command = []string{"/bin/sh", "-c"}
465472
}
466473

467-
// hasFlag returns true if flag exists in expandedArgs.
468-
func hasFlag(expandedArgs []string, flag string) bool {
469-
for _, arg := range expandedArgs {
470-
if arg == flag {
471-
return true
472-
}
473-
}
474-
return false
475-
}
476-
477474
func injectDataParallelLaunchFlags(container *corev1.Container, role Role, serviceName string, multinodeDeployer MultinodeDeployer, resources *corev1.ResourceRequirements, numberOfNodes int32) {
478475
expandedArgs := getExpandedArgs(container)
479476
leaderHostname := multinodeDeployer.GetLeaderHostname(serviceName)

deploy/operator/internal/dynamo/backend_vllm_test.go

Lines changed: 56 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1052,18 +1052,18 @@ func TestVLLMBackend_UpdateContainer_InterPodGMS(t *testing.T) {
10521052

10531053
count := 0
10541054
for _, e := range container.Env {
1055-
if e.Name == "DYN_VLLM_GMS_SHADOW_MODE" {
1055+
if e.Name == vllmGMSShadowModeEnvVar {
10561056
count++
10571057
if e.Value != "true" {
1058-
t.Errorf("DYN_VLLM_GMS_SHADOW_MODE value = %q, want %q", e.Value, "true")
1058+
t.Errorf("%s value = %q, want %q", vllmGMSShadowModeEnvVar, e.Value, "true")
10591059
}
10601060
}
10611061
}
10621062
if count != tt.shadowModeCount {
1063-
t.Errorf("DYN_VLLM_GMS_SHADOW_MODE env var count = %d, want %d", count, tt.shadowModeCount)
1063+
t.Errorf("%s env var count = %d, want %d", vllmGMSShadowModeEnvVar, count, tt.shadowModeCount)
10641064
}
10651065
if got := count > 0; got != tt.wantShadowMode {
1066-
t.Errorf("DYN_VLLM_GMS_SHADOW_MODE present = %v, want %v", got, tt.wantShadowMode)
1066+
t.Errorf("%s present = %v, want %v", vllmGMSShadowModeEnvVar, got, tt.wantShadowMode)
10671067
}
10681068
})
10691069
}
@@ -1086,8 +1086,58 @@ func TestVLLMBackend_UpdateContainer_NoInterPodGMS(t *testing.T) {
10861086
t.Errorf("--load-format gms must not be injected when inter-pod GMS is disabled")
10871087
}
10881088
for _, e := range container.Env {
1089-
if e.Name == "DYN_VLLM_GMS_SHADOW_MODE" {
1090-
t.Errorf("DYN_VLLM_GMS_SHADOW_MODE must not be injected when inter-pod GMS is disabled")
1089+
if e.Name == vllmGMSShadowModeEnvVar {
1090+
t.Errorf("%s must not be injected when inter-pod GMS is disabled", vllmGMSShadowModeEnvVar)
1091+
}
1092+
}
1093+
}
1094+
1095+
func TestSGLangBackend_UpdateContainer_InterPodGMS(t *testing.T) {
1096+
backend := &SGLangBackend{}
1097+
component := betaComponent(t, &v1alpha1.DynamoComponentDeploymentSharedSpec{
1098+
GPUMemoryService: &v1alpha1.GPUMemoryServiceSpec{
1099+
Enabled: true,
1100+
Mode: v1alpha1.GMSModeInterPod,
1101+
},
1102+
})
1103+
container := &corev1.Container{
1104+
Command: []string{"python3"},
1105+
Args: []string{"-m", "dynamo.sglang"},
1106+
}
1107+
1108+
backend.UpdateContainer(container, 1, RoleMain, component, "svc", &GroveMultinodeDeployer{})
1109+
1110+
if !containerHasGMSLoadFormat(container) {
1111+
t.Errorf("expected --load-format gms to be injected for SGLang inter-pod GMS; got args=%q", container.Args)
1112+
}
1113+
for _, e := range container.Env {
1114+
if e.Name == vllmGMSShadowModeEnvVar {
1115+
t.Errorf("vLLM-specific env var must not be injected for SGLang")
1116+
}
1117+
}
1118+
}
1119+
1120+
func TestTRTLLMBackend_UpdateContainer_InterPodGMS(t *testing.T) {
1121+
backend := &TRTLLMBackend{}
1122+
component := betaComponent(t, &v1alpha1.DynamoComponentDeploymentSharedSpec{
1123+
GPUMemoryService: &v1alpha1.GPUMemoryServiceSpec{
1124+
Enabled: true,
1125+
Mode: v1alpha1.GMSModeInterPod,
1126+
},
1127+
})
1128+
container := &corev1.Container{
1129+
Command: []string{"python3"},
1130+
Args: []string{"-m", "dynamo.trtllm"},
1131+
}
1132+
1133+
backend.UpdateContainer(container, 1, RoleMain, component, "svc", &GroveMultinodeDeployer{})
1134+
1135+
if !containerHasGMSLoadFormat(container) {
1136+
t.Errorf("expected --load-format gms to be injected for TRT-LLM inter-pod GMS; got args=%q", container.Args)
1137+
}
1138+
for _, e := range container.Env {
1139+
if e.Name == vllmGMSShadowModeEnvVar {
1140+
t.Errorf("vLLM-specific env var must not be injected for TRT-LLM")
10911141
}
10921142
}
10931143
}

0 commit comments

Comments
 (0)