Prometheus の SNMP Exporter で net-snmp からメトリクス収集するメモ

net-snmp が動いているホストから Prometheus でメトリクス収集するために SNMP Exporter を設定していたのだけど、ググって出てくる情報だとバージョン違いでわりと動かないっぽい気がしたので、下記を見ながら設定したメモ。

- https://github.com/prometheus/snmp_exporter/

概ね下記を参考に設定すれば OK です。

- https://github.com/prometheus/snmp_exporter/blob/v0.4.0/snmp.yml

これは下記のツールで自動生成されたものです。

- https://github.com/prometheus/snmp_exporter/tree/v0.4.0/generator

## ざっくりした設定ファイルの説明

SNMP Exporter のバージョンは現時点で最新の v0.4.0 を使っています。

net-snmp の機器からメトリクスを取得する設定ファイルの例です（`metrics` の箇所は後述します）。

```yaml
# snmp.yml

default:
  version: 2
  auth:
    community: hogehoge
  walk:
    - 1.3.6.1.4.1.2021.4    # UCD-SNMP-MIB::memory
    - 1.3.6.1.4.1.2021.9    # UCD-SNMP-MIB::dskTable
    - 1.3.6.1.4.1.2021.11   # UCD-SNMP-MIB::systemStats
  metrics:
    ... snip ...
```

`default` は Prometheus の設定ファイルで指定する `module` パラメータの値です。例えば **prometheus.yml** で次のように指定します。

```yaml
# prometheus.yml

  - job_name: snmp

    ... snip ...

    params:
      module: [default]
```

1つの SNMP Exporter でいろいろな機器を監視するときは下記のように複数のモジュールを定義します。

```yaml
# snmp.yml

linux:
  version: 2
  auth:
    community: hogehoge
  walk:
    - 1.3.6.1.4.1.2021.4    # UCD-SNMP-MIB::memory
    - 1.3.6.1.4.1.2021.9    # UCD-SNMP-MIB::dskTable
    - 1.3.6.1.4.1.2021.11   # UCD-SNMP-MIB::systemStats
  metrics:
    ... snip ...

network:
  version: 2
  auth:
    community: hogehoge
  walk:
    - 1.3.6.1.2.1.2         # IF-MIB::interfaces
  metrics:
    ... snip ...
```

`version` は使用する SNMP プロトコルバージョンです。1 とか 2 とか 3 とか。`version` と同じ階層で、`max_repititions` `retries` `timeout` とかも設定できます。

`auth` で認証情報を指定します。プロトコルバージョン 1 や 2 なら `community` だけですが、3 ならいろいろ指定します（`security_level` `username` `password` `auth_protocol` `priv_protocol` `priv_password`）。

`walk` は SNMP Exporter で取得するツリーの OID を指定します。指定した OID にぶら下がるすべてのデータが取得されます。先頭の `.` は無くて良いようです（他の OID を指定する箇所でも同じ）。

`walk` で指定しただけでは Prometheus のメトリクスとしてはエクスポートされません。snmpwalk でゲットした値をどのようなメトリクスとしてエクスポートするか `metrics` で指定します。

`metrics` の箇所の例です。

```yaml
# snmp.yml

default:

  ... snip ...

  metrics:
    - name: sysUpTime
      oid: 1.3.6.1.2.1.1.3.0
      type: gauge

    - name: ifInOctets
      oid: 1.3.6.1.2.1.2.2.1.10
      type: counter
      indexes:
        - labelname: ifIndex
          type: Integer32
      lookups:
        - labelname: ifDescr
          labels: [ifIndex]
          oid: 1.3.6.1.2.1.2.2.1.2
          type: DisplayString
```

この例では2つのメトリクス `sysUpTime` と `ifInOctets` を設定しています。

`sysUpTime` は単一の値を取得する例です。OID `1.3.6.1.2.1.1.3.0` の値が `sysUpTime` という名前のメトリクスとしてエクスポートされます。簡単なので説明は不要かと思います。

`ifInOctets` は SEQUENCE から値を取得する例です。`1.3.6.1.2.1.2.2.1.10` はサフィックスに `.1` とか `.2` のようなインデックスが付いて複数の I/F の値を返しますが、それらをメトリクスのラベルで区別して複数の値としてエクスポートします。

