Skip to content

Commit f45c6aa

Browse files
committed
feat(mtc): Enable MTC at job submission stage
1 parent 75331e8 commit f45c6aa

10 files changed

Lines changed: 92 additions & 52 deletions

File tree

cmd/job/submit.go

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,9 @@ var (
7070
volumeStr []string
7171
pathways orchestrator.PathwaysJobDefinition
7272

73+
mtcEnabled bool
74+
ramdiskDirectory string
75+
7376
gkeNapProvisioning string
7477
gkeNapReservation string
7578

@@ -180,8 +183,8 @@ func init() {
180183
SubmitCmd.Flags().StringArrayVar(&pathwaysWorkerEnv, "pathways-worker-env", []string{}, "Custom environment variables for the Pathways worker container in KEY=VALUE format. Can be specified multiple times.")
181184
SubmitCmd.Flags().StringVar(&pathways.ColocatedPythonSidecarImage, "pathways-colocated-python-sidecar-image", "", "Image for an optional Python-based sidecar container to run alongside the Pathways head components.")
182185
SubmitCmd.Flags().StringVar(&pathways.HeadNodePool, "pathways-head-np", "", "The node pool to use for the Pathways head job. If empty, it will be auto-detected (looking for 'cpu-np' or 'pathways-np').")
183-
SubmitCmd.Flags().BoolVar(&pathways.MTCEnabled, "pathways-mtc-enabled", false, "Enable Multi-Tier Checkpointing (MTC) for Pathways.")
184-
SubmitCmd.Flags().StringVar(&pathways.RamdiskDirectory, "pathways-ramdisk-directory", "", "The ramdisk directory path for local checkpoints in MTC.")
186+
SubmitCmd.Flags().BoolVar(&mtcEnabled, "mtc-enabled", false, "Enable Multi-Tier Checkpointing (MTC).")
187+
SubmitCmd.Flags().StringVar(&ramdiskDirectory, "mtc-ramdisk-directory", "", "The ramdisk directory path for local checkpoints in MTC.")
185188

186189
_ = SubmitCmd.MarkFlagRequired("name")
187190
_ = SubmitCmd.MarkFlagRequired("compute-type")
@@ -257,6 +260,8 @@ func runSubmitCmd(cmd *cobra.Command, args []string) error {
257260
GKENAPReservation: gkeNapReservation,
258261
IsPathwaysJob: isPathwaysJob,
259262
Pathways: pathways,
263+
MTCEnabled: mtcEnabled,
264+
RamdiskDirectory: ramdiskDirectory,
260265
RawMounts: volumeStr,
261266
Env: parseEnvFlags(envVars),
262267
Verbose: verbose,

cmd/job/submit_test.go

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -984,20 +984,20 @@ func TestSubmitCmd_PathwaysMTCFlags(t *testing.T) {
984984
"--project", "test-project",
985985
"--pathways-gcs-location", "gs://my-bucket",
986986
"--compute-type", "n2-standard-4",
987-
"--pathways-mtc-enabled",
988-
"--pathways-ramdisk-directory", "/tmp/custom_mtc_dir",
987+
"--mtc-enabled",
988+
"--mtc-ramdisk-directory", "/tmp/custom_mtc_dir",
989989
)
990990

991991
if err != nil {
992992
t.Fatalf("command failed with error: %v", err)
993993
}
994994

995-
if !pathways.MTCEnabled {
996-
t.Errorf("expected pathways.MTCEnabled to be true")
995+
if !mtcEnabled {
996+
t.Errorf("expected mtcEnabled to be true")
997997
}
998998

999-
if pathways.RamdiskDirectory != "/tmp/custom_mtc_dir" {
1000-
t.Errorf("expected pathways.RamdiskDirectory to be /tmp/custom_mtc_dir, got %s", pathways.RamdiskDirectory)
999+
if ramdiskDirectory != "/tmp/custom_mtc_dir" {
1000+
t.Errorf("expected ramdiskDirectory to be /tmp/custom_mtc_dir, got %s", ramdiskDirectory)
10011001
}
10021002

10031003
}

docs/gcluster_job_guide.md

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,12 @@ If you use `--build-context` to build images on-the-fly, you must set:
2929
>
3030
> Successful checks are remembered in `~/.gcluster/job_prereq_state.json` to optimize subsequent runs. Checks are re-run if the state is older than 24 hours or if you switch projects.
3131
32+
### 1.1 Multi-Tier Checkpointing (MTC) Prerequisites
33+
34+
If you plan to use Multi-Tier Checkpointing (`--mtc-enabled` flag), ensure that your cluster was created with `enable_multi_tier_checkpointing = true` (or `stateful_ha_config { enabled = true }`).
35+
36+
To use this feature, the cluster administrator must deploy the required `CheckpointConfiguration` Custom Resource globally at cluster creation. This is done by specifying the `mtc_target_bucket` variable in the Terraform blueprint. Job submitters then only need to pass `--mtc-enabled` to their jobs.
37+
3238
## 2. Prepare Sample Application Code
3339

3440
Create a directory named `job_details` and place your application files inside it. This will serve as your build context for the job. The tool will package all files in this directory and add them to the image.
@@ -1098,6 +1104,8 @@ The `gcluster job submit` command deploys a container image as a job (Kubernetes
10981104
| `--env` | `stringArray` | Custom environment variables to pass exclusively to the user's workload container in KEY=VALUE format (e.g. `--env KEY=VALUE`). Applies to both standard and Pathways workloads. Can be specified multiple times. |
10991105
| `--await-job-completion` | `bool` | If true, the CLI waits for the job to complete before exiting. |
11001106
| `--timeout` | `string` | Time to wait for job completion (e.g., `1h`, `10m`). Used with `--await-job-completion`. |
1107+
| `--mtc-enabled` | `flag` | If present, enables Multi-Tier Checkpointing (MTC) for the workload. |
1108+
| `--mtc-ramdisk-directory` | `string` | The ramdisk directory path for local checkpoints in MTC (defaults to `/tmp/mtc_checkpoints`). |
11011109
| `--verbose` | `bool` | Enable verbose logging for the workload. |
11021110

11031111
*(Note: `--cluster`, `--location`, and `--project` are also supported as common flags, see 9.1)*
@@ -1124,8 +1132,6 @@ The `gcluster job submit` command deploys a container image as a job (Kubernetes
11241132
| `--pathways-worker-env` | `stringArray` | Custom environment variables injected specifically into the Pathways worker containers (KEY=VALUE). |
11251133
| `--pathways-colocated-python-sidecar-image` | `string` | Image for an optional Python-based sidecar container running alongside workers. |
11261134
| `--pathways-head-np` | `string` | The node pool name to target for the Pathways head job. |
1127-
| `--pathways-mtc-enabled` | `flag` | If present, enables Multi-Tier Checkpointing (MTC) for the Pathways workload. |
1128-
| `--pathways-ramdisk-directory` | `string` | The ramdisk directory path for local checkpoints in MTC (defaults to `/tmp/mtc_checkpoints`). |
11291135

11301136
#### 9.3.3 GPU Related Flags
11311137
*Use these flags to tune specialized multi-GPU topologies and related node parameters.*

pkg/orchestrator/gke/gke_job_orchestrator.go

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -369,8 +369,8 @@ func (g *GKEOrchestrator) GeneratePathwaysManifest(job orchestrator.JobDefinitio
369369
// WorkerImage defaults to ServerImage if not explicitly set
370370
job.Pathways.WorkerImage = job.Pathways.ServerImage
371371
}
372-
if job.Pathways.MTCEnabled && job.Pathways.RamdiskDirectory == "" {
373-
job.Pathways.RamdiskDirectory = "/tmp/mtc_checkpoints"
372+
if job.MTCEnabled && job.RamdiskDirectory == "" {
373+
job.RamdiskDirectory = "/tmp/mtc_checkpoints"
374374
}
375375

376376
tmpl, err := yamltemplate.New("pathways_jobset.tmpl").ParseFS(templatesFS, "templates/pathways_jobset.tmpl")
@@ -499,6 +499,12 @@ func (g *GKEOrchestrator) populateClusterMetadata(job *orchestrator.JobDefinitio
499499
g.nodePoolSAs = nodePoolSAs
500500
logging.Info("Calculated cluster capacity: %+v", g.capacity)
501501

502+
if job.MTCEnabled {
503+
if clusterDesc.AddonsConfig == nil || clusterDesc.AddonsConfig.StatefulHaConfig == nil || !clusterDesc.AddonsConfig.StatefulHaConfig.Enabled {
504+
return fmt.Errorf("MTC is not enabled on cluster '%s'. If you created your cluster using Cluster Toolkit, you can enable MTC by updating your cluster blueprint's gke-cluster module settings with:\n enable_multi_tier_checkpointing: true\n mtc_target_bucket: \"gs://YOUR_BUCKET\"\n mtc_cache_size: \"50Gi\"\nThen run 'gcluster deploy'. For clusters provisioned otherwise or for underlying GKE concepts, see: https://docs.cloud.google.com/kubernetes-engine/docs/how-to/machine-learning/training/multi-tier-checkpointing", job.ClusterName)
505+
}
506+
}
507+
502508
return nil
503509
}
504510

@@ -1373,6 +1379,8 @@ func (g *GKEOrchestrator) prepareJobSetTemplateData(opts ManifestOptions, comman
13731379
PathwaysWorkerEnv: sortedEnvVars(opts.Pathways.WorkerEnv),
13741380
IsTPU: isTPU,
13751381
IsGPU: isGPU,
1382+
MTCEnabled: opts.MTCEnabled,
1383+
RamdiskDirectory: opts.RamdiskDirectory,
13761384
}
13771385
}
13781386

pkg/orchestrator/gke/gke_job_orchestrator_test.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -535,8 +535,9 @@ func TestGeneratePathwaysManifest_MTC(t *testing.T) {
535535
ColocatedPythonSidecarImage: "sidecar:latest",
536536
GCSLocation: "gs://my-bucket",
537537
HeadNodePool: "pathways-np",
538-
MTCEnabled: true,
539538
},
539+
IsPathwaysJob: true,
540+
MTCEnabled: true,
540541
}
541542

542543
mockResponses := map[string][]shell.CommandResult{

pkg/orchestrator/gke/manifest_generator.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -165,6 +165,10 @@ func (g *GKEOrchestrator) PrepareManifestOptions(job orchestrator.JobDefinition,
165165
Topology: schedOpts.Topology,
166166
Verbose: job.Verbose,
167167
Env: job.Env,
168+
IsPathwaysJob: job.IsPathwaysJob,
169+
Pathways: job.Pathways,
170+
MTCEnabled: job.MTCEnabled,
171+
RamdiskDirectory: job.RamdiskDirectory,
168172
}
169173

170174
if err := g.fillManifestStrings(&opts, schedOpts, job, isDynamicSlicing, isStaticSlicing, profile.IsCPUMachine); err != nil {

pkg/orchestrator/gke/storage.go

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -289,10 +289,6 @@ func sanitizePVCName(name string) string {
289289

290290
// AddVolumeOptions marshals and indents the volume and volume mount specifications into the manifest options.
291291
func (sm *StorageManager) AddVolumeOptions(opts *ManifestOptions, vols []MountInfo) {
292-
if len(vols) == 0 {
293-
return
294-
}
295-
296292
var volSpecs []map[string]interface{}
297293
var mountSpecs []map[string]interface{}
298294
gcsFuseEnabled := false
@@ -305,6 +301,24 @@ func (sm *StorageManager) AddVolumeOptions(opts *ManifestOptions, vols []MountIn
305301
}
306302
}
307303

304+
if opts.MTCEnabled {
305+
mountSpecs = append(mountSpecs,
306+
map[string]interface{}{"name": "cache", "mountPath": opts.RamdiskDirectory},
307+
)
308+
volSpecs = append(volSpecs,
309+
map[string]interface{}{"name": "cache", "csi": map[string]interface{}{"driver": "multitier-checkpoint.csi.storage.gke.io"}},
310+
)
311+
312+
if opts.IsPathwaysJob {
313+
mountSpecs = append(mountSpecs, map[string]interface{}{"name": "sidecar-shared-memory", "mountPath": "/tmp/sidecar"})
314+
volSpecs = append(volSpecs, map[string]interface{}{"name": "sidecar-shared-memory", "emptyDir": map[string]interface{}{"medium": "Memory"}})
315+
}
316+
}
317+
318+
if len(volSpecs) == 0 {
319+
return
320+
}
321+
308322
opts.GCSFuseEnabled = gcsFuseEnabled
309323

310324
if b, err := yaml.Marshal(mountSpecs); err == nil {

pkg/orchestrator/gke/templates/pathways_jobset.tmpl

Lines changed: 16 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -203,17 +203,17 @@ spec:
203203
echo "Exit code: $EXIT_CODE"
204204
exit $EXIT_CODE
205205
volumeMounts:
206-
- name: shared-tmp
207-
mountPath: /tmp
206+
- name: shared-tmp
207+
mountPath: /tmp
208208
{{- if .VolumeMountsYAML }}
209209
{{(StructuralData .VolumeMountsYAML)}}
210210
{{- end }}
211211
{{- end}}
212212
volumes:
213-
- name: shared-tmp
214-
hostPath:
215-
path: /tmp
216-
type: DirectoryOrCreate
213+
- name: shared-tmp
214+
hostPath:
215+
path: /tmp
216+
type: DirectoryOrCreate
217217
{{- if .VolumesYAML }}
218218
{{(StructuralData .VolumesYAML)}}
219219
{{- end }}
@@ -285,9 +285,9 @@ spec:
285285
volumeMounts:
286286
- name: shared-tmp
287287
mountPath: /tmp
288-
{{- if .Pathways.MTCEnabled}}
288+
{{- if .MTCEnabled}}
289289
- name: cache
290-
mountPath: {{.Pathways.RamdiskDirectory}}
290+
mountPath: {{.RamdiskDirectory}}
291291
- name: sidecar-shared-memory
292292
mountPath: /tmp/sidecar
293293
{{- end}}
@@ -305,7 +305,7 @@ spec:
305305
- --server_port=29005
306306
- --resource_manager_address=$(PATHWAYS_HEAD):29001
307307
- "--gcs_scratch_location={{.Pathways.GCSLocation}}"
308-
{{- if .Pathways.MTCEnabled}}
308+
{{- if .MTCEnabled}}
309309
- --cloud_pathways_sidecar_shm_directory=/tmp/sidecar
310310
{{- end}}
311311
{{- range .WorkerArgsList }}
@@ -354,30 +354,17 @@ spec:
354354
{{- end }}
355355
{{(StructuralData .ResourcesString)}}
356356
volumeMounts:
357-
- name: shared-tmp
358-
mountPath: /tmp
359-
{{- if .Pathways.MTCEnabled}}
360-
- name: cache
361-
mountPath: {{.Pathways.RamdiskDirectory}}
362-
- name: sidecar-shared-memory
363-
mountPath: /tmp/sidecar
364-
{{- end}}
357+
- name: shared-tmp
358+
mountPath: /tmp
365359
{{- if .VolumeMountsYAML }}
366360
{{(StructuralData .VolumeMountsYAML)}}
367361
{{- end }}
368362
volumes:
369-
- name: shared-tmp
370-
hostPath:
371-
path: /tmp
372-
type: DirectoryOrCreate
373-
{{- if .Pathways.MTCEnabled}}
374-
- name: cache
375-
csi:
376-
driver: multitier-checkpoint.csi.storage.gke.io
377-
- name: sidecar-shared-memory
378-
emptyDir:
379-
medium: Memory
380-
{{- end}}
363+
- name: shared-tmp
364+
hostPath:
365+
path: /tmp
366+
type: DirectoryOrCreate
367+
381368
{{- if .VolumesYAML }}
382369
{{(StructuralData .VolumesYAML)}}
383370
{{- end }}

pkg/orchestrator/gke/types.go

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -179,6 +179,9 @@ type ManifestOptions struct {
179179
IsStaticSlicing bool
180180
IsCPUMachine bool
181181
Pathways orchestrator.PathwaysJobDefinition
182+
IsPathwaysJob bool
183+
MTCEnabled bool
184+
RamdiskDirectory string
182185
Verbose bool
183186
Env map[string]string
184187
AdditionalManifests []string
@@ -276,9 +279,18 @@ type gkeClusterAutoscaling struct {
276279
}
277280

278281
type gkeCluster struct {
279-
Locations []string `json:"locations"`
280-
NodePools []gkeJobNodePool `json:"nodePools"`
281-
Autoscaling gkeClusterAutoscaling `json:"autoscaling"`
282+
Locations []string `json:"locations"`
283+
NodePools []gkeJobNodePool `json:"nodePools"`
284+
Autoscaling gkeClusterAutoscaling `json:"autoscaling"`
285+
AddonsConfig *gkeAddonsConfig `json:"addonsConfig,omitempty"`
286+
}
287+
288+
type gkeAddonsConfig struct {
289+
StatefulHaConfig *gkeStatefulHaConfig `json:"statefulHaConfig,omitempty"`
290+
}
291+
292+
type gkeStatefulHaConfig struct {
293+
Enabled bool `json:"enabled"`
282294
}
283295

284296
// Types for JobSet status unmarshaling
@@ -353,6 +365,8 @@ type jobSetTemplateData struct {
353365
PathwaysWorkerEnv []EnvVar
354366
IsTPU bool
355367
IsGPU bool
368+
MTCEnabled bool
369+
RamdiskDirectory string
356370
}
357371

358372
// Types for parsing kubectl get nodes -o json

pkg/orchestrator/orchestrator.go

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -43,9 +43,6 @@ type PathwaysJobDefinition struct {
4343

4444
HeadNodePool string // Resolved node pool to use for the Pathways head job.
4545

46-
// Multi-Tier Checkpointing (MTC)
47-
MTCEnabled bool
48-
RamdiskDirectory string
4946
}
5047

5148
type VolumeDefinition struct {
@@ -98,6 +95,10 @@ type JobDefinition struct {
9895
IsPathwaysJob bool
9996
Pathways PathwaysJobDefinition // Embedded struct for Pathways-specific args
10097

98+
// Multi-Tier Checkpointing (MTC)
99+
MTCEnabled bool
100+
RamdiskDirectory string
101+
101102
RawMounts []string
102103
Env map[string]string
103104

0 commit comments

Comments
 (0)