🩺

推論サーバーが起動しないときに見るべき AWS Neuron メトリクス入門

に公開

Neuronインスタンスがブラックボックスになりがち問題

先端技術開発グループ(WAND)の小島です。エクサウィザーズ Advent Calendar 2025 6日目の記事です。

AWSの機械学習アクセラレータであるAWS TrainiumとAWS Inferentia(Neuron)は、LLMホストに有望な選択肢です。これまでの検証を通じ、「サーバー起動失敗時の原因(ホストメモリ、Neuronメモリ、あるいはその他)の特定に手間取る」という課題が見えてきました。

Neuronインスタンスはブラックボックスになりがちなため、本記事では「開発時の可観測性(オブザーバビリティ)」に焦点を当て、これらのエラーの原因を効率的に切り分ける方法を解説します。

Neuronインスタンスの監視系コマンド

GPUインスタンスでは、nvidia-smiを実行すれば、「インスタンスにどのGPUが割り当てられていて、どのぐらいVRAMが使われているか」が一目瞭然でわかります。nvidia-smiのように、一目でリソース状況を確認できるNeuron向けツールには、主に以下の3つがあります。

  • Neuron-Topneuron-top
  • Neuron-Monitorneuron-monitor
  • Neuron-LSneuron-ls

※なお、Neuronの公式ドキュメントでは、Neuron Profiler、Neuron-DET、Neuron-Sysfsなども存在しますが、今回は必要以上に低レイヤーな情報となるため割愛します。

Neuron-Top

Neuron-Topneuron-top)は、AWS TrainiumとAWS Inferentiaでのパフォーマンスを監視、デバッグするためのツールです。リアルタイムで各Neuronコアの使用率やメモリ使用量、ロードされたモデルやNeuronアプリケーションの情報を表示することで、アプリケーションのパフォーマンスを監視できます。

こちらは人間による目視確認用で、nvidia-smi に最も近い使用感のツールです。自動化の用途には、次のneuron-monitorを活用します。

画像の読み方

neuron-topは、Neuronコア、vCPU、メモリ状況を一画面でリアルタイムに表示します。見るべきポイントは上から順に以下の4点です。

  1. NeuronCore Utilization(青・緑のバー)
    Neuronコアの稼働率です。バーが右端(100%)に張り付いている場合は計算リソースが飽和しており、逆に低い場合は余力がある状態です。
  2. vCPU Utilization
    ホスト側のCPU負荷です。Neuron側が余裕なのにここが高い場合、CPUボトルネックを疑います。
  3. Memory Usage Summary
    ホスト側とデバイス側のメモリ使用量の総計です。モデルの重み(Constants)や計算用の一時データ(Tensors)の内訳も確認できます。
  4. Memory Usage Details(赤色のリスト)
    「どのデバイス(ND)で、どのモデルが、何GB消費しているか」の詳細です。特定のモデルがメモリを圧迫していないか特定するのに使います。

画像の例では:
ND0~ND3のコア使用率が100%に張り付いており、デバイスメモリも100GB以上消費されています。大規模なモデルによって計算リソースとメモリがフル活用されている状態であることが読み取れます。

inf2.24xlargeはND5まで搭載されていますが、今回はTP=8(8コア)構成のため、使用したのは4デバイスのみです。

使い方

AWS公開の「Deep Learning AMI Neuron」なら、追加のインストールの手間なく利用可能です。

neuron-top

Neuron-Monitor

Neuron-Monitorneuron-monitor)は、Neuronコア使用率、メモリ、エラーなどの各種メトリクスを収集し、JSON形式で出力するツールです。CloudWatchやPrometheusなどの外部ツールと連携しやすく、システム全体のパフォーマンス監視やトラブルシューティングの自動化に最適です。

Neuron-Topが人間による目視確認用だったのに対し、Neuron-Monitorは自動化用です。neuron-monitorを実行すると、以下のようなJSONが生成されます。

※ログはinf2.xlargeのものです

