SNMP Exporter の generatorを使う

# Prometheus Advent Calendar 2017 2日目
の記事です。
SNMP Exporterで何かというネタです。

# [SNMP Exporter](https://github.com/prometheus/snmp_exporter)とは
Prometheusを利用して、SNMPで得られる情報をモニタリングしようというときに利用するExporterです。
既存のSNMPマネージャからPrometheusに移行したいと言うときに利用します。
機能は単純で、リクエストが来ると、SNMPを利用して情報を集め、表示するだけのものです。

# SNMP Exporterの設定
SNMP Exporterの設定は、 SNMPを利用して何を(どうやって)収集するのか？という１点です。
SNMP Exporterでは、モジュールという単位で何を収集するのかを設定します。
設定ファイルには、多数のモジュールが並列して記載されています。

下記に例をあげます
if_mibというモジュールからの抜粋です

walk にて、snmpwalkするOIDを指定します。
そして、
metricsにて、各OIDの名称と、値のタイプ、Indexが存在する場合はIndexの情報を記載します。
この他、authという、SNMP v2cでいうCommunityの設定やSNMP v3のユーザ名、パスワードなどを設定するセクションなどがあります。

```
if_mib:
  walk:
  - 1.3.6.1.2.1.1.3
  - 1.3.6.1.2.1.2
  - 1.3.6.1.2.1.31.1.1
  metrics:
  - name: sysUpTime
    oid: 1.3.6.1.2.1.1.3
    type: gauge
    help: The time (in hundredths of a second) since the network management portion
      of the system was last re-initialized. - 1.3.6.1.2.1.1.3
  - name: ifNumber
    oid: 1.3.6.1.2.1.2.1
    type: gauge
    help: The number of network interfaces (regardless of their current state) present
      on this system. - 1.3.6.1.2.1.2.1
  - name: ifIndex
    oid: 1.3.6.1.2.1.2.2.1.1
    type: gauge
    help: A unique value, greater than zero, for each interface - 1.3.6.1.2.1.2.2.1.1
    indexes:
    - labelname: ifIndex
      type: gauge
  - name: ifDescr
    oid: 1.3.6.1.2.1.2.2.1.2
    type: DisplayString
    help: A textual string containing information about the interface - 1.3.6.1.2.1.2.2.1.2
    indexes:
    - labelname: ifIndex
      type: gauge
  - name: ifType
    oid: 1.3.6.1.2.1.2.2.1.3
    type: gauge
    help: The type of interface - 1.3.6.1.2.1.2.2.1.3
    indexes:
    - labelname: ifIndex
      type: gauge
  - name: ifMtu
    oid: 1.3.6.1.2.1.2.2.1.4
    type: gauge
    help: The size of the largest packet which can be sent/received on the interface,
      specified in octets - 1.3.6.1.2.1.2.2.1.4
    indexes:
    - labelname: ifIndex
      type: gauge
  - name: ifSpeed
    oid: 1.3.6.1.2.1.2.2.1.5
    type: gauge
    help: An estimate of the interface's current bandwidth in bits per second - 1.3.6.1.2.1.2.2.1.5
    indexes:
    - labelname: ifIndex
      type: gauge
  - name: ifPhysAddress
    oid: 1.3.6.1.2.1.2.2.1.6
    type: PhysAddress48
    help: The interface's address at its protocol sub-layer - 1.3.6.1.2.1.2.2.1.6
    indexes:
    - labelname: ifIndex
      type: gauge
  - name: ifAdminStatus
    oid: 1.3.6.1.2.1.2.2.1.7
    type: gauge
    help: The desired state of the interface - 1.3.6.1.2.1.2.2.1.7
    indexes:
    - labelname: ifIndex
      type: gauge
  - name: ifOperStatus
    oid: 1.3.6.1.2.1.2.2.1.8
    type: gauge
    help: The current operational state of the interface - 1.3.6.1.2.1.2.2.1.8
    indexes:
    - labelname: ifIndex
      type: gauge
# 以下略
```
これは、オフィシャルにあるサンプルからの抜粋です。
インターフェースの情報を取得するためのものの一部です。
設定自体はSNMP Exporterに付属のgeneratorで作られています。

