Add A4X MAX Kimi-K2 FP8mx 256 GPUs recipe - #279
Conversation
There was a problem hiding this comment.
Code Review
This pull request introduces a Helm chart recipe to run a kimi-k2 pretraining workload on a4x-max GKE Node pools using the Nvidia Megatron-Bridge framework. The review feedback highlights several critical areas for improvement: hardcoded namespaces and Hugging Face tokens should be parameterized; runtime package installations and git cloning should be moved to a pre-built container image to avoid fragility and rate-limiting; Helm templates should handle null GCS mounts gracefully; and shell/Python scripting in the launcher should be optimized for robustness and memory efficiency.
| config_overrides="${config_overrides[*]}" | ||
| } | ||
|
|
||
| config_overrides=() |
There was a problem hiding this comment.
The script does not enable set -eo pipefail. Without this, any failures in the torchrun pipeline or other commands will be silently ignored, and the script will exit with status 0 (success), masking critical training crashes from Kubernetes. Enable set -eo pipefail at the start of the script.
| config_overrides=() | |
| set -eo pipefail | |
| config_overrides=() |
| echo "VERSION_DIAGNOSTICS: ${kv}" | ||
|
|
||
|
|
||
| export HF_TOKEN=<YOUR_HF_TOKEN> |
There was a problem hiding this comment.
The hardcoded export HF_TOKEN=<YOUR_HF_TOKEN> in the launcher script overrides any Hugging Face token passed via container environment variables with the literal string <YOUR_HF_TOKEN>. This will cause authentication failures when downloading models or tokenizers. Use the existing environment variable if available.
| export HF_TOKEN=<YOUR_HF_TOKEN> | |
| export HF_TOKEN="${HF_TOKEN:-}" |
| kind: JobSet | ||
| metadata: | ||
| name: "{{ .Release.Name }}" | ||
| namespace: default |
There was a problem hiding this comment.
The namespace of the JobSet is hardcoded to default. If the Helm chart is installed in a different namespace, the JobSet will be created in the default namespace while other resources (like ConfigMaps and Services) are created in the target namespace, causing the deployment to fail. Use {{ .Release.Namespace }} instead.
namespace: "{{ .Release.Namespace }}"| value: "{{.Release.Name}}-workload-0-0.{{.Release.Name}}.default.svc.cluster.local" | ||
| - name: HOSTNAME_PREFIX | ||
| value: "{{.Release.Name}}-workload-" | ||
| - name: DOMAIN_NAME | ||
| value: "{{.Release.Name}}.default.svc.cluster.local" | ||
| - name: MASTER_ADDR | ||
| value: "{{.Release.Name}}-workload-0-0.{{.Release.Name}}.default.svc.cluster.local" | ||
| - name: MASTER_PORT | ||
| value: "6002" |
There was a problem hiding this comment.
The namespace default is hardcoded in RANK_0_FQDN, DOMAIN_NAME, and MASTER_ADDR. If this Helm chart is installed in any namespace other than default, the pods will fail to resolve the master address and the training job will fail to initialize. Use {{ .Release.Namespace }} instead of hardcoding default.
- name: RANK_0_FQDN
value: "{{.Release.Name}}-workload-0-0.{{.Release.Name}}.{{.Release.Namespace}}.svc.cluster.local"
- name: HOSTNAME_PREFIX
value: "{{.Release.Name}}-workload-"
- name: DOMAIN_NAME
value: "{{.Release.Name}}.{{.Release.Namespace}}.svc.cluster.local"
- name: MASTER_ADDR
value: "{{.Release.Name}}-workload-0-0.{{.Release.Name}}.{{.Release.Namespace}}.svc.cluster.local"
- name: MASTER_PORT| # Install DOCA-OFED | ||
| apt update -y | ||
| apt install -y curl | ||
| export DOCA_URL="https://linux.mellanox.com/public/repo/doca/3.1.0/ubuntu22.04/arm64-sbsa/" | ||
| BASE_URL=$([ "${DOCA_PREPUBLISH:-false}" = "true" ] && echo https://doca-repo-prod.nvidia.com/public/repo/doca || echo https://linux.mellanox.com/public/repo/doca) | ||
| DOCA_SUFFIX=${DOCA_URL#*public/repo/doca/}; DOCA_URL="$BASE_URL/$DOCA_SUFFIX" | ||
| curl $BASE_URL/GPG-KEY-Mellanox.pub | gpg --dearmor > /etc/apt/trusted.gpg.d/GPG-KEY-Mellanox.pub | ||
| echo "deb [signed-by=/etc/apt/trusted.gpg.d/GPG-KEY-Mellanox.pub] $DOCA_URL ./" > /etc/apt/sources.list.d/doca.list | ||
| apt update | ||
| apt install -y --allow-downgrades --allow-change-held-packages -o Dpkg::Options::="--force-overwrite" doca-ofed-userspace || apt --fix-broken install -y | ||
|
|
||
| # Install NCCL and nccl-gib-plugins package | ||
| apt install --only-upgrade --allow-change-held-packages -y libnccl2 libnccl-dev | ||
|
|
||
| # If image not from Google, trust the GCP signing key | ||
| curl https://packages.cloud.google.com/apt/doc/apt-key.gpg | gpg --dearmor -o /etc/apt/trusted.gpg.d/cloud.google.gpg | ||
|
|
||
| # Add gpudirect-gib-apt repo | ||
| echo 'deb https://packages.cloud.google.com/apt gpudirect-gib-apt main' | tee /etc/apt/sources.list.d/nccl-gib.list | ||
|
|
||
| apt update | ||
| apt install -y nccl-gib-plugins |
There was a problem hiding this comment.
Performing system-level package installations (apt update, doca-ofed-userspace, libnccl2, nccl-gib-plugins) at runtime on all 64 nodes simultaneously is highly inefficient and fragile. It significantly delays job startup, requires public internet access on all nodes (which fails in private clusters), and is highly susceptible to transient network failures or rate-limiting from external package repositories. These dependencies should be pre-installed in a custom Docker image instead.
| cd /opt | ||
| rm -rf Megatron-Bridge | ||
| git clone https://github.com/NVIDIA-NeMo/Megatron-Bridge.git | ||
| cd Megatron-Bridge | ||
| git checkout 5cb3444c43f7499cf3872b2d46870cf8bc2e00ce | ||
| git submodule update --init --recursive |
There was a problem hiding this comment.
Cloning the Megatron-Bridge repository from GitHub at runtime on all 64 nodes simultaneously can lead to GitHub rate-limiting, slow startup times, and will fail entirely in private GKE clusters without internet access. Consider pre-building these dependencies into a custom container image or using an init container with a shared volume to clone it once.
| --rdzv_id="${JOB_IDENTIFIER}" \ | ||
| --master_addr="${MASTER_ADDR}" \ | ||
| --master_port="${MASTER_PORT}" \ | ||
| --no-python bash worker_command.sh 2>&1 | python3 -u -c "import sys, time; [sys.stdout.write('[{}] {}'.format(time.strftime('%Y-%m-%d %H:%M:%S'), line)) for line in iter(sys.stdin.readline, '')]" |
There was a problem hiding this comment.
Using a list comprehension [sys.stdout.write(...) for line in ...] for its side effects causes Python to accumulate a list of return values (integers representing bytes written) in memory for the entire duration of the training run. For long-running jobs with large log volumes, this will lead to unnecessary memory growth. Use sys.stdout.writelines with a generator expression instead to process the stream lazily without memory overhead.
| --no-python bash worker_command.sh 2>&1 | python3 -u -c "import sys, time; [sys.stdout.write('[{}] {}'.format(time.strftime('%Y-%m-%d %H:%M:%S'), line)) for line in iter(sys.stdin.readline, '')]" | |
| --no-python bash worker_command.sh 2>&1 | python3 -u -c "import sys, time; sys.stdout.writelines('[{}] {}'.format(time.strftime('%Y-%m-%d %H:%M:%S'), line) for line in iter(sys.stdin.readline, ''))" |
| {{- range $gcs := $root.Values.volumes.gcsMounts }} | ||
| - name: "{{ $gcs.bucketName }}" | ||
| csi: | ||
| driver: gcsfuse.csi.storage.gke.io | ||
| volumeAttributes: | ||
| bucketName: "{{ $gcs.bucketName }}" | ||
| {{- if $gcs.mountOptions }} | ||
| mountOptions: "{{ $gcs.mountOptions }}" | ||
| {{- end }} | ||
| {{- end}} |
There was a problem hiding this comment.
If volumes.gcsMounts contains null placeholders (as defined by default in values.yaml), the template will render invalid volume definitions with name "null" and bucketName "null". Wrap the volume rendering in a check for if $gcs.bucketName to avoid rendering empty/invalid mounts.
{{- range $gcs := $root.Values.volumes.gcsMounts }}
{{- if $gcs.bucketName }}
- name: "{{ $gcs.bucketName }}"
csi:
driver: gcsfuse.csi.storage.gke.io
volumeAttributes:
bucketName: "{{ $gcs.bucketName }}"
{{- if $gcs.mountOptions }}
mountOptions: "{{ $gcs.mountOptions }}"
{{- end }}
{{- end }}
{{- end}}| resources: | ||
| requests: | ||
| nvidia.com/gpu: {{ $gpusPerNode }} | ||
| limits: | ||
| nvidia.com/gpu: {{ $gpusPerNode }} | ||
| claims: |
There was a problem hiding this comment.
The workload container does not specify any CPU or memory requests/limits. Since the pod uses a large shared-memory tmpfs volume of up to 250Gi (which consumes host memory), not specifying memory requests can lead to severe node overcommit and unpredictable Out-Of-Memory (OOM) kills by the kernel. It is highly recommended to set explicit CPU and memory requests/limits.
| {{- range $gcs := $root.Values.volumes.gcsMounts }} | ||
| - name: "{{ $gcs.bucketName }}" | ||
| mountPath: "{{ $gcs.mountPath }}" | ||
| {{- end }} |
There was a problem hiding this comment.
If volumes.gcsMounts contains null placeholders (as defined by default in values.yaml), the template will render invalid volumeMount definitions with name "null" and mountPath "null". Wrap the volumeMount rendering in a check for if $gcs.bucketName to avoid rendering empty/invalid mounts.
{{- range $gcs := $root.Values.volumes.gcsMounts }}
{{- if $gcs.bucketName }}
- name: "{{ $gcs.bucketName }}"
mountPath: "{{ $gcs.mountPath }}"
{{- end }}
{{- end }}
Add A4X MAX Kimi-K2 256 GPUs FP8mx recipe