Slurm Metric을 Amazon Managed Service for Prometheus(AMP)로 수집하고 Grafana로 시각화해 봤습니다

Slurm Metric을 Amazon Managed Service for Prometheus(AMP)로 수집하고 Grafana로 시각화해 봤습니다

Slurm Metric을 Amazon Managed Service for Prometheus(AMP)로 수집하고 Grafana로 시각화해 봤습니다.
2026.09.02

안녕하세요 클래스메소드 김재욱(Kim Jaewook) 입니다. 이번에는 Slurm Metric을 Amazon Managed Service for Prometheus(AMP)로 수집하고 Grafana로 시각화해 봤습니다.

이번 작업의 목적은 운영 환경을 완성하는 것이 아니라 Slurm → AMP → Grafana까지 Metric이 정상적으로 전달되고 시각화되는지 End-to-End로 검증하는 것입니다.

※ 블로그에 적힌 arn과 ip 주소는 해당 블로그 작성 이후 삭제될 리소스의 정보입니다.

PCS 설정

먼저 PCS를 생성할 때 추가 스케줄러를 설정합니다.

pcs-1

파라미터 설정 파일
CommunicationParameters enable_http slurm.conf
MetricsType metrics/openmetrics slurm.conf

CommunicationParametersenable_http를 지정하면 Slurm의 HTTP 기반 통신 기능을 활성화할 수 있습니다.

또한 MetricsTypemetrics/openmetrics로 설정하면 OpenMetrics 형식으로 Slurm 메트릭을 노출할 수 있습니다.

따라서 PCS 생성 단계에서 위 두 설정을 적용한 후, 이후 단계에서 메트릭 수집 및 모니터링 구성을 진행할 수 있습니다.

그 외 로그인 노드와 작업 노드를 생성하는 과정은 아래와 같습니다.

https://dev.classmethod.jp/articles/jw-aws-pcs-auto-scaling/

Prometheus Metric Endpoint 확인

설정이 정상적으로 적용되었는지 확인하기 위해 Slurm에서 실제로 Metric Endpoint가 열려 있는지도 테스트했습니다.

Prometheus Metric을 제공하는 Endpoint는 다음과 같았습니다.

10.0.136.177:6817

해당 Endpoint에 HTTP로 접근하면 Slurm의 상태 및 리소스 정보를 포함한 Metric을 Prometheus/OpenMetrics 형식으로 확인할 수 있습니다.

즉, 단순히 slurm.conf의 설정이 적용된 것뿐만 아니라, 실제 Endpoint를 통해 Metric이 정상적으로 노출되는 것을 확인할 수 있었습니다.

이어서 로그인 노드에 접속하여 Metric Endpoint가 정상적으로 동작하는지 확인합니다.

아래 명령어를 실행하면 HTTP/1.1 200 OK 응답과 함께 slurmctld에서 제공하는 Metric Endpoint 목록을 확인할 수 있습니다.

curl -v --connect-timeout 5 http://10.0.136.177:6817/metrics

정상적으로 설정되었다면 다음과 같이 200 OK 응답이 반환됩니다.

HTTP/1.1 200 OK
Content-Type: text/plain

slurmctld index of metrics endpoints:
  '/metrics/jobs': get job metrics
  '/metrics/nodes': get node metrics
  '/metrics/partitions': get partition metrics
  '/metrics/jobs-users-accts': get user and account jobs metrics
  '/metrics/scheduler': get scheduler metrics

이를 통해 slurmctld의 HTTP Metric Endpoint가 정상적으로 활성화되었으며, /metrics를 통해 Slurm에서 제공하는 Metric Endpoint 목록을 확인할 수 있음을 검증합니다.

AMP Workspace 생성

먼저 Amazon Managed Service for Prometheus Workspace를 생성합니다.

pcs-2

여기서 주의할 점은 Workspace가 존재한다고 해서 해당 Workspace가 Scraper와 연결되어 있는 것은 아니라는 것입니다.