Neuronデバイスが使われていないとき(例:インスタンス起動直後)
{
  "neuron_runtime_data": [],
  "system_data": {
    "memory_info": {
      "period": 4.99629154,
      "memory_total_bytes": 16378953728,
      "memory_used_bytes": 3868246016,
      "swap_total_bytes": 0,
      "swap_used_bytes": 0,
      "error": ""
    },
    "vcpu_usage": {
      "period": 4.99381553,
      "average_usage": {
        "user": 44.77,
        "nice": 0,
        "system": 7.09,
        "idle": 46.98,
        "io_wait": 1.11,
        "irq": 0,
        "soft_irq": 0.05
      },
      "usage_data": {
        "0": { "user": 86.75, "nice": 0, "system": 10.24, "idle": 2.01, "io_wait": 1, "irq": 0, "soft_irq": 0 },
        "1": { "user": 33, "nice": 0, "system": 6.04, "idle": 59.96, "io_wait": 1.01, "irq": 0, "soft_irq": 0 },
        "2": { "user": 1.21, "nice": 0, "system": 4.84, "idle": 93.95, "io_wait": 0, "irq": 0, "soft_irq": 0 },
        "3": { "user": 58.27, "nice": 0, "system": 7.06, "idle": 32.26, "io_wait": 2.42, "irq": 0, "soft_irq": 0 }
      },
      "context_switch_count": 196542,
      "error": ""
    }
  },
  "instance_info": {
    "instance_name": "********-neuron-handson",
    "instance_id": "i-*****************",
    "instance_type": "inf2.xlarge",
    "instance_availability_zone": "ap-northeast-1a",
    "instance_availability_zone_id": "apne1-az4",
    "instance_region": "ap-northeast-1",
    "ami_id": "ami-*****************",
    "subnet_id": "subnet-*****************",
    "error": ""
  },
  "neuron_hardware_info": {
    "neuron_device_type": "inferentia2",
    "neuron_device_version": "v3",
    "neuroncore_version": "v2",
    "neuron_device_count": 1,
    "neuron_device_memory_size": 34359738368,
    "neuroncore_per_device_count": 2,
    "logical_neuroncore_config": 1,
    "error": ""
  }
}
Neuronデバイスが使われているとき(例:推論サーバー起動後)
{
  "neuron_runtime_data": [
    {
      "pid": 14893,
      "neuron_runtime_tag": "14893",
      "error": "",
      "report": {
        "execution_stats": {
          "period": 5.000083083,
          "error_summary": { "numerical": 0, "transient": 0, "model": 0, "runtime": 0, "hardware": 0 },
          "execution_summary": { "completed": 0, "completed_with_err": 0, "completed_with_num_err": 0, "timed_out": 0, "incorrect_input": 0, "failed_to_queue": 0 },
          "latency_stats": { "total_latency": null, "device_latency": null },
          "error": ""
        },
        "memory_used": {
          "period": 5.000085522,
          "neuron_runtime_used_bytes": {
            "host": 6579920896,
            "neuron_device": 29054226820,
            "usage_breakdown": {
              "host": { "application_memory": 6579396608, "constants": 0, "dma_buffers": 524288, "tensors": 0 },
              "neuroncore_memory_usage": {
                "0": { "constants": 269453292, "model_code": 1590863948, "model_shared_scratchpad": 1762231296, "runtime_memory": 1914144, "tensors": 10902650730 },
                "1": { "constants": 269453292, "model_code": 1590863948, "model_shared_scratchpad": 1762231296, "runtime_memory": 1914144, "tensors": 10902650730 }
              }
            }
          }
        },
        "loaded_models": [
          {
            "name": "/tmp/nxd_model/context_encoding_model/_tp0_bk1/model.MODULE_65c702dfd6778a3f3f71+176c7a0b.neff",
            "uuid": "6c1151d173f34e27acfbd27b91588703",
            "model_id": 10006,
            "is_running": false,
            "subgraphs": {
              "sg_00": {
                "memory_used_bytes": {
                  "host": 0,
                  "neuron_device": 763138702,
                  "usage_breakdown": {
                    "host": { "application_memory": 0, "constants": 0, "dma_buffers": 0, "tensors": 0 },
                    "neuron_device": { "constants": 323470, "model_code": 687005952, "runtime_memory": 0, "tensors": 0 }
                  }
                },
                "neuroncore_index": 0,
                "v2": "nc_v2.0",
                "neuron_device_index": 0
              }
            }
          },
          {
            "name": "/tmp/nxd_model/token_generation_model/_tp0_bk0/model.MODULE_343ce9cb0821246510db+a6f78b53.neff",
            "uuid": "dac34c597d6e48a6abd049baf376120b",
            "model_id": 10007,
            "is_running": false,
            "subgraphs": {
              "sg_00": {
                "memory_used_bytes": {
                  "host": 0,
                  "neuron_device": 42019336,
                  "usage_breakdown": {
                    "host": { "application_memory": 0, "constants": 0, "dma_buffers": 0, "tensors": 0 },
                    "neuron_device": { "constants": 2197768, "model_code": 39821568, "runtime_memory": 0, "tensors": 0 }
                  }
                },
                "neuroncore_index": 0,
                "v2": "nc_v2.0",
                "neuron_device_index": 0
              }
            }
          },
          {
            "name": "/tmp/nxd_model/token_generation_model/_tp0_bk0/model.MODULE_343ce9cb0821246510db+a6f78b53.neff",
            "uuid": "dac34c597d6e48a6abd049baf376120b",
            "model_id": 10008,
            "is_running": false,
            "subgraphs": {
              "sg_00": {
                "memory_used_bytes": {
                  "host": 0,
                  "neuron_device": 42019336,
                  "usage_breakdown": {
                    "host": { "application_memory": 0, "constants": 0, "dma_buffers": 0, "tensors": 0 },
                    "neuron_device": { "constants": 2197768, "model_code": 39821568, "runtime_memory": 0, "tensors": 0 }
                  }
                },
                "neuroncore_index": 1,
                "v2": "nc_v2.1",
                "neuron_device_index": 0
              }
            }
          },
          {
            "name": "/tmp/nxd_model/token_generation_model/_tp0_bk1/model.MODULE_97886a9c34288fb35320+116152bb.neff",
            "uuid": "b30dfcaa0dd449d6b4225dd764865635",
            "model_id": 10009,
            "is_running": false,
            "subgraphs": {
              "sg_00": {
                "memory_used_bytes": {
                  "host": 0,
                  "neuron_device": 42145160,
                  "usage_breakdown": {
                    "host": { "application_memory": 0, "constants": 0, "dma_buffers": 0, "tensors": 0 },
                    "neuron_device": { "constants": 2214152, "model_code": 39931008, "runtime_memory": 0, "tensors": 0 }
                  }
                },
                "neuroncore_index": 1,
                "v2": "nc_v2.1",
                "neuron_device_index": 0
              }
            }
          },
          {
            "name": "/tmp/nxd_model/token_generation_model/_tp0_bk1/model.MODULE_97886a9c34288fb35320+116152bb.neff",
            "uuid": "b30dfcaa0dd449d6b4225dd764865635",
            "model_id": 10010,
            "is_running": false,
            "subgraphs": {
              "sg_00": {
                "memory_used_bytes": {
                  "host": 0,
                  "neuron_device": 42145160,
                  "usage_breakdown": {
                    "host": { "application_memory": 0, "constants": 0, "dma_buffers": 0, "tensors": 0 },
                    "neuron_device": { "constants": 2214152, "model_code": 39931008, "runtime_memory": 0, "tensors": 0 }
                  }
                },
                "neuroncore_index": 0,
                "v2": "nc_v2.0",
                "neuron_device_index": 0
              }
            }
          },
          {
            "name": "/tmp/nxd_model/context_encoding_model/_tp0_bk0/model.MODULE_a822cae0afb6d6cd44a9+743f48e6.neff",
            "uuid": "467703cd611140eaa069189816cafb7c",
            "model_id": 10003,
            "is_running": false,
            "subgraphs": {
              "sg_00": {
                "memory_used_bytes": {
                  "host": 0,
                  "neuron_device": 760343758,
                  "usage_breakdown": {
                    "host": { "application_memory": 0, "constants": 0, "dma_buffers": 0, "tensors": 0 },
                    "neuron_device": { "constants": 311182, "model_code": 684223296, "runtime_memory": 0, "tensors": 0 }
                  }
                },
                "neuroncore_index": 0,
                "v2": "nc_v2.0",
                "neuron_device_index": 0
              }
            }
          },
          {
            "name": "/tmp/nxd_model/context_encoding_model/_tp0_bk0/model.MODULE_a822cae0afb6d6cd44a9+743f48e6.neff",
            "uuid": "467703cd611140eaa069189816cafb7c",
            "model_id": 10004,
            "is_running": false,
            "subgraphs": {
              "sg_00": {
                "memory_used_bytes": {
                  "host": 0,
                  "neuron_device": 760343758,
                  "usage_breakdown": {
                    "host": { "application_memory": 0, "constants": 0, "dma_buffers": 0, "tensors": 0 },
                    "neuron_device": { "constants": 311182, "model_code": 684223296, "runtime_memory": 0, "tensors": 0 }
                  }
                },
                "neuroncore_index": 1,
                "v2": "nc_v2.1",
                "neuron_device_index": 0
              }
            }
          },
          {
            "name": "/tmp/nxd_model/context_encoding_model/_tp0_bk1/model.MODULE_65c702dfd6778a3f3f71+176c7a0b.neff",
            "uuid": "6c1151d173f34e27acfbd27b91588703",
            "model_id": 10005,
            "is_running": false,
            "subgraphs": {
              "sg_00": {
                "memory_used_bytes": {
                  "host": 0,
                  "neuron_device": 763138702,
                  "usage_breakdown": {
                    "host": { "application_memory": 0, "constants": 0, "dma_buffers": 0, "tensors": 0 },
                    "neuron_device": { "constants": 323470, "model_code": 687005952, "runtime_memory": 0, "tensors": 0 }
                  }
                },
                "neuroncore_index": 1,
                "v2": "nc_v2.1",
                "neuron_device_index": 0
              }
            }
          }
        ],
        "error": ""
      },
      "neuron_runtime_vcpu_usage": {
        "period": 5.000084693,
        "vcpu_usage": { "user": 0.05, "system": 0.15 },
        "error": ""
      },
      "neuroncore_counters": {
        "period": 5.000087752,
        "neuroncores_in_use": {
          "0": { "neuroncore_utilization": 0, "effective_flops": 0 },
          "1": { "neuroncore_utilization": 0, "effective_flops": 0 }
        },
        "error": ""
      }
    }
  ],
  "system_data": {
    "memory_info": {
      "period": 5.000099323,
      "memory_total_bytes": 16378953728,
      "memory_used_bytes": 12882739200,
      "swap_total_bytes": 0,
      "swap_used_bytes": 0,
      "error": ""
    },
    "vcpu_usage": {
      "period": 5.000117865,
      "average_usage": {
        "user": 0.61,
        "nice": 0,
        "system": 0.91,
        "idle": 98.43,
        "io_wait": 0.05,
        "irq": 0,
        "soft_irq": 0
      },
      "usage_data": {
        "0": { "user": 0.61, "nice": 0, "system": 0.81, "idle": 98.59, "io_wait": 0, "irq": 0, "soft_irq": 0 },
        "1": { "user": 0.4, "nice": 0, "system": 1.01, "idle": 98.59, "io_wait": 0, "irq": 0, "soft_irq": 0 },
        "2": { "user": 0.61, "nice": 0, "system": 1.01, "idle": 98.38, "io_wait": 0, "irq": 0, "soft_irq": 0 },
        "3": { "user": 1.21, "nice": 0, "system": 0.61, "idle": 98.18, "io_wait": 0, "irq": 0, "soft_irq": 0 }
      },
      "context_switch_count": 50115,
      "error": ""
    }
  },
  "instance_info": {
    "instance_name": "********-neuron-handson",
    "instance_id": "i-*****************",
    "instance_type": "inf2.xlarge",
    "instance_availability_zone": "ap-northeast-1a",
    "instance_availability_zone_id": "apne1-az4",
    "instance_region": "ap-northeast-1",
    "ami_id": "ami-*****************",
    "subnet_id": "subnet-*****************",
    "error": ""
  },
  "neuron_hardware_info": {
    "neuron_device_type": "inferentia2",
    "neuron_device_version": "v3",
    "neuroncore_version": "v2",
    "neuron_device_count": 1,
    "neuron_device_memory_size": 34359738368,
    "neuroncore_per_device_count": 2,
    "logical_neuroncore_config": 1,
    "error": ""
  }
}

