All Products
Search
Document Center

Container Service for Kubernetes:Deploy containerized Slurm on ACK

Last Updated:Jun 25, 2026

Container Service for Kubernetes (ACK) provides ack-slurm-operator to deploy and manage Slurm on ACK clusters for high-performance computing (HPC) and large-scale AI/ML workloads.

Slurm

Slurm is an open-source cluster resource management and job scheduling platform for supercomputers and large compute clusters. The following figure shows how Slurm works.

image
  • slurmctld (Slurm Control Daemon): The central controller that monitors resources, schedules jobs, and manages cluster state. Configure a standby slurmctld for high availability.

  • slurmd (Slurm Node Daemon): Runs on each compute node to receive instructions from slurmctld, execute jobs, and report status.

  • slurmdbd (Slurm Database Daemon): An optional component that maintains a centralized database for job history and accounting. Supports data aggregation across multiple Slurm-managed clusters.

  • SlurmCLI: Command-line tools for job management and system monitoring:

    • scontrol: Manages cluster configuration and state.

    • squeue: Queries the status of the job queue.

    • srun: Submits and manages jobs.

    • sbatch: Submits a batch job.

    • sinfo: Displays cluster state and node availability.

Slurm on ACK

The Slurm Operator uses the SlurmCluster CustomResource (CR) to deploy and manage Slurm clusters. An administrator creates a SlurmCluster object, and the operator provisions the control plane components. Mount configuration files through shared storage or a ConfigMap. The following figure shows the architecture of Slurm on ACK.

image

Prerequisites

You need an ACK cluster running Kubernetes 1.22 or later with at least one GPU-accelerated node. See Add GPU-accelerated nodes to a cluster and Update clusters.

Step 1: Install the ack-slurm-operator

  1. Log on to the ACK console. In the left navigation pane, click Marketplace > Marketplace.

  2. On the Marketplace page, search for and click the ack-slurm-operator card. On the ack-slurm-operator details page, click Deploy and follow the prompts to configure the component.

    Select a target cluster. Keep all other parameters at their defaults.

  3. Click OK.

Step 2: Create a SlurmCluster

Create manually

  1. Create a Secret in your ACK cluster for MUNGE-based authentication.

    1. Generate a MUNGE authentication key with OpenSSL.

      openssl rand -base64 512 | tr -d '\r\n'
    2. Create a Secret to store the generated MUNGE key.

      kubectl create secret generic <$MungeKeyName> --from-literal=munge.key=<$MungeKey>
      • Replace <$MungeKeyName> with a custom name for your key, such as mungekey.

      • Replace <$MungeKey> with the key string that you generated in the previous step.

    Configure the SlurmCluster resource to use this Secret for MUNGE-based authentication.

  2. Create the ConfigMap required by the SlurmCluster resource.

    Specifying slurmConfPath in the CR mounts the ConfigMap to pods, ensuring automatic restoration if a pod is recreated.

    The data parameter contains a sample configuration. Generate configurations with the Easy Configurator or Full Configurator.

    Command details

    kubectl create -f - << EOF
    apiVersion: v1
    data:
      slurm.conf: |
        ProctrackType=proctrack/linuxproc
        ReturnToService=1
        SlurmctldPidFile=/var/run/slurmctld.pid
        SlurmctldPort=6817
        SlurmdPidFile=/var/run/slurmd.pid
        SlurmdPort=6818
        SlurmdSpoolDir=/var/spool/slurmd
        SlurmUser=root # test2
        StateSaveLocation=/var/spool/slurmctld
        TaskPlugin=task/none
        InactiveLimit=0
        KillWait=30
        MinJobAge=300
        SlurmctldTimeout=120
        SlurmdTimeout=300
        Waittime=0
        SchedulerType=sched/builtin
        SelectType=select/cons_tres
        JobCompType=jobcomp/none
        JobAcctGatherFrequency=30
        SlurmctldDebug=info
        SlurmctldLogFile=/var/log/slurmctld.log
        SlurmdDebug=info
        SlurmdLogFile=/var/log/slurmd.log
        TreeWidth=65533
        MaxNodeCount=10000
        PartitionName=debug Nodes=ALL Default=YES MaxTime=INFINITE State=UP
    
        ClusterName=slurm-job-demo
        # SlurmctldHost should be set to the name of the SlurmCluster resource with a -0 suffix.
        # For a high-availability deployment, you can use the following configuration.
        # The number of entries depends on the number of slurmctld replicas.
        # SlurmctldHost=slurm-job-demo-0
        # SlurmctldHost=slurm-job-demo-1
        SlurmctldHost=slurm-job-demo-0
    kind: ConfigMap
    metadata:
      name: slurm-test
      namespace: default
    EOF

    Expected output:

    configmap/slurm-test created

    The ConfigMap was created.

  3. Submit the SlurmCluster CR.

    1. Create a file named slurmcluster.yaml and copy the following content into it.

      Note

      This example uses an Ubuntu-based image with CUDA 11.4, Slurm 23.06, and a Cloud Node auto scaling component. To use a custom image, create and upload it yourself.

      YAML example

      # This is a Kubernetes configuration file for deploying a Slurm-managed cluster on Alibaba Cloud ACK by using a Kai Custom Resource Definition (CRD).
      apiVersion: kai.alibabacloud.com/v1
      kind: SlurmCluster
      metadata:
        name: slurm-job-demo # The name of the cluster.
        namespace: default # The namespace where the cluster is deployed.
      spec:
        mungeConfPath: /var/munge # The configuration file path for the MUNGE service.
        slurmConfPath: /var/slurm # The configuration file path for the Slurm service.
        slurmctld: # Specifications for the head node (control plane node). A StatefulSet is created to manage the head node.
          template:
            metadata: {}
            spec:
              containers:
              - image: registry-cn-hangzhou.ack.aliyuncs.com/acs/slurm-cuda:23.06-aliyun-cuda-11.4
                imagePullPolicy: Always
                name: slurmctld
                ports:
                - containerPort: 8080
                  protocol: TCP
                resources:
                  requests:
                    cpu: "1"
                    memory: 1Gi
                volumeMounts:
                - mountPath: /var/slurm # The volume mount for the Slurm configuration file.
                  name: config-slurm-test
                - mountPath: /var/munge # The volume mount for the MUNGE key file.
                  name: secret-slurm-test 
              volumes:
              - configMap:
                  name: slurm-test
                name: config-slurm-test
              - name: secret-slurm-test
                secret:
                  secretName: slurm-test
        workerGroupSpecs: # Specifications for the worker nodes. Two groups are defined here: cpu and cpu1.
        - groupName: cpu
          replicas: 2
          template:
            metadata: {}
            spec:
              containers:
              - env:
                - name: NVIDIA_REQUIRE_CUDA
                image: registry-cn-hangzhou.ack.aliyuncs.com/acs/slurm-cuda:23.06-aliyun-cuda-11.4
                imagePullPolicy: Always
                name: slurmd
                resources:
                  requests:
                    cpu: "1"
                    memory: 1Gi
                volumeMounts:
                - mountPath: /var/slurm
                  name: config-slurm-test
                - mountPath: /var/munge
                  name: secret-slurm-test
              volumes:
              - configMap:
                  name: slurm-test
                name: config-slurm-test
              - name: secret-slurm-test
                secret:
                  secretName: slurm-test
        - groupName: cpu1 # The second worker node group. It is similar to the first one, but you can adjust resources or configurations as needed.
          replicas: 2
          template:
            metadata: {}
            spec:
              containers:
              - env:
                - name: NVIDIA_REQUIRE_CUDA
                image: registry-cn-hangzhou.ack.aliyuncs.com/acs/slurm-cuda:23.06-aliyun-cuda-11.4
                imagePullPolicy: Always
                name: slurmd
                resources:
                  requests:
                    cpu: "1"
                    memory: 1Gi
                securityContext: # The security context is configured to allow the container to run in privileged mode.
                  privileged: true
                volumeMounts:
                - mountPath: /var/slurm
                  name: config-slurm-test
                - mountPath: /var/munge
                  name: secret-slurm-test
              volumes:
              - configMap:
                  name: slurm-test
                name: config-slurm-test
              - name: secret-slurm-test
                secret:
                  secretName: slurm-test

      This CR creates a Slurm-managed cluster with one head node and four worker nodes running as pods. The mungeConfPath and slurmConfPath must match the mount paths in the slurmctld and workerGroupSpecs templates.

    2. Deploy slurmcluster.yaml to the cluster:

      kubectl apply -f slurmcluster.yaml

      Expected output:

      slurmcluster.kai.alibabacloud.com/slurm-job-demo created
    3. Check the SlurmCluster status.

      kubectl get slurmcluster

      Expected output:

      NAME             AVAILABLE WORKERS   STATUS   AGE
      slurm-job-demo   5                   ready    14m

      The Slurm-managed cluster is deployed with all 5 nodes in Ready state.

    4. Verify that the slurm-job-demo cluster pods are running.

      kubectl get pod

      Expected output:

      NAME                                          READY   STATUS      RESTARTS     AGE
      slurm-job-demo-head-x9sgs                     1/1     Running     0            14m
      slurm-job-demo-worker-cpu-0                   1/1     Running     0            14m
      slurm-job-demo-worker-cpu-1                   1/1     Running     0            14m
      slurm-job-demo-worker-cpu1-0                  1/1     Running     0            14m
      slurm-job-demo-worker-cpu1-1                  1/1     Running     0            14m

      The head node and four worker nodes are running.