`indexes` でサフィックスに付くインデックスの型と、メトリクスに付与するラベルを指定します。この例だとインデックスは `.1` のように整数1個なので、`Integer32` を1つだけ指定し、その値でメトリクスに `ifIndex` というラベルを付けます。

`lookups` はメトリクスに追加で付与するラベルです。`ifIndex` だけだとインデックス番号しか無いので、デバイス名などのわかりやすいラベルを付けるために使います。

この例だと、OID `1.3.6.1.2.1.2.2.1.2` に `labels` で指定された `ifIndex` の値をサフィックスとして追加し、取得した値を DisplayString 型の値と解釈して `ifDescr` というラベルでメトリクスに設定する、みたいな感じです。

例えば、以下のような OID でデータが得られる場合、

```
.1.3.6.1.2.1.2.2.1.1.1 = INTEGER: 1
.1.3.6.1.2.1.2.2.1.1.2 = INTEGER: 2
.1.3.6.1.2.1.2.2.1.2.1 = STRING: lo
.1.3.6.1.2.1.2.2.1.2.2 = STRING: eth0
.1.3.6.1.2.1.2.2.1.10.1 = Counter32: 2878606
.1.3.6.1.2.1.2.2.1.10.2 = Counter32: 847816235
```

`.1.3.6.1.2.1.2.2.1.10.1` と `.1.3.6.1.2.1.2.2.1.10.2` の値がエクスポートされるメトリクスで、それぞれ `ifIndex=1` `ifIndex=2` というラベルが付きます。更に `.1.3.6.1.2.1.2.2.1.2` にサフィックス `.1` と `.2` を付与した OID を元に `ifDescr=lo` と `ifDescr=eth0` というラベルが付きます。

結果、下記のようなメトリクスが得られます。

```
ifInOctets{ifIndex="1",ifDescr="lo"} 2878606
ifInOctets{ifIndex="2",ifDescr="eth0"} 847816235
```

## metrics の type

- https://github.com/prometheus/snmp_exporter/blob/v0.4.0/collector.go#L177-L180

`metrics` の `type` には大抵の場合は `counter` か `gauge` を指定します。どちらを指定するかは `snmptranslate -Tp <oid>` でなんとなくわかると思います。`Integer` とかは `gauge` で `Counter` とかは `counter` です。

```sh
snmptranslate -Tp .1.3.6.1.4.1.2021.11
# +--systemStats(11)
#    |
#    +-- -R-- Integer32 ssIndex(1)
#    +-- -R-- String    ssErrorName(2)
#    |        Textual Convention: DisplayString
#    |        Size: 0..255
#    +-- -R-- Integer32 ssSwapIn(3)
#    +-- -R-- Integer32 ssSwapOut(4)
#    +-- -R-- Integer32 ssIOSent(5)
#    +-- -R-- Integer32 ssIOReceive(6)
#    +-- -R-- Integer32 ssSysInterrupts(7)
#    +-- -R-- Integer32 ssSysContext(8)
#    +-- -R-- Integer32 ssCpuUser(9)
#    +-- -R-- Integer32 ssCpuSystem(10)
#    +-- -R-- Integer32 ssCpuIdle(11)
#    +-- -R-- Counter   ssCpuRawUser(50)
#    +-- -R-- Counter   ssCpuRawNice(51)
#    +-- -R-- Counter   ssCpuRawSystem(52)
#    +-- -R-- Counter   ssCpuRawIdle(53)
#    +-- -R-- Counter   ssCpuRawWait(54)
#    +-- -R-- Counter   ssCpuRawKernel(55)
#    +-- -R-- Counter   ssCpuRawInterrupt(56)
#    +-- -R-- Counter   ssIORawSent(57)
#    +-- -R-- Counter   ssIORawReceived(58)
#    +-- -R-- Counter   ssRawInterrupts(59)
#    +-- -R-- Counter   ssRawContexts(60)
#    +-- -R-- Counter   ssCpuRawSoftIRQ(61)
#    +-- -R-- Counter   ssRawSwapIn(62)
#    +-- -R-- Counter   ssRawSwapOut(63)
#    +-- -R-- Counter   ssCpuRawSteal(64)
#    +-- -R-- Counter   ssCpuRawGuest(65)
#    +-- -R-- Counter   ssCpuRawGuestNice(66)
```