CloudWatchと連携することで、Neuronコアごとのメモリ使用状況などをEC2コンソール上で直接可視化できます(後述)。

使い方

neuron-monitorはJSON形式のメトリクスをストリーム出力しますが、これを手動で処理するだけでなく、CloudWatch Agent等の収集ツールへスムーズに連携(パイプ)する仕組みが別途用意されています(後述)。

neuron-monitor

Neuron-LS

Neuron-LSneuron-ls)は、インスタンスに接続されたNeuronデバイスを検出し、そのデバイス数やチップ間の接続構成(トポロジー)を一覧表示するツールです。

OSがハードウェアを正しく認識しているかの確認や、各デバイスIDおよびNeuronCore IDを正確に特定する際に役立ちます。

> neuron-ls --topology
instance-type: inf2.24xlarge
instance-id: i-aabbccdd123456789
+--------+--------+----------+--------+-----------+--------------+-------------+------+
| NEURON | NEURON |  NEURON  | NEURON | CONNECTED |     PCI      |     CPU     | NUMA |
| DEVICE | CORES  | CORE IDS | MEMORY |  DEVICES  |     BDF      |  AFFINITY   | NODE |
+--------+--------+----------+--------+-----------+--------------+-------------+------+
| 0      | 2      | 0-1      | 32 GB  | 1         | 0000:10:1e.0 | 0-23,48-71  | 0    |
| 1      | 2      | 2-3      | 32 GB  | 0, 2      | 0000:20:1e.0 | 24-47,72-95 | 1    |
| 2      | 2      | 4-5      | 32 GB  | 1, 3      | 0000:10:1d.0 | 0-23,48-71  | 0    |
| 3      | 2      | 6-7      | 32 GB  | 2, 4      | 0000:20:1f.0 | 24-47,72-95 | 1    |
| 4      | 2      | 8-9      | 32 GB  | 3, 5      | 0000:10:1f.0 | 0-23,48-71  | 0    |
| 5      | 2      | 10-11    | 32 GB  | 4         | 0000:20:1d.0 | 24-47,72-95 | 1    |
+--------+--------+----------+--------+-----------+--------------+-------------+------+