실제로 어떤 Workspace로 Metric을 전달하고 있는지는 Scraper의 destination을 확인해야 합니다.

AMP Scraper 설정

AMP Scraper를 콘솔 환경에서 생성하려고 하면 소스를 반드시 EKS로 입력해야 하기 때문에 EKS가 없는 현재 환경에서는 에러가 발생합니다.

pcs-3

Cloudshell에서 다음을 수행합니다.

cat > scraper-config.yaml <<'EOF'
global:
  scrape_interval: 60s
  scrape_timeout: 30s

scrape_configs:
  - job_name: 'slurm-jobs'
    metrics_path: /metrics/jobs
    static_configs:
      - targets:
          - '10.0.136.177:6817'
    relabel_configs:
      - target_label: cluster
        replacement: 'hpc-cluster-1'

  - job_name: 'slurm-nodes'
    metrics_path: /metrics/nodes
    static_configs:
      - targets:
          - '10.0.136.177:6817'
    relabel_configs:
      - target_label: cluster
        replacement: 'hpc-cluster-1'

  - job_name: 'slurm-scheduler'
    metrics_path: /metrics/scheduler
    static_configs:
      - targets:
          - '10.0.136.177:6817'
    relabel_configs:
      - target_label: cluster
        replacement: 'hpc-cluster-1'

  - job_name: 'slurm-partitions'
    metrics_path: /metrics/partitions
    static_configs:
      - targets:
          - '10.0.136.177:6817'
    relabel_configs:
      - target_label: cluster
        replacement: 'hpc-cluster-1'
EOF

그다음 기존 JSON의 configurationBlob을 새 YAML로 교체하면 됩니다.

base64 -w 0 scraper-config.yaml > scraper-config.base64

이어서 create-scraper-input.json파일을 생성합니다.

cat > create-scraper-input.json <<'EOF'
{
  "alias": "pcs-hpc-cluster-1",
  "source": {
    "vpcConfiguration": {
      "subnetIds": [
        "<SUBNET-ID-1>",
        "<SUBNET-ID-2>"
      ],
      "securityGroupIds": [
        "<SECURITY-GROUP-ID>"
      ]
    }
  },
  "destination": {
    "ampConfiguration": {
      "workspaceArn": "<AMP-WORKSPACE-ARN>"
    }
  },
  "scrapeConfiguration": {
    "configurationBlob": "<BASE64-CONFIGURATION>"
  }
}
EOF

서브넷과 보안 그룹의 ID를 입력합니다. 보안 그룹의 경우 pcs-prometheus-collector라는 보안 그룹을 만들었으며, 인바운드 규칙은 비워두고 아웃바운드는 0.0.0.0을 설정했습니다.

workspaceArn은 다음과 같은 형식이 됩니다.

arn:aws:aps:ap-northeast-1:AccountID:workspace/ws-c8500960-5926-4be4-a5ee-3db5cbf939c9

CONFIG=$(base64 -w 0 scraper-config.yaml)

jq --arg config "$CONFIG" \
  '.scrapeConfiguration.configurationBlob = $config' \
  create-scraper-input.json > create-scraper-input-final.json

만들어진 JSON을 확인합니다.

결과 값으로 <BASE64-CONFIGURATION>"configurationBlob": "Z2xvYmFsOgogIHNjcmFwZV9pbnRlcnZhb..." 처럼 출력되면 성공입니다.

jq . create-scraper-input-final.json

그다음 바로 생성하면 됩니다.

aws amp create-scraper \
  --cli-input-json file://create-scraper-input-final.json \
  --region ap-northeast-1

이어서 생성한 scraper 상태를 확인합니다.

결과 값으로 statusCode가 ACTIVE가 되는지 확인합니다.

aws amp describe-scraper \
  --scraper-id s-522abbd6-fec9-414e-bbec-422e34de5556 \
  --region ap-northeast-1

CloudShell에서 awscurl를 설치합니다.

pip install awscurl

