From 4ce46312856523c14ed1a833738304836fec4871 Mon Sep 17 00:00:00 2001 From: Alexis Espinosa Date: Mon, 23 May 2022 16:46:54 +0800 Subject: [PATCH 1/5] Updates for the MPICH installation to use --- _episodes/14.hpc.md | 171 +++++++++++----------- exercises/gromacs/typical_output.out | 140 ++++++++++++++++++ exercises/openfoam/mpirun.sh | 4 +- exercises/openfoam/set-mpi.sh | 6 + exercises/openfoam_wing/mpirun/mpirun.sh | 4 +- exercises/openfoam_wing/mpirun/set-mpi.sh | 6 + exercises/tensorflow/gpu_pawsey.sh | 4 +- 7 files changed, 248 insertions(+), 87 deletions(-) create mode 100644 exercises/gromacs/typical_output.out create mode 100644 exercises/openfoam/set-mpi.sh create mode 100644 exercises/openfoam_wing/mpirun/set-mpi.sh diff --git a/_episodes/14.hpc.md b/_episodes/14.hpc.md index 1cdf129..c02a7b5 100644 --- a/_episodes/14.hpc.md +++ b/_episodes/14.hpc.md @@ -34,10 +34,11 @@ Key concepts around the setup for running MPI and GPU enabled containers with Si ### Configure the MPI/interconnect bind approach -Before we start, let us cd to the `openfoam` example directory: +Before we start, let us cd to the `openfoam` example directory and set-up the MPI to use: ```bash cd ~/isc-tutorial/exercises/openfoam +source set-mpi.sh ``` Now, suppose you have an MPI installation in your host and a containerised MPI application, built upon MPI libraries that are ABI compatible with the former. @@ -183,7 +184,7 @@ NTASKS="2" image="library://marcodelapierre/beta/openfoam:v2012" # this configuration depends on the host -export MPICH_ROOT="/opt/spack" +export MPICH_ROOT="/opt/intel/oneapi" export MPICH_LIBS="$( which mpirun )" export MPICH_LIBS="${MPICH_LIBS%/bin/mpirun*}/lib" @@ -356,83 +357,87 @@ As you can see, the difference is negligible! --> -### DEMO: Run a molecular dynamics simulation on a GPU with containers - -NOTE: this part was executed on the Pawsey Topaz cluster with Nvidia GPUs. You can follow the outputs here. - -For our example we are going to use Gromacs, a quite popular molecular dynamics package, among the ones that have been optimised to run on GPUs through Nvidia containers. -To start, let us cd to the `gromacs` example directory: - -```bash -cd ~/isc-tutorial/exercises/gromacs -``` - -This directory has got sample input files picked from the collection of [Gromacs benchmark examples](ftp://ftp.gromacs.org/pub/benchmarks/water_GMX50_bare.tar.gz). In particular, we're going to use the subset `water-cut1.0_GMX50_bare/1536/`. - -Now, from a Singularity perspective, all we need to do to run a GPU application on Nvidia GPUs from a container is to add the runtime flag `--nv`. This will make Singularity look for the Nvidia drivers in the host, and mount them inside the container. -Then, on the host system side, when running GPU applications through Singularity the only requirement consists of the Nvidia driver for the relevant GPU card (the corresponding file is typically called `libcuda.so.` and is located in some library subdirectory of `/usr`). -Finally, GPU resources are usually made available in HPC systems through schedulers, to which Singularity natively and transparently interfaces. So, for instance let us have a look in the current directory at the Slurm batch script called `gpu_pawsey.sh`: - -```bash -#!/bin/bash -l - -#SBATCH --job-name=gpu -#SBATCH --partition=gpuq -#SBATCH --gres=gpu:1 -#SBATCH --ntasks=1 -#SBATCH --time=01:00:00 - -image="docker://nvcr.io/hpc/gromacs:2018.2" -module load singularity - - -# uncompress configuration input file -if [ -e conf.gro.gz ] ; then - gunzip conf.gro.gz -fi - - -# run Gromacs preliminary step with container -srun singularity exec --nv $image \ - gmx grompp -f pme.mdp - -# Run Gromacs MD with container -srun singularity exec --nv $image \ - gmx mdrun -ntmpi 1 -nb gpu -pin on -v -noconfout -nsteps 5000 -s topol.tpr -ntomp 1 -``` - -Here, there are two key execution lines, who run a preliminary Gromacs job and the proper production job, respectively. -See how we have simply combined the Slurm command `srun` with `singularity exec --nv <..>` (similar to what we did in the episode on MPI): - -```bash -srun singularity exec --nv $image gmx <..> -``` - -We can submit the script with: - -``` -$ sbatch gpu_pawsey.sh -``` -{: .bash} - -A few files are produced, including the main output of the molecular dynamics run, `md.log`: +> ## DEMO: Run a molecular dynamics simulation on a GPU with containers +> +> NOTE: this part was executed on the Pawsey Topaz cluster with Nvidia GPUs. You can follow the outputs here. +> +> For our example we are going to use Gromacs, a quite popular molecular dynamics package, among the ones that have been optimised to run on GPUs through Nvidia containers. +> To start, let us cd to the `gromacs` example directory: +> +> ```bash +> cd ~/isc-tutorial/exercises/gromacs +> ``` +> +> This directory has got sample input files picked from the collection of [Gromacs benchmark examples](ftp://ftp.gromacs.org/pub/benchmarks/water_GMX50_bare.tar.gz). In particular, we're going to use the subset `water-cut1.0_GMX50_bare/1536/`. +> +> Now, from a Singularity perspective, all we need to do to run a GPU application on Nvidia GPUs from a container is to add the runtime flag `--nv`. This will make Singularity look for the Nvidia drivers in the host, and mount them inside the container. +> Then, on the host system side, when running GPU applications through Singularity the only requirement consists of the Nvidia driver for the relevant GPU card (the corresponding file is typically called `libcuda.so.` and is located in some library subdirectory of `/usr`). +> Finally, GPU resources are usually made available in HPC systems through schedulers, to which Singularity natively and transparently interfaces. So, for instance let us have a look in the current directory at the Slurm batch script called `gpu_pawsey.sh`: +> +> ```bash +> #!/bin/bash -l +> +> #SBATCH --job-name=gpu +> #SBATCH --partition=gpuq +> #SBATCH --gres=gpu:1 +> #SBATCH --ntasks=1 +> #SBATCH --time=01:00:00 +> +> image="docker://nvcr.io/hpc/gromacs:2018.2" +> module load singularity +> +> +> # uncompress configuration input file +> if [ -e conf.gro.gz ] ; then +> gunzip conf.gro.gz +> fi +> +> +> # run Gromacs preliminary step with container +> srun singularity exec --nv $image \ +> gmx grompp -f pme.mdp +> +> # Run Gromacs MD with container +> srun singularity exec --nv $image \ +> gmx mdrun -ntmpi 1 -nb gpu -pin on -v -noconfout -nsteps 5000 -s topol.tpr -ntomp 1 +> ``` +> +> Here, there are two key execution lines, who run a preliminary Gromacs job and the proper production job, respectively. +> See how we have simply combined the Slurm command `srun` with `singularity exec --nv <..>` (similar to what we did in the episode on MPI): +> +> ```bash +> srun singularity exec --nv $image gmx <..> +> ``` +> +> We can submit the script with: +> +> ``` +> $ sbatch gpu_pawsey.sh +> ``` +> {: .bash} +> +> A few files are produced, including the main output of the molecular dynamics run, `md.log`: +> +> ```bash +> ls -ltr +> ``` +> ```output +> total 139600 +> -rw-rw----+ 1 mdelapierre pawsey0001 664 Nov 5 14:07 topol.top +> -rw-rw----+ 1 mdelapierre pawsey0001 950 Nov 5 14:07 rf.mdp +> -rw-rw----+ 1 mdelapierre pawsey0001 939 Nov 5 14:07 pme.mdp +> -rw-rw----+ 1 mdelapierre pawsey0001 556 Nov 5 14:07 gpu_pawsey.sh +> -rw-rw----+ 1 mdelapierre pawsey0001 105984045 Nov 5 14:07 conf.gro +> -rw-rw----+ 1 mdelapierre pawsey0001 11713 Nov 5 14:12 mdout.mdp +> -rw-rw----+ 1 mdelapierre pawsey0001 36880760 Nov 5 14:12 topol.tpr +> -rw-rw----+ 1 mdelapierre pawsey0001 9247 Nov 5 14:17 slurm-101713.out +> -rw-rw----+ 1 mdelapierre pawsey0001 22768 Nov 5 14:17 md.log +> -rw-rw----+ 1 mdelapierre pawsey0001 1152 Nov 5 14:17 ener.edr +> -rw-rw----+ 1 mdelapierre pawsey0001 8112 Nov 5 14:17 typical_output.out +> ``` +> +{: .solution} -```bash -ls -ltr -``` -```output -total 139600 --rw-rw----+ 1 mdelapierre pawsey0001 664 Nov 5 14:07 topol.top --rw-rw----+ 1 mdelapierre pawsey0001 950 Nov 5 14:07 rf.mdp --rw-rw----+ 1 mdelapierre pawsey0001 939 Nov 5 14:07 pme.mdp --rw-rw----+ 1 mdelapierre pawsey0001 556 Nov 5 14:07 gpu_pawsey.sh --rw-rw----+ 1 mdelapierre pawsey0001 105984045 Nov 5 14:07 conf.gro --rw-rw----+ 1 mdelapierre pawsey0001 11713 Nov 5 14:12 mdout.mdp --rw-rw----+ 1 mdelapierre pawsey0001 36880760 Nov 5 14:12 topol.tpr --rw-rw----+ 1 mdelapierre pawsey0001 9247 Nov 5 14:17 slurm-101713.out --rw-rw----+ 1 mdelapierre pawsey0001 22768 Nov 5 14:17 md.log --rw-rw----+ 1 mdelapierre pawsey0001 1152 Nov 5 14:17 ener.edr -``` ### DEMO: Run a machine learning distributed training on GPUs with tensorflow container NOTE: this part was executed on the Pawsey Topaz cluster with Nvidia GPUs. You can follow the outputs here. @@ -444,7 +449,7 @@ To start, let us cd to the `tensorflow` example directory: cd ~/isc-tutorial/exercises/tensorflow ``` -This example uses the training procedure described in the tensorflow tutorials for distributed training using Keras: [Distributed training with Keras](https://www.tensorflow.org/tutorials/distribute/keras). The procedure has been included in the python script: `distributedMNIST.py` which, as the name suggests, uses the classical MNIST dataset to train the model to recognise handwritten single digits between 0 and 9. +This example uses the model described in the tensorflow tutorials for distributed training using Keras: [Distributed training with Keras](https://www.tensorflow.org/tutorials/distribute/keras). The model uses the [`tf.distribute.MirroredStrategy`](https://www.tensorflow.org/api_docs/python/tf/distribute/MirroredStrategy) to perform in-graph replication with synchronous training on many GPUs on one node, and has been defined in the python script: `distributedMNIST.py` . As the name of the script suggests, it uses the classical MNIST dataset, which is used to train a model to recognise handwritten single digits between 0 and 9. Now, from a Singularity perspective, all we need to do to run a GPU application on Nvidia GPUs from a container is to add the runtime flag `--nv`. This will make Singularity look for the Nvidia drivers and libraries in the host, and mount them inside the container. Then, on the host system side, when running GPU applications through Singularity the only requirement is to count with a working installation of the Nvidia driver for the GPU card (typically called `libcuda.so.` ) and the Nvidia libraries to be visible in the system's library path. All these are usually located in some library subdirectory of `/usr`. @@ -468,12 +473,12 @@ module load singularity ###---Python script containing the training procedure theScript=distributedMNIST.py -###---Creating the directory that will act as a fake home +###---Creating the directory that will act as a fake $HOME for containerised Python applications if ! [ -d "fake_home" ]; then    mkdir fake_home fi  -###---Launching the distribute tensorflow case +###---Launching the distributed tensorflow case srun singularity exec -e -B fake_home:$HOME --nv $theImage python $theScript ``` @@ -488,7 +493,7 @@ We can submit the script with: $ sbatch gpu_pawsey.sh ``` -Several directories are produced with the training checkpoints, the saved model and, of course, the dataset under the `fake_home`. +Several directories are produced with the training checkpoints, the saved model and, of course, the dataset and other Python hidden output under the `fake_home`. ```bash ls -ltr @@ -503,4 +508,4 @@ drwxr-s---+ 2 espinosa pawsey0001   4096 May 20 14:27 training_checkpoints -rw-rw----+ 1 espinosa pawsey0001 142944 May 20 14:27 slurm-311273.out drwxr-s---+ 4 espinosa pawsey0001   4096 May 20 14:27 saved_model ``` -For this demo, a `typical_output.out` file is also provided, where you can check that both GPUs were used during training. \ No newline at end of file +For this demo, a `typical_output.out` file is also provided, where you can check that both GPUs were used during training. diff --git a/exercises/gromacs/typical_output.out b/exercises/gromacs/typical_output.out new file mode 100644 index 0000000..5b888c1 --- /dev/null +++ b/exercises/gromacs/typical_output.out @@ -0,0 +1,140 @@ +Note: '/astro' is momentarily unavailable and currently is not auto bind mounted. +If you require '/astro', please try running singularity with '-B /astro' +Or using the *-with-astro version of this module. +INFO: Using cached SIF image + :-) GROMACS - gmx grompp, 2018.2 (-: + + GROMACS is written by: + Emile Apol Rossen Apostolov Paul Bauer Herman J.C. Berendsen + Par Bjelkmar Aldert van Buuren Rudi van Drunen Anton Feenstra + Gerrit Groenhof Aleksei Iupinov Christoph Junghans Anca Hamuraru + Vincent Hindriksen Dimitrios Karkoulis Peter Kasson Jiri Kraus + Carsten Kutzner Per Larsson Justin A. Lemkul Viveca Lindahl + Magnus Lundborg Pieter Meulenhoff Erik Marklund Teemu Murtola + Szilard Pall Sander Pronk Roland Schulz Alexey Shvetsov + Michael Shirts Alfons Sijbers Peter Tieleman Teemu Virolainen + Christian Wennberg Maarten Wolf + and the project leaders: + Mark Abraham, Berk Hess, Erik Lindahl, and David van der Spoel + +Copyright (c) 1991-2000, University of Groningen, The Netherlands. +Copyright (c) 2001-2017, The GROMACS development team at +Uppsala University, Stockholm University and +the Royal Institute of Technology, Sweden. +check out http://www.gromacs.org for more information. + +GROMACS is free software; you can redistribute it and/or modify it +under the terms of the GNU Lesser General Public License +as published by the Free Software Foundation; either version 2.1 +of the License, or (at your option) any later version. + +GROMACS: gmx grompp, version 2018.2 +Executable: /gromacs/bin.AVX2_256/gmx +Data prefix: /gromacs +Working dir: /group/pawsey0001/espinosa/ISC22preparation/isc-tutorial/exercises/gromacs_work +Command line: + gmx grompp -f pme.mdp + +Ignoring obsolete mdp entry 'title' +Ignoring obsolete mdp entry 'cpp' +Replacing old mdp entry 'nstxtcout' by 'nstxout-compressed' +Setting the LD random seed to -1216779503 +Generated 330891 of the 330891 non-bonded parameter combinations +Generating 1-4 interactions: fudge = 0.5 +Generated 330891 of the 330891 1-4 parameter combinations +Excluding 2 bonded neighbours molecule type 'SOL' +Excluding 2 bonded neighbours molecule type 'SOL' +Removing all charge groups because cutoff-scheme=Verlet +Number of degrees of freedom in T-Coupling group System is 3071997.00 +Estimate for the relative computational load of the PME mesh part: 0.26 + +GROMACS reminds you: "I'm Gonna Get Medieval On Your Ass" (Pulp Fiction) + +turning all bonds into constraints... +Analysing residue names: +There are: 512000 Water residues +Determining Verlet buffer for a tolerance of 0.005 kJ/mol/ps at 300 K +Calculated rlist for 1x1 atom pair-list as 1.036 nm, buffer size 0.036 nm +Set rlist, assuming 4x4 atom pair-list, to 1.000 nm, buffer size 0.000 nm +Note that mdrun will redetermine rlist based on the actual pair-list setup +Calculating fourier grid dimensions for X Y Z +Using a fourier grid of 200x200x200, spacing 0.125 0.125 0.125 +This run will generate roughly 117 Mb of data +INFO: Using cached SIF image + :-) GROMACS - gmx mdrun, 2018.2 (-: + + GROMACS is written by: + Emile Apol Rossen Apostolov Paul Bauer Herman J.C. Berendsen + Par Bjelkmar Aldert van Buuren Rudi van Drunen Anton Feenstra + Gerrit Groenhof Aleksei Iupinov Christoph Junghans Anca Hamuraru + Vincent Hindriksen Dimitrios Karkoulis Peter Kasson Jiri Kraus + Carsten Kutzner Per Larsson Justin A. Lemkul Viveca Lindahl + Magnus Lundborg Pieter Meulenhoff Erik Marklund Teemu Murtola + Szilard Pall Sander Pronk Roland Schulz Alexey Shvetsov + Michael Shirts Alfons Sijbers Peter Tieleman Teemu Virolainen + Christian Wennberg Maarten Wolf + and the project leaders: + Mark Abraham, Berk Hess, Erik Lindahl, and David van der Spoel + +Copyright (c) 1991-2000, University of Groningen, The Netherlands. +Copyright (c) 2001-2017, The GROMACS development team at +Uppsala University, Stockholm University and +the Royal Institute of Technology, Sweden. +check out http://www.gromacs.org for more information. + +GROMACS is free software; you can redistribute it and/or modify it +under the terms of the GNU Lesser General Public License +as published by the Free Software Foundation; either version 2.1 +of the License, or (at your option) any later version. + +GROMACS: gmx mdrun, version 2018.2 +Executable: /gromacs/bin.AVX2_256/gmx +Data prefix: /gromacs +Working dir: /group/pawsey0001/espinosa/ISC22preparation/isc-tutorial/exercises/gromacs_work +Command line: + gmx mdrun -ntmpi 1 -nb gpu -pin on -v -noconfout -nsteps 5000 -s topol.tpr -ntomp 1 + +Reading file topol.tpr, VERSION 2018.2 (single precision) +Overriding nsteps with value passed on the command line: 5000 steps, 10 ps +Changing nstlist from 10 to 80, rlist from 1 to 1.147 + + +Using 1 MPI thread +Using 1 OpenMP thread + +1 GPU auto-selected for this run. +Mapping of GPU IDs to the 2 GPU tasks in the 1 rank on this node: + PP:0,PME:0 + +Cannot change application clocks for Tesla V100-PCIE-16GB to optimal values due to insufficient permissions. Current values are (877,1245), max values are (877,1380). +Please contact your admin to change application clocks. + +Cannot change application clocks for Tesla V100-PCIE-16GB to optimal values due to insufficient permissions. Current values are (877,1245), max values are (877,1380). +Please contact your admin to change application clocks. + +Overriding thread affinity set outside gmx mdrun +starting mdrun 'Water' +5000 steps, 10.0 ps. + step 160: timed with pme grid 200 200 200, coulomb cutoff 1.000: 10562.8 M-cycles + step 320: timed with pme grid 168 168 168, coulomb cutoff 1.187: 11393.7 M-cycles + step 480: timed with pme grid 144 144 144, coulomb cutoff 1.384: 12169.1 M-cycles + step 640: timed with pme grid 160 160 160, coulomb cutoff 1.246: 11499.3 M-cycles + step 800: timed with pme grid 168 168 168, coulomb cutoff 1.187: 11166.8 M-cycles + step 960: timed with pme grid 192 192 192, coulomb cutoff 1.038: 10757.7 M-cycles + step 1120: timed with pme grid 200 200 200, coulomb cutoff 1.000: 10612.9 M-cycles + step 1280: timed with pme grid 160 160 160, coulomb cutoff 1.246: 11526.6 M-cycles + step 1440: timed with pme grid 168 168 168, coulomb cutoff 1.187: 11242.2 M-cycles + step 1600: timed with pme grid 192 192 192, coulomb cutoff 1.038: 10593.7 M-cycles + step 1760: timed with pme grid 200 200 200, coulomb cutoff 1.000: 10632.4 M-cycles + optimal pme grid 200 200 200, coulomb cutoff 1.000 + step 1900, remaining wall clock time: 182 s step 2000, remaining wall clock time: 176 s step 2100, remaining wall clock time: 169 s step 2200, remaining wall clock time: 163 s step 2300, remaining wall clock time: 156 s step 2400, remaining wall clock time: 150 s step 2500, remaining wall clock time: 144 s step 2600, remaining wall clock time: 138 s step 2700, remaining wall clock time: 131 s step 2800, remaining wall clock time: 126 s step 2900, remaining wall clock time: 119 s step 3000, remaining wall clock time: 113 s step 3100, remaining wall clock time: 107 s step 3200, remaining wall clock time: 102 s step 3300, remaining wall clock time: 96 s step 3400, remaining wall clock time: 90 s step 3500, remaining wall clock time: 84 s step 3600, remaining wall clock time: 79 s step 3700, remaining wall clock time: 73 s step 3800, remaining wall clock time: 67 s step 3900, remaining wall clock time: 61 s step 4000, remaining wall clock time: 56 s step 4100, remaining wall clock time: 50 s step 4200, remaining wall clock time: 44 s step 4300, remaining wall clock time: 39 s step 4400, remaining wall clock time: 33 s step 4500, remaining wall clock time: 27 s step 4600, remaining wall clock time: 22 s step 4700, remaining wall clock time: 16 s step 4800, remaining wall clock time: 11 s step 4900, remaining wall clock time: 5 s step 5000, remaining wall clock time: 0 s +NOTE: 12 % of the run time was spent in pair search, + you might want to increase nstlist (this has no effect on accuracy) + + Core t (s) Wall t (s) (%) + Time: 278.113 278.113 100.0 + (ns/day) (hour/ns) +Performance: 3.107 7.724 + +GROMACS reminds you: "The time for theory is over" (J. Hajdu) + diff --git a/exercises/openfoam/mpirun.sh b/exercises/openfoam/mpirun.sh index a3cc57e..d164c07 100755 --- a/exercises/openfoam/mpirun.sh +++ b/exercises/openfoam/mpirun.sh @@ -4,7 +4,9 @@ NTASKS="2" image="library://marcodelapierre/beta/openfoam:v2012" # this configuration depends on the host -export MPICH_ROOT="/opt/spack" #intel/oneapi" +source ./set-mpi.sh +#export MPICH_ROOT="/opt/spack" +export MPICH_ROOT="/opt/intel/oneapi" export MPICH_LIBS="$( which mpirun )" export MPICH_LIBS="${MPICH_LIBS%/bin/mpirun*}/lib" diff --git a/exercises/openfoam/set-mpi.sh b/exercises/openfoam/set-mpi.sh new file mode 100644 index 0000000..33cfc90 --- /dev/null +++ b/exercises/openfoam/set-mpi.sh @@ -0,0 +1,6 @@ +#!/bin/bash + +###--- Unloading spack installations +echo "Unloading spack installations" +spack unload openmpi +spack unload mpich diff --git a/exercises/openfoam_wing/mpirun/mpirun.sh b/exercises/openfoam_wing/mpirun/mpirun.sh index 516aeb3..320c1e1 100755 --- a/exercises/openfoam_wing/mpirun/mpirun.sh +++ b/exercises/openfoam_wing/mpirun/mpirun.sh @@ -4,7 +4,9 @@ NTASKS="4" export theImage="library://marcodelapierre/beta/openfoam:v2012" # this configuration depends on the host -export MPICH_ROOT="/opt/spack" #intel/oneapi" +source ./set-mpi.sh +#export MPICH_ROOT="/opt/spack" +export MPICH_ROOT="/opt/intel/oneapi" export MPICH_LIBS="$( which mpirun )" export MPICH_LIBS="${MPICH_LIBS%/bin/mpirun*}/lib" diff --git a/exercises/openfoam_wing/mpirun/set-mpi.sh b/exercises/openfoam_wing/mpirun/set-mpi.sh new file mode 100644 index 0000000..33cfc90 --- /dev/null +++ b/exercises/openfoam_wing/mpirun/set-mpi.sh @@ -0,0 +1,6 @@ +#!/bin/bash + +###--- Unloading spack installations +echo "Unloading spack installations" +spack unload openmpi +spack unload mpich diff --git a/exercises/tensorflow/gpu_pawsey.sh b/exercises/tensorflow/gpu_pawsey.sh index 17a0c45..46377f2 100644 --- a/exercises/tensorflow/gpu_pawsey.sh +++ b/exercises/tensorflow/gpu_pawsey.sh @@ -15,10 +15,10 @@ module load singularity ###---Python script containing the training procedure theScript=distributedMNIST.py -###---Creating the directory that will act as a fake home +###---Creating the directory that will act as a fake $HOME for containerised Python applications if ! [ -d "fake_home" ]; then mkdir fake_home fi -###---Launching the distribute tensorflow case +###---Launching the distributed tensorflow case srun singularity exec -e -B fake_home:$HOME --nv $theImage python $theScript From 2b4c6a3164922de61514fe9981e3c8f399fc4a39 Mon Sep 17 00:00:00 2001 From: Carlos Eduardo Arango Gutierrez Date: Mon, 23 May 2022 16:18:53 -0400 Subject: [PATCH 2/5] Re-do advanced container builds Signed-off-by: Carlos Eduardo Arango Gutierrez --- _episodes/23.image.md | 605 +++++++++++++++++++++++++++++++++++++ _episodes/23.spack.md | 322 -------------------- fig/build-diagram.png | Bin 0 -> 43326 bytes fig/buildah-logo_large.png | Bin 0 -> 33351 bytes files/golang.json | 120 ++++++++ 5 files changed, 725 insertions(+), 322 deletions(-) create mode 100644 _episodes/23.image.md delete mode 100644 _episodes/23.spack.md create mode 100644 fig/build-diagram.png create mode 100644 fig/buildah-logo_large.png create mode 100644 files/golang.json diff --git a/_episodes/23.image.md b/_episodes/23.image.md new file mode 100644 index 0000000..c581675 --- /dev/null +++ b/_episodes/23.image.md @@ -0,0 +1,605 @@ +--- +title: "Advanced container builds" +teaching: 15 +exercises: 5 +questions: +- How can I build better container images? +- How can I build more reproducible images? +- How can I build more performant images? +objectives: +- Learn about OCI image spec +- Learn about SIF (Singularity Image File) +- Learn about Buildah +- Learn about Buildah advanced build feature +- Learn to edit the OCI image spec +keypoints: +- The OCI Image Format project creates and maintains the software shipping container image format spec (OCI Image spec) +- Buildah can take more robust recipes by skipping the use of a Dockerfile +--- +# OCI Image Spec + +The OCI Image spec defines a container image, consisting of a manifest, an image index (optional), a set of filesystem layers, and a configuration. +The goal of this specification is to enable the creation of interoperable tools for building, transporting, and preparing a container image to run. + +At a high level the image manifest contains metadata about the contents and dependencies of the image including the content-addressable identity of one or more filesystem layer changeset archives that will be unpacked to make up the final runnable filesystem. The image configuration includes information such as application arguments, environments, etc. The image index is a higher-level manifest which points to a list of manifests and descriptors. Typically, these manifests may provide different implementations of the image, possibly varying by platform or other attributes. + +OCI image + + +## The Schema + +The Config-schema is usually a json file, stored in the same tar file of a container bundle. + +```json +{ + "description": "OpenContainer Config Specification", + "$schema": "https://json-schema.org/draft-04/schema#", + "id": "https://opencontainers.org/schema/image/config", + "type": "object", + "properties": { + "created": { + "type": "string", + "format": "date-time" + }, + "author": { + "type": "string" + }, + "architecture": { + "type": "string" + }, + "variant": { + "type": "string" + }, + "os": { + "type": "string" + }, + "os.version": { + "type": "string" + }, + "os.features": { + "type": "array", + "items": { + "type": "string" + } + }, + "config": { + "type": "object", + "properties": { + "User": { + "type": "string" + }, + "ExposedPorts": { + "$ref": "defs.json#/definitions/mapStringObject" + }, + "Env": { + "type": "array", + "items": { + "type": "string" + } + }, + "Entrypoint": { + "oneOf": [ + { + "type": "array", + "items": { + "type": "string" + } + }, + { + "type": "null" + } + ] + }, + "Cmd": { + "oneOf": [ + { + "type": "array", + "items": { + "type": "string" + } + }, + { + "type": "null" + } + ] + }, + "Volumes": { + "oneOf": [ + { + "$ref": "defs.json#/definitions/mapStringObject" + }, + { + "type": "null" + } + ] + }, + "WorkingDir": { + "type": "string" + }, + "Labels": { + "oneOf": [ + { + "$ref": "defs.json#/definitions/mapStringString" + }, + { + "type": "null" + } + ] + }, + "StopSignal": { + "type": "string" + } + } + }, + "rootfs": { + "type": "object", + "properties": { + "diff_ids": { + "type": "array", + "items": { + "type": "string" + } + }, + "type": { + "type": "string", + "enum": [ + "layers" + ] + } + }, + "required": [ + "diff_ids", + "type" + ] + }, + "history": { + "type": "array", + "items": { + "type": "object", + "properties": { + "created": { + "type": "string", + "format": "date-time" + }, + "author": { + "type": "string" + }, + "created_by": { + "type": "string" + }, + "comment": { + "type": "string" + }, + "empty_layer": { + "type": "boolean" + } + } + } + } + }, + "required": [ + "architecture", + "os", + "rootfs" + ] +} +``` + +Example: + +Example image `docker.io/library/golang:1.18.1-buster` + +```json +[ + { + "Id": "7f157abc65790f9f770cdae8b17c2fac5183f98a632b5cf186dee17d3ea4d867", + "Digest": "sha256:d8f864bac466e488bb425a59ff9bc104828cd44d0985a8d2fcb5570a9250258a", + "RepoTags": [ + "docker.io/library/golang:1.18.1-buster" + ], + "RepoDigests": [ + "docker.io/library/golang@sha256:d8f864bac466e488bb425a59ff9bc104828cd44d0985a8d2fcb5570a9250258a", + "docker.io/library/golang@sha256:f1e97d64a50f4c2b4fa61211f5206e636a54f992a047d192d6d068fbcd1946c3" + ], + "Parent": "", + "Comment": "", + "Created": "2022-04-20T22:28:29.609431856Z", + "Config": { + "Env": [ + "PATH=/go/bin:/usr/local/go/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin", + "GOLANG_VERSION=1.18.1", + "GOPATH=/go" + ], + "Cmd": [ + "bash" + ], + "WorkingDir": "/go" + }, + "Version": "20.10.12", + "Author": "", + "Architecture": "amd64", + "Os": "linux", + "Size": 929774290, + "VirtualSize": 929774290, + "GraphDriver": { + "Name": "overlay", + "Data": { + "LowerDir": "/home/eduardo/.local/share/containers/storage/overlay/e1479c5af0eec492b4942886f33cea901664fcbf30a90cce2ec4f94cbe849399/diff:/home/eduardo/.local/share/containers/storage/overlay/f88ec5845fe9da6e7a4c4278b464d995af00477fb3d5ca6bb8c25e931ee4afb5/diff:/home/eduardo/.local/share/containers/storage/overlay/2819380fc5af7064586ce4a9fc8956257b3e858f1aa9c13eb7d9a9d69e6fad5a/diff:/home/eduardo/.local/share/containers/storage/overlay/989c0ec34539574af4207a1687000b58b0586142096cf0e746ed6079cdf6131f/diff:/home/eduardo/.local/share/containers/storage/overlay/08209b595f6969502c34ba6021037ab0d99b422cf007bcd6da3912f14227bf2a/diff:/home/eduardo/.local/share/containers/storage/overlay/b9fd5db9c9a6470a6c668ade7fa1faf299b734be4f20fef105ec719ac161cee4/diff", + "UpperDir": "/home/eduardo/.local/share/containers/storage/overlay/865684fea3913d035c50efe6e3f679ca43c1386ed7e2e4c2b78878e58a331a11/diff", + "WorkDir": "/home/eduardo/.local/share/containers/storage/overlay/865684fea3913d035c50efe6e3f679ca43c1386ed7e2e4c2b78878e58a331a11/work" + } + }, + "RootFS": { + "Type": "layers", + "Layers": [ + "sha256:b9fd5db9c9a6470a6c668ade7fa1faf299b734be4f20fef105ec719ac161cee4", + "sha256:5d253e59e5233a8eeebb8acdf660c9a5dff6f064656a00fe856e45cd091be47f", + "sha256:85fe0038088133c12b3556a09e7817998e2043b1dc73630561a0d446d969f755", + "sha256:957a6eed8d1f1779b315b725c58f42fa20dd6bd59dbea586bf05b9dd32f1d3f2", + "sha256:0526a14b6add2837dc89f4e600994984b77d3fac897b4a494dcfe53d8fa1974a", + "sha256:7b3901e2c555ecef791cc80f2c81499a6cc94af0c54d3ae47ee421b437c903ee", + "sha256:874249d61bed46f7b94af7e81ed64f60b33c363f1a022e69699e2dfa538e123b" + ] + }, + "Labels": null, + "Annotations": {}, + "ManifestType": "application/vnd.docker.distribution.manifest.v2+json", + "User": "", + "History": [ + { + "created": "2022-04-20T04:43:37.180912862Z", + "created_by": "/bin/sh -c #(nop) ADD file:7c5789fb822bda2652d7addee832c5a3d71733f0f94f97d89b0c5570c0840829 in / " + }, + { + "created": "2022-04-20T04:43:37.732864614Z", + "created_by": "/bin/sh -c #(nop) CMD [\"bash\"]", + "empty_layer": true + }, + { + "created": "2022-04-20T06:59:01.964685664Z", + "created_by": "/bin/sh -c set -eux; \tapt-get update; \tapt-get install -y --no-install-recommends \t\tca-certificates \t\tcurl \t\tnetbase \t\twget \t; \trm -rf /var/lib/apt/lists/*" + }, + { + "created": "2022-04-20T06:59:07.088636152Z", + "created_by": "/bin/sh -c set -ex; \tif ! command -v gpg \u003e /dev/null; then \t\tapt-get update; \t\tapt-get install -y --no-install-recommends \t\t\tgnupg \t\t\tdirmngr \t\t; \t\trm -rf /var/lib/apt/lists/*; \tfi" + }, + { + "created": "2022-04-20T06:59:22.097083802Z", + "created_by": "/bin/sh -c apt-get update \u0026\u0026 apt-get install -y --no-install-recommends \t\tgit \t\tmercurial \t\topenssh-client \t\tsubversion \t\t\t\tprocps \t\u0026\u0026 rm -rf /var/lib/apt/lists/*" + }, + { + "created": "2022-04-20T22:28:14.68738972Z", + "created_by": "/bin/sh -c set -eux; \tapt-get update; \tapt-get install -y --no-install-recommends \t\tg++ \t\tgcc \t\tlibc6-dev \t\tmake \t\tpkg-config \t; \trm -rf /var/lib/apt/lists/*" + }, + { + "created": "2022-04-20T22:28:15.101594936Z", + "created_by": "/bin/sh -c #(nop) ENV PATH=/usr/local/go/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin", + "empty_layer": true + }, + { + "created": "2022-04-20T22:28:15.202569938Z", + "created_by": "/bin/sh -c #(nop) ENV GOLANG_VERSION=1.18.1", + "empty_layer": true + }, + { + "created": "2022-04-20T22:28:27.638161981Z", + "created_by": "/bin/sh -c set -eux; \tarch=\"$(dpkg --print-architecture)\"; arch=\"${arch##*-}\"; \turl=; \tcase \"$arch\" in \t\t'amd64') \t\t\turl='https://dl.google.com/go/go1.18.1.linux-amd64.tar.gz'; \t\t\tsha256='b3b815f47ababac13810fc6021eb73d65478e0b2db4b09d348eefad9581a2334'; \t\t\t;; \t\t'armel') \t\t\texport GOARCH='arm' GOARM='5' GOOS='linux'; \t\t\t;; \t\t'armhf') \t\t\turl='https://dl.google.com/go/go1.18.1.linux-armv6l.tar.gz'; \t\t\tsha256='9edc01c8e7db64e9ceeffc8258359e027812886ceca3444e83c4eb96ddb068ee'; \t\t\t;; \t\t'arm64') \t\t\turl='https://dl.google.com/go/go1.18.1.linux-arm64.tar.gz'; \t\t\tsha256='56a91851c97fb4697077abbca38860f735c32b38993ff79b088dac46e4735633'; \t\t\t;; \t\t'i386') \t\t\turl='https://dl.google.com/go/go1.18.1.linux-386.tar.gz'; \t\t\tsha256='9a8df5dde9058f08ac01ecfaae42534610db398e487138788c01da26a0d41ff9'; \t\t\t;; \t\t'mips64el') \t\t\texport GOARCH='mips64le' GOOS='linux'; \t\t\t;; \t\t'ppc64el') \t\t\turl='https://dl.google.com/go/go1.18.1.linux-ppc64le.tar.gz'; \t\t\tsha256='33db623d1eecf362fe365107c12efc90eff0b9609e0b3345e258388019cb552a'; \t\t\t;; \t\t's390x') \t\t\turl='https://dl.google.com/go/go1.18.1.linux-s390x.tar.gz'; \t\t\tsha256='5d9301324148ed4dbfaa0800da43a843ffd65c834ee73fcf087255697c925f74'; \t\t\t;; \t\t*) echo \u003e\u00262 \"error: unsupported architecture '$arch' (likely packaging update needed)\"; exit 1 ;; \tesac; \tbuild=; \tif [ -z \"$url\" ]; then \t\tbuild=1; \t\turl='https://dl.google.com/go/go1.18.1.src.tar.gz'; \t\tsha256='efd43e0f1402e083b73a03d444b7b6576bb4c539ac46208b63a916b69aca4088'; \t\techo \u003e\u00262; \t\techo \u003e\u00262 \"warning: current architecture ($arch) does not have a compatible Go binary release; will be building from source\"; \t\techo \u003e\u00262; \tfi; \t\twget -O go.tgz.asc \"$url.asc\"; \twget -O go.tgz \"$url\" --progress=dot:giga; \techo \"$sha256 *go.tgz\" | sha256sum -c -; \t\tGNUPGHOME=\"$(mktemp -d)\"; export GNUPGHOME; \tgpg --batch --keyserver keyserver.ubuntu.com --recv-keys 'EB4C 1BFD 4F04 2F6D DDCC EC91 7721 F63B D38B 4796'; \tgpg --batch --keyserver keyserver.ubuntu.com --recv-keys '2F52 8D36 D67B 69ED F998 D857 78BD 6547 3CB3 BD13'; \tgpg --batch --verify go.tgz.asc go.tgz; \tgpgconf --kill all; \trm -rf \"$GNUPGHOME\" go.tgz.asc; \t\ttar -C /usr/local -xzf go.tgz; \trm go.tgz; \t\tif [ -n \"$build\" ]; then \t\tsavedAptMark=\"$(apt-mark showmanual)\"; \t\tapt-get update; \t\tapt-get install -y --no-install-recommends golang-go; \t\t\t\t( \t\t\tcd /usr/local/go/src; \t\t\texport GOROOT_BOOTSTRAP=\"$(go env GOROOT)\" GOHOSTOS=\"$GOOS\" GOHOSTARCH=\"$GOARCH\"; \t\t\t./make.bash; \t\t); \t\t\t\tapt-mark auto '.*' \u003e /dev/null; \t\tapt-mark manual $savedAptMark \u003e /dev/null; \t\tapt-get purge -y --auto-remove -o APT::AutoRemove::RecommendsImportant=false; \t\trm -rf /var/lib/apt/lists/*; \t\t\t\trm -rf \t\t\t/usr/local/go/pkg/*/cmd \t\t\t/usr/local/go/pkg/bootstrap \t\t\t/usr/local/go/pkg/obj \t\t\t/usr/local/go/pkg/tool/*/api \t\t\t/usr/local/go/pkg/tool/*/go_bootstrap \t\t\t/usr/local/go/src/cmd/dist/dist \t\t; \tfi; \t\tgo version" + }, + { + "created": "2022-04-20T22:28:28.825361004Z", + "created_by": "/bin/sh -c #(nop) ENV GOPATH=/go", + "empty_layer": true + }, + { + "created": "2022-04-20T22:28:28.916195707Z", + "created_by": "/bin/sh -c #(nop) ENV PATH=/go/bin:/usr/local/go/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin", + "empty_layer": true + }, + { + "created": "2022-04-20T22:28:29.514751109Z", + "created_by": "/bin/sh -c mkdir -p \"$GOPATH/src\" \"$GOPATH/bin\" \u0026\u0026 chmod -R 777 \"$GOPATH\"" + }, + { + "created": "2022-04-20T22:28:29.609431856Z", + "created_by": "/bin/sh -c #(nop) WORKDIR /go", + "empty_layer": true + } + ], + "NamesHistory": [ + "docker.io/library/golang:1.18.1-buster" + ] + } +] +``` + +# Buildah + +Buildah logo + +## Building OCI container images + +The purpose of this tutorial is to demonstrate how Buildah can be used to build container images compliant with the [Open Container Initiative](https://www.opencontainers.org/) (OCI) [image specification](https://github.com/opencontainers/image-spec). Images can be built based on existing images, from scratch, and using Dockerfiles. OCI images built using the Buildah command line tool (CLI) and the underlying OCI based technologies (e.g. [containers/image](https://github.com/containers/image) and [containers/storage](https://github.com/containers/storage)) are portable and can therefore run in a Docker environment. + +In brief the `containers/image` project provides mechanisms to copy (push, pull), inspect, and sign container images. The `containers/storage` project provides mechanisms for storing filesystem layers, container images, and containers. Buildah is a CLI that takes advantage of these underlying projects and therefore allows you to build, move, and manage container images and containers. + +Buildah works on a number of Linux distributions, but is not supported on Windows or Mac platforms at this time. Buildah specializes mainly in building OCI images while [Podman](https://podman.io) provides a broader set of commands and functions that help you to maintain, modify and run OCI images and containers. For more information on the difference between the projects please refer to the [Buildah and Podman relationship](https://github.com/containers/buildah#buildah-and-podman-relationship) section on the main README.md. + +## Configure and Install Buildah + +Note that installation instructions below assume you are running a Linux distro that uses `dnf` as its package manager, and have all prerequisites fulfilled. See Buildah's [installation instructions][buildah-install] for a full list of prerequisites, and the `buildah` installation section in the [official Red Hat documentation][rh-repo-docs] for RHEL-specific instructions. + +[buildah-install]:../../install.md +[rh-repo-docs]:https://access.redhat.com/documentation/en-us/red_hat_enterprise_linux/8/html/building_running_and_managing_containers/ + +First step is to install Buildah. Run as root because you will need to be root for installing the Buildah package: + + $ sudo -s + +Then install buildah by running: + + # dnf -y install buildah + +## Rootless User Configuration + +If you plan to run Buildah as a user without root privileges, i.e. a "rootless user", the administrator of the system might have to do a bit of additional configuration beforehand. The setup required for this is listed on the Podman GitHub site [here](https://github.com/containers/podman/blob/main/docs/tutorials/rootless_tutorial.md). Buildah has the same setup and configuration requirements that Podman does for rootless users. + +## Post Installation Verification + +After installing Buildah we can see there are no images installed. The `buildah images` command will list all the images: + + # buildah images + +We can also see that there are also no working containers by running: + + # buildah containers + +When you build a working container from an existing image, Buildah defaults to appending '-working-container' to the image's name to construct a name for the container. The Buildah CLI conveniently returns the name of the new container. You can take advantage of this by assigning the returned value to a shell variable using standard shell assignment: + + # container=$(buildah from fedora) + +It is not required to assign the container's name to a shell variable. Running `buildah from fedora` is sufficient. It just helps simplify commands later. To see the name of the container that we stored in the shell variable: + + # echo $container + +What can we do with this new container? Let's try running bash: + + # buildah run $container bash + +Notice we get a new shell prompt because we are running a bash shell inside of the container. It should be noted that `buildah run` is primarily intended for debugging and running commands as part of the build process. A more full-featured engine like Podman or a container runtime interface service like [CRI-O](https://github.com/kubernetes-sigs/cri-o) is more suited for starting containers in production. + +Be sure to `exit` out of the container and let's try running something else: + + # buildah run $container java + +Oops. Java is not installed. A message containing something like the following was returned. + + runc create failed: unable to start start container process: exec: "java": executable file not found in $PATH + +Let's try installing it inside the container using: + + # buildah run $container -- dnf -y install java + +The `--` syntax basically tells Buildah: there are no more `buildah run` command options after this point. The options after this point are for the command that's started inside the container. It is required if the command we specify includes command line options which are not meant for Buildah. + +Now running `buildah run $container java` will show that Java has been installed. It will return the standard Java `Usage` output. + +## Building a container from scratch + +One of the advantages of using `buildah` to build OCI compliant container images is that you can easily build a container image from scratch and therefore exclude unnecessary packages from your image. Most final container images for production probably don't need a package manager like `dnf`. + +Let's build a container and image from scratch. The special "image" name "scratch" tells Buildah to create an empty container. The container has a small amount of metadata about the container but no real Linux content. + + # newcontainer=$(buildah from scratch) + +You can see this new empty container by running: + + # buildah containers + +You should see output similar to the following: + + CONTAINER ID BUILDER IMAGE ID IMAGE NAME CONTAINER NAME + 82af3b9a9488 * 3d85fcda5754 docker.io/library/fedora:latest fedora-working-container + ac8fa6be0f0a * scratch working-container + +Its container name is working-container by default and it's stored in the `$newcontainer` variable. Notice the image name (IMAGE NAME) is "scratch". This is a special value that indicates that the working container wasn't based on an image. When we run: + + # buildah images + +We don't see the "scratch" image listed. There is no corresponding scratch image. A container based on "scratch" starts from nothing. + +So does this container actually do anything? Let's see. + + # buildah run $newcontainer bash + +Nope. This really is empty. The package installer `dnf` is not even inside this container. It's essentially an empty layer on top of the kernel. So what can be done with that? Thankfully there is a `buildah mount` command. + + # scratchmnt=$(buildah mount $newcontainer) + +Note: If attempting to mount in rootless mode, the command fails. Mounting a container can only be done in a mount namespace that you own. Create and enter a user namespace and mount namespace by executing the `buildah unshare` command. See buildah-mount(1) man page for more information. + + $ buildah unshare + # scratchmnt=$(buildah mount $newcontainer) + +By echoing `$scratchmnt` we can see the path for the [overlay mount point](https://wiki.archlinux.org/index.php/Overlay_filesystem), which is used as the root file system for the container. + + # echo $scratchmnt + /var/lib/containers/storage/overlay/b78d0e11957d15b5d1fe776293bd40a36c28825fb6cf76f407b4d0a95b2a200d/merged + +Notice that the overlay mount point is somewhere under `/var/lib/containers/storage` if you started out as root, and under your home directory's `.local/share/containers/storage` directory if you're in rootless mode. (See above on `containers/storage` or for more information see [containers/storage](https://github.com/containers/storage).) + +Now that we have a new empty container we can install or remove software packages or simply copy content into that container. So let's install `bash` and `coreutils` so that we can run bash scripts. This could easily be `nginx` or other packages needed for your container. + +**NOTE:** the version in the example below (35) relates to a Fedora version which is the Linux platform this example was run on. If you are running dnf on the host to populate the container, the version you specify must be valid for the host or dnf will throw an error. I.e. If you were to run this on a RHEL platform, you'd need to specify `--releasever 8.1` or similar instead of `--releasever 35`. If you want the container to be a particular Linux platform, change `scratch` in the first line of the example to the platform you want, i.e. `# newcontainer=$(buildah from fedora)`, and then you can specify an appropriate version number for that Linux platform. + + # dnf install --installroot $scratchmnt --releasever 35 bash coreutils --setopt install_weak_deps=false -y + +Let's try it out (showing the prompt in this example to demonstrate the difference): + + # buildah run $newcontainer sh + sh-5.1# cd /usr/bin + sh-5.1# ls + sh-5.1# exit + +Notice we now have a `/usr/bin` directory in the newcontainer's root file system. Let's first copy a simple file from our host into the container. Create a file called runecho.sh which contains the following: + + #!/usr/bin/env bash + for i in `seq 0 9`; + do + echo "This is a new container from ipbabble [" $i "]" + done + +Change the permissions on the file so that it can be run: + + # chmod +x runecho.sh + +With `buildah` files can be copied into the new container. We can then use `buildah run` to run that command within the container by specifying the command. We can also configure the image we'll create from this container to run the command directly when we run it using [Podman](https://github.com/containers/podman) and its `podman run` command. In short the `buildah run` command is equivalent to the "RUN" command in a Dockerfile (it always needs to be told what to run), whereas `podman run` is equivalent to the `docker run` command (it can look at the image's configuration to see what to run). Now let's copy this new command into the container's `/usr/bin` directory, configure the command to be run when the image is run by `podman`, and create an image from the container's root file system and configuration settings: + + # To test with Podman, first install via: + # dnf -y install podman + # buildah copy $newcontainer ./runecho.sh /usr/bin + # buildah config --cmd /usr/bin/runecho.sh $newcontainer + # buildah commit $newcontainer newimage + +We've got a new image named "newimage". The container is still there because we didn't remove it. +Now run the command in the container with Buildah specifying the command to run in the container: + + # buildah run $newcontainer /usr/bin/runecho.sh + This is a new container from ipbabble [ 0 ] + This is a new container from ipbabble [ 1 ] + This is a new container from ipbabble [ 2 ] + This is a new container from ipbabble [ 3 ] + This is a new container from ipbabble [ 4 ] + This is a new container from ipbabble [ 5 ] + This is a new container from ipbabble [ 6 ] + This is a new container from ipbabble [ 7 ] + This is a new container from ipbabble [ 8 ] + This is a new container from ipbabble [ 9 ] + +Now use Podman to run the command in a new container based on our new image (no command required): + + # podman run --rm newimage + This is a new container from ipbabble [ 0 ] + This is a new container from ipbabble [ 1 ] + This is a new container from ipbabble [ 2 ] + This is a new container from ipbabble [ 3 ] + This is a new container from ipbabble [ 4 ] + This is a new container from ipbabble [ 5 ] + This is a new container from ipbabble [ 6 ] + This is a new container from ipbabble [ 7 ] + This is a new container from ipbabble [ 8 ] + This is a new container from ipbabble [ 9 ] + +It works! Congratulations, you have built a new OCI container image from scratch that uses bash scripting. + +Back to Buildah, let's add some more configuration information. + + # buildah config --created-by "ipbabble" $newcontainer + # buildah config --author "wgh at redhat.com @ipbabble" --label name=fedora35-bashecho $newcontainer + +We can inspect the working container's metadata using the `inspect` command: + + # buildah inspect $newcontainer + +We should probably unmount the working container's rootfs. We will need to commit the container again to create an image that includes the two configuration changes we just made: + + # buildah unmount $newcontainer + # buildah commit $newcontainer fedora-bashecho + # buildah images + +And you can see there is a new image called `localhost/fedora-bashecho:latest`. You can inspect the new image using: + + # buildah inspect --type=image fedora-bashecho + +Later when you want to create a new container or containers from this image, you simply need to do `buildah from fedora-bashecho`. This will create a new container based on this image for you. + +Now that you have the new image you can remove the scratch container called working-container: + + # buildah rm $newcontainer + +or + + # buildah rm working-container + +## OCI images built using Buildah are portable + +Let's test if this new OCI image is really portable to another container engine like Docker. First you should install Docker and start it. Notice that Docker requires a running daemon process in order to run any client commands. Buildah and Podman have no daemon requirement. + + # dnf -y install docker + # systemctl start docker + +Let's copy that image from where containers/storage stores it to where the Docker daemon stores its images, so that we can run it using Docker. We can achieve this using `buildah push`. This copies the image to Docker's storage area which is located under `/var/lib/docker`. Docker's storage is managed by the Docker daemon. This needs to be explicitly stated by telling Buildah to push the image to the Docker daemon using `docker-daemon:`. + + # buildah push fedora-bashecho docker-daemon:fedora-bashecho:latest + +Under the covers, the containers/image library calls into the containers/storage library to read the image's contents from where buildah keeps them, and sends them to the local Docker daemon, which writes them to where it keeps them. This can take a little while. And usually you won't need to do this. If you're using `buildah` you are probably not using Docker. This is just for demo purposes. Let's try it: + + # docker run --rm fedora-bashecho + This is a new container from ipbabble [ 0 ] + This is a new container from ipbabble [ 1 ] + This is a new container from ipbabble [ 2 ] + This is a new container from ipbabble [ 3 ] + This is a new container from ipbabble [ 4 ] + This is a new container from ipbabble [ 5 ] + This is a new container from ipbabble [ 6 ] + This is a new container from ipbabble [ 7 ] + This is a new container from ipbabble [ 8 ] + This is a new container from ipbabble [ 9 ] + +OCI container images built with `buildah` are completely standard as expected. So now it might be time to run: + + # dnf -y remove docker + +## Using Containerfiles/Dockerfiles with Buildah + +What if you have been using Docker for a while and have some existing Dockerfiles? Not a problem. Buildah can build images using a Dockerfile. The `build` command takes a Dockerfile as input and produces an OCI image. + +Find one of your Dockerfiles or create a file called Dockerfile. Use the following example or some variation if you'd like: + + # Base on the most recently released Fedora + FROM fedora:latest + MAINTAINER ipbabble email buildahboy@redhat.com # not a real email + + # Install updates and httpd + RUN echo "Updating all fedora packages"; dnf -y update; dnf -y clean all + RUN echo "Installing httpd"; dnf -y install httpd && dnf -y clean all + + # Expose the default httpd port 80 + EXPOSE 80 + + # Run the httpd + CMD ["/usr/sbin/httpd", "-DFOREGROUND"] + +Now run `buildah build` with the name of the Dockerfile and the name to be given to the created image (e.g. fedora-httpd): + + # buildah build -f Dockerfile -t fedora-httpd . + +or, because `buildah build` defaults to `Dockerfile` and using the current directory as the build context: + + # buildah build -t fedora-httpd + +You will see all the steps of the Dockerfile executing. Afterwards `buildah images` will show you the new image. Now we can create a container from the image and test it with `podman run`: + + # podman run --rm -p 8123:80 fedora-httpd + +While that container is running, in another shell run: + + # curl localhost:8123 + +You will see the standard Apache webpage. + +Why not try and modify the Dockerfile. Do not install httpd, but instead ADD the runecho.sh file and have it run as the CMD. + +For more information on Buildah and how you might contribute please visit the [Buildah home page on GitHub](https://github.com/containers/buildah). + +# SIF + +## The Singularity Image Format (SIF) + +For more information please visit [SIF official page](https://github.com/sylabs/sif) + +SIF layout +> Image taken from the [SIF official page](https://github.com/sylabs/sif) + +A SIF container image is constructed as a single file that encapsulates an entire file system and a number of predefined and user-defined annotations and arbitrary data. This differs somewhat from OCI image bundle, in that SIF packages the runtime data and the image spec, whereas you'd normally expect to maintain data outside a container when running with Podman, Docker, Kubernetes, etc. + +A number of large supercomputing sites have been using SIF-formatted containers. Because everything is contained in a single file, it is very portable and can be transported more easily and efficiently than trying to move workloads with data and applications separately. diff --git a/_episodes/23.spack.md b/_episodes/23.spack.md deleted file mode 100644 index dc6c344..0000000 --- a/_episodes/23.spack.md +++ /dev/null @@ -1,322 +0,0 @@ ---- -title: "Advanced container builds" -teaching: 15 -exercises: 5 -questions: -- How can I build better container images? -- How can I build more reproducible images? -- How can I build more performant images? -objectives: -- Learn about how Spack and EasyBuild can generate container recipes -- Learn about Buildah -- Learn about Buildah advanced build feature -keypoints: -- Spack and EasyBuild are tools to manage dependencies and better way to install apps -- Buildah can take more robust recipes by skipping the use of a Dockerfile ---- -# Spack - -## Container Images - -Spack can be an ideal tool to setup images for containers since all the -features discussed in :ref:`environments` can greatly help to manage -the installation of software during the image build process. Nonetheless, -building a production image from scratch still requires a lot of -boilerplate to: - -- Get Spack working within the image, possibly running as root -- Minimize the physical size of the software installed -- Properly update the system software in the base image - -To facilitate users with these tedious tasks, Spack provides a command -to automatically generate recipes for container images based on -Environments: - -``` - $ ls - spack.yaml - - $ spack containerize - # Build stage with Spack pre-installed and ready to be used - FROM spack/centos7:latest as builder - - # What we want to install and how we want to install it - # is specified in a manifest file (spack.yaml) - RUN mkdir /opt/spack-environment \ - && (echo "spack:" \ - && echo " specs:" \ - && echo " - gromacs+mpi" \ - && echo " - mpich" \ - && echo " concretization: together" \ - && echo " config:" \ - && echo " install_tree: /opt/software" \ - && echo " view: /opt/view") > /opt/spack-environment/spack.yaml - - # Install the software, remove unnecessary deps - RUN cd /opt/spack-environment && spack env activate . && spack install && spack gc -y - - # Strip all the binaries - RUN find -L /opt/view/* -type f -exec readlink -f '{}' \; | \ - xargs file -i | \ - grep 'charset=binary' | \ - grep 'x-executable\|x-archive\|x-sharedlib' | \ - awk -F: '{print $1}' | xargs strip -s - - # Modifications to the environment that are necessary to run - RUN cd /opt/spack-environment && \ - spack env activate --sh -d . >> /etc/profile.d/z10_spack_environment.sh - - # Bare OS image to run the installed executables - FROM centos:7 - - COPY --from=builder /opt/spack-environment /opt/spack-environment - COPY --from=builder /opt/software /opt/software - COPY --from=builder /opt/view /opt/view - COPY --from=builder /etc/profile.d/z10_spack_environment.sh /etc/profile.d/z10_spack_environment.sh - - RUN yum update -y && yum install -y epel-release && yum update -y \ - && yum install -y libgomp \ - && rm -rf /var/cache/yum && yum clean all - - RUN echo 'export PS1="\[$(tput bold)\]\[$(tput setaf 1)\][gromacs]\[$(tput setaf 2)\]\u\[$(tput sgr0)\]:\w $ "' >> ~/.bashrc - - LABEL "app"="gromacs" - LABEL "mpi"="mpich" - - ENTRYPOINT ["/bin/bash", "--rcfile", "/etc/profile", "-l"] -``` -{: .bash} - -In order to build and run the image, execute: - -``` - $ spack containerize > Dockerfile - $ docker build -t myimage . - $ docker run -it myimage -``` -{: .bash} - -The bits that make this automation possible are discussed in details -below. All the images generated in this way will be based on -multi-stage builds with: - -- A fat ``build`` stage containing common build tools and Spack itself -- A minimal ``final`` stage containing only the software requested by the user - -### Environment Configuration - -Any Spack Environment can be used for the automatic generation of container -recipes. Sensible defaults are provided for things like the base image or the -version of Spack used in the image. If a finer tuning is needed it can be -obtained by adding the relevant metadata under the ``container`` attribute -of environments: - -``` - spack: - specs: - - gromacs+mpi - - mpich - - container: - # Select the format of the recipe e.g. docker, - # singularity or anything else that is currently supported - format: docker - - # Select from a valid list of images - base: - image: "centos:7" - spack: develop - - # Whether or not to strip binaries - strip: true - - # Additional system packages that are needed at runtime - os_packages: - - libgomp - - # Extra instructions - extra_instructions: - final: | - RUN echo 'export PS1="\[$(tput bold)\]\[$(tput setaf 1)\][gromacs]\[$(tput setaf 2)\]\u\[$(tput sgr0)\]:\w $ "' >> ~/.bashrc - - # Labels for the image - labels: - app: "gromacs" - mpi: "mpich" -``` -{: .yaml} - -The tables below describe the configuration options that are currently supported: - -.. list-table:: General configuration options for the ``container`` section of ``spack.yaml`` - :header-rows: 1 - - * - Option Name - - Description - - Allowed Values - - Required - * - ``format`` - - The format of the recipe - - ``docker`` or ``singularity`` - - Yes - * - ``base:image`` - - Base image for ``final`` stage - - See :ref:`containers-supported-os` - - Yes - * - ``base:spack`` - - Version of Spack - - Valid tags for ``base:image`` - - Yes - * - ``strip`` - - Whether to strip binaries - - ``true`` (default) or ``false`` - - No - * - ``os_packages`` - - System packages to be installed - - Valid packages for the ``final`` OS - - No - * - ``extra_instructions:build`` - - Extra instructions (e.g. `RUN`, `COPY`, etc.) at the end of the ``build`` stage - - Anything understood by the current ``format`` - - No - * - ``extra_instructions:final`` - - Extra instructions (e.g. `RUN`, `COPY`, etc.) at the end of the ``final`` stage - - Anything understood by the current ``format`` - - No - * - ``labels`` - - Labels to tag the image - - Pairs of key-value strings - - No - -.. list-table:: Configuration options specific to Singularity - :header-rows: 1 - - * - Option Name - - Description - - Allowed Values - - Required - * - ``singularity:runscript`` - - Content of ``%runscript`` - - Any valid script - - No - * - ``singularity:startscript`` - - Content of ``%startscript`` - - Any valid script - - No - * - ``singularity:test`` - - Content of ``%test`` - - Any valid script - - No - * - ``singularity:help`` - - Description of the image - - Description string - - No - -Once the Environment is properly configured a recipe for a container -image can be printed to standard output by issuing the following -command from the directory where the ``spack.yaml`` resides: - -``` - $ spack containerize -``` -{: .bash} - -The example ``spack.yaml`` above would produce for instance the -following ``Dockerfile``: - -``` - # Build stage with Spack pre-installed and ready to be used - FROM spack/centos7:latest as builder - - # What we want to install and how we want to install it - # is specified in a manifest file (spack.yaml) - RUN mkdir /opt/spack-environment \ - && (echo "spack:" \ - && echo " specs:" \ - && echo " - gromacs+mpi" \ - && echo " - mpich" \ - && echo " concretization: together" \ - && echo " config:" \ - && echo " install_tree: /opt/software" \ - && echo " view: /opt/view") > /opt/spack-environment/spack.yaml - - # Install the software, remove unnecessary deps - RUN cd /opt/spack-environment && spack env activate . && spack install && spack gc -y - - # Strip all the binaries - RUN find -L /opt/view/* -type f -exec readlink -f '{}' \; | \ - xargs file -i | \ - grep 'charset=binary' | \ - grep 'x-executable\|x-archive\|x-sharedlib' | \ - awk -F: '{print $1}' | xargs strip -s - - # Modifications to the environment that are necessary to run - RUN cd /opt/spack-environment && \ - spack env activate --sh -d . >> /etc/profile.d/z10_spack_environment.sh - - - # Bare OS image to run the installed executables - FROM centos:7 - - COPY --from=builder /opt/spack-environment /opt/spack-environment - COPY --from=builder /opt/software /opt/software - COPY --from=builder /opt/view /opt/view - COPY --from=builder /etc/profile.d/z10_spack_environment.sh /etc/profile.d/z10_spack_environment.sh - - RUN yum update -y && yum install -y epel-release && yum update -y \ - && yum install -y libgomp \ - && rm -rf /var/cache/yum && yum clean all - - RUN echo 'export PS1="\[$(tput bold)\]\[$(tput setaf 1)\][gromacs]\[$(tput setaf 2)\]\u\[$(tput sgr0)\]:\w $ "' >> ~/.bashrc - - - LABEL "app"="gromacs" - LABEL "mpi"="mpich" - - ENTRYPOINT ["/bin/bash", "--rcfile", "/etc/profile", "-l"] -``` -{: .docker} - - -> Spack can also produce Singularity definition files to build the image. The minimum version of Singularity required to build a SIF (Singularity Image Format) from them is ``3.5.3``. - --------------- -Best Practices --------------- - -^^^ -MPI -^^^ - -Due to the dependency on Fortran for OpenMPI, which is the spack default -implementation, consider adding ``gfortran`` to the ``apt-get install`` list. - -Recent versions of OpenMPI will require you to pass ``--allow-run-as-root`` -to your ``mpirun`` calls if started as root user inside Docker. - -For execution on HPC clusters, it can be helpful to import the docker -image into Singularity in order to start a program with an *external* -MPI. Otherwise, also add ``openssh-server`` to the ``apt-get install`` list. - -^^^^ -CUDA -^^^^ - -Starting from CUDA 9.0, Nvidia provides minimal CUDA images based on -Ubuntu. Please see `their instructions `_. -Avoid double-installing CUDA by adding, e.g. - -``` - packages: - cuda: - externals: - - spec: "cuda@9.0.176%gcc@5.4.0 arch=linux-ubuntu16-x86_64" - prefix: /usr/local/cuda - buildable: False -``` -{: .yaml} - -to your ``spack.yaml``. - -Users will either need ``nvidia-docker`` or e.g. Singularity to *execute* -device kernels. diff --git a/fig/build-diagram.png b/fig/build-diagram.png new file mode 100644 index 0000000000000000000000000000000000000000..9ad9b84eb5f052c30cf444c43a3e21cdff1f4f64 GIT binary patch literal 43326 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 Y@&En@){e8C#0Q|Ky-@4I?A1H}7exi_OaK4? literal 0 HcmV?d00001 diff --git a/fig/buildah-logo_large.png b/fig/buildah-logo_large.png new file mode 100644 index 0000000000000000000000000000000000000000..1d69ff3348fbe84506c3473094b8d9f90177237b GIT binary patch literal 33351 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 Date: Mon, 23 May 2022 16:20:02 -0400 Subject: [PATCH 3/5] Add reference to image OCI Signed-off-by: Carlos Eduardo Arango Gutierrez --- _episodes/23.image.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/_episodes/23.image.md b/_episodes/23.image.md index c581675..94e5d57 100644 --- a/_episodes/23.image.md +++ b/_episodes/23.image.md @@ -24,7 +24,7 @@ The goal of this specification is to enable the creation of interoperable tools At a high level the image manifest contains metadata about the contents and dependencies of the image including the content-addressable identity of one or more filesystem layer changeset archives that will be unpacked to make up the final runnable filesystem. The image configuration includes information such as application arguments, environments, etc. The image index is a higher-level manifest which points to a list of manifests and descriptors. Typically, these manifests may provide different implementations of the image, possibly varying by platform or other attributes. OCI image - +> Image taken from https://github.com/opencontainers/image-spec ## The Schema From 9f8cac47f32afdf0484709665852babe259a4625 Mon Sep 17 00:00:00 2001 From: Carlos Eduardo Arango Gutierrez Date: Mon, 23 May 2022 17:57:41 -0400 Subject: [PATCH 4/5] Fix image sizing Signed-off-by: Carlos Eduardo Arango Gutierrez --- _episodes/23.image.md | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/_episodes/23.image.md b/_episodes/23.image.md index 94e5d57..7b54ffd 100644 --- a/_episodes/23.image.md +++ b/_episodes/23.image.md @@ -23,7 +23,7 @@ The goal of this specification is to enable the creation of interoperable tools At a high level the image manifest contains metadata about the contents and dependencies of the image including the content-addressable identity of one or more filesystem layer changeset archives that will be unpacked to make up the final runnable filesystem. The image configuration includes information such as application arguments, environments, etc. The image index is a higher-level manifest which points to a list of manifests and descriptors. Typically, these manifests may provide different implementations of the image, possibly varying by platform or other attributes. -OCI image +OCI image > Image taken from https://github.com/opencontainers/image-spec ## The Schema @@ -314,7 +314,7 @@ Example image `docker.io/library/golang:1.18.1-buster` # Buildah -Buildah logo +Buildah logo ## Building OCI container images @@ -591,13 +591,15 @@ Why not try and modify the Dockerfile. Do not install httpd, but instead ADD the For more information on Buildah and how you might contribute please visit the [Buildah home page on GitHub](https://github.com/containers/buildah). +> Inspired by https://github.com/containers/buildah + # SIF ## The Singularity Image Format (SIF) For more information please visit [SIF official page](https://github.com/sylabs/sif) -SIF layout +SIF layout > Image taken from the [SIF official page](https://github.com/sylabs/sif) A SIF container image is constructed as a single file that encapsulates an entire file system and a number of predefined and user-defined annotations and arbitrary data. This differs somewhat from OCI image bundle, in that SIF packages the runtime data and the image spec, whereas you'd normally expect to maintain data outside a container when running with Podman, Docker, Kubernetes, etc. From 6cc61f284f8198ec9f0c832e4d27f49cf9b08bff Mon Sep 17 00:00:00 2001 From: Carlos Eduardo Arango Gutierrez Date: Mon, 23 May 2022 18:01:20 -0400 Subject: [PATCH 5/5] Bigger images Signed-off-by: Carlos Eduardo Arango Gutierrez --- _episodes/23.image.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/_episodes/23.image.md b/_episodes/23.image.md index 7b54ffd..50911ce 100644 --- a/_episodes/23.image.md +++ b/_episodes/23.image.md @@ -23,7 +23,7 @@ The goal of this specification is to enable the creation of interoperable tools At a high level the image manifest contains metadata about the contents and dependencies of the image including the content-addressable identity of one or more filesystem layer changeset archives that will be unpacked to make up the final runnable filesystem. The image configuration includes information such as application arguments, environments, etc. The image index is a higher-level manifest which points to a list of manifests and descriptors. Typically, these manifests may provide different implementations of the image, possibly varying by platform or other attributes. -OCI image +OCI image > Image taken from https://github.com/opencontainers/image-spec ## The Schema @@ -314,7 +314,7 @@ Example image `docker.io/library/golang:1.18.1-buster` # Buildah -Buildah logo +Buildah logo ## Building OCI container images