Neuron Device Topology
                                                          
                                                          
                                                          
    [ 0 ]◄––►[ 1 ]◄––►[ 2 ]◄––►[ 3 ]◄––►[ 4 ]◄––►[ 5 ]    
                                                          
                                                          

出力の例では

neuron-ls --topologyは、各チップのスペックに加え、デバイス間の接続構成(トポロジー)を視覚的に図示します。

  • 上の表: 各デバイス(ID 0~5)が「2コア・32GBメモリ」を持ち、どのCPUコアや他のデバイスと紐付いているかの一覧です。
  • 下の図: デバイス0から5までが、高速通信(NeuronLink)によって一列に双方向で相互接続されている様子を可視化したものです。

何を使うべきか

用途に応じて使い分けるのが良いでしょう。

  • SSHでの開発時: 裏でneuron-topを常駐させるだけで事足りることが多いです。
  • 高度な監視: neuron-monitorとCloudWatchの連携が有効です。

モデル別の使用デバイスメモリ

以前の記事ではコンパイルの可否、推論エンドポイントの起動可否のみ確認しましたが、neuron-topを併用すれば「失敗の原因」まで深掘り可能です。

以下のコマンドでエンドポイントを起動し、neuron-topの「Memory Usage Details」を確認し、モデル別のデバイスごとのメモリ使用量を比較してみましょう。

