Skip to content

Commit 8757966

Browse files
author
照微
committed
feat: add K8s scheduling options, image pull secrets, and dedicated model downloader images
1 parent a46f8d4 commit 8757966

20 files changed

Lines changed: 482 additions & 127 deletions

cmd/llmctl/autobenchmark/dashboard.go

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -50,11 +50,12 @@ const (
5050
)
5151

5252
type dashboardOptions struct {
53-
cf *genericclioptions.ConfigFlags
54-
configFile string
55-
name string
56-
image string
57-
localPort int
53+
cf *genericclioptions.ConfigFlags
54+
configFile string
55+
name string
56+
image string
57+
imagePullSecrets []string
58+
localPort int
5859
}
5960

6061
func newDashboardCmd(cf *genericclioptions.ConfigFlags) *cobra.Command {
@@ -83,6 +84,7 @@ If [name] is provided, it overrides the experiment name from the config file.`,
8384

8485
cmd.Flags().StringVarP(&opts.configFile, "config", "f", "", "Path to auto-benchmark config file (required)")
8586
cmd.Flags().StringVar(&opts.image, "image", dashboardImage, "Dashboard container image")
87+
cmd.Flags().StringArrayVar(&opts.imagePullSecrets, "image-pull-secret", nil, "Image pull secret names for private registries (can be specified multiple times)")
8688
cmd.Flags().IntVarP(&opts.localPort, "port", "p", defaultLocalPort, "Local port for port-forward")
8789
_ = cmd.MarkFlagRequired("config")
8890

@@ -180,6 +182,13 @@ func (o *dashboardOptions) run(ctx context.Context) error {
180182
},
181183
}
182184

185+
// Apply image pull secrets
186+
if len(o.imagePullSecrets) > 0 {
187+
for _, name := range o.imagePullSecrets {
188+
deploy.Spec.Template.Spec.ImagePullSecrets = append(deploy.Spec.Template.Spec.ImagePullSecrets, corev1.LocalObjectReference{Name: name})
189+
}
190+
}
191+
183192
// Delete existing if present, then create
184193
deployClient := clientset.AppsV1().Deployments(namespace)
185194
_, err = deployClient.Get(ctx, deployName, metav1.GetOptions{})

cmd/llmctl/autobenchmark/run.go

Lines changed: 19 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -37,11 +37,12 @@ import (
3737
)
3838

3939
type runOptions struct {
40-
cf *genericclioptions.ConfigFlags
41-
configFile string
42-
name string
43-
image string
44-
serviceAccount string
40+
cf *genericclioptions.ConfigFlags
41+
configFile string
42+
name string
43+
image string
44+
serviceAccount string
45+
imagePullSecrets []string
4546
}
4647

4748
func newRunCmd(cf *genericclioptions.ConfigFlags) *cobra.Command {
@@ -67,6 +68,7 @@ See the guide for RBAC setup instructions.`,
6768
cmd.Flags().StringVar(&opts.name, "name", "", "Experiment name (defaults to auto-generated name from config)")
6869
cmd.Flags().StringVar(&opts.image, "image", controllerImage, "Controller image")
6970
cmd.Flags().StringVar(&opts.serviceAccount, "service-account", "", "ServiceAccount for the controller Job (required)")
71+
cmd.Flags().StringArrayVar(&opts.imagePullSecrets, "image-pull-secret", nil, "Image pull secret names for private registries (can be specified multiple times)")
7072
_ = cmd.MarkFlagRequired("config")
7173
_ = cmd.MarkFlagRequired("service-account")
7274

@@ -187,6 +189,7 @@ func (o *runOptions) run(ctx context.Context) error {
187189
Template: corev1.PodTemplateSpec{
188190
Spec: corev1.PodSpec{
189191
ServiceAccountName: o.serviceAccount,
192+
ImagePullSecrets: toImagePullSecrets(o.imagePullSecrets),
190193
RestartPolicy: corev1.RestartPolicyNever,
191194
Containers: []corev1.Container{
192195
{
@@ -266,6 +269,17 @@ func validateExpName(name string) error {
266269
return nil
267270
}
268271

272+
func toImagePullSecrets(names []string) []corev1.LocalObjectReference {
273+
if len(names) == 0 {
274+
return nil
275+
}
276+
refs := make([]corev1.LocalObjectReference, len(names))
277+
for i, name := range names {
278+
refs[i] = corev1.LocalObjectReference{Name: name}
279+
}
280+
return refs
281+
}
282+
269283
func sanitizeCMKey(name string) string {
270284
name = strings.ToLower(name)
271285
name = invalidCMKeyChars.ReplaceAllString(name, "-")

cmd/llmctl/benchmark/benchmark.go

Lines changed: 15 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -70,11 +70,12 @@ type BenchmarkOptions struct {
7070

7171
extraArgs map[string]string
7272

73-
image string
74-
cpuRequest string
75-
cpuLimit string
76-
memoryRequest string
77-
memoryLimit string
73+
image string
74+
imagePullSecrets []string
75+
cpuRequest string
76+
cpuLimit string
77+
memoryRequest string
78+
memoryLimit string
7879

7980
wait bool
8081
}
@@ -103,11 +104,12 @@ type BenchmarkConfig struct {
103104

104105
ExtraArgs map[string]string `json:"extraArgs,omitempty"`
105106

106-
Image string `json:"image,omitempty"`
107-
CPURequest string `json:"cpuRequest,omitempty"`
108-
CPULimit string `json:"cpuLimit,omitempty"`
109-
MemoryRequest string `json:"memoryRequest,omitempty"`
110-
MemoryLimit string `json:"memoryLimit,omitempty"`
107+
Image string `json:"image,omitempty"`
108+
ImagePullSecrets []string `json:"imagePullSecrets,omitempty"`
109+
CPURequest string `json:"cpuRequest,omitempty"`
110+
CPULimit string `json:"cpuLimit,omitempty"`
111+
MemoryRequest string `json:"memoryRequest,omitempty"`
112+
MemoryLimit string `json:"memoryLimit,omitempty"`
111113

112114
Wait *bool `json:"wait,omitempty"`
113115
}
@@ -195,6 +197,7 @@ version management.`,
195197
"Defaults to the generated job name if not specified.")
196198

197199
cmd.Flags().StringVar(&benchmarkOpts.image, "image", benchmarkOpts.image, "Container image used for benchmark job")
200+
cmd.Flags().StringArrayVar(&benchmarkOpts.imagePullSecrets, "image-pull-secret", nil, "Image pull secret names for private registries (can be specified multiple times)")
198201
cmd.Flags().StringVar(&benchmarkOpts.cpuRequest, "cpu-request", benchmarkOpts.cpuRequest, "CPU request for benchmark pod")
199202
cmd.Flags().StringVar(&benchmarkOpts.cpuLimit, "cpu-limit", benchmarkOpts.cpuLimit, "CPU limit for benchmark pod")
200203
cmd.Flags().StringVar(&benchmarkOpts.memoryRequest, "memory-request", benchmarkOpts.memoryRequest, "Memory request for benchmark pod")
@@ -262,7 +265,7 @@ func getConflictingFlags(cmd *cobra.Command) []string {
262265
"api-backend", "api-base", "api-port", "api-key", "api-model-name",
263266
"model-tokenizer",
264267
"experiment-base-dir", "experiment-folder-name",
265-
"image", "cpu-request", "cpu-limit", "memory-request", "memory-limit",
268+
"image", "image-pull-secret", "cpu-request", "cpu-limit", "memory-request", "memory-limit",
266269
"wait", "extra-args",
267270
}
268271
var conflicting []string
@@ -291,6 +294,7 @@ func applyConfigToOptions(cfg *BenchmarkConfig, cmd *cobra.Command) {
291294
setStringOpt(&benchmarkOpts.experimentBaseDir, cfg.ExperimentBaseDir, "experiment-base-dir", cmd)
292295
setStringOpt(&benchmarkOpts.experimentFolderName, cfg.ExperimentFolderName, "experiment-folder-name", cmd)
293296
setStringOpt(&benchmarkOpts.image, cfg.Image, "image", cmd)
297+
setStringSliceOpt(&benchmarkOpts.imagePullSecrets, cfg.ImagePullSecrets, "image-pull-secret", cmd)
294298
setStringOpt(&benchmarkOpts.cpuRequest, cfg.CPURequest, "cpu-request", cmd)
295299
setStringOpt(&benchmarkOpts.cpuLimit, cfg.CPULimit, "cpu-limit", cmd)
296300
setStringOpt(&benchmarkOpts.memoryRequest, cfg.MemoryRequest, "memory-request", cmd)

cmd/llmctl/benchmark/dashboard_cmd.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ type DashboardOptions struct {
5353
localPort int
5454
openBrowser bool
5555
image string
56+
imagePullSecrets []string
5657
}
5758

5859
var dashboardOpts DashboardOptions
@@ -99,6 +100,8 @@ Press Ctrl+C to stop the dashboard and clean up resources.`,
99100
"Automatically open browser after dashboard is ready")
100101
cmd.Flags().StringVar(&dashboardOpts.image, "image", defaultDashboardImage,
101102
"Container image for the benchmark dashboard")
103+
cmd.Flags().StringArrayVar(&dashboardOpts.imagePullSecrets, "image-pull-secret", nil,
104+
"Image pull secret names for private registries (can be specified multiple times)")
102105

103106
return cmd
104107
}

cmd/llmctl/benchmark/dashboard_pod.go

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ func buildDashboardPod(namespace string, pvcComponents *PVCComponents) *corev1.P
3838
volumeMount.SubPath = pvcComponents.SubPath
3939
}
4040

41-
return &corev1.Pod{
41+
pod := &corev1.Pod{
4242
ObjectMeta: metav1.ObjectMeta{
4343
GenerateName: "rbg-benchmark-dashboard-",
4444
Namespace: namespace,
@@ -94,4 +94,12 @@ func buildDashboardPod(namespace string, pvcComponents *PVCComponents) *corev1.P
9494
RestartPolicy: corev1.RestartPolicyNever,
9595
},
9696
}
97+
98+
if len(dashboardOpts.imagePullSecrets) > 0 {
99+
for _, name := range dashboardOpts.imagePullSecrets {
100+
pod.Spec.ImagePullSecrets = append(pod.Spec.ImagePullSecrets, corev1.LocalObjectReference{Name: name})
101+
}
102+
}
103+
104+
return pod
97105
}

cmd/llmctl/benchmark/job.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,12 @@ func buildBenchmarkJob(namespace, rbgName string) (*batchv1.Job, error) {
132132
RestartPolicy: corev1.RestartPolicyNever,
133133
}
134134

135+
if len(benchmarkOpts.imagePullSecrets) > 0 {
136+
for _, name := range benchmarkOpts.imagePullSecrets {
137+
podSpec.ImagePullSecrets = append(podSpec.ImagePullSecrets, corev1.LocalObjectReference{Name: name})
138+
}
139+
}
140+
135141
backoffLimit := int32(0)
136142
ttlSecondsAfterFinished := int32(604800) // 7 days
137143

@@ -178,6 +184,7 @@ func buildBenchmarkAnnotations() (map[string]string, error) {
178184
ExperimentFolderName: benchmarkOpts.experimentFolderName,
179185
ExtraArgs: benchmarkOpts.extraArgs,
180186
Image: benchmarkOpts.image,
187+
ImagePullSecrets: benchmarkOpts.imagePullSecrets,
181188
CPURequest: benchmarkOpts.cpuRequest,
182189
CPULimit: benchmarkOpts.cpuLimit,
183190
MemoryRequest: benchmarkOpts.memoryRequest,

cmd/llmctl/model/list.go

Lines changed: 19 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,9 @@ import (
3636

3737
func newListCmd(cf *genericclioptions.ConfigFlags) *cobra.Command {
3838
var (
39-
storage string
39+
storage string
40+
image string
41+
imagePullSecrets []string
4042
)
4143

4244
cmd := &cobra.Command{
@@ -94,7 +96,7 @@ Examples:
9496
}
9597

9698
// Create a job to scan storage and list models
97-
job := buildListModelsJob(storageplugin.DefaultMountPath)
99+
job := buildListModelsJob(storageplugin.DefaultMountPath, image, imagePullSecrets)
98100

99101
// Mount storage (provisions resources for OSS and adds volumes/mounts)
100102
ctrlClient, err := util.GetControllerRuntimeClient(cf)
@@ -141,12 +143,16 @@ Examples:
141143
}
142144

143145
cmd.Flags().StringVar(&storage, "storage", "", "Storage to use (overrides default)")
146+
cmd.Flags().StringVar(&image, "image", defaultListImage, "Container image for the list job")
147+
cmd.Flags().StringArrayVar(&imagePullSecrets, "image-pull-secret", nil, "Image pull secret names for private registries (can be specified multiple times)")
144148

145149
return cmd
146150
}
147151

152+
const defaultListImage = "alpine:latest"
153+
148154
// buildListModelsJob creates a Job to scan storage and list models
149-
func buildListModelsJob(mountPath string) *batchv1.Job {
155+
func buildListModelsJob(mountPath, image string, imagePullSecrets []string) *batchv1.Job {
150156
timestamp := time.Now().Unix()
151157
jobName := fmt.Sprintf("list-models-%d", timestamp)
152158

@@ -198,7 +204,7 @@ echo "]" >> "$OUTPUT_FILE"
198204
cat "$OUTPUT_FILE"
199205
`, mountPath)
200206

201-
return &batchv1.Job{
207+
job := &batchv1.Job{
202208
ObjectMeta: metav1.ObjectMeta{
203209
Name: jobName,
204210
Labels: labels,
@@ -215,7 +221,7 @@ cat "$OUTPUT_FILE"
215221
Containers: []corev1.Container{
216222
{
217223
Name: "scanner",
218-
Image: "alpine:latest",
224+
Image: image,
219225
Command: []string{"/bin/sh", "-c"},
220226
Args: []string{scanScript},
221227
},
@@ -225,6 +231,14 @@ cat "$OUTPUT_FILE"
225231
},
226232
},
227233
}
234+
235+
if len(imagePullSecrets) > 0 {
236+
for _, name := range imagePullSecrets {
237+
job.Spec.Template.Spec.ImagePullSecrets = append(job.Spec.Template.Spec.ImagePullSecrets, corev1.LocalObjectReference{Name: name})
238+
}
239+
}
240+
241+
return job
228242
}
229243

230244
// getJobOutput retrieves the output from the job's pod

cmd/llmctl/model/list_test.go

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -45,38 +45,38 @@ func TestInt64Ptr(t *testing.T) {
4545
// --- buildListModelsJob ---
4646

4747
func TestBuildListModelsJob_NamePrefix(t *testing.T) {
48-
job := buildListModelsJob("/models")
48+
job := buildListModelsJob("/models", defaultListImage, nil)
4949
assert.True(t, strings.HasPrefix(job.Name, "list-models-"), "job name should start with list-models-")
5050
}
5151

5252
func TestBuildListModelsJob_Labels(t *testing.T) {
53-
job := buildListModelsJob("/models")
53+
job := buildListModelsJob("/models", defaultListImage, nil)
5454
assert.Equal(t, "true", job.Labels["rbg-list-job"])
5555
}
5656

5757
func TestBuildListModelsJob_ContainerSpec(t *testing.T) {
58-
job := buildListModelsJob("/data/models")
58+
job := buildListModelsJob("/models", defaultListImage, nil)
5959
require.Len(t, job.Spec.Template.Spec.Containers, 1)
6060
c := job.Spec.Template.Spec.Containers[0]
6161
assert.Equal(t, "scanner", c.Name)
62-
assert.Equal(t, "alpine:latest", c.Image)
62+
assert.Equal(t, defaultListImage, c.Image)
6363
assert.Equal(t, []string{"/bin/sh", "-c"}, c.Command)
6464
}
6565

6666
func TestBuildListModelsJob_ScriptContainsMountPath(t *testing.T) {
67-
job := buildListModelsJob("/custom/path")
67+
job := buildListModelsJob("/custom/path", defaultListImage, nil)
6868
args := job.Spec.Template.Spec.Containers[0].Args
6969
require.Len(t, args, 1)
7070
assert.Contains(t, args[0], "/custom/path")
7171
}
7272

7373
func TestBuildListModelsJob_RestartPolicy(t *testing.T) {
74-
job := buildListModelsJob("/models")
74+
job := buildListModelsJob("/models", defaultListImage, nil)
7575
assert.Equal(t, corev1.RestartPolicyNever, job.Spec.Template.Spec.RestartPolicy)
7676
}
7777

7878
func TestBuildListModelsJob_Limits(t *testing.T) {
79-
job := buildListModelsJob("/models")
79+
job := buildListModelsJob("/models", defaultListImage, nil)
8080
assert.Equal(t, int32(2), *job.Spec.BackoffLimit)
8181
assert.Equal(t, int64(300), *job.Spec.ActiveDeadlineSeconds)
8282
assert.Equal(t, int32(60), *job.Spec.TTLSecondsAfterFinished)

cmd/llmctl/model/pull.go

Lines changed: 21 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -41,12 +41,14 @@ import (
4141

4242
func newPullCmd(cf *genericclioptions.ConfigFlags) *cobra.Command {
4343
var (
44-
revision string
45-
source string
46-
storage string
47-
wait bool
48-
memory string
49-
cpu string
44+
revision string
45+
source string
46+
storage string
47+
wait bool
48+
memory string
49+
cpu string
50+
image string
51+
imagePullSecrets []string
5052
)
5153

5254
cmd := &cobra.Command{
@@ -144,6 +146,10 @@ Examples:
144146
return fmt.Errorf("failed to generate download template: %w", err)
145147
}
146148

149+
if image != "" && len(podTemplate.Spec.Containers) > 0 {
150+
podTemplate.Spec.Containers[0].Image = image
151+
}
152+
147153
// Inject metadata saving logic - wraps the original command to save model info after download
148154
injectMetadataSave(podTemplate, modelID, revision, modelPath)
149155

@@ -164,6 +170,13 @@ Examples:
164170
// Apply resource limits to the download container
165171
applyPullResources(podTemplate, cpu, memory)
166172

173+
// Apply image pull secrets
174+
if len(imagePullSecrets) > 0 {
175+
for _, name := range imagePullSecrets {
176+
podTemplate.Spec.ImagePullSecrets = append(podTemplate.Spec.ImagePullSecrets, corev1.LocalObjectReference{Name: name})
177+
}
178+
}
179+
167180
// Create the Job
168181
job := buildPullJob(modelID, podTemplate)
169182

@@ -208,6 +221,8 @@ Examples:
208221
cmd.Flags().BoolVar(&wait, "wait", true, "Wait for the pull job to complete and stream logs")
209222
cmd.Flags().StringVar(&memory, "memory", defaultPullMemory, "Memory request/limit for the download container (e.g. 8Gi, 16Gi)")
210223
cmd.Flags().StringVar(&cpu, "cpu", defaultPullCPU, "CPU request/limit for the download container (e.g. 2, 4)")
224+
cmd.Flags().StringVar(&image, "image", "", "Container image override for the download job (default: from source plugin)")
225+
cmd.Flags().StringArrayVar(&imagePullSecrets, "image-pull-secret", nil, "Image pull secret names for private registries (can be specified multiple times)")
211226

212227
return cmd
213228
}

0 commit comments

Comments
 (0)