`type` がそれ以外や未指定の場合は文字列項目であるとみなされて、メトリクス名がそのままラベルになり、そのラベルの値に取得した値が設定されます（メトリクスの値は `1` で固定）。

このとき `type` で指定した値に応じて文字列化が行われます。

- https://github.com/prometheus/snmp_exporter/blob/v0.4.0/collector.go#L223
- https://github.com/prometheus/snmp_exporter/blob/v0.4.0/collector.go#L266

未指定だと `OctetString` で16進文字列になるため、適切な型を指定する必要があります。大抵は `DisplayString` で良いです。

例えば `SNMPv2-MIB::system` の `sysUpTime` や `sysName` で次のように設定すると。

```yaml
  metrics:
    - name: sysUpTime
      oid: 1.3.6.1.2.1.1.3.0
      type: gauge
    - name: sysName
      oid: 1.3.6.1.2.1.1.5.0
      type: DisplayString
```

次のようなメトリクスが得られます。

```
# HELP sysName 
# TYPE sysName gauge
sysName{sysName="example.com"} 1

# HELP sysUpTime 
# TYPE sysUpTime gauge
sysUpTime 8.28781844e+08
```

## indexes や lookups の type

`indexes` や `lookups`  の `type` には下記の `switch` にあるものを指定します。

- https://github.com/prometheus/snmp_exporter/blob/v0.4.0/collector.go#L266

MIB の定義を参考に設定すると良いでしょう。

`indexes` は大抵の場合 `Integer32` です（あるいは `gauge` でも）。`IP-MIB::ip` とか `TCP-MIB::tcp` のテーブルでそうじゃないこともあります、この類のデータを Prometheus で収集することは無いと思いますが。

`lookups` は大抵の場合 `DisplayString` です。ラベルの値が IP アドレスとかであれば適宜変更してください。

## snmp.yml のサンプル

net-snmp からメトリクスを収集する設定ファイルのサンプルです。手書きなのでエイリアスやアンカーを使ってなるべく重複を排除しています。