# 元モデルのパスと、コンパイル済みモデルの保存先を指定
export MODEL_PATH='/home/ubuntu/{ローカルにダウンロードしたモデル名}'
export NEURON_COMPILED_ARTIFACTS='/home/ubuntu/{ローカルにダウンロードしたモデル名}-neuron-{max-model-lenの値}'

# コンパイル+推論サーバーを起動
python3 -m vllm.entrypoints.openai.api_server \
  --model $MODEL_PATH \
  --device neuron \
  --tensor-parallel-size 8 \
  --max-model-len {max-model-lenの値} \
  --max-num-seqs 1

Inferentia 2のメモリ上限は1デバイスあたり32GBです。「Device Memory」の値がこの範囲内に収まっているかが、起動可否の判断基準となります(図の赤枠の値)。

検証環境

  • インスタンスタイプ:inf2.24xlarge
  • AMI:Deep Learning AMI Neuron (Ubuntu 22.04) 20251029
  • 仮想環境:aws_neuronx_venv_pytorch_2_8_nxd_inference
  • upstreaming-to-vllmバージョン:2.26.1
  • Tensor Parallelの値:8
  • 使用モデル

モデル別のデバイスメモリ結果

max_len Qwen3-4B Qwen3-8B Llama-3.1-8B Qwen3-14B Qwen3-32B Llama-3.2-11B-Vision-Instruct
2048 4.6 GB 6.5 GB 6.4 GB 9.7 GB 18.3 GB 13.3 GB
8192 5.6 GB 8.6 GB 8.4 GB 12.0 GB 21.2 GB 17.8 GB
32768 10.5 GB 12.5 GB 12.4 GB 18.4 GB 28.2 GB 27.9 GB