Create with Helm

The Alibaba Cloud SlurmCluster Helm chart simplifies deployment by creating the required resources, such as RBAC permissions, a ConfigMap, a Secret, and the SlurmCluster CR.

The chart includes these resources:

Resource type

Resource name

Description

ConfigMap

{{ .Values.slurmConfigs.configMapName }}

Created when .Values.slurmConfigs.createConfigsByConfigMap is true. Stores the Slurm configuration file, mounted at .Values.slurmConfigs.slurmConfigPathInPod. This path maps to .Spec.SlurmConfPath in the CR. On startup, the pod copies the file to /etc/slurm/ with correct permissions.

ServiceAccount

{{ .Release.Namespace }}/{{ .Values.clusterName }}

Grants slurmctld permission to modify the SlurmCluster CR for Cloud Node auto scaling.

Role

{{ .Release.Namespace }}/{{ .Values.clusterName }}

Grants slurmctld permission to modify the SlurmCluster CR for Cloud Node auto scaling.

RoleBinding

{{ .Release.Namespace }}/{{ .Values.clusterName }}

Grants slurmctld permission to modify the SlurmCluster CR for Cloud Node auto scaling.

Role

{{ .Values.slurmOperatorNamespace }}/{{ .Values.clusterName }}

Grants slurmctld permission to modify Secrets in the SlurmOperator namespace for token updates in mixed deployments.

RoleBinding

{{ .Values.slurmOperatorNamespace }}/{{ .Values.clusterName }}

Grants slurmctld permission to modify Secrets in the SlurmOperator namespace for token updates in mixed deployments.

Secret

{{ .Values.mungeConfigs.secretName }}

Authenticates communications between Slurm components. Created when .Values.mungeConfigs.createConfigsBySecret is true, with content "munge.key"={{ .Values.mungeConfigs.content }}. Its path maps to .Spec.MungeConfPath and initializes /etc/munge/munge.key on startup.

SlurmCluster

The rendered SlurmCluster CR.

The following table describes the parameters.

Parameter

Sample value

Description

clusterName

""

The cluster name, used to generate resources like Secrets and Roles. Must match ClusterName in the Slurm configuration file.

headNodeConfig

None

Required. Defines the pod configuration for slurmctld.