ただ、バイナリ配布されているものには現在generatorは付属していません。
ソースコードからビルドする必要があります。

generatorがNet-SNMPに依存しており、現状はDynamic Linkでバイナリが作成されるようになっているためこのような状態です。
[issueにあがっており](https://github.com/prometheus/snmp_exporter/issues/223)いずれ同梱されるようになるかもしれません。

# generatorの利用
## generatorのビルド

前述の通り、generator を利用するためには、ビルドする必要があります。
Net-SNMPのライブラリと、includeファイルを入手しておき、
(`yum install net-snmp-devel` , `apt install libsnmp-base libsnmp-devel`など)
golangをインストールし、
`go get github.com/prometheus/snmp_exporter`
でsnmp_exporterのソースコードを入手し、
`cd $GOPATH/src/github.com/prometheus/snmp_exporter/generator`
`go build`
とビルドすることで、generatorのバイナリが作成されます。

あとは generator.ymlを見ながら記載していくだけです。

上記で上げたsnmp設定のサンプルはgeneratorでは

```
modules:
  if_mib:
    walk: [sysUpTime, interfaces, ifXTable]
```

のわずか３行です。

わかりやすく、管理し易いと思いますので、基本的には、generatorでの設定ファイル生成をおすすめします。

generatorの設定ファイルではOID情報がありませんが、これはすべてMIBファイルから持ってきています。
ですので、設定したい機器のSNMP-MIBファイルを別途入手しておく必要があります。
[generatorのREADME](https://github.com/prometheus/snmp_exporter/tree/master/generator)の下の方に主なMIBファイルの入手先と、設置の仕方が書いてありますので参考にすると良いでしょう。


## generatorの設定(オフィシャルページからの抜粋と解説)
generatorの設定は、 generator.yml に記載します。
設定はモジュール単位で行います。

```
module:
  <module1>:
    module1の設定
  <module2>:
    module2の設定
  <module3>:
    module3の設定
```
のような構成です。
各モジュールは

```
  <modulename>:
    walk:
      snmpwalkするもの
    version: 2  # snmp v1,2,3 を設定するもの
    max_repetitions: 25  #SNMP BULK Requestするときの最大値
    retries: 3 # リトライ回数
    timeout: 10s # タイムアウト時間
    auth:
      認証情報
    lookups:
      Indexを他のものに置き換えるための情報
    overrides:
      metric情報を書き換えるための情報
```
から構成されています。
`<modulename>`には好きな名称を使いましょう。prometheusで設定するmodule名です。収集する際にも記録されます。
SNMP Exporterの設定にあった、metricsの記載がありませんが、これは`walk:`から自動的に生成されます。
(ので、大きな範囲を`waｌｋ:`に書くと、結構な量が設定されてしまいます)
あまりに量が多いと、機器によっては、タイムアウトしてしまうので注意しましょう。
上記if_mib設定をとある機器に利用したところ、60秒待っても帰ってこないという事がありました。

### lookups
lookups ですが、 Indexをわかりやすいものに書き換えるためのものです。

```
    lookups:
      - old_index: ifIndex
        new_index: ifName
      - old_index: someIndex
        new_index: someDescr
```
のように、元のIndexと新しいIndexを並べます。

注意点として、 置き換え後のIndexがユニークである必要があります。
サンプルには、 mib_ifDescr や mib_ifNameなど、ifIndexを他のものに書き換える設定が例として記載されていますが、
ifDescrなどは、ユニークである保証がありません、機器によっては、これを使うと情報が取れなくなりますので気をつけましょう。
その手の情報が欲しければ、prometheusには優秀なクエリがありますので、そちらで合成すると良いでしょう。

```
ifInOctets * on(ifIndex) group_left(ifName) ifName
```
とか

```
ifInOctets * on(ifIndex) group_left(ifName) ifName * on(ifIndex) group_left(ifDescr) ifDescr
```
のようなクエリにて、ifNameやついでにifDescrを紐付けられます。
ただし、これもやりすぎるとクエリ応答が遅くなるので程々に。

私の環境で

```
rate(ifHCInOctets[5m]) * 8
rate(ifHCInOctets[5m]) * 8 * on(instance,ifIndex) group_left(ifName) ifName * on(instance,ifIndex) group_left(ifDescr) ifDescr
```
の２つで試してみたところ、 前者で1500msくらいで帰ってきていたものがifNameとifDescrをつけた後者では2900ms前後の時間がかかるようになりました。

クエリに関しては16日に出て来るクエリ道場で覚えましょう

こうなるともう、 prometheusのルールを利用して収集時に追加したほうが良いと思います。

### overrides
overridesは、SNMPで取得した情報を書き換えて保存するためのものです。
例えば、SNMPで値を取得すると、文字列で　"OK" "NG" "UNKNOWN"などが返ってくる場合、メトリクスとして扱えないので、OK=1 NG=2 UNKNOWN=3などとして、数値に置き換えることでメトリクスとして扱えるようにしようという場合などに利用できます。
利用する際には正規表現が使えます

```
    overrides:
      <MetricName>:
        regex_extracts:
          <SubName>:
            - regex: 'OK'
              value: '1'
            - regex: 'NG'
              value: '2'
            - regex: 'UNKNOWN'
              value: '3'
            - regex: '(.*)'
              value: '0'
```
のように、それぞれ割り当てることができます。これで監視できるようになりますね。
`<MetricName>`が元の名称、`<SubName>`が新しくつける名称です。
この場合、`<MetricName><SubName>`という名称(元の名前に自分でつけた名前が連結されたもの)がデータにつけられます。
なお、この時、もとの`<MetricName>`というデータはSNMP Exporter上には現れなくなります。

### snmp.ymlの作成
generator.ymlができあがったら、
あとは generator.yml　のあるディレクトリで、 
`/path/to/generator generate`
を実行すればsnmp.ymlファイルが作成されます。 snmp_exporterからこのファイルを指定して動かし、動作テストをしてみましょう。


# 実際に作成してみたgenerator.yml
検証等を行う中で作成したgenerator.yml からの抜粋です

```
  yamaha:
    auth:
      community: public
    walk:
      - sysName
      - yrhCpuUtil1min
      - yrhInboxTemperature
      - yrhMemoryUtil
      - yrhFanModuleStatus
      - yrhFanModuleIndex
  if_name:
    auth:
      community: public
    walk:
      - sysUpTime
      - ifAdminStatus
      - ifOperStatus
      - ifInErrors
      - ifOutErrors
      - ifInDiscards
      - ifOutDiscards
      - ifOutQLen
      - ifDescr
      - ifName
      - ifAlias
      - ifHCInOctets
      - ifHCOutOctets
      - ifHCInUcastPkts
      - ifHCInBroadcastPkts
      - ifHCInMulticastPkts
      - ifHCOutUcastPkts
      - ifHCOutBroadcastPkts
      - ifHCOutMulticastPkts
    lookups:
      - old_index: ifIndex
        new_index: ifName
  paloalto:
    auth:
      community: public
    walk:
      - entPhysicalDescr
      - entPhySensorValue
      - entPhySensorOperStatus
      - hrProcessorLoad
      - hrDeviceDescr
      - hrStorageDescr
      - hrStorageUsed
      - hrStorageSize
      - panSessionActive
      - panSessionActiveICMP
      - panSessionActiveTcp
      - panSessionActiveUdp
      - panSessionUtilization
      - panSysHAPeerState
      - panSysHAState
      - sysName
    overrides:
      panSysHAPeerState:
        regex_extracts:
          Status:
            - regex: '.*active.*'
              value: '1'
            - regex: '.*passive.*'
              value: '2'
            - regex: '.*'
              value: '0'
      panSysHAState:
        regex_extracts:
          Status:
            - regex: '.*active.*'
              value: '1'
            - regex: '.*passive.*'
              value: '2'
            - regex: '.*'
              value: '0'
```


# 終わりに
SNMP Exporterの基本的な使い方の解説で終わってしまいました。

他のSNMPを利用したメトリック収集ツールからPrometheusに移行したい！というときの参考にでもなれば幸いです。

PS. 間違っているところなどのご指摘、歓迎します。