3. Containerization fundamentals for CAE

Containerization fundamentals for CAE 学习卡片

Crash CAE workflows—LS-DYNA, Pam-Crash, Radioss, Abaqus/Explicit, and their pre/post-processing chains—depend on a fragile stack of compilers, MPI libraries, math kernels, and license clients. A model that runs on one cluster node may fail on another because the solver was linked against a different OpenMPI ABI or a different MKL version. Containerization solves this by packaging the runtime environment as an immutable image, but the container technology used on a laptop is not the one that works on a shared HPC cluster. This section explains the three execution models you will encounter and how to choose between them.

Images, Containers, and the Runtime Contract

A container image is a filesystem plus metadata: layers of files, an entrypoint, environment variables, and a default command. A container is a running instance of that image, isolated by kernel namespaces (PID, mount, network, user) and constrained by cgroups. The critical point for CAE is that the container shares the host kernel. It does not virtualize hardware. This is why a container can use InfiniBand, GPUs, and the host's MPI fabric directly—provided the runtime is configured to expose them.

Two properties matter most for reproducibility. First, the image is content-addressable: a digest such as sha256:9f2c… pins the exact bits, so "it worked last month" becomes verifiable. Second, the image is portable across hosts with a compatible kernel and CPU architecture. For crash solvers, that means you can freeze the exact combination of solver build, MPI, and math library, then reproduce a run months later on a different partition.

Docker: The Reference Model and Its HPC Limits

Docker popularized the image/container model and remains the standard for building images. A typical CAE image build starts from a base and layers the solver:

FROM ubuntu:22.04
RUN apt-get update && apt-get install -y \
    libopenmpi3 libhwloc15 libnuma1 \
    && rm -rf /var/lib/apt/lists/*
COPY ls-dyna_smp_d_R13.0_linux_x86_64_ifort160 /opt/lsdyna/
COPY mpp/ls-dyna_mpp_d_R13.0_linux_x86_64_ifort160 /opt/lsdyna/
ENV LSTC_LICENSE=network
ENV LSTC_LICENSE_SERVER=27000@license.internal
ENTRYPOINT ["/opt/lsdyna/ls-dyna_mpp_d_R13.0_linux_x86_64_ifort160"]

On a workstation this runs with docker run --rm -v $PWD:/work -w /work image i=crash.k. On an HPC cluster, however, Docker is usually unavailable or forbidden. The reason is architectural: the Docker daemon runs as root, and any user who can talk to the daemon can effectively become root on the host. On a multi-tenant login node this is an unacceptable privilege escalation path. Docker also assumes it owns the network and storage stack, which conflicts with Slurm, Lustre, and the site's fabric configuration.

Apptainer/Singularity: Designed for Shared Clusters

Apptainer (the successor to Singularity, now under the Linux Foundation) was built for exactly this environment. Its design inverts Docker's assumptions:

  • No daemon. The apptainer binary is invoked directly by the user; there is no privileged background service.
  • Runs as the invoking user. By default, your UID/GID inside the container match your UID/GID on the host, so files written to Lustre or GPFS have correct ownership.
  • Images are single files. A .sif (Singularity Image Format) file is a self-contained, optionally signed artifact that can live on shared storage and be referenced by path.
  • Host integration is explicit. You bind-mount the directories you need, and the runtime can inherit the host's MPI, GPU, and InfiniBand libraries.

Converting a Docker image is a one-liner, and the result is a portable file:

apptainer build lsdyna-r13.sif docker://registry.internal/cae/lsdyna:r13
apptainer exec --nv --bind /scratch/$USER:/work lsdyna-r13.sif \
    ls-dyna_mpp_d_R13.0_linux_x86_64_ifort160 i=crash.k ncpu=64

The --nv flag injects the host's NVIDIA driver and device nodes; without it, a GPU-accelerated solver will not see the GPU. The --bind flag makes the working directory visible inside the container. Note that the solver binary is invoked directly—Apptainer does not require the image's ENTRYPOINT, which gives you flexibility to run different executables from the same image.

Rootless Execution and the User Namespace

"Rootless" means the container runtime does not require root privileges on the host. There are two distinct mechanisms, and confusing them causes most of the practical problems.

Rootless Docker uses the Linux user namespace: a daemon runs as your unprivileged user, and UID 0 inside the container maps to your UID outside. It works well on workstations and single-user nodes. On HPC it is rarely deployed because it still needs a per-user daemon and systemd --user, which many clusters do not provide.

Apptainer's rootless model is different and simpler: there is no daemon at all. The runtime uses user namespaces only when needed (for example, to run a build step that requires fakeroot). For normal execution, no namespace remapping is required—your process simply runs with your identity, inside a mount namespace that presents the image's filesystem. This is why Apptainer works on clusters where Docker cannot.

If a site disables unprivileged user namespaces (kernel.unprivileged_userns_clone=0), Apptainer can still run existing images in setuid mode, where a small setuid helper performs the mount operations. Check with apptainer exec --version and the site's documentation before assuming rootless builds will work.

Choosing the Right Model

RequirementDockerApptainer/Singularity
Runs on shared HPC login/compute nodesUsually forbiddenYes, designed for it
Requires a daemonYes (root)No
Image formatLayered OCISingle .sif file
File ownership on shared FSOften root-ownedMatches invoking user
MPI / InfiniBand passthroughManual, fragileNative host integration
Best useBuilding images, workstationsRunning on clusters

The practical workflow is hybrid: build and test images with Docker on a workstation or a dedicated build node, push them to an internal registry, then convert to .sif on the cluster and run under Slurm. This gives you Docker's mature build tooling and Apptainer's cluster-safe execution.

Container Execution Models for CAE on HPC Build Node / Workstation Dockerfile + solver binaries docker build → OCI image push to registry convert HPC Compute Node (Slurm) apptainer build lsdyna-r13.sif srun apptainer exec --bind /scratch/$USER:/work --nv --pwd /work MPI + InfiniBand + GPU inherited from host Rootless Docker • Per-user daemon (systemd --user) • User namespace: UID 0 → your UID • Needs unprivileged userns enabled • Rarely available on shared clusters • Good for single-user workstations Apptainer Rootless • No daemon, no background service • Runs directly as invoking user • Userns only for fakeroot builds • Setuid fallback if userns disabled • Standard on HPC clusters

Practical Verification and Common Failures

After building a .sif, verify it before submitting a production job. A minimal smoke test checks that the solver starts, sees the right MPI, and can read the input deck:

apptainer exec lsdyna-r13.sif ls-dyna_mpp_d_R13.0_linux_x86_64_ifort160 i=smoke.k ncpu=2
# Expected: "LS-DYNA ... version R13.0" banner, then normal termination
# with "Normal termination" in the d3hsp file.

Three failure modes dominate in practice. First, MPI ABI mismatch: the solver was built against OpenMPI 4.x but the host provides 5.x, producing undefined symbol: ompi_mpi_init. Fix by building the image against the cluster's MPI or by using the host's MPI via --bind of the MPI library path. Second, missing GPU driver: the solver reports "no CUDA device" because --nv was omitted. Third, license client failure: the container cannot reach the license server because the network namespace is isolated; use --net or ensure the host network is inherited. Each of these is diagnosable from the solver's output file, which is why the smoke test must be run before scaling to a full crash model.

Never assume a container is reproducible just because it runs. Pin the image by digest, record the Apptainer version and the host kernel in your run log, and re-run the smoke test after any cluster upgrade. The container freezes the userspace; it does not freeze the kernel, the scheduler, or the fabric.

With these fundamentals in place, the next step is to apply them to a concrete CAE toolchain: building a solver image, wiring it into Slurm, and validating that a crash model produces bit-identical results across nodes.