workerNodesConfig

None

Defines the pod configuration for slurmd.

workerNodesConfig.deleteSelfBeforeSuspend

true

When true, a preStop hook drains the node and marks it as down before suspension.

slurmdbdConfigs

None

Defines the slurmdbd pod configuration. Omit to skip slurmdbd creation.

slurmrestdConfigs

None

Defines the slurmrestd pod configuration. Omit to skip slurmrestd creation.

headNodeConfig.hostNetwork

slurmdbdConfigs.hostNetwork

slurmrestdConfigs.hostNetwork

workerNodesConfig.workerGroups[].hostNetwork

false

Sets the hostNetwork field for the corresponding pod(s).

headNodeConfig.setHostnameAsFQDN

slurmdbdConfigs.setHostnameAsFQDN

slurmrestdConfigs.setHostnameAsFQDN

workerNodesConfig.workerGroups[].setHostnameAsFQDN

false

Sets the setHostnameAsFQDN field for the corresponding pod(s).

headNodeConfig.nodeSelector

slurmdbdConfigs.nodeSelector

slurmrestdConfigs.nodeSelector

workerNodesConfig.workerGroups[].nodeSelector

nodeSelector:
  example: example

Sets the nodeSelector field for the corresponding pod(s).

headNodeConfig.tolerations

slurmdbdConfigs.tolerations

slurmrestdConfigs.tolerations

workerNodesConfig.workerGroups[].tolerations

tolerations:
- key:
  value:
  operator:

Sets the tolerations field for the corresponding pod(s).

headNodeConfig.affinity

slurmdbdConfigs.affinity

slurmrestdConfigs.affinity

workerNodesConfig.workerGroups[].affinity

affinity:
  nodeAffinity:
    requiredDuringSchedulingIgnoredDuringExecution:
      nodeSelectorTerms:
      - matchExpressions:
        - key: topology.kubernetes.io/zone
          operator: In
          values:
          - zone-a
    preferredDuringSchedulingIgnoredDuringExecution:
    - weight: 1
      preference:
        matchExpressions:
        - key: another-node-label-key
          operator: In
          values:
          - another-node-label-value

Sets the affinity field for the corresponding pod(s).

headNodeConfig.resources

slurmdbdConfigs.resources

slurmrestdConfigs.resources

workerNodesConfig.workerGroups[].resources

resources:
  requests:
    cpu: 1
  limits:
    cpu: 1

Resources for the main container. In worker pods, the main container's resource limits determine the Slurm node capacity.

headNodeConfig.image

slurmdbdConfigs.image

slurmrestdConfigs.image

workerNodesConfig.workerGroups[].image

"registry-cn-hangzhou.ack.aliyuncs.com/acs/slurm:23.06-1.6-aliyun-49259f59"

The container image for the main container. For custom images, see ai-models-on-ack/framework/slurm/building-slurm-image at main · AliyunContainerService/ai-models-on-ack (github.com).

headNodeConfig.imagePullSecrets

slurmdbdConfigs.imagePullSecrets

slurmrestdConfigs.imagePullSecrets

workerNodesConfig.workerGroups[].imagePullSecrets

imagePullSecrets:
- name: example

Sets the image pull secret for the corresponding pod(s).

headNodeConfig.podSecurityContext

slurmdbdConfigs.podSecurityContext

slurmrestdConfigs.podSecurityContext

workerNodesConfig.workerGroups[].podSecurityContext

podSecurityContext:
  runAsUser: 1000
  runAsGroup: 3000
  fsGroup: 2000
  supplementalGroups: [4000]

Sets the podSecurityContext for the corresponding pod(s).

headNodeConfig.securityContext

slurmdbdConfigs.securityContext

slurmrestdConfigs.securityContext

workerNodesConfig.workerGroups[].securityContext

securityContext:
  allowPrivilegeEscalation: false

Sets the security context for the main container of the corresponding pod(s).

headNodeConfig.volumeMounts

slurmdbdConfigs.volumeMounts

slurmrestdConfigs.volumeMounts

workerNodesConfig.workerGroups[].volumeMounts

None

Sets the volume mounts for the main container of the corresponding pod(s).

headNodeConfig.volumes

slurmdbdConfigs.volumes

slurmrestdConfigs.volumes

workerNodesConfig.workerGroups[].volumes

None

Sets the volumes for the corresponding pod(s).

slurmConfigs.slurmConfigPathInPod

""

Mount path for Slurm configurations in the pod. Declare the slurm.conf location when mounting via a volume. The startup command copies files from this path to /etc/slurm/ with correct permissions.

slurmConfigs.createConfigsByConfigMap

true

Whether to auto-create a ConfigMap for Slurm configuration files.

slurmConfigs.configMapName

""

The name of the ConfigMap that stores the Slurm configuration files.

slurmConfigs.filesInConfigMap

""

Configuration file content when the ConfigMap is auto-created.

mungeConfigs.mungeConfigPathInPod

None

Mount path for MUNGE configurations in the pod. Declare the munge.key location when mounting via a volume. The startup command copies the file from this path to /etc/munge/ with correct permissions.

mungeConfigs.createConfigsBySecret

None

Whether to auto-create a Secret for the MUNGE configuration file.

mungeConfigs.secretName

None

The name of the Secret when it is automatically created.

mungeConfigs.content

None

MUNGE configuration content when the Secret is auto-created.

See the Slurm System Configuration Tool for slurmConfigs.filesInConfigMap options.

Important

If you modify slurmConfigs.filesInConfigMap after pods start, recreate the pods for changes to take effect. Confirm file content before installation.

