Running Snakemake in an HPC environment
Learning outcomes
After having completed this chapter you will be able to:
- Understand resource usage in each rule with the
benchmark
directive - Optimise resource usage in a workflow with the
resources
directive - Run a Snakemake workflow in an SLURM-based HPC environment
- Set up workflow-specific configuration profiles
Material
Workflow from previous session
If you didn’t finish the previous part or didn’t do the optional exercises, you can restart from a fully commented workflow with a supplementary .fastq files quality check rule and multithreading implemented in all rules. You can download all the files (workflow and config) here or copy them after cloning the course repository locally:
git clone https://github.com/sib-swiss/containers-snakemake-training.git # Clone repository
cd containers-snakemake-training/ # Go in repository
cp -r docs/solutions/session4 destination_path # Copy folder where needed; adapt destination_path to required path
Exercises
In this last series exercises, you will learn how to get a better idea of the resources used by your workflow and how to optimise their usage, as well as the necessary command line arguments, files and rule directives to run your workflow in an SLURM-based High Performance Computing (HPC) environment. In addition, you will also learn how to set up configuration profiles and further develop the Snakefile to define rule-specific settings.
Benchmarking a workflow
Knowing how many resources are being used by each job can be very useful to optimise your workflow for both local and remote execution. The benchmark
directive allows you to do exactly that. Adding it in your rule definitions will trigger the recording of several metrics for every job generated by your workflow execution. Each job will have an associated benchmark file with the following information:
s | h: m: s | max_rss | max_vms | max_uss | max_pss | io_in | io_out | mean_load | cpu_time |
---|---|---|---|---|---|---|---|---|---|
31.3048 | 0:00:31 | 763.04 | 904.29 | 757.89 | 758.37 | 1.81 | 230.18 | 37.09 | 11.78 |
s
andh:m:s
give the job wall clock time (in seconds and hours-minutes-seconds, respectively), which is the actual time taken from the start of a software to its end. You can use these results to infer a safe value for theruntime
keyword- Likewise, you can use
max_rss
(shown in megabytes) to figure out how much memory was used by the job and use this value in thememory
keyword
What are the other columns?
In case you are wondering about the other columns of the table, the official documentation has detailed explanations about their content.
Exercise: Add the and benchmark
directive to the rules. You can follow the same logic as you did with the log
directive.
Benchmarks and wildcards
benchmark
directives must contain the same wildcards
as the output
directive, here sample
Answer
The code snippet below shows the answer for the reads_quantification_genes
rule. You can do the same with the other rules.
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 |
|
This means that a workflow has to run at least once to produce benchmark values, which leads to a “vicious circle”: to run a workflow, you need benchmark information. But to get them, you need to run the workflow. A good practice is to make a first run with only a small part of the samples and sensible resource requirements to allow for the benchmarking to happen.
Controlling memory usage and runtime
Controlling resources such as the amount of memory or runtime of each job is a very good way to optimise resource usage in a workflow. This ensures your instance (computer, HPC cluster…) won’t run out of memory during computation (which could interrupt jobs or even crash the instance) and that your jobs will run in a reasonable amount of time (a job taking more time to run than usual might be a sign that something is going on).
Resource usage and schedulers
Optimising resource usage is especially important when submitting jobs to a scheduler (for instance on a cluster), as it allows a better and more precise definition of your job priority: jobs with low threads/memory/runtime requirements often have a higher priority than heavy jobs, which means they will often start first.
Controlling memory usage and runtime in Snakemake is very simple: you only need to need to use the resources
directive with the memory
or runtime
keywords and in most software, you don’t even need to specify the amount of memory available to the software via a parameter.
Here are some suggested values of memory usage in the current workflow:
fastq_trim
: 500 MBread_mapping
: 2 GBsam_to_bam
: 250 MBreads_quantification_genes
: 500 MB
Exercise: Implement memory usage limits in the workflow.
Two ways to declare memory values
There are two ways to declare memory values in resources
:
mem_<unit> = n
mem = 'n<unit>'
: in this case, you must pass a string, so you have to enclosen<unit>
with quotes''
<unit>
is a unit in [B, KB, MB, GB, TB, PB, KiB, MiB, GiB, TiB, PiB] and n
is a float.
Answer
We implemented memory usage control in all the rules so that you can check everything. We implemented all the memory usage definitions using mem_mb
. Feel free to copy this in your Snakefile:
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 117 118 119 120 121 122 123 124 125 126 127 |
|
In the following section we will see that the values established in resources
will be very useful to communicate with the job schedulers.
Setting up Snakemake to send jobs via the SLURM job scheduler
Running jobs remotely on an HPC environment usually requires interaction with job schedulers such as SLURM. Thankfully, adapting our workflow to send jobs via a scheduler does not require a lot of changes. In this section you will use the cluster-generic
plugin to send jobs via SLURM.
Snakemake plugins
In Snakemake v8+, interaction with job schedulers requires the installation of certain plugins. There are multiple plugins available for different schedulers. You can find them here.
In this tutorial, you will use the cluster-generic
plugin and use it to configure how to send jobs through SLURM. While there is a SLURM-specific plugin, we will focus on the cluster-generic
one because it can be used with any scheduler.
Among others, the cluster-generic
plugin can take the following settings, which can be passed in different ways:
--cluster-generic-submit-cmd
: command that will be used to submit each job to the cluster. Since we are using SLURM, the command issbatch
. This string can contain any validsbatch
arguments and use values from theSnakefile
(e.g. usingthreads
specified in the Snakefile as a value for the--cpus-per-task
argument)--cluster-generic-cancel-cmd
: command to use to cancel jobs. This is important if you want to stop your workflow while jobs are running while also cancel the running jobs
Then, you will need to tell Snakemake to run the workflow through SLURM. An important parameter when doing so is --jobs
, which will tell Snakemake how many jobs it can submit concurrently. In addition, you will also need to specify which executor plugin to use and the required settings:
snakemake \
--executor cluster-generic \
--cluster-generic-submit-cmd \
'sbatch <slurm_arguments>' \
--cluster-generic-cancel-cmd 'scancel' \
--jobs 2
The command above will:
- Tell Snakemake to use the
cluster-generic
plugin - Use the
sbatch
command to submit jobs - Use the
scancel
command to cancel submitted jobs if you stop the execution of the workflow - Set the maximum number of simultaneously running jobs to 2
As you can see, the command to launch the workflow can get quite long. Thankfully, Snakemake allows the usage of configuration profiles to store all of these settings in a file, increasing reproducibility and also simplifying running your workflow. The next section will show how to set up a config file.
Using configuration profiles
Configuration profiles allow you to specify settings regarding the execution of your workflow. Currently, Snakemake supports two types of profiles: global and workflow-specific. In this tutorial we will focus on workflow-specific profiles.
Exercise: Create a workflow profile named slurm_profile
by:
- Creating a directory named
slurm_profile
in the project directory. - Creating a
config.yaml
file insideslurm_profile
.
File structure example
Your file structure should resemble this:
│── config
│ └── config.yaml
│── slurm_profile
│ └── config.yaml
└── workflow
│── Snakefile
│── envs
│ │── tool1.yaml
│ └── tool2.yaml
│── rules
│ │── module1.smk
│ └── module2.smk
└── scripts
│── script1.py
└── script2.R
The config.yaml
file inside the slurm_profile
directory can contain multiple settings related to the execution of the workflow. Below you can find an example of configuration file:
executor: cluster-generic
cluster-generic-submit-cmd: 'sbatch --job-name={rule}_{wildcards} --cpus-per-task={threads}'
jobs: 2
Exercise: What do --job-name={rule}_{wildcards}
and --cpus-per-task={threads}
do?
Answer
These settings allow binding information defined in the workflow to SLURM arguments such as --job-name
or --cpus-per-task
. For example, a rule where threads: 2
, --cpus-per-task={threads}
will become --cpus-per-task=2
, indicating SLURM that the job should have 2 cpus available.
Exercise: Add the required SLURM argument to:
- Log the SLURM output of each job to a file in
slurm_logs/{rule}/{rule}_{wildcards}.log
- Use the thread number specified in each rule during job submission
- Use the
scancel
command to cancel running jobs
SLURM arguments
You can find the SLURM argument to use by running sbatch -h
or by checking this SLURM cheatsheet.
Make sure slurm_logs
exists!
In older SLURM versions, the directory slurm_logs
needed to exist before running the workflow! Therefore, in order to be able to save the logs into a directory, you need to have created the folder before running Snakemake. There are two ways to go about this:
- Manually create the folder before running the workflow
- Adding the directory creation command in
cluster-generic-submit-cmd
. This option ensures that no problems arise if you are running the workflow for the very first time
Answer
Your config file should look like this:
1 2 3 4 5 6 7 8 |
|
mkdir -p slurm_logs/{rule}
: create directory where logs will be saved--job-name={rule}_{wildcards}
: set job name to rule name and wildcards--cpus-per-task={threads}
: set number of CPUs to number of threads defined in the rule--output=slurm_logs/{rule}/{rule}_{wildcards}.log
: set output file for job logs toslurm_logs/{rule}/{rule}_{wildcards}.log
cluster-generic-cancel-cmd: scancel
: set command to cancel jobs toscancel
Passing rule-specific resources to the job scheduler
As shown before, it is possible to specify rule-specific memory usage to have better control over how the workflow uses the available resources with the resources
directive:
1 2 3 4 5 6 7 8 9 10 11 |
|
This will ensure that jobs spawn from rule myrule
never use more than 100 MB of RAM. Note that the jobs will crash if they surpass this limit, so try to account for some wiggle room when you set resources.
Exercise: Update the configuration profile to set the amount of memory per job to the values specified in each rule through the resources
directive. To simplify things, make sure all rule memory requirements are set in megabytes.
Think about the SLURM parameter you need to use and how to set its value
Answer
The SLURM parameter to use is --mem
. Its value should be set to the mem_mb
keyword defined in the resources
directive:
1 2 3 4 5 6 7 8 9 |
|
In addition to remote execution parameters, a configuration profile allows to set anything that can be passed through the command line. This can significantly simplify launching a workflow if there are a lot of arguments to pass when to run it. For example, the extra lines below indicate that we should run a maximum of 2 jobs at a time and to use conda and singularity:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
|
Exercise: Add the jobs
and software-deployment-method
parameters to your configuration profile.
Adapting the workflow to the available resources
Before launching a workflow, it is very important to quantify the resources available in the machine supposed to run it. This information is typically provided by the HPC user guide. In our case, you can use the following commands to know the total number of cores and the amount of memory available in the server:
nproc --all # Number of cores
free -g # Amount of memory in GB in row "Mem" and column "total"
Exercise: Based on the available resources, figure out if you need to update any of the rule resources.
Answer
The cluster has 48 CPUs and 96 GB of memory, so there is no need to change the resource settings. It is also more than enough to run several jobs in parallel without any problem.
Always check the available resources
A very important role of schedulers is to ensure that running jobs do not surpass the total amount of available resources. When a job is asking for more resources than a machine has (e.g. the job asks for 50 CPUs, but the machine only has 20), the HPC (if configured properly) will prevent Snakemake from submitting the job and the workflow will fail. However, if the HPC is not configured properly or if the workflow is running on another type of instance without proper safety checks, Snakemake will be able launch the job but will display the message Waiting for more resources.
to indicate that the job is waiting until enough resources are available for it to run, which can never happen. In this case, Snakemake will get stuck and never manage to launch the job.
Final exercise
Exercise: To conclude, run the workflow in the HPC environment with the following command:
snakemake --profile slurm_profile
You can then see what jobs are being run by using the squeue
command in combination with watch
to check the status of your workflow on regular intervals (here, 10s):
watch -n 10 squeue -u <username>
This will give you information such as the job id, the job status, how long is has been running for, and the reason if the status is PD
(pending).
JOBID | PARTITION | NAME | USER | ST | TIME | NODES | NODELIST(REASON) |
---|---|---|---|---|---|---|---|
91 | local | read_map | user1 | PD | 0:00 | 1 | (Resources) |
90 | local | read_map | user1 | R | 0:30 | 1 | localhost |
Congratulations, you made it to the end! You are now able to create a Snakemake workflow, make it reproducible thanks to Conda and Docker/Apptainer and even run it in an HPC! This is a great time to get yourself a coffee/tea and celebrate!
To make things even better, have a look at some additional concepts and Snakemake’s best practices!