※TP(Tensor Parallel)利用時は重みやキャッシュが各デバイスに分散されるため、表の値はすべて「1デバイスあたりの消費量」を示します。

以前の記事で動作上限だった「Llama-3.2-11B-Vision-Instruct (max_len=32768)」は、実測値が 27.9 / 32.0GB でした。これにより、設定を倍の65536にした際の失敗原因は、明確に「Neuronデバイスのメモリ不足」と結論付けられました。

グラフからは以下の傾向が読み取れます。

  • モデルサイズ: メモリ使用量と相関する。同サイズならモデル間(Qwen3/Llama 3.1)の差はほぼない。
  • max_len: 値を増やすと、メモリ使用量はほぼ線形に増加する。
  • モデル種別: テキスト専用より、マルチモーダルモデルの方がメモリ消費が多い。

深堀り:Neuronデバイスのメモリ使用量の計算

Neuronデバイスのメモリ使用量は、以下の式でイメージすると挙動を理解しやすくなります。

\text{使用メモリ} \approx \underbrace{\text{モデルの重み}}_{\text{ほぼ固定}} + \underbrace{\text{KVキャッシュ}}_{\text{max\_lenに比例}} + \text{ワークスペース}
  • モデルの重み (Constants): モデルのパラメータ数と精度(FP16など)で決まるベースラインです。ロード後はほぼ一定です。
  • KVキャッシュ (Tensors): 文脈を保持する領域です。max_len(シーケンス長)やバッチサイズに比例して線形に増加します。
  • ワークスペース: 計算処理に使う一時領域です。

つまり、モデルが決まっている場合、デバイスメモリ(例: 32GB)に収めるための主な調整弁は max_len(KVキャッシュサイズ) ということになります。前回 max_len を倍にして溢れたのは、この可変部分が肥大化したためです。

その他の失敗事例と原因の切り分け

検証で遭遇したその他の失敗パターンと、それぞれの症状は以下の通りです。

パターン 原因の種別 ツール / 症状
max_len を倍にしたら起動しない Neuron メモリ不足 neuron-top の Device Memory が 32GB を超える
Llama 3.3 70B モデルサイズオーバー コンパイル/ロード時エラー
Qwen-VL 系(2.5, 3) ライブラリ未サポート コンパイルエラーのメッセージを確認
transformers バージョン不整合 ソフトウェア構成の問題 起動は成功するがリクエストでエラー

Neuron-Monitorを活用した高度な監視

SSHでのデバッグならNeuron-Topで十分ですが、運用監視にはCloudWatch連携が定石です。EC2のメモリ監視にCloudWatch Agentを使うのと同様、Neuron-Monitorを使えばNeuronのメトリクスをCloudWatchへ出力できます。

これにより、EC2の詳細画面からNeuronデバイスの利用状況を確認したり、CloudWatchダッシュボードを使って集計することが可能になります。

前提

CloudWatch Agentと併用することを前提に、EC2のIAMロールには以下のマネージドポリシーを付与します(Neuron-Monitor単体ではもう少しポリシーを絞ることは可能)。

  • AmazonSSMManagedInstanceCore
  • CloudWatchAgentServerPolicy