To install the chart:

  1. Add the Alibaba Cloud chart repository to your local Helm client.

    helm repo add aliyun https://aliacs-app-catalog.oss-cn-hangzhou.aliyuncs.com/charts-incubator/

    This adds access to Alibaba Cloud charts, including the Slurm chart.

  2. Pull and extract the Helm chart.

    helm pull aliyun/ack-slurm-cluster --untar=true

    This creates an ack-slurm-cluster directory containing the chart files and templates.

  3. Modify the chart parameters in the values.yaml file.

    Edit values.yaml to customize the Slurm configuration, resource requests, and storage options.

    cd ack-slurm-cluster
    vi values.yaml
  4. Install the chart.

    cd ..
    helm install my-slurm-cluster ack-slurm-cluster # You can replace my-slurm-cluster with a custom release name.

    This deploys the Slurm-managed cluster.

  5. Verify the deployment

    Use kubectl to verify that the Slurm cluster pods are running.

    kubectl get pods -l app.kubernetes.io/name=slurm-cluster

Step 3: Log on to the Slurm cluster

For Kubernetes cluster administrators

Kubernetes cluster administrators can use kubectl to log on to any pod in the Slurm-managed cluster, which grants root permissions within Slurm.

Log on to any pod of the Slurm-managed cluster.

# Replace slurm-job-demo-xxxxx with the name of a specific pod in your cluster.
kubectl exec -it slurm-job-demo-xxxxx -- bash

For regular Slurm cluster users

Users without kubectl exec permissions must log on to the Slurm-managed cluster through SSH.

  • A Service external IP provides persistent access through a load balancer, accessible from anywhere within your internal network.

  • Port forwarding is a temporary solution for debugging, as kubectl port-forward must run continuously.

Use an external IP

  1. Create a LoadBalancer Service to expose internal services. See Use an existing Server Load Balancer instance to expose an application or Expose an application by using an automatically created LoadBalancer Service.

    • Use an internal-facing Classic Load Balancer (CLB) instance.

    • Add the kai.alibabacloud.com/slurm-cluster: ack-slurm-cluster-1 and kai.alibabacloud.com/slurm-node-type: head labels to route requests to the correct pod.

  2. Obtain the external IP address of the LoadBalancer Service.

    kubectl get svc
  3. Log on to the head pod with SSH.

    # Replace $YOURUSER with the username in the pod and $EXTERNAL_IP with the external IP address obtained from the Service.
    ssh $YOURUSER@$EXTERNAL_IP

Use port forwarding

Warning

Port forwarding requires saving the KubeConfig file locally, which poses a security risk. Do not use in production.

  1. Start port forwarding on your local machine. This maps local port $LOCALPORT to port 22 (SSH) of the slurmctld pod.

    # Replace $NAMESPACE, $CLUSTERNAME, and $LOCALPORT with their actual values.
    kubectl port-forward -n $NAMESPACE svc/$CLUSTERNAME $LOCALPORT:22
  2. While port-forward is running, log on to the cluster and submit jobs.

    # $YOURUSER is the username to use when logging on to the pod.
    ssh -p $LOCALPORT $YOURUSER@localhost

Step 4: Use SlurmCluster

Configure user synchronization, shared logging, and auto scaling for your SlurmCluster.

User synchronization across nodes

