File size: 6,283 Bytes
f08eb2a 7f3ce4d f08eb2a 7f3ce4d f08eb2a 7f3ce4d f08eb2a 7f3ce4d f08eb2a 7f3ce4d f08eb2a | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 | #!/usr/bin/env bash
# Drop-in PYTHON_BIN for scripts/train_2node_16gpu.slurm. One container per node;
# train_large.py launches eight workers using container sys.executable.
set +x # Never trace runtime credentials, including if launched with bash -x.
set -euo pipefail
HERE="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)"
if [[ "${1:-}" == --help-container ]]; then
echo 'IMAGE=/path/environment.sif PROJECT_DIR=/shared/checkout apptainer/python.sh [Python arguments]'
echo 'Optional: DATA_DIR MODEL_DIR ASSET_DIR OUTPUT_DIR CONTAINER_CACHE_DIR WANDB_NETRC_FILE GPUS=all|none DRY_RUN=1.'
echo 'Stage0 external assets: LORAT_ROOT LORAT_WEIGHT_PATH TORCH_HOME (existing host paths, automatically bound).'
echo 'Credentials are forwarded through the environment only; dry-run redacts values.'
exit 0
fi
ROOT="$(cd -- "${PROJECT_DIR:-${TRACK_ANYTHING_ROOT:-$HERE/..}}" && pwd)"
test -f "$ROOT/scripts/train_large.py" || { echo 'PROJECT_DIR must be the large-training checkout.' >&2; exit 2; }
test -f "$ROOT/apptainer/entrypoint.sh" || { echo 'PROJECT_DIR must include apptainer/entrypoint.sh.' >&2; exit 2; }
: "${IMAGE:?Set IMAGE to the built large-cu128-wandb-v1 SIF}"
IMAGE="$(realpath -e -- "$IMAGE")"
test -f "$IMAGE" || { echo 'IMAGE must be a regular SIF file.' >&2; exit 2; }
CACHE="$(realpath -m -- "${CONTAINER_CACHE_DIR:-$ROOT/.container-cache}")"
check_path() { [[ "$2" != *:* && "$2" != *,* && "$2" != *$'\n'* ]] || { echo "$1 cannot contain colon, comma or newline." >&2; exit 2; }; }
for name in ROOT CACHE; do check_path "$name" "${!name}"; done
args=(exec --cleanenv --no-eval --no-home --no-mount home,cwd,hostfs,bind-paths
--pwd "$ROOT" --bind "$ROOT:$ROOT" --bind "$CACHE:/cache")
for name in DATA_DIR MODEL_DIR ASSET_DIR OUTPUT_DIR; do
value="${!name:-}"
if [[ -n "$value" ]]; then
test -d "$value" || { echo "$name must already exist on each node." >&2; exit 2; }
value="$(cd -- "$value" && pwd)"; check_path "$name" "$value"
if [[ "$name" == OUTPUT_DIR ]]; then mode=rw; else mode=ro; fi
args+=(--bind "$value:$value:$mode")
fi
done
# --cleanenv drops host asset settings, and --no-mount disables implicit host
# binds. Resolve both the forwarded value and its bind from the SAME path;
# canonicalization also handles symlinks whose targets leave the checkout.
# Bind only the weight file, not its possibly private parent directory.
for name in LORAT_ROOT LORAT_WEIGHT_PATH TORCH_HOME; do
value="${!name:-}"
if [[ -z "$value" && "$name" == LORAT_ROOT && -d "$ROOT/third_party/LoRAT/trackit" ]]; then
value="$ROOT/third_party/LoRAT"
fi
if [[ -n "$value" ]]; then
if [[ "$name" == LORAT_WEIGHT_PATH ]]; then
test -f "$value" || { echo "$name must be an existing file on each node." >&2; exit 2; }
else
test -d "$value" || { echo "$name must be an existing directory on each node." >&2; exit 2; }
fi
value="$(realpath -e -- "$value")"; check_path "$name" "$value"
if [[ "$name" == LORAT_ROOT ]]; then
test -d "$value/trackit" || { echo 'LORAT_ROOT must contain the LoRAT trackit package.' >&2; exit 2; }
fi
if [[ "$name" == TORCH_HOME ]]; then mode=rw; else mode=ro; fi
args+=(--bind "$value:$value:$mode")
printf -v "$name" '%s' "$value"
export "$name"
else
# An empty optional setting means use the image/application default; it
# must not overwrite TORCH_HOME=/cache/torch with an empty string.
unset "$name"
fi
done
if [[ -n "${WANDB_NETRC_FILE:-}" ]]; then
test -f "$WANDB_NETRC_FILE" || { echo 'WANDB_NETRC_FILE must be an existing private credentials file.' >&2; exit 2; }
value="$(realpath -e -- "$WANDB_NETRC_FILE")"; check_path WANDB_NETRC_FILE "$value"
args+=(--bind "$value:/cache/home/.netrc:ro")
fi
unset APPTAINER_HOME SINGULARITY_HOME APPTAINER_BIND APPTAINER_BINDPATH SINGULARITY_BIND SINGULARITY_BINDPATH
# Do not inherit arbitrary APPTAINERENV_ or SINGULARITYENV_ variables, especially
# an old PYTHONPATH, HOME, or an accidental conflicting CUDA_VISIBLE_DEVICES.
while IFS= read -r variable; do
case "$variable" in APPTAINERENV_*|SINGULARITYENV_*) unset "$variable" ;; esac
done < <(compgen -e)
export APPTAINERENV_TRACK_ANYTHING_ROOT="$ROOT"
export APPTAINERENV_WANDB_MODE="${WANDB_MODE:-online}"
case "$APPTAINERENV_WANDB_MODE" in online|offline|disabled) ;; *) echo 'Invalid WANDB_MODE.' >&2; exit 2 ;; esac
case "${GPUS:-all}" in
none) export APPTAINERENV_CUDA_VISIBLE_DEVICES=-1 ;;
all)
args+=(--nv)
if [[ "${CUDA_VISIBLE_DEVICES+x}" ]]; then export APPTAINERENV_CUDA_VISIBLE_DEVICES="$CUDA_VISIBLE_DEVICES"; fi
;;
*) echo 'GPUS must be all (allocated set) or none; use the scheduler to allocate GPUs.' >&2; exit 2 ;;
esac
forward=(SLURM_NODEID SLURM_JOB_ID SLURM_JOB_NUM_NODES SLURM_NNODES SLURM_LOCALID
MASTER_ADDR MASTER_PORT NODE_RANK TRACKING_SMOKE_RUN_ID
NCCL_DEBUG NCCL_DEBUG_SUBSYS NCCL_SOCKET_IFNAME NCCL_SOCKET_FAMILY
NCCL_IB_HCA NCCL_IB_GID_INDEX NCCL_IB_TC NCCL_IB_TIMEOUT NCCL_IB_DISABLE
NCCL_NET_GDR_LEVEL NCCL_CUMEM_ENABLE NCCL_P2P_DISABLE
TORCH_DISTRIBUTED_DEBUG TORCH_NCCL_ASYNC_ERROR_HANDLING
LORAT_ROOT LORAT_WEIGHT_PATH TORCH_HOME
WANDB_API_KEY WANDB_BASE_URL WANDB_ENTITY
HTTP_PROXY HTTPS_PROXY ALL_PROXY NO_PROXY http_proxy https_proxy all_proxy no_proxy)
for variable in "${forward[@]}"; do
if [[ "${!variable+x}" ]]; then
export "APPTAINERENV_${variable}=${!variable}"
fi
done
# Keep the environment-only SIF reusable: launch the checked-out entrypoint so
# runtime fixes do not depend on rebuilding an already published image.
args+=("$IMAGE" /bin/bash "$ROOT/apptainer/entrypoint.sh" /opt/venv/bin/python "$@")
if [[ "${DRY_RUN:-0}" == 1 ]]; then
printf 'Container command (environment values omitted): '
printf '%q ' "${APPTAINER:-apptainer}" "${args[@]}"; printf '\n'
printf 'Forwarded environment names only:'
for variable in "${forward[@]}"; do
if [[ "${!variable+x}" ]]; then printf ' %s' "$variable"; fi
done
printf '\nNo process launched.\n'
exit 0
fi
command -v "${APPTAINER:-apptainer}" >/dev/null || { echo 'Load/install Apptainer on each node first.' >&2; exit 127; }
umask 077
mkdir -p -- "$CACHE/home"
exec "${APPTAINER:-apptainer}" "${args[@]}"
|