起動方法

CloudWatchへの出力は、AMIに同梱されている /opt/aws/neuron/bin/neuron-monitor-cloudwatch.py を利用します。

<your-region>に、リージョン名(ap-northeast-1、us-east-1など)を指定して実行します。なお、--namespaceCWAgentと設定することで、EC2の詳細画面から直接メトリクスを確認可能になります。

# ディレクトリの移動
cd /opt/aws/neuron/bin

# Neuron-MonitorとCloudWatchを連携
neuron-monitor | neuron-monitor-cloudwatch.py \
    --namespace CWAgent \
    --region <your-region> \
    --common-dims instance_id

このコマンドは単なるスクリプト実行に過ぎないため、本格運用時はSystemd等でプロセスを常駐化する必要があります。

項目の読み方

EC2の詳細画面に表示される各メトリクス項目の意味は以下の通りです。

メトリクス項目 説明
RuntimeMemUsedNeuronDevice Neuronデバイスのメモリ使用量(バイト)
RuntimeMemUsedHost Neuronランタイムが使用するホストDRAM使用量(バイト)
RuntimeCpuUsageUser Neuronアプリの「ユーザ空間」CPU使用率(%)
RuntimeCpuUsageSystem Neuronアプリの「カーネル空間」CPU使用率(%)
NeuronCoreUtilizationNC{x} 各Neuronコア({x})の利用率(%)

出力項目を絞るには

Neuron-Monitorのメトリクスはカスタムメトリクスとして課金されます。デフォルトの全項目出力はコスト増の要因となるため、必要な項目に絞るとよいでしょう。不要な項目をフィルタリングする方法については、以下の記事が参考になります。

[AWS/Inf1]Neuron Coreの使用率のみをカスタムメトリクスとして送信する

制約

Neuron-Monitorはデバイス稼働時のみ(例:エンドポイント起動後)メトリクスを出力する仕様です。そのため、非稼働時はCloudWatch上で欠損値となります。

コンパイル中などもデータが欠損するため、EC2自体のメモリ(RAM)を常時監視したい場合は、CloudWatch Agentを併用します。

CloudWatchダッシュボード化

各デバイスのメトリクスをCloudWatchで集計(合計)することで、インスタンス全体のリソース状況をダッシュボード化できます。実際の表示イメージは以下の通りです。