Slurm lacks built-in centralized user authentication. Jobs submitted with sbatch fail if the user account does not exist on the target node. Configure LDAP as a centralized authentication backend to resolve this.

  1. Create a file named ldap.yaml with the following content to deploy a basic LDAP service.

    The ldap.yaml file defines a pod for the LDAP service and a Service to expose it.

    LDAP backend Pod and Service

    ---
    apiVersion: apps/v1
    kind: Deployment
    metadata:
      namespace: default
      name: ldap
      labels:
        app: ldap
    spec:
      selector:
        matchLabels:
          app: ldap
      revisionHistoryLimit: 10
      template:
        metadata:
          labels:
            app: ldap
        spec:
          securityContext:
            seLinuxOptions: {}
          imagePullSecrets: []
          restartPolicy: Always
          initContainers: []
          containers:
            - image: 'osixia/openldap:1.4.0'
              imagePullPolicy: IfNotPresent
              name: ldap
              volumeMounts:
                - name: openldap-data
                  mountPath: /var/lib/ldap
                  subPath: data
                - name: openldap-data
                  mountPath: /etc/ldap/slapd.d
                  subPath: config
                - name: openldap-data
                  mountPath: /container/service/slapd/assets/certs
                  subPath: certs
                - name: secret-volume
                  mountPath: /container/environment/01-custom
                - name: container-run
                  mountPath: /container/run
              args:
                - '--copy-service'
              resources:
                limits:
                requests:
              env: []
              readinessProbe:
                tcpSocket:
                  port: openldap
                initialDelaySeconds: 20
                timeoutSeconds: 1
                periodSeconds: 10
                successThreshold: 1
                failureThreshold: 10
              livenessProbe:
                tcpSocket:
                  port: openldap
                initialDelaySeconds: 20
                timeoutSeconds: 1
                periodSeconds: 10
                successThreshold: 1
                failureThreshold: 10
              lifecycle: {}
              ports:
                - name: openldap
                  containerPort: 389
                  protocol: TCP
                - name: ssl-ldap-port
                  containerPort: 636
                  protocol: TCP
          volumes:
            - name: openldap-data
              emptyDir: {}
            - name: secret-volume
              secret:
                secretName: ldap-secret
                defaultMode: 420
                items: []
            - name: container-run
              emptyDir: {}
          dnsPolicy: ClusterFirst
          dnsConfig: {}
          terminationGracePeriodSeconds: 30
      progressDeadlineSeconds: 600
      strategy:
        type: RollingUpdate
        rollingUpdate:
          maxUnavailable: 25%
          maxSurge: 25%
      replicas: 1
    ---
    apiVersion: v1
    kind: Service
    metadata:
      annotations: {}
      labels:
        app: ldap
      name: ldap-service
      namespace: default
    spec:
      ports:
        - name: openldap
          port: 389
          protocol: TCP
          targetPort: openldap
        - name: ssl-ldap-port
          port: 636
          protocol: TCP
          targetPort: ssl-ldap-port
      selector:
        app: ldap
      sessionAffinity: None
      type: ClusterIP
    ---
    metadata:
      name: ldap-secret
      namespace: default
      annotations: {}
    data:
      env.startup.yaml: >-
        IyBUaGlzIGlzIHRoZSBkZWZhdWx0IGltYWdlIHN0YXJ0dXAgY29uZmlndXJhdGlvbiBmaWxlCiMgdGhpcyBmaWxlIGRlZmluZSBlbnZpcm9ubWVudCB2YXJpYWJsZXMgdXNlZCBkdXJpbmcgdGhlIGNvbnRhaW5lciAqKmZpcnN0IHN0YXJ0KiogaW4gKipzdGFydHVwIGZpbGVzKiouCgojIFRoaXMgZmlsZSBpcyBkZWxldGVkIHJpZ2h0IGFmdGVyIHN0YXJ0dXAgZmlsZXMgYXJlIHByb2Nlc3NlZCBmb3IgdGhlIGZpcnN0IHRpbWUsCiMgYWZ0ZXIgdGhhdCBhbGwgdGhlc2UgdmFsdWVzIHdpbGwgbm90IGJlIGF2YWlsYWJsZSBpbiB0aGUgY29udGFpbmVyIGVudmlyb25tZW50LgojIFRoaXMgaGVscHMgdG8ga2VlcCB5b3VyIGNvbnRhaW5lciBjb25maWd1cmF0aW9uIHNlY3JldC4KIyBtb3JlIGluZm9ybWF0aW9uIDogaHR0cHM6Ly9naXRodWIuY29tL29zaXhpYS9kb2NrZXItbGlnaHQtYmFzZWltYWdlCgojIFJlcXVpcmVkIGFuZCB1c2VkIGZvciBuZXcgbGRhcCBzZXJ2ZXIgb25seQpMREFQX09SR0FOSVNBVElPTjogRXhhbXBsZSBJbmMuCkxEQVBfRE9NQUlOOiBleGFtcGxlLm9yZwpMREFQX0JBU0VfRE46ICNpZiBlbXB0eSBhdXRvbWF0aWNhbGx5IHNldCBmcm9tIExEQVBfRE9NQUlOCgpMREFQX0FETUlOX1BBU1NXT1JEOiBhZG1pbgpMREFQX0NPTkZJR19QQVNTV09SRDogY29uZmlnCgpMREFQX1JFQURPTkxZX1VTRVI6IGZhbHNlCkxEQVBfUkVBRE9OTFlfVVNFUl9VU0VSTkFNRTogcmVhZG9ubHkKTERBUF9SRUFET05MWV9VU0VSX1BBU1NXT1JEOiByZWFkb25seQoKIyBCYWNrZW5kCkxEQVBfQkFDS0VORDogaGRiCgojIFRscwpMREFQX1RMUzogdHJ1ZQpMREFQX1RMU19DUlRfRklMRU5BTUU6IGxkYXAuY3J0CkxEQVBfVExTX0tFWV9GSUxFTkFNRTogbGRhcC5rZXkKTERBUF9UTFNfQ0FfQ1JUX0ZJTEVOQU1FOiBjYS5jcnQKCkxEQVBfVExTX0VORk9SQ0U6IGZhbHNlCkxEQVBfVExTX0NJUEhFUl9TVUlURTogU0VDVVJFMjU2Oi1WRVJTLVNTTDMuMApMREFQX1RMU19QUk9UT0NPTF9NSU46IDMuMQpMREFQX1RMU19WRVJJRllfQ0xJRU5UOiBkZW1hbmQKCiMgUmVwbGljYXRpb24KTERBUF9SRVBMSUNBVElPTjogZmFsc2UKIyB2YXJpYWJsZXMgJExEQVBfQkFTRV9ETiwgJExEQVBfQURNSU5fUEFTU1dPUkQsICRMREFQX0NPTkZJR19QQVNTV09SRAojIGFyZSBhdXRvbWF0aWNhbHkgcmVwbGFjZWQgYXQgcnVuIHRpbWUKCiMgaWYgeW91IHdhbnQgdG8gYWRkIHJlcGxpY2F0aW9uIHRvIGFuIGV4aXN0aW5nIGxkYXAKIyBhZGFwdCBMREFQX1JFUExJQ0FUSU9OX0NPTkZJR19TWU5DUFJPViBhbmQgTERBUF9SRVBMSUNBVElPTl9EQl9TWU5DUFJPViB0byB5b3VyIGNvbmZpZ3VyYXRpb24KIyBhdm9pZCB1c2luZyAkTERBUF9CQVNFX0ROLCAkTERBUF9BRE1JTl9QQVNTV09SRCBhbmQgJExEQVBfQ09ORklHX1BBU1NXT1JEIHZhcmlhYmxlcwpMREFQX1JFUExJQ0FUSU9OX0NPTkZJR19TWU5DUFJPVjogYmluZGRuPSJjbj1hZG1pbixjbj1jb25maWciIGJpbmRtZXRob2Q9c2ltcGxlIGNyZWRlbnRpYWxzPSRMREFQX0NPTkZJR19QQVNTV09SRCBzZWFyY2hiYXNlPSJjbj1jb25maWciIHR5cGU9cmVmcmVzaEFuZFBlcnNpc3QgcmV0cnk9IjYwICsiIHRpbWVvdXQ9MSBzdGFydHRscz1jcml0aWNhbApMREFQX1JFUExJQ0FUSU9OX0RCX1NZTkNQUk9WOiBiaW5kZG49ImNuPWFkbWluLCRMREFQX0JBU0VfRE4iIGJpbmRtZXRob2Q9c2ltcGxlIGNyZWRlbnRpYWxzPSRMREFQX0FETUlOX1BBU1NXT1JEIHNlYXJjaGJhc2U9IiRMREFQX0JBU0VfRE4iIHR5cGU9cmVmcmVzaEFuZFBlcnNpc3QgaW50ZXJ2YWw9MDA6MDA6MDA6MTAgcmV0cnk9IjYwICsiIHRpbWVvdXQ9MSBzdGFydHRscz1jcml0aWNhbApMREFQX1JFUExJQ0FUSU9OX0hPU1RTOgogIC0gbGRhcDovL2xkYXAuZXhhbXBsZS5vcmcgIyBUaGUgb3JkZXIgbXVzdCBiZSB0aGUgc2FtZSBvbiBhbGwgbGRhcCBzZXJ2ZXJzCiAgLSBsZGFwOi8vbGRhcDIuZXhhbXBsZS5vcmcKCgojIFJlbW92ZSBjb25maWcgYWZ0ZXIgc2V0dXAKTERBUF9SRU1PVkVfQ09ORklHX0FGVEVSX1NFVFVQOiB0cnVlCgojIGNmc3NsIGVudmlyb25tZW50IHZhcmlhYmxlcyBwcmVmaXgKTERBUF9DRlNTTF9QUkVGSVg6IGxkYXAgIyBjZnNzbC1oZWxwZXIgZmlyc3Qgc2VhcmNoIGNvbmZpZyBmcm9tIExEQVBfQ0ZTU0xfKiB2YXJpYWJsZXMsIGJlZm9yZSBDRlNTTF8qIHZhcmlhYmxlcy4K
      env.yaml: >-
        IyBUaGlzIGlzIHRoZSBkZWZhdWx0IGltYWdlIGNvbmZpZ3VyYXRpb24gZmlsZQojIFRoZXNlIHZhbHVlcyB3aWxsIHBlcnNpc3RzIGluIGNvbnRhaW5lciBlbnZpcm9ubWVudC4KCiPCoEFsbCBlbnZpcm9ubWVudCB2YXJpYWJsZXMgdXNlZCBhZnRlciB0aGUgY29udGFpbmVyIGZpcnN0IHN0YXJ0CiMgbXVzdCBiZSBkZWZpbmVkIGhlcmUuCiMgbW9yZSBpbmZvcm1hdGlvbiA6IGh0dHBzOi8vZ2l0aHViLmNvbS9vc2l4aWEvZG9ja2VyLWxpZ2h0LWJhc2VpbWFnZQoKIyBHZW5lcmFsIGNvbnRhaW5lciBjb25maWd1cmF0aW9uCiMgc2VlIHRhYmxlIDUuMSBpbiBodHRwOi8vd3d3Lm9wZW5sZGFwLm9yZy9kb2MvYWRtaW4yNC9zbGFwZGNvbmYyLmh0bWwgZm9yIHRoZSBhdmFpbGFibGUgbG9nIGxldmVscy4KTERBUF9MT0dfTEVWRUw6IDI1Ngo=
    type: Opaque
    kind: Secret
    apiVersion: v1
    
  2. Deploy the LDAP backend service:

    kubectl apply -f ldap.yaml

    Expected output:

    deployment.apps/ldap created
    service/ldap-service created
    secret/ldap-secret created
  3. (Optional) Deploy a frontend interface for improved management. Create a file named phpldapadmin.yaml with the following content.

    LDAP frontend Pod and Service

    ---
    apiVersion: apps/v1
    kind: Deployment
    metadata:
      namespace: default
      name: phpldapadmin
      labels:
        io.kompose.service: phpldapadmin
    spec:
      selector:
        matchLabels:
          io.kompose.service: phpldapadmin
      revisionHistoryLimit: 10
      template:
        metadata:
          labels:
            io.kompose.service: phpldapadmin
        spec:
          securityContext:
            seLinuxOptions: {}
          imagePullSecrets: []
          restartPolicy: Always
          initContainers: []
          containers:
            - image: 'osixia/phpldapadmin:0.9.0'
              imagePullPolicy: Always
              name: phpldapadmin
              volumeMounts: []
              resources:
                limits:
                requests:
              env:
                - name: PHPLDAPADMIN_HTTPS
                  value: 'false'
                - name: PHPLDAPADMIN_LDAP_HOSTS
                  value: ldap-service
              lifecycle: {}
              ports:
                - containerPort: 80
                  protocol: TCP
          volumes: []
          dnsPolicy: ClusterFirst
          dnsConfig: {}
          terminationGracePeriodSeconds: 30
      progressDeadlineSeconds: 600
      strategy:
        type: RollingUpdate
        rollingUpdate:
          maxUnavailable: 25%
          maxSurge: 25%
      replicas: 1
    ---
    apiVersion: v1
    kind: Service
    metadata:
      namespace: default
      name: phpldapadmin
      annotations:
        k8s.kuboard.cn/workload: phpldapadmin
      labels:
        io.kompose.service: phpldapadmin
    spec:
      selector:
        io.kompose.service: phpldapadmin
      type: ClusterIP
      ports:
        - port: 8080
          targetPort: 80
          protocol: TCP
          name: '8080'
          nodePort: 0
      sessionAffinity: None

    Deploy the LDAP frontend service:

    kubectl apply -f phpldapadmin.yaml
  4. Log on to a SlurmCluster pod as described in Step 3, then install the LDAP client package:

    apt update
    apt install libnss-ldapd
  5. After installing libnss-ldapd, configure the network authentication service from within the pod.

    1. Install Vim for editing scripts and files:

      apt update
      apt install vim
    2. Configure the LDAP client in /etc/ldap/ldap.conf:

      ...
      BASE	dc=example,dc=org # Replace this with the base DN of your LDAP directory.
      URI	ldap://ldap-service # Replace this with the address of your LDAP server.
      ...
    3. Define the LDAP server connection in /etc/nslcd.conf:

      ...
      uri ldap://ldap-service # Replace this with the address of your LDAP server.
      base dc=example,dc=org # Set this based on your LDAP directory structure.
      ...
      tls_cacertfile /etc/ssl/certs/ca-certificates.crt # Specifies the path to the CA certificate file used to verify the LDAP server certificate.
      ...