그리고 AMP Query API에 간단한 PromQL을 날립니다.

awscurl --service aps \
  --region ap-northeast-1 \
  "https://aps-workspaces.ap-northeast-1.amazonaws.com/workspaces/ws-c8500960-5926-4be4-a5ee-3db5cbf939c9/api/v1/query?query=up"

이 결과로 PCS → AMP Scraper → AMP Workspace까지 Metric 수집이 성공했다는 것을 확인했습니다.

{"status":"success","data":{"resultType":"vector","result":[{"metric":{"__name__":"up","cluster":"hpc-cluster-1","instance":"10.0.136.177:6817","job":"slurm-nodes"},"value":[1788339348,"1"]},{"metric":{"__name__":"up","cluster":"hpc-cluster-1","instance":"10.0.136.177:6817","job":"slurm-jobs"},"value":[1788339348,"1"]},{"metric":{"__name__":"up","cluster":"hpc-cluster-1","instance":"10.0.136.177:6817","job":"slurm-partitions"},"value":[1788339348,"1"]},{"metric":{"__name__":"up","cluster":"hpc-cluster-1","instance":"10.0.136.177:6817","job":"slurm-scheduler"},"value":[1788339348,"1"]}]}}~ $ 

이제 실제 Slurm Metric도 확인해 보겠습니다.

여기서 Slurm 관련 metric 이름들이 나오면 실제 데이터까지 AMP에 들어온 것입니다.

awscurl --service aps \
  --region ap-northeast-1 \
  "https://aps-workspaces.ap-northeast-1.amazonaws.com/workspaces/ws-c8500960-5926-4be4-a5ee-3db5cbf939c9/api/v1/label/__name__/values"

이걸로 AMP에 실제 Slurm Metric이 들어오는 것까지 확인 완료입니다.

