|
| 1 | +# SLURM Operations Guide |
| 2 | + |
| 3 | +## Template Generation |
| 4 | + |
| 5 | +ORB generates templates directly from your `slurm.conf` partition definitions, ensuring that provisioned instances match SLURM's expected resource specs. |
| 6 | + |
| 7 | +### Basic Usage |
| 8 | + |
| 9 | +```bash |
| 10 | +orb templates generate --slurm-conf /etc/slurm/slurm.conf |
| 11 | +``` |
| 12 | + |
| 13 | +### How It Works |
| 14 | + |
| 15 | +1. Parses `NodeName=` lines for CPUs and RealMemory declarations |
| 16 | +2. Parses `PartitionName=` lines to associate partitions with node specs |
| 17 | +3. Maps each partition's CPU/memory requirements to an appropriate AWS instance type |
| 18 | +4. Generates one template per partition with `template_id` = partition name |
| 19 | + |
| 20 | +Example: given this `slurm.conf`: |
| 21 | + |
| 22 | +```ini |
| 23 | +NodeName=compute-[001-050] CPUs=4 RealMemory=16000 State=CLOUD |
| 24 | +NodeName=gpu-[001-010] CPUs=8 RealMemory=32000 State=CLOUD |
| 25 | + |
| 26 | +PartitionName=batch Nodes=compute-[001-050] Default=YES MaxTime=INFINITE State=UP |
| 27 | +PartitionName=gpu Nodes=gpu-[001-010] MaxTime=INFINITE State=UP |
| 28 | +``` |
| 29 | + |
| 30 | +ORB generates: |
| 31 | + |
| 32 | +```json |
| 33 | +[ |
| 34 | + {"template_id": "batch", "machine_types": {"t3.xlarge": 1}, "provider_api": "EC2Fleet"}, |
| 35 | + {"template_id": "gpu", "machine_types": {"t3.2xlarge": 1}, "provider_api": "EC2Fleet"} |
| 36 | +] |
| 37 | +``` |
| 38 | + |
| 39 | +### slurm.conf Path Resolution |
| 40 | + |
| 41 | +The path to `slurm.conf` is resolved in this order: |
| 42 | + |
| 43 | +1. `--slurm-conf /path` CLI flag (highest priority) |
| 44 | +2. `scheduler.slurm.config_path` in config.json |
| 45 | +3. `SLURM_CONF` environment variable |
| 46 | +4. Default paths: `/etc/slurm/slurm.conf`, `/usr/local/etc/slurm.conf`, `$ORB_ROOT_DIR/slurm.conf` |
| 47 | + |
| 48 | +### User-Specified Instance Type Preferences |
| 49 | + |
| 50 | +For partitions where you want to control instance selection (e.g., for cost optimization or spot diversity), add preferences in `config.json`: |
| 51 | + |
| 52 | +```json |
| 53 | +{ |
| 54 | + "scheduler": { |
| 55 | + "type": "slurm", |
| 56 | + "slurm": { |
| 57 | + "config_path": "/etc/slurm/slurm.conf", |
| 58 | + "partitions": { |
| 59 | + "batch": { |
| 60 | + "instance_types": ["t3.medium", "t3.large", "m5.large"], |
| 61 | + "allocation_strategy": "capacityOptimized" |
| 62 | + }, |
| 63 | + "gpu": { |
| 64 | + "instance_types": ["g4dn.xlarge", "g4dn.2xlarge"], |
| 65 | + "allocation_strategy": "lowestPrice" |
| 66 | + } |
| 67 | + } |
| 68 | + } |
| 69 | + } |
| 70 | +} |
| 71 | +``` |
| 72 | + |
| 73 | +Rules: |
| 74 | + |
| 75 | +- `instance_types` is an ordered list (first = highest priority) |
| 76 | +- All listed types should have CPU >= partition's declared CPUs and Memory >= partition's declared RealMemory |
| 77 | +- Partitions without a config entry fall back to auto-mapping from slurm.conf |
| 78 | +- Multiple instance types are used as EC2 Fleet overrides for capacity diversification |
| 79 | + |
| 80 | +Generated template output with preferences: |
| 81 | + |
| 82 | +```json |
| 83 | +{ |
| 84 | + "template_id": "batch", |
| 85 | + "machine_types": {"t3.medium": 1, "t3.large": 1, "m5.large": 1}, |
| 86 | + "allocation_strategy": "capacityOptimized", |
| 87 | + "provider_api": "EC2Fleet", |
| 88 | + "fleet_type": "instant" |
| 89 | +} |
| 90 | +``` |
| 91 | + |
| 92 | +### Validation |
| 93 | + |
| 94 | +At generation time, ORB validates that all specified instance types meet the partition's resource requirements. If validation fails, template generation is refused: |
| 95 | + |
| 96 | +``` |
| 97 | +ERROR: Template 'batch' validation failed: |
| 98 | + - Instance type 't3.micro' (1 vCPU, 1024MB) does not meet partition requirements (CPUs=2, RealMemory=3800) |
| 99 | +Fix: Remove undersized instance types from scheduler.slurm.partitions.batch.instance_types |
| 100 | + or reduce partition resource requirements in slurm.conf |
| 101 | +``` |
| 102 | + |
| 103 | +Use `--force` to bypass validation (for advanced users). |
| 104 | + |
| 105 | +### Overwriting Existing Templates |
| 106 | + |
| 107 | +```bash |
| 108 | +orb templates generate --slurm-conf /etc/slurm/slurm.conf --force |
| 109 | +``` |
| 110 | + |
| 111 | +The `--force` flag both overwrites existing template files and skips instance type validation. |
| 112 | + |
| 113 | +--- |
| 114 | + |
| 115 | +## Per-Instance Tagging (orb:node-name) |
| 116 | + |
| 117 | +### Mechanism |
| 118 | + |
| 119 | +When machines are provisioned with `--nodes` (or via SLURM ResumeProgram): |
| 120 | + |
| 121 | +1. Application layer assigns each instance a SLURM node name from the request |
| 122 | +2. After provisioning, ORB dispatches a `TAG_INSTANCES` operation to the provider |
| 123 | +3. Provider calls `ec2:CreateTags` with `orb:node-name=<node_name>` on each instance |
| 124 | +4. Compute nodes read this tag on boot to start slurmd with the correct identity |
| 125 | + |
| 126 | +### Why It Exists |
| 127 | + |
| 128 | +SLURM requires each compute node to register with the name declared in `slurm.conf`. Since ORB provisions generic instances, the node name assignment happens post-launch via instance tags. The compute AMI's boot script reads the tag and starts `slurmd -N <name>`. |
| 129 | + |
| 130 | +### Provider Requirements |
| 131 | + |
| 132 | +- Must support `TAG_INSTANCES` operation type |
| 133 | +- Must be able to apply per-instance tags after launch |
| 134 | +- AWS: requires `ec2:CreateTags` permission on the controller's IAM role |
| 135 | +- Compute nodes: require `ec2:DescribeTags` permission to read their own tags |
| 136 | + |
| 137 | +### Error Handling |
| 138 | + |
| 139 | +Tag failures are logged at ERROR level but do NOT fail the overall provisioning request (fire-and-forget). The `TAG_INSTANCES` operation returns `error_result` on failure so monitoring can alert on it. |
| 140 | + |
| 141 | +--- |
| 142 | + |
| 143 | +## Caveats and Known Limitations |
| 144 | + |
| 145 | +### Environment |
| 146 | + |
| 147 | +| Issue | Detail | Mitigation | |
| 148 | +|-------|--------|------------| |
| 149 | +| POSIX locale | AL2023 SSM sessions default to `LANG=C` | `PYTHONUTF8=1` is set automatically in hook scripts and CLI entry point | |
| 150 | +| Hook env isolation | systemd env vars don't propagate to ResumeProgram | All config goes in `$ORB_ROOT_DIR/slurm_hooks.env` | |
| 151 | + |
| 152 | +### Networking |
| 153 | + |
| 154 | +| Requirement | Reason | |
| 155 | +|-------------|--------| |
| 156 | +| Security groups: controller ↔ compute all TCP | srun uses ephemeral ports for I/O forwarding | |
| 157 | +| Controller on fixed IP/hostname | slurm.conf `SlurmctldHost` must be reachable from compute | |
| 158 | +| DNS resolution | Compute nodes must resolve controller hostname | |
| 159 | + |
| 160 | +### Timing |
| 161 | + |
| 162 | +| Parameter | Recommended | Reason | |
| 163 | +|-----------|-------------|--------| |
| 164 | +| `ResumeTimeout` | ≥ 300s | Instance boot (~60s) + tag propagation (~5s) + tag read with retries (~30s) + slurmd registration | |
| 165 | +| `SuspendTime` | 30-300s | How long idle nodes wait before termination; lower = faster cost savings | |
| 166 | +| `SuspendTimeout` | 120s | Time for SuspendProgram to complete termination | |
| 167 | +| Tag retry window | 30s (15 retries × 2s) | Built into boot script; covers EC2 tag propagation delay | |
| 168 | + |
| 169 | +### IAM Requirements |
| 170 | + |
| 171 | +**Controller node (slurmctld) IAM role:** |
| 172 | + |
| 173 | +- `ec2:RunInstances`, `ec2:CreateFleet`, `ec2:TerminateInstances` |
| 174 | +- `ec2:CreateLaunchTemplate`, `ec2:CreateLaunchTemplateVersion` |
| 175 | +- `ec2:CreateTags` (for per-instance tagging) |
| 176 | +- `ec2:DescribeInstances`, `ec2:DescribeFleets` |
| 177 | +- `iam:PassRole` (to attach instance profile to compute nodes) |
| 178 | +- `ssm:GetParameter` (if using SSM for AMI resolution) |
| 179 | + |
| 180 | +**Compute node IAM role (instance profile):** |
| 181 | + |
| 182 | +- `ec2:DescribeTags` (read own `orb:node-name` tag) |
| 183 | +- `ssm:GetParameter` (if fetching munge key / slurm.conf from SSM) |
| 184 | + |
| 185 | +The instance profile must be specified in config: |
| 186 | +```json |
| 187 | +"provider_defaults": { |
| 188 | + "aws": { |
| 189 | + "template_defaults": { |
| 190 | + "iam_instance_profile": "orb-compute-instance-profile" |
| 191 | + } |
| 192 | + } |
| 193 | +} |
| 194 | +``` |
| 195 | + |
| 196 | +### SLURM Resource Matching |
| 197 | + |
| 198 | +- SLURM requires all nodes in a partition to match the declared `CPUs` and `RealMemory` |
| 199 | +- Instances with MORE resources than declared are accepted (SLURM uses declared values for scheduling) |
| 200 | +- Instances with FEWER resources will fail `slurmd` registration |
| 201 | +- Generated templates should use a single instance type per partition, OR multiple types that ALL exceed the partition's declared resources |
| 202 | +- `orb templates generate` validates this automatically |
| 203 | + |
| 204 | +### Tag Race Condition |
| 205 | + |
| 206 | +EC2 tags are eventually consistent. Between `CreateTags` and the instance boot script reading the tag, there is a propagation window (typically < 5s, worst case ~30s). |
| 207 | + |
| 208 | +The recommended boot script pattern: |
| 209 | + |
| 210 | +```bash |
| 211 | +# Retry loop for tag propagation |
| 212 | +for i in $(seq 1 15); do |
| 213 | + NODE_NAME=$(aws ec2 describe-tags --region "${REGION}" \ |
| 214 | + --filters "Name=resource-id,Values=${INSTANCE_ID}" "Name=key,Values=orb:node-name" \ |
| 215 | + --query 'Tags[0].Value' --output text 2>/dev/null) |
| 216 | + if [ -n "${NODE_NAME}" ] && [ "${NODE_NAME}" != "None" ]; then |
| 217 | + break |
| 218 | + fi |
| 219 | + sleep 2 |
| 220 | +done |
| 221 | +``` |
| 222 | + |
| 223 | +--- |
| 224 | + |
| 225 | +## Compute AMI Requirements |
| 226 | + |
| 227 | +### Base Packages |
| 228 | + |
| 229 | +- Amazon Linux 2023 (al2023) or Ubuntu 22.04+ |
| 230 | +- SLURM 24.05.5+ compiled with `--with-systemd` for cgroup/v2 support |
| 231 | +- Build dependencies: `systemd-devel`, `dbus-devel`, `munge-devel` |
| 232 | +- Runtime: `munge`, `slurm-slurmd` |
| 233 | + |
| 234 | +### Configuration Files |
| 235 | + |
| 236 | +| File | Purpose | |
| 237 | +|------|---------| |
| 238 | +| `/etc/slurm/slurm.conf` | Must match controller's config (SlurmctldHost, partitions) | |
| 239 | +| `/etc/slurm/cgroup.conf` | cgroup/v2 configuration for resource enforcement | |
| 240 | +| `/etc/munge/munge.key` | Shared authentication key (must match controller) | |
| 241 | + |
| 242 | +### Boot Script |
| 243 | + |
| 244 | +The AMI must include a boot script (via cloud-init or systemd oneshot) that: |
| 245 | + |
| 246 | +1. Reads `orb:node-name` EC2 tag (with retry loop for propagation) |
| 247 | +2. Sets hostname to the SLURM node name |
| 248 | +3. Optionally fetches `slurm.conf` and `munge.key` from SSM Parameter Store |
| 249 | +4. Starts munge |
| 250 | +5. Starts slurmd with `-N <node_name>` |
| 251 | + |
| 252 | +Example minimal boot script: |
| 253 | + |
| 254 | +```bash |
| 255 | +#!/bin/bash |
| 256 | +set -euo pipefail |
| 257 | + |
| 258 | +INSTANCE_ID=$(curl -s http://169.254.169.254/latest/meta-data/instance-id) |
| 259 | +REGION=$(curl -s http://169.254.169.254/latest/meta-data/placement/region) |
| 260 | + |
| 261 | +# Wait for orb:node-name tag |
| 262 | +for i in $(seq 1 15); do |
| 263 | + NODE_NAME=$(aws ec2 describe-tags --region "${REGION}" \ |
| 264 | + --filters "Name=resource-id,Values=${INSTANCE_ID}" "Name=key,Values=orb:node-name" \ |
| 265 | + --query 'Tags[0].Value' --output text 2>/dev/null) |
| 266 | + [ -n "${NODE_NAME}" ] && [ "${NODE_NAME}" != "None" ] && break |
| 267 | + sleep 2 |
| 268 | +done |
| 269 | + |
| 270 | +if [ -z "${NODE_NAME}" ] || [ "${NODE_NAME}" = "None" ]; then |
| 271 | + echo "ERROR: Could not read orb:node-name tag after 30s" >&2 |
| 272 | + exit 1 |
| 273 | +fi |
| 274 | + |
| 275 | +# Set hostname and start SLURM |
| 276 | +hostnamectl set-hostname "${NODE_NAME}" |
| 277 | +systemctl start munge |
| 278 | +slurmd -N "${NODE_NAME}" |
| 279 | +``` |
| 280 | + |
| 281 | +### cgroup.conf Example |
| 282 | + |
| 283 | +```ini |
| 284 | +CgroupAutomount=yes |
| 285 | +ConstrainCores=yes |
| 286 | +ConstrainRAMSpace=yes |
| 287 | +ConstrainSwapSpace=yes |
| 288 | +``` |
| 289 | + |
| 290 | +### Verification Checklist |
| 291 | + |
| 292 | +- [ ] `slurmd -C` reports correct CPUs/RealMemory matching slurm.conf |
| 293 | +- [ ] munge auth succeeds: `munge -n | ssh controller unmunge` |
| 294 | +- [ ] slurmd can reach slurmctld on port 6817 |
| 295 | +- [ ] Instance profile has `ec2:DescribeTags` permission |
| 296 | +- [ ] Boot script handles tag propagation delay (retry loop) |
| 297 | +- [ ] Security groups allow controller ↔ compute traffic (all TCP) |
0 commit comments