デプロイのためのTerraformのコード(inf2.24xlarge前提)
resource "aws_cloudwatch_dashboard" "neuron" {
  dashboard_name = var.neuron_dashboard_name

  dashboard_body = jsonencode({
    widgets = [
      # 1. RuntimeMemUsedNeuronDevice
      {
        "type"   : "metric",
        "x"      : 0,
        "y"      : 0,
        "width"  : 12,
        "height" : 6,
        "properties" : {
          "title"  : "RuntimeMemUsedNeuronDevice (device memory bytes)",
          "region" : var.neuron_region,
          "view"   : "timeSeries",
          "stacked": false,
          "metrics": [
            [
              var.cwagent_namespace,
              "RuntimeMemUsedNeuronDevice",
              "InstanceId", var.neuron_instance_id,
              { "stat" : "Average", "label" : "RuntimeMemUsedNeuronDevice" }
            ]
          ]
        }
      },

      # 2. RuntimeMemUsedHost
      {
        "type"   : "metric",
        "x"      : 12,
        "y"      : 0,
        "width"  : 12,
        "height" : 6,
        "properties" : {
          "title"  : "RuntimeMemUsedHost (host memory bytes)",
          "region" : var.neuron_region,
          "view"   : "timeSeries",
          "stacked": false,
          "metrics": [
            [
              var.cwagent_namespace,
              "RuntimeMemUsedHost",
              "InstanceId", var.neuron_instance_id,
              { "stat" : "Average", "label" : "RuntimeMemUsedHost" }
            ]
          ]
        }
      },

      # 3. NeuronCoreUtilizationNC0〜NC11 の合計
      {
        "type"   : "metric",
        "x"      : 0,
        "y"      : 6,
        "width"  : 24,
        "height" : 6,
        "properties" : {
          "title"  : "Total NeuronCore Utilization (NC0–NC11)",
          "region" : var.neuron_region,
          "view"   : "timeSeries",
          "stacked": false,
          "metrics": [
            # 合計の式
            [
              {
                "expression": "m0+m1+m2+m3+m4+m5+m6+m7+m8+m9+m10+m11",
                "label"     : "Sum NeuronCoreUtilization (NC0–NC11)",
                "id"        : "e1",
                "region"    : var.neuron_region
              }
            ],

            # 個々のコア(非表示)
            [
              var.cwagent_namespace, "NeuronCoreUtilizationNC0",
              "InstanceId", var.neuron_instance_id,
              { "stat": "Average", "id": "m0", "visible": false }
            ],
            [
              ".", "NeuronCoreUtilizationNC1",
              ".", var.neuron_instance_id,
              { "stat": "Average", "id": "m1", "visible": false }
            ],
            [
              ".", "NeuronCoreUtilizationNC2",
              ".", var.neuron_instance_id,
              { "stat": "Average", "id": "m2", "visible": false }
            ],
            [
              ".", "NeuronCoreUtilizationNC3",
              ".", var.neuron_instance_id,
              { "stat": "Average", "id": "m3", "visible": false }
            ],
            [
              ".", "NeuronCoreUtilizationNC4",
              ".", var.neuron_instance_id,
              { "stat": "Average", "id": "m4", "visible": false }
            ],
            [
              ".", "NeuronCoreUtilizationNC5",
              ".", var.neuron_instance_id,
              { "stat": "Average", "id": "m5", "visible": false }
            ],
            [
              ".", "NeuronCoreUtilizationNC6",
              ".", var.neuron_instance_id,
              { "stat": "Average", "id": "m6", "visible": false }
            ],
            [
              ".", "NeuronCoreUtilizationNC7",
              ".", var.neuron_instance_id,
              { "stat": "Average", "id": "m7", "visible": false }
            ],
            [
              ".", "NeuronCoreUtilizationNC8",
              ".", var.neuron_instance_id,
              { "stat": "Average", "id": "m8", "visible": false }
            ],
            [
              ".", "NeuronCoreUtilizationNC9",
              ".", var.neuron_instance_id,
              { "stat": "Average", "id": "m9", "visible": false }
            ],
            [
              ".", "NeuronCoreUtilizationNC10",
              ".", var.neuron_instance_id,
              { "stat": "Average", "id": "m10", "visible": false }
            ],
            [
              ".", "NeuronCoreUtilizationNC11",
              ".", var.neuron_instance_id,
              { "stat": "Average", "id": "m11", "visible": false }
            ]
          ]
        }
      }
    ]
  })
}

# ==============================
# variables(例)
# ==============================

variable "neuron_dashboard_name" {
  description = "CloudWatch dashboard name for monitoring Neuron metrics"
  type        = string
  default     = "neuron-monitoring-dashboard"
}

variable "cwagent_namespace" {
  description = "CloudWatch namespace used by the CloudWatch Agent"
  type        = string
  default     = "CWAgent"
}

variable "neuron_region" {
  description = "Region where the Neuron instance and CloudWatch metrics are located (e.g. ap-northeast-1)"
  type        = string
  # 例: "ap-northeast-1"
}

# CloudWatch メトリクスツリーで「CWAgent > InstanceId」の “InstanceId”
variable "neuron_instance_id" {
  description = "EC2 instance ID that exposes Neuron metrics via CloudWatch Agent (e.g. i-0123456789abcdef0)"
  type        = string
  # 例: "i-0123456789abcdef0"
}

これにより、全デバイスのメモリ総量(RuntimeMemUsedNeuronDevice)、ホストメモリ(RuntimeMemUsedHost)、全コア使用率の合計(Total NeuronCore Utilization)をダッシュボードで可視化できました。

まとめ

Neuronインスタンスのブラックボックス化を防ぐため、neuron-topによるリアルタイム分析やneuron-monitorを用いたCloudWatch連携の手法を解説しました。これらを活用し、推論サーバー起動時のメモリ不足原因を特定する具体的な手順や、運用時のダッシュボード構築についてまとめました。

エクサウィザーズ Tech Blog

Discussion