Log sharing and access

By default, sbatch job logs are stored on the node where the job runs. Create a NAS file system to centralize log storage across all nodes.

  1. Create a NAS file system to store and share logs from all nodes.

  2. Log on to the ACK console and create a PV and PVC for the NAS file system. See Use a statically provisioned NAS volume.

  3. Modify the SlurmCluster CR.

    Add volumeMounts and volumes to headGroupSpec and each workerGroupSpec to mount the PVC to /home:

    headGroupSpec:
    ...
    # Add a volume mount for /home.
      volumeMounts:
      - mountPath: /home
        name: test  # The name of the volume that references the PVC.
      volumes:
    # Add the PVC definition.
      - name: test  # This must match the name in volumeMounts.
        persistentVolumeClaim:
          claimName: test  # Replace this with the name of your PVC.
    ...
    workerGroupSpecs:
      # ... Repeat the preceding volume and volumeMounts configuration for each workerGroupSpec.
  4. Apply the changes to the SlurmCluster CR:

    Important

    If the SlurmCluster CR fails to deploy, delete it with kubectl delete slurmcluster slurm-job-demo and redeploy.

    kubectl  apply -f slurmcluster.yaml

    All worker nodes now share the same file system.

Auto scaling

The default Slurm image includes slurm-resume.sh, slurm-suspend.sh, and slurmctld-copilot in the root directory. These scripts interact with slurmctld to manage cluster scaling.