```yaml
linux:
  version: 2
  auth:
    community: hogehoge
  walk:
    - 1.3.6.1.2.1.1.1           # SNMPv2-MIB::sysDescr
    - 1.3.6.1.2.1.1.3           # SNMPv2-MIB::sysUpTime
    - 1.3.6.1.2.1.1.5           # SNMPv2-MIB::sysName
    - 1.3.6.1.2.1.2             # IF-MIB::interfaces
    - 1.3.6.1.2.1.6.5           # TCP-MIB::tcpActiveOpens
    - 1.3.6.1.2.1.6.6           # TCP-MIB::tcpPassiveOpens
    - 1.3.6.1.2.1.6.7           # TCP-MIB::tcpAttemptFails
    - 1.3.6.1.2.1.6.8           # TCP-MIB::tcpEstabResets
    - 1.3.6.1.2.1.6.9           # TCP-MIB::tcpCurrEstab
    - 1.3.6.1.4.1.2021.4        # UCD-SNMP-MIB::memory
    - 1.3.6.1.4.1.2021.9        # UCD-SNMP-MIB::dskTable
    - 1.3.6.1.4.1.2021.10.1.5   # UCD-SNMP-MIB::laLoadInt
    - 1.3.6.1.4.1.2021.11       # UCD-SNMP-MIB::systemStats
    - 1.3.6.1.4.1.2021.13.15.1  # UCD-DISKIO-MIB::diskIOTable
  metrics:

    ################################################################################
    # SNMPv2-MIB::system

    - { name: sysDescr,  oid: 1.3.6.1.2.1.1.1.0, type: DisplayString }
    - { name: sysUpTime, oid: 1.3.6.1.2.1.1.3.0, type: gauge }
    - { name: sysName,   oid: 1.3.6.1.2.1.1.5.0, type: DisplayString }

    ################################################################################
    # IF-MIB::interfaces

    - &interfaces
      name: ifIndex
      oid: 1.3.6.1.2.1.2.2.1.1
      type: gauge
      indexes:
        - labelname: ifIndex
          type: Integer32
      lookups:
        - labelname: ifDescr
          labels: [ifIndex]
          oid: 1.3.6.1.2.1.2.2.1.2
          type: DisplayString
        - labelname: ifAdminStatus
          labels: [ifIndex]
          oid: 1.3.6.1.2.1.2.2.1.7
          type: Integer32

    - <<: *interfaces
      name: ifMtu
      oid: 1.3.6.1.2.1.2.2.1.4
      type: gauge

    - <<: *interfaces
      name: ifSpeed
      oid: 1.3.6.1.2.1.2.2.1.5
      type: gauge

    - <<: *interfaces
      name: ifOperStatus
      oid: 1.3.6.1.2.1.2.2.1.8
      type: gauge

    - <<: *interfaces
      name: ifInOctets
      oid: 1.3.6.1.2.1.2.2.1.10
      type: counter

    - <<: *interfaces
      name: ifInUcastPkts
      oid: 1.3.6.1.2.1.2.2.1.11
      type: counter

    - <<: *interfaces
      name: ifInNUcastPkts
      oid: 1.3.6.1.2.1.2.2.1.12
      type: counter

    - <<: *interfaces
      name: ifInDiscards
      oid: 1.3.6.1.2.1.2.2.1.13
      type: counter

    - <<: *interfaces
      name: ifInErrors
      oid: 1.3.6.1.2.1.2.2.1.14
      type: counter

    - <<: *interfaces
      name: ifInUnknownProtos
      oid: 1.3.6.1.2.1.2.2.1.15
      type: counter

    - <<: *interfaces
      name: ifOutOctets
      oid: 1.3.6.1.2.1.2.2.1.16
      type: counter

    - <<: *interfaces
      name: ifOutUcastPkts
      oid: 1.3.6.1.2.1.2.2.1.17
      type: counter

    - <<: *interfaces
      name: ifOutNUcastPkts
      oid: 1.3.6.1.2.1.2.2.1.18
      type: counter

    - <<: *interfaces
      name: ifOutDiscards
      oid: 1.3.6.1.2.1.2.2.1.19
      type: counter

    - <<: *interfaces
      name: ifOutErrors
      oid: 1.3.6.1.2.1.2.2.1.20
      type: counter

    - <<: *interfaces
      name: ifOutQLen
      oid: 1.3.6.1.2.1.2.2.1.21
      type: gauge

    ################################################################################
    # TCP-MIB::tcp

    - { name: tcpActiveOpens  , oid: 1.3.6.1.2.1.6.5 , type: counter }
    - { name: tcpPassiveOpens , oid: 1.3.6.1.2.1.6.6 , type: counter }
    - { name: tcpAttemptFails , oid: 1.3.6.1.2.1.6.7 , type: counter }
    - { name: tcpEstabResets  , oid: 1.3.6.1.2.1.6.8 , type: counter }
    - { name: tcpCurrEstab    , oid: 1.3.6.1.2.1.6.9 , type: gauge   }

    ################################################################################
    # UCD-SNMP-MIB::memory

    - { name: memTotalSwap    , oid: 1.3.6.1.4.1.2021.4.3.0   , type: gauge }
    - { name: memAvailSwap    , oid: 1.3.6.1.4.1.2021.4.4.0   , type: gauge }
    - { name: memTotalReal    , oid: 1.3.6.1.4.1.2021.4.5.0   , type: gauge }
    - { name: memAvailReal    , oid: 1.3.6.1.4.1.2021.4.6.0   , type: gauge }
    - { name: memTotalFree    , oid: 1.3.6.1.4.1.2021.4.11.0  , type: gauge }
    - { name: memMinimumSwap  , oid: 1.3.6.1.4.1.2021.4.12.0  , type: gauge }
    - { name: memBuffer       , oid: 1.3.6.1.4.1.2021.4.14.0  , type: gauge }
    - { name: memCached       , oid: 1.3.6.1.4.1.2021.4.15.0  , type: gauge }

    ################################################################################
    # UCD-SNMP-MIB::dskTable

    - &dskTable
      name: dskIndex
      oid: 1.3.6.1.4.1.2021.9.1.1
      type: gauge
      indexes:
        - labelname: dskIndex
          type: Integer32
      lookups:
        - labelname: dskPath
          labels: [dskIndex]
          oid: 1.3.6.1.4.1.2021.9.1.2
          type: DisplayString
        - labelname: dskDevice
          labels: [dskIndex]
          oid: 1.3.6.1.4.1.2021.9.1.3
          type: DisplayString

    - <<: *dskTable
      name: dskTotal
      oid: 1.3.6.1.4.1.2021.9.1.6
      type: gauge

    - <<: *dskTable
      name: dskTotal
      oid: 1.3.6.1.4.1.2021.9.1.6
      type: gauge

    - <<: *dskTable
      name: dskAvail
      oid: 1.3.6.1.4.1.2021.9.1.7
      type: gauge

    - <<: *dskTable
      name: dskUsed
      oid: 1.3.6.1.4.1.2021.9.1.8
      type: gauge

    ################################################################################
    # UCD-SNMP-MIB::laLoadInt

    - { name: laLoadInt1  , oid: 1.3.6.1.4.1.2021.10.1.5.1 , type: gauge }
    - { name: laLoadInt5  , oid: 1.3.6.1.4.1.2021.10.1.5.2 , type: gauge }
    - { name: laLoadInt15 , oid: 1.3.6.1.4.1.2021.10.1.5.3 , type: gauge }

    ################################################################################
    # UCD-SNMP-MIB::systemStats

    - { name: ssCpuRawUser      , oid: 1.3.6.1.4.1.2021.11.50.0 , type: counter }
    - { name: ssCpuRawNice      , oid: 1.3.6.1.4.1.2021.11.51.0 , type: counter }
    - { name: ssCpuRawSystem    , oid: 1.3.6.1.4.1.2021.11.52.0 , type: counter }
    - { name: ssCpuRawIdle      , oid: 1.3.6.1.4.1.2021.11.53.0 , type: counter }
    - { name: ssCpuRawWait      , oid: 1.3.6.1.4.1.2021.11.54.0 , type: counter }
    - { name: ssCpuRawKernel    , oid: 1.3.6.1.4.1.2021.11.55.0 , type: counter }
    - { name: ssCpuRawInterrupt , oid: 1.3.6.1.4.1.2021.11.56.0 , type: counter }
    - { name: ssIORawSent       , oid: 1.3.6.1.4.1.2021.11.57.0 , type: counter }
    - { name: ssIORawReceived   , oid: 1.3.6.1.4.1.2021.11.58.0 , type: counter }
    - { name: ssRawInterrupts   , oid: 1.3.6.1.4.1.2021.11.59.0 , type: counter }
    - { name: ssRawContexts     , oid: 1.3.6.1.4.1.2021.11.60.0 , type: counter }
    - { name: ssCpuRawSoftIRQ   , oid: 1.3.6.1.4.1.2021.11.61.0 , type: counter }
    - { name: ssRawSwapIn       , oid: 1.3.6.1.4.1.2021.11.62.0 , type: counter }
    - { name: ssRawSwapOut      , oid: 1.3.6.1.4.1.2021.11.63.0 , type: counter }

    ################################################################################
    # UCD-DISKIO-MIB::diskIOTable

    - &diskIOTable
      name: diskIOIndex
      oid: 1.3.6.1.4.1.2021.13.15.1.1.1
      type: gauge
      indexes:
        - labelname: diskIOIndex
          type: Integer32
      lookups:
        - labelname: diskIODevice
          labels: [diskIOIndex]
          oid: 1.3.6.1.4.1.2021.13.15.1.1.2
          type: DisplayString
    - <<: *diskIOTable
      name: diskIONRead
      oid: 1.3.6.1.4.1.2021.13.15.1.1.3
      type: counter

    - <<: *diskIOTable
      name: diskIONWritten
      oid: 1.3.6.1.4.1.2021.13.15.1.1.4
      type: counter

    - <<: *diskIOTable
      name: diskIOReads
      oid: 1.3.6.1.4.1.2021.13.15.1.1.5
      type: counter

    - <<: *diskIOTable
      name: diskIOWrites
      oid: 1.3.6.1.4.1.2021.13.15.1.1.6
      type: counter
```