"https://aps-workspaces.ap-northeast-1.amazonaws.com/workspaces/ws-c8500960-5926-4be4-a5ee-3db5cbf939c9/api/v1/label/__name__/values"{"status":"success","data":["scrape_duration_seconds","scrape_samples_post_metric_relabeling","scrape_samples_scraped","scrape_series_added","slurm_agent_cnt","slurm_agent_queue_size","slurm_agent_thread_cnt","slurm_backfilled_het_jobs","slurm_backfilled_jobs","slurm_bf_active","slurm_bf_cycle_cnt","slurm_bf_cycle_last","slurm_bf_cycle_max","slurm_bf_cycle_tot","slurm_bf_depth_mean","slurm_bf_depth_tot","slurm_bf_depth_try_tot","slurm_bf_exit_end","slurm_bf_exit_max_job_start","slurm_bf_exit_max_job_test","slurm_bf_exit_state_changed","slurm_bf_exit_table_limit","slurm_bf_exit_timeout","slurm_bf_last_depth","slurm_bf_last_depth_try","slurm_bf_mean_cycle","slurm_bf_mean_table_sz","slurm_bf_queue_len","slurm_bf_queue_len_mean","slurm_bf_queue_len_tot","slurm_bf_table_size","slurm_bf_table_size_tot","slurm_bf_try_depth_mean","slurm_bf_when_last_cycle","slurm_jobs","slurm_jobs_bootfail","slurm_jobs_cancelled","slurm_jobs_completed","slurm_jobs_completing","slurm_jobs_configuring","slurm_jobs_cpus_alloc","slurm_jobs_deadline","slurm_jobs_expediting","slurm_jobs_failed","slurm_jobs_fed_requeued","slurm_jobs_finished","slurm_jobs_hold","slurm_jobs_memory_alloc","slurm_jobs_node_failed","slurm_jobs_nodes_alloc","slurm_jobs_outofmemory","slurm_jobs_pending","slurm_jobs_powerup_node","slurm_jobs_preempted","slurm_jobs_requeued","slurm_jobs_resizing","slurm_jobs_revoked","slurm_jobs_running","slurm_jobs_signaling","slurm_jobs_stageout","slurm_jobs_started","slurm_jobs_suspended","slurm_jobs_timeout","slurm_last_backfilled_jobs","slurm_last_proc_req_start","slurm_node_cpus","slurm_node_cpus_alloc","slurm_node_cpus_effective","slurm_node_cpus_idle","slurm_node_memory_alloc_bytes","slurm_node_memory_bytes","slurm_node_memory_effective_bytes","slurm_node_memory_free_bytes","slurm_nodes","slurm_nodes_alloc","slurm_nodes_blocked","slurm_nodes_cloud","slurm_nodes_completing","slurm_nodes_down","slurm_nodes_drain","slurm_nodes_drained","slurm_nodes_draining","slurm_nodes_dyn_future","slurm_nodes_dyn_normal","slurm_nodes_external","slurm_nodes_fail","slurm_nodes_future","slurm_nodes_idle","slurm_nodes_invalid_reg","slurm_nodes_maint","slurm_nodes_mixed","slurm_nodes_noresp","slurm_nodes_planned","slurm_nodes_power_down","slurm_nodes_power_up","slurm_nodes_powered_down","slurm_nodes_powering_up","slurm_nodes_reboot_issued","slurm_nodes_reboot_req","slurm_nodes_resv","slurm_nodes_unknown","slurm_partition_cpus","slurm_partition_jobs","slurm_partition_jobs_bootfail","slurm_partition_jobs_cancelled","slurm_partition_jobs_completed","slurm_partition_jobs_completing","slurm_partition_jobs_configuring","slurm_partition_jobs_cpus_alloc","slurm_partition_jobs_deadline","slurm_partition_jobs_expediting","slurm_partition_jobs_failed","slurm_partition_jobs_fed_requeued","slurm_partition_jobs_finished","slurm_partition_jobs_hold","slurm_partition_jobs_max_job_nodes","slurm_partition_jobs_max_job_nodes_nohold","slurm_partition_jobs_memory_alloc","slurm_partition_jobs_min_job_nodes","slurm_partition_jobs_min_job_nodes_nohold","slurm_partition_jobs_node_failed","slurm_partition_jobs_outofmemory","slurm_partition_jobs_pending","slurm_partition_jobs_powerup_node","slurm_partition_jobs_preempted","slurm_partition_jobs_requeued","slurm_partition_jobs_resizing","slurm_partition_jobs_revoked","slurm_partition_jobs_running","slurm_partition_jobs_signaling","slurm_partition_jobs_stageout","slurm_partition_jobs_started","slurm_partition_jobs_suspended","slurm_partition_jobs_timeout","slurm_partition_jobs_wait_part_node_limit","slurm_partition_nodes","slurm_partition_nodes_alloc","slurm_partition_nodes_blocked","slurm_partition_nodes_cg","slurm_partition_nodes_cloud","slurm_partition_nodes_cpus_alloc","slurm_partition_nodes_cpus_efctv","slurm_partition_nodes_cpus_idle","slurm_partition_nodes_down","slurm_partition_nodes_drain","slurm_partition_nodes_drained","slurm_partition_nodes_draining","slurm_partition_nodes_dyn_future","slurm_partition_nodes_dyn_normal","slurm_partition_nodes_external","slurm_partition_nodes_fail","slurm_partition_nodes_future","slurm_partition_nodes_idle","slurm_partition_nodes_invalid_reg","slurm_partition_nodes_maint","slurm_partition_nodes_mem_alloc","slurm_partition_nodes_mem_avail","slurm_partition_nodes_mem_free","slurm_partition_nodes_mem_tot","slurm_partition_nodes_mixed","slurm_partition_nodes_no_resp","slurm_partition_nodes_planned","slurm_partition_nodes_power_down","slurm_partition_nodes_power_up","slurm_partition_nodes_powered_down","slurm_partition_nodes_powering_down","slurm_partition_nodes_powering_up","slurm_partition_nodes_reboot_issued","slurm_partition_nodes_reboot_requested","slurm_partition_nodes_resv","slurm_partition_nodes_unknown","slurm_partitions","slurm_sched_exit_end","slurm_sched_exit_lic","slurm_sched_exit_max_depth","slurm_sched_exit_max_job_start","slurm_sched_exit_rpc_cnt","slurm_sched_exit_timeout","slurm_sched_mean_cycle","slurm_sched_mean_depth_cycle","slurm_sched_stats_timestamp","slurm_schedule_cycle_cnt","slurm_schedule_cycle_depth","slurm_schedule_cycle_last","slurm_schedule_cycle_max","slurm_schedule_cycle_tot","slurm_schedule_queue_len","slurm_sdiag_job_states_ts","slurm_sdiag_jobs_canceled","slurm_sdiag_jobs_completed","slurm_sdiag_jobs_failed","slurm_sdiag_jobs_pending","slurm_sdiag_jobs_running","slurm_sdiag_jobs_started","slurm_sdiag_jobs_submitted","slurm_sdiag_latency","slurm_server_thread_cnt","slurm_slurmdbd_queue_size","up"]}

Grafana 설정

AWS에서 제공하고 있는 Grafana를 사용하기 위해서는 아래 두 가지 인증 중 하나를 선택해야 합니다.

  • AWS IAM Identity Center
  • SAML 2.0

개인 환경에서 검증하는 경우 상기 두 가지 인증을 사용하기에는 무리가 있기 때문에 이번 블로그에서는 Grafana를 운영하는 EC2 서버를 구축하도록 하겠습니다.

Grafana 서버에서 사용할 IAM 정책은 다음과 같습니다.

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": [
                "aps:QueryMetrics",
                "aps:GetLabels",
                "aps:GetSeries",
                "aps:GetMetricMetadata"
            ],
            "Resource": "arn:aws:aps:ap-northeast-1:AccountID:workspace/ws-adc01e07-1a2c-4965-a001-dd8a67703182"
        }
    ]
}

SSM 접속을 위해 AmazonSSMManagedInstanceCore도 추가했습니다.

보안 그룹은 인바운드 규칙은 아무것도 설정하지 않았고, 아웃바운드는 0.0.0.0을 설정했습니다.

EC2 구축이 끝났다면, SSM으로 접속한 다음 Grafana 패키지 저장소를 추가합니다.

sudo tee /etc/yum.repos.d/grafana.repo > /dev/null <<'EOF'
[grafana]
name=grafana
baseurl=https://rpm.grafana.com
repo_gpgcheck=1
enabled=1
gpgcheck=1
gpgkey=https://rpm.grafana.com/gpg.key
sslverify=1
sslcacert=/etc/pki/tls/certs/ca-bundle.crt
EOF

저장소를 추가한 후 Grafana를 설치합니다.

sudo dnf install -y grafana

설치가 완료되면 버전을 확인합니다.

grafana-server -v

Deprecation warning: 'grafana-server' is deprecated and will be removed in a future release. Use the 'grafana server' subcommand instead.
Version 13.2.0 (commit: f681b1359f6a0b8ecb9f2c49a88ac72b75bde73b, branch: release-13.2.0#patched)

Grafana를 부팅 시 자동으로 시작하도록 설정하고 서비스를 시작합니다.

sudo systemctl enable --now grafana-server

서비스 상태를 확인합니다.

sudo systemctl status grafana-server

Grafana의 기본 HTTP 포트는 3000이므로 EC2의 Security Group에서 TCP 3000 포트에 대한 접근을 허용할 필요가 있습니다만, 외부에서 접근이 불가능한 프라이빗에 Grafana 서버를 구축했기 때문에 직접적으로 Grafana 서버로 접속하는 것이 아닌 SSM 포트포워딩을 통해 로컬 환경에서 접속하겠습니다.

aws ssm start-session \
  --target i-00cec76ee4c994312 \
  --document-name AWS-StartPortForwardingSession \
  --parameters '{"portNumber":["3000"],"localPortNumber":["3000"]}' \
  --region ap-northeast-1

이제 로컬에서 접속을 시도합니다.

http://localhost:3000/login

Grafana 웹 사이트가 표시되면 성공입니다.

pcs-4

이제 Grafana와 AMP를 연동합니다.

먼저 Grafana EC2 서버의 터미널에서 실행합니다.

sudo grafana cli \
  --homepath /usr/share/grafana \
  plugins install grafana-amazonprometheus-datasource

Grafana를 재시작합니다.

sudo systemctl restart grafana-server

아래 명령어로 running 상태인지 확인합니다.

sudo systemctl status grafana-server

이제 Grafana 접속을 위해 비밀번호를 설정합니다.

혹은 아이디와 비밀번호를 admin으로 입력하면 새 비밀번호를 입력하라는 화면이 나오기 때문에 UI에서 비밀번호를 지정해도 됩니다.

sudo grafana cli --homepath /usr/share/grafana \
  admin reset-admin-password 'MyStrongPassword123!'

Data sources에서 grafana-amazonprometheus-datasource를 선택합니다.

이어서 Connection에는 Amazon Prometheus의 Endpoint - query URL (IPv4)를 입력합니다.

/api/v1/query는 없애고 입력합니다.

pcs-5

그 다음 리전을 선택합니다.

pcs-7

Save & test를 클릭해서 Successfully queried the Prometheus API가 표시되면 성공입니다.

pcs-8

Explore에서 Metric를 선택해서 slurm_nodes를 선택하고 Run query를 클릭해서 결과가 표시되면 성공입니다.

pcs-9

마무리

이번에는 AWS PCS에서 실행 중인 Slurm의 Metric을 OpenMetrics 형식으로 노출하고, Amazon Managed Service for Prometheus(AMP)를 통해 수집한 뒤 Grafana에서 시각화하는 과정을 구성해 봤습니다.

전체 구성은 다음과 같습니다.

AWS PCS (Slurm)

 OpenMetrics

Slurm Metric Endpoint
10.0.136.177:6817

 AMP Scraper

Amazon Managed Service for Prometheus

 SigV4

Grafana

먼저 PCS의 slurm.conf에 enable_http와 metrics/openmetrics를 설정하여 Slurm의 Metric Endpoint를 활성화했습니다. 이후 실제 Endpoint에 curl 요청을 보내 HTTP/1.1 200 OK가 반환되는 것을 확인함으로써 Slurm에서 Metric이 정상적으로 노출되고 있는지 검증했습니다.

다음으로 AMP Scraper를 구성하여 /metrics/jobs, /metrics/nodes, /metrics/scheduler, /metrics/partitions의 Metric을 AMP Workspace로 수집하도록 설정했습니다. Scraper가 ACTIVE 상태가 된 이후 awscurl을 이용해 AMP Query API를 호출했고, up Metric과 다양한 slurm_* Metric이 정상적으로 조회되는 것을 확인했습니다.

마지막으로 별도의 Amazon Linux 2023 EC2에 Grafana를 구성하고 AMP를 데이터 소스로 연결했습니다. Grafana에서는 AWS SigV4 인증을 사용하여 AMP Workspace에 접근하도록 설정했으며, Explore에서 slurm_nodes Metric을 조회하여 실제 Slurm Metric이 Grafana까지 전달되는 것도 확인했습니다.

이번 구성은 실제 운영 환경을 위한 모니터링 아키텍처라기보다는 PCS → Slurm Metric Endpoint → AMP Scraper → AMP → Grafana로 이어지는 Metric 수집 및 시각화 흐름을 End-to-End로 검증하는 데 목적이 있습니다.

이를 통해 AWS PCS 환경에서도 별도의 Prometheus 서버를 직접 운영하지 않고 AMP를 활용하여 Slurm Metric을 중앙에서 수집하고, Grafana를 통해 조회 및 시각화할 수 있음을 확인했습니다.

この記事をシェアする

関連記事