diff --git a/README.md b/README.md index 848dd9fd3..60f36fef9 100644 --- a/README.md +++ b/README.md @@ -5,6 +5,7 @@ built as a plugin on top of [DMTCP](https://github.com/dmtcp/dmtcp). For details of installing and using MANA, please see: - [MANA Manual](https://github.com/mpickpt/mana/wiki) + * [Distributed MPICH/Hydra checkpoint and restart](doc/distributed-mpich-hydra.md) Older documentation: - [MANA documentation (https://mana-doc.readthedocs.io/en/latest/) ](https://mana-doc.readthedocs.io/en/latest/) diff --git a/doc/distributed-mpich-hydra.md b/doc/distributed-mpich-hydra.md new file mode 100644 index 000000000..787f9b6ba --- /dev/null +++ b/doc/distributed-mpich-hydra.md @@ -0,0 +1,569 @@ +# Distributed MPICH/Hydra checkpoint and restart + +This guide describes an opt-in, multi-node MANA workflow using MPICH's +Hydra process manager and SSH launch. + +Paths and node names below are examples. Unless a directory is stored on +shared storage, every participating node must use the same absolute paths for: + +- MANA; +- MPICH; +- the MPI application; +- the working directory; +- temporary files; +- checkpoint files; +- the isolated test home containing `.mana.rc`. + +## 1. Prerequisites + +Each node needs: + +- the same MANA revision and build; +- the same MPICH installation; +- passwordless SSH from the launch node; +- a working C and C++ compiler toolchain; +- compatible runtime libraries; +- write access to the selected working, checkpoint, temporary, and log + directories; +- loader debug symbols when required by the lower half. + +The launch node also needs a Hydra hostfile containing one host or host +specification per line. + +Do not store private SSH keys, scheduler credentials, checkpoint images, +status files, or cluster-specific hostfiles in the MANA repository. + +## 2. Build MANA against the intended MPICH + +Do not rely on whichever `mpicc` happens to be first in the system path. +An accidental Open MPI build can instantiate incompatible `ompi_*` handle +types. + +```bash +export MPICH_HOME=/opt/mpich-mana + +export PATH="$MPICH_HOME/bin:$PATH" +export LD_LIBRARY_PATH="$MPICH_HOME/lib:${LD_LIBRARY_PATH:-}" + +CFLAGS="-O2 -fno-stack-protector" \ +CXXFLAGS="-O2 -fno-stack-protector" \ +MPI_BIN="$MPICH_HOME/bin" \ +MPI_INCLUDE="$MPICH_HOME/include" \ +MPI_LIB="$MPICH_HOME/lib" \ +MPICC="$MPICH_HOME/bin/mpicc" \ +MPICXX="$MPICH_HOME/bin/mpicxx" \ +MPIFORTRAN="$MPICH_HOME/bin/mpifort" \ +MPIRUN="$MPICH_HOME/bin/mpirun" \ +MPI_LD_FLAG="-lmpich" \ +MPI_CFLAGS="" \ +MPI_CXXFLAGS="" \ +MPI_LDFLAGS="" \ +MAKE=/usr/bin/make \ +./configure + +/usr/bin/make -j"$(nproc)" mana +``` + +Confirm that the generated configuration refers to the selected MPICH: + +```bash +grep -E \ + '^(MPI_BIN|MPI_INCLUDE|MPI_LIB|MPICC|MPICXX|MPIFORTRAN|MPIRUN|MPI_LD_FLAG)' \ + mpi-proxy-split/Makefile_config +``` + +Check the lower-half linkage: + +```bash +ldd bin/lower-half | +grep -E 'libmpi|not found' +``` + +The MPI library should resolve under `$MPICH_HOME/lib`, and no library should +be reported as `not found`. + +The file below is a generated executable and must not be committed: + +```text +mpi-proxy-split/lower-half/lower-half +``` + +## 3. Define the distributed test environment + +For example: + +```bash +export MANA_HOME=/opt/mana +export MPICH_HOME=/opt/mpich-mana + +export HOSTFILE=/path/to/hosts.txt +export REMOTE_USER="$USER" + +export WORKDIR=/tmp/mana-distributed-test +export TEST_HOME="$WORKDIR/home" +export CKPTDIR="$WORKDIR/checkpoints" +export TMPDIR="$WORKDIR/tmp" +export LOGDIR="$WORKDIR/logs" + +export STATUS_FILE="$TEST_HOME/.mana.rc" +``` + +`TEST_HOME` is intentionally separate from the user's normal home directory. +MANA launch and restart commands read coordinator information from: + +```text +$HOME/.mana.rc +``` + +The MPI ranks will therefore receive: + +```bash +-genv HOME "$TEST_HOME" +``` + +The same absolute `TEST_HOME` path must exist on every node. + +## 4. Validate SSH, paths, and runtime libraries + +For every unique host: + +```bash +while IFS= read -r node || [[ -n "$node" ]]; do + node="${node%%[[:space:]]*}" + + [[ -z "$node" || "$node" == \#* ]] && continue + + ssh -n "$REMOTE_USER@$node" " + set -e + + test -x '$MANA_HOME/bin/mana_launch' + test -x '$MANA_HOME/bin/mana_restart' + test -x '$MANA_HOME/bin/lower-half' + test -x '$MPICH_HOME/bin/hydra_pmi_proxy' + + mkdir -p \ + '$WORKDIR' \ + '$TEST_HOME' \ + '$CKPTDIR' \ + '$TMPDIR' \ + '$LOGDIR' + " +done < "$HOSTFILE" +``` + +Use `ssh -n` in loops that read standard input. Without `-n`, SSH can consume +the remaining hostfile lines. + +Check unresolved libraries on each node: + +```bash +while IFS= read -r node || [[ -n "$node" ]]; do + node="${node%%[[:space:]]*}" + + [[ -z "$node" || "$node" == \#* ]] && continue + + echo "===== $node =====" + + ssh -n "$REMOTE_USER@$node" " + export LD_LIBRARY_PATH='$MANA_HOME/lib:$MANA_HOME/lib/dmtcp:$MPICH_HOME/lib' + + unresolved=\$( + ldd '$MANA_HOME/bin/lower-half' | + grep 'not found' || true + ) + + if [[ -n \"\$unresolved\" ]]; then + printf '%s\n' \"\$unresolved\" + exit 1 + fi + + ldd '$MANA_HOME/bin/lower-half' | + grep -E 'libmpi|libatomic' || true + " +done < "$HOSTFILE" +``` + +A copied binary can fail remotely when a runtime package exists only on the +build node. Resolve all missing libraries before testing MANA. + +## 5. Run a native MPICH/Hydra smoke test + +Hydra normally propagates the launch node's current directory. Supply `-wdir` +explicitly so remote ranks do not inherit a directory that exists only on the +launch node. + +```bash +"$MPICH_HOME/bin/mpiexec" \ + -launcher ssh \ + -launcher-exec /usr/bin/ssh \ + -wdir "$WORKDIR" \ + -f "$HOSTFILE" \ + -n 2 \ + /absolute/path/to/mpi_application +``` + +Confirm that every expected host and rank appears before involving MANA. + +The native application should execute at least one MPI collective and terminate +normally. + +## 6. Start the coordinator + +For an externally launched Hydra job, the coordinator can briefly have zero +clients before the MPI ranks connect. + +Current `mana_coordinator` invokes DMTCP with `--exit-on-last`. A current-main +workflow can therefore start the underlying coordinator directly without that +option: + +```bash +mkdir -p \ + "$WORKDIR" \ + "$TEST_HOME" \ + "$CKPTDIR" \ + "$TMPDIR" \ + "$LOGDIR" + +COORD_LOG="$LOGDIR/coordinator.log" + +"$MANA_HOME/bin/dmtcp_coordinator" \ + --port 7780 \ + --interval 0 \ + --ckptdir "$CKPTDIR" \ + --coord-logfile "$COORD_LOG" \ + --daemon \ + --status-file "$STATUS_FILE" +``` + +Inspect the generated status file: + +```bash +cat "$STATUS_FILE" +``` + +Copy it to the same absolute path on every node when the path is not shared: + +```bash +while IFS= read -r node || [[ -n "$node" ]]; do + node="${node%%[[:space:]]*}" + + [[ -z "$node" || "$node" == \#* ]] && continue + + ssh -n "$REMOTE_USER@$node" \ + "mkdir -p '$TEST_HOME'" + + scp -q \ + "$STATUS_FILE" \ + "$REMOTE_USER@$node:$STATUS_FILE" \ +