Slurm auto scaling with cloud nodes

  • local node: A physical compute node that is directly connected to the cluster manager.

  • cloud node: A logical node that represents a VM instance that can be created and terminated on demand by a cloud provider.

image

Auto scaling in Slurm on ACK

image

Procedure

  1. Configure permissions for auto scaling. If you installed with Helm, skip this step — permissions are already created.

    Auto scaling requires the head pod to access and update the SlurmCluster CR. Grant permissions with RBAC.

    Create the ServiceAccount, Role, and RoleBinding required by slurmctld. For a SlurmCluster named slurm-job-demo in the default namespace, save the following to rbac.yaml:

    apiVersion: v1
    kind: ServiceAccount
    metadata:
      name: slurm-job-demo
    ---
    apiVersion: rbac.authorization.k8s.io/v1
    kind: Role
    metadata:
      name: slurm-job-demo
    rules:
    - apiGroups: ["kai.alibabacloud.com"]
      resources: ["slurmclusters"]
      verbs: ["get", "watch", "list", "update", "patch"]
      resourceNames: ["slurm-job-demo"]
    ---
    apiVersion: rbac.authorization.k8s.io/v1
    kind: RoleBinding
    metadata:
      name: slurm-job-demo
    subjects:
    - kind: ServiceAccount
      name: slurm-job-demo
    roleRef:
      kind: Role
      name: slurm-job-demo
      apiGroup: rbac.authorization.k8s.io

    Apply the manifest with kubectl apply -f rbac.yaml.

    Assign permissions to the slurmctld pod. Run kubectl edit slurmcluster slurm-job-demo and set .spec.slurmctld.template.spec.serviceAccountName to the ServiceAccount you created.

    apiVersion: kai.alibabacloud.com/v1
    kind: SlurmCluster
    ...
    spec:
      slurmctld:
        template:
          spec:
            serviceAccountName: slurm-job-demo
    ...

    Recreate the slurmctld StatefulSet to apply changes. View it with kubectl get sts slurm-job-demo and delete it with kubectl delete sts slurm-job-demo. The operator recreates the StatefulSet with the new configuration.

  2. Configure auto scaling in the /etc/slurm/slurm.conf file.

    Shared file system

    # The following settings are required when using cloud nodes. 
    # SuspendProgram and ResumeProgram are custom-developed features.
    SuspendTimeout=600
    ResumeTimeout=600
    # The interval after which an idle node is automatically suspended. 
    SuspendTime=600
    # The number of nodes that can be scaled out or in per minute. 
    ResumeRate=1
    SuspendRate=1
    # The NodeName format must be ${cluster_name}-worker-${group_name}-. You must declare the node's resources in this line.
    # Otherwise, slurmctld treats the node as having only 1 CPU core.
    # To avoid resource waste, ensure that the resources declared here match the resources specified in the workerGroup.
    NodeName=slurm-job-demo-worker-cpu-[0-10] Feature=cloud State=CLOUD
    # The following settings are fixed and should not be changed.
    CommunicationParameters=NoAddrCache
    ReconfigFlags=KeepPowerSaveSettings
    SuspendProgram="/slurm-suspend.sh"
    ResumeProgram="/slurm-resume.sh"

    ConfigMap

    If slurm.conf is in the slurm-config ConfigMap, run kubectl edit slurm-config to add the following:

    slurm.conf:
    ...
      # The following settings are required when using cloud nodes. 
      # SuspendProgram and ResumeProgram are custom-developed features.
      SuspendTimeout=600
      ResumeTimeout=600
      # The interval after which an idle node is automatically suspended. 
      SuspendTime=600
      # The number of nodes that can be scaled out or in per minute. 
      ResumeRate=1
      SuspendRate=1
      # The NodeName format must be ${cluster_name}-worker-${group_name}-. You must declare the node's resources in this line.
      # Otherwise, slurmctld treats the node as having only 1 CPU core.
      # To avoid resource waste, ensure that the resources declared here match the resources specified in the workerGroup.
      NodeName=slurm-job-demo-worker-cpu-[0-10] Feature=cloud State=CLOUD
      # The following settings are fixed and should not be changed.
      CommunicationParameters=NoAddrCache
      ReconfigFlags=KeepPowerSaveSettings
      SuspendProgram="/slurm-suspend.sh"
      ResumeProgram="/slurm-resume.sh"

    Helm

    1. Add the following to values.yaml:

      slurm.conf:
      ...
        # The following settings are required when using cloud nodes. 
        # SuspendProgram and ResumeProgram are custom-developed features.
        SuspendTimeout=600
        ResumeTimeout=600
        # The interval after which an idle node is automatically suspended. 
        SuspendTime=600
        # The number of nodes that can be scaled out or in per minute. 
        ResumeRate=1
        SuspendRate=1
        # The NodeName format must be ${cluster_name}-worker-${group_name}-. You must declare the node's resources in this line.
        # Otherwise, slurmctld treats the node as having only 1 CPU core.
        # To avoid resource waste, ensure that the resources declared here match the resources specified in the workerGroup.
        NodeName=slurm-job-demo-worker-cpu-[0-10] Feature=cloud State=CLOUD
        # The following settings are fixed and should not be changed.
        CommunicationParameters=NoAddrCache
        ReconfigFlags=KeepPowerSaveSettings
        SuspendProgram="/slurm-suspend.sh"
        ResumeProgram="/slurm-resume.sh"
    2. Run helm upgrade to update the Slurm configuration.

  3. Apply the new configuration.

    For a SlurmCluster named slurm-job-demo, run kubectl delete sts slurm-job-demo to apply the new configuration to the slurmctld pod.

  4. Set the worker node replica count to 0 to observe auto scaling from the beginning.

    Manual

    For a SlurmCluster named slurm-job-demo, run kubectl edit slurmcluster slurm-job-demo and set workerCount to 0 in the workerGroup.

    Helm

    In values.yaml, set .Values.workerGroup[].workerCount to 0, then run helm upgrade slurm-job-demo . to apply.

  5. Submit an sbatch job.

    1. Create a shell script:

      cat << EOF > cloudnodedemo.sh

      Enter the following:

      #!/bin/bash
      srun hostname
      EOF
    2. Verify the script content:

      cat cloudnodedemo.sh

      Expected output:

        #!/bin/bash
        srun hostname

      The script output is correct.

    3. Submit the script to the SlurmCluster.

      sbatch cloudnodedemo.sh

      Expected output:

      Submitted batch job 1

      The job was submitted and assigned a job ID.

  6. View the cluster scaling status.

    1. View the SlurmCluster scaling logs.

      cat /var/log/slurm-resume.log

      Expected output:

       namespace: default cluster: slurm-demo
        resume called, args [slurm-demo-worker-cpu-0]
        slurm cluster metadata: default slurm-demo
        get SlurmCluster CR slurm-demo succeed
        hostlists: [slurm-demo-worker-cpu-0]
        resume node slurm-demo-worker-cpu-0
        resume worker -cpu-0
        resume node -cpu-0 end

      The SlurmCluster automatically added a compute node to meet job demand.

    2. View the pod status in the cluster.

      kubectl get pod

      Expected output:

      NAME                                          READY   STATUS    RESTARTS        AGE
      slurm-demo-head-9hn67                         1/1     Running   0               21m
      slurm-demo-worker-cpu-0                       1/1     Running   0               43s

      slurm-demo-worker-cpu-0 is the new pod, indicating the job triggered scale-out.

    3. View the cluster node information.

      sinfo

      Expected output:

      PARTITION AVAIL  TIMELIMIT  NODES  STATE NODELIST
      debug*       up   infinite      10  idle~ slurm-job-demo-worker-cpu-[2-10]
      debug*       up   infinite      1   idle slurm-job-demo-worker-cpu-[0-1]

      slurm-demo-worker-cpu-0 is the newly launched node. 10 additional nodes (1-10) are available for scaling out.

    4. View the completed job information.

      scontrol show job 1

      Expected output:

      JobId=1 JobName=cloudnodedemo.sh
         UserId=root(0) GroupId=root(0) MCS_label=N/A
         Priority=4294901757 Nice=0 Account=(null) QOS=(null)
         JobState=COMPLETED Reason=None Dependency=(null)
         Requeue=1 Restarts=0 BatchFlag=1 Reboot=0 ExitCode=0:0
         RunTime=00:00:00 TimeLimit=UNLIMITED TimeMin=N/A
         SubmitTime=2024-05-28T11:37:36 EligibleTime=2024-05-28T11:37:36
         AccrueTime=2024-05-28T11:37:36
         StartTime=2024-05-28T11:37:36 EndTime=2024-05-28T11:37:36 Deadline=N/A
         SuspendTime=None SecsPreSuspend=0 LastSchedEval=2024-05-28T11:37:36 Scheduler=Main
         Partition=debug AllocNode:Sid=slurm-job-demo:93
         ReqNodeList=(null) ExcNodeList=(null)
         NodeList=slurm-job-demo-worker-cpu-0
         BatchHost=slurm-job-demo-worker-cpu-0
         NumNodes=1 NumCPUs=1 NumTasks=1 CPUs/Task=1 ReqB:S:C:T=0:0:*:*
         ReqTRES=cpu=1,mem=1M,node=1,billing=1
         AllocTRES=cpu=1,mem=1M,node=1,billing=1
         Socks/Node=* NtasksPerN:B:S:C=0:0:*:* CoreSpec=*
         MinCPUsNode=1 MinMemoryNode=0 MinTmpDiskNode=0
         Features=(null) DelayBoot=00:00:00
         OverSubscribe=OK Contiguous=0 Licenses=(null) Network=(null)
         Command=//cloudnodedemo.sh
         WorkDir=/
         StdErr=//slurm-1.out
         StdIn=/dev/null
         StdOut=//slurm-1.out
         Power=

      NodeList=slurm-demo-worker-cpu-0 indicates the job ran on the newly added node.

    5. After a while, view the node scale-in information.

      sinfo

      Expected output:

      PARTITION AVAIL  TIMELIMIT  NODES  STATE NODELIST
      debug*       up   infinite     11  idle~ slurm-demo-worker-cpu-[0-10]

      Nodes 0-10 are available again, indicating automatic scale-in is complete.