cactiの過去データを平均化させずにデータ量を1/5に削減した

# はじめに

以前に記載した **[cactiの過去グラフが平均化されないようにする方法](https://qiita.com/bashaway/items/bc4de1ba6d1b97298031)** では、デフォルトのDataProfileを利用すると、過去データが平均化されれしまい、過去に遡って詳しい情報が見れなくなってしまう、という対策のために行いました。

ですが、データソースあたりのファイル容量が大きくなってしまうため、なんとかしたかったので、その対策を行いました。
備忘録としてまとめます。


# 結局なにをしたんだっけ？
[![Screenshot from Gyazo](https://gyazo.com/6c5fbe12f55afd501e916859bbe2657b/raw)](https://gyazo.com/6c5fbe12f55afd501e916859bbe2657b)
たいしたことはしてない。


# 対象機器および環境
* RRDtool (1.7.0)
* cacti (バージョンには依存せず)


# 現状の問題点

## 前回のエントリで作成したDataProfile
過去のデータを平均化させず、５分毎の取得値をそのまま1.5年間閲覧可能なようにRRAの解像度を高く設定しています。
[![Screenshot from Gyazo](https://gyazo.com/21961a43def8c13212ac81e1f778d96c/raw)](https://gyazo.com/21961a43def8c13212ac81e1f778d96c)

このDataProfileでは、ひとつのデータソース（以下、DS）に対して、AVERAGE、MIN、MAX、LASTの４つのCF（Consolidation Functions）が定義されており、それぞれのCFに対して、Daily、Weekly、Monthly、Yearlyの４種類のRRA定義を対応させています。

そのため、１データソースあたり6MByte程度必要となり、グラフあたりの関連データソース数が多いものや、ポート数の多いネットワーク機器などが大量にある場合には、データ量がハンパない状態になってしまいます。

たとえば、４８ポートのスイッチの場合、
トラフィックを取得する場合のデータソースは`traffic_in`と`traffic_out`の２つ
[![Screenshot from Gyazo](https://gyazo.com/b41602ec90d23e065b5a8da38e1ca656/raw)](https://gyazo.com/b41602ec90d23e065b5a8da38e1ca656)

エラーを取得する場合のデータソースは
`discards_in`と`discards_out`と`errors_in`と`erros_out`の４つ
[![Screenshot from Gyazo](https://gyazo.com/a181c2bad5add97d7e9e3c14308f55c7/raw)](https://gyazo.com/a181c2bad5add97d7e9e3c14308f55c7)

これらを合算すると、48(port) * 6(DS) * 6MByte = 1728MByte
**スイッチ１台あたり2GByte弱（！）** も食ってしまいます。
業務で使おうもんなら、１ラックで100GBとかいっちゃうんじゃないか、これ。

## デフォルトで用意されているDataProfile
これに対してSystemDefaultは以下
[![Screenshot from Gyazo](https://gyazo.com/75ba5f8340b96fa877ca9baacdaeac5a/raw)](https://gyazo.com/75ba5f8340b96fa877ca9baacdaeac5a)

デフォルトは、1DSあたり92Kbyteなので、48ポートスイッチでも、 **30MByte弱と、約1/60** です。おさいふに優しい。
ですが、以下のようにデータが平均化されます。

|参照期間|データ粒度|
|:-:|:-:|:-:|
|現在～２日前まで|５分毎の取得値の値そのまま|
|～２週間前まで|５分毎の取得値の３０分平均|
|～２か月前まで|５分毎の取得値の２時間平均|
|～２年前まで|５分毎の取得値の１日平均|

つまり、２か月より前のデータと参照しようとすると、解像度の低いRRAに保管されている１日につき１つのデータしか参照できません。
３か月前のイベントの時のトラフィックってどうだったっけ？と、その日を抽出して見ようとすると、１日のグラフが長方形になり、なんのこっちゃ状態です。

## で、どうするか

cactiは取得値をそのまま使うよりも、「つい最近どうだったか」を見る意味合いが強いと思いますし、過去情報はトレンドが確認できればよいともとらえることができますので、デフォルト設定もしかたなしと思います。
（プリセットのDataProfileでは30秒ごとの取得とかもありますしね。。。）

でも、過去のデータもそのまま参照したい、でもデータ量がこんなになるのは許せない。
ということで、対策を考えることにしました。


# 解決方法

実はこれ、グラフテンプレートのGPRINTで記載されたCFはRRAで設定したCFじゃなさそうだ、と気づいたことがキッカケでした。
つまり、 **グラフ作成で参照されないCFは消しちゃえばいいんじゃね？** ということです。

## 仮説と検証

グラフテンプレートを確認して、不要なCFとRRAを削除しても問題ないかどうかを確認していきます。
一般のご家庭での利用なので、このくらいです。
[![Screenshot from Gyazo](https://gyazo.com/13848e72e5e518b5826c5722fc0bf9d7/raw)](https://gyazo.com/13848e72e5e518b5826c5722fc0bf9d7)

解説が簡単なCPUを2種類取り上げます

### GT - CPU Usage Cisco

これはCiscoデバイス用のCPU利用率のグラフを作成するための、グラフテンプレート
設定は以下のようなものです。
[![Screenshot from Gyazo](https://gyazo.com/32ca074f2a1b7b6a5677e03bd9199dad/raw)](https://gyazo.com/32ca074f2a1b7b6a5677e03bd9199dad)

これにより実際に生成されるグラフはこれ。
[![Screenshot from Gyazo](https://gyazo.com/7c43d66e61c3d3cf9c78c36974bbd738/raw)](https://gyazo.com/7c43d66e61c3d3cf9c78c36974bbd738)

グラフの右上にある歯車マーク → スパナマークとクリックしていくと、RRDtoolのグラフ生成コマンドが確認できます。

```
/bin/rrdtool graph - \
--imgformat=PNG \
--start='1579556940' \
--end='1579719840' \
～（省略）～
DEF:a='/usr/share/cacti/rra/rhq01_5min_cpu_38.rrd':'5min_cpu':AVERAGE \
AREA:a#FF0000FF:'CPU Usage'  \
GPRINT:a:LAST:'Current\:%8.0lf'  \
GPRINT:a:AVERAGE:'Average\:%8.0lf'  \
GPRINT:a:MAX:'Maximum\:%8.0lf\n' 
RRDtool Command lengths = 924 charaters.
RRDtool Says:
OK
```

このうち、`DEF:～～`の部分が、グラフを作成する元データとなる、「RRDファイル」「DS」「CF」を指定しています。
ここは元データなるので、RRDファイルから削除してはいけません。

`GPRINT:～～`にもCFがLAST/AVERAGE/MAXが記載されていますが、これはRRDファイルのデータが使われているのでしょうか？
RRDファイルのデータを参照していないようであれば、削除できますね。確認してみます。

#### 元データからグラフを作成

先ほどのグラフ作成のコマンドを最小限のものに切り出して、グラフを作成してみましょう。

```
// グラフを作成する。
$ cp /usr/share/cacti/rra/rhq01_5min_cpu_38.rrd ./origin.rrd
$ /bin/rrdtool graph -  --imgformat=PNG \
--start='1579556940' --end='1579719840' \
DEF:a='./origin.rrd':'5min_cpu':AVERAGE \
AREA:a#FF0000FF:'CPU Usage'  \
GPRINT:a:LAST:'Current\:%8.0lf'  \
GPRINT:a:AVERAGE:'Average\:%8.0lf'  \
GPRINT:a:MAX:'Maximum\:%8.0lf\n' \
> origin.png

// ハッシュを確認
$ md5sum origin.png
a72f5bc258a088443209eb9a0db0c54d  origin.png
```

#### 参照されていないCFを削除したRRDデータからグラフを作成

RRDファイルの複製からAVERAGE以外を削除して同一のグラフが生成されるか確認します。

```
// 元のRRDファイルをコピーし、AVERAGE以外のCFのインデックスを探します。
$ cp /usr/share/cacti/rra/rhq01_5min_cpu_38.rrd ./check.rrd
$ rrdtool info check.rrd | grep 'MIN\|MAX\|LAST'
rra[4].cf = "MIN"
rra[5].cf = "MIN"
rra[6].cf = "MIN"
rra[7].cf = "MIN"
rra[8].cf = "MAX"
rra[9].cf = "MAX"
rra[10].cf = "MAX"
rra[11].cf = "MAX"
rra[12].cf = "LAST"
rra[13].cf = "LAST"
rra[14].cf = "LAST"
rra[15].cf = "LAST"

// RRAの4-15を削除すれば、AVERAGEのみが残ります。
$ rrdtool tune check.rrd DELRRA:4 DELRRA:5 DELRRA:6 \
DELRRA:7 DELRRA:8 DELRRA:9 DELRRA:10 DELRRA:11 \
DELRRA:12 DELRRA:13 DELRRA:14 DELRRA:15

// AVE以外のCFを削除したRRDファイルからグラフを作成する。
$ /bin/rrdtool graph -  --imgformat=PNG \
--start='1579556940' --end='1579719840' \
DEF:a='./check.rrd':'5min_cpu':AVERAGE \
AREA:a#FF0000FF:'CPU Usage'  \
GPRINT:a:LAST:'Current\:%8.0lf'  \
GPRINT:a:AVERAGE:'Average\:%8.0lf'  \
GPRINT:a:MAX:'Maximum\:%8.0lf\n' \
> check.png

// ハッシュを確認
$ md5sum check.png
a72f5bc258a088443209eb9a0db0c54d  check.png
```

同一であることが確認できました。では、AVERAGE以外のCFは削除できそうですね。

#### 解像度の低いRRAを削除したRRDデータからグラフを作成

次に、解像度が低いRRAを削除して、もとのグラフと同一のものが生成できるか確認します。
（解像度が最も高いものは、STEPが１なので、`rra[x].pdp_per_row = 1` なのですが、わかりやすいように、cactiで設定したときの `rra[x].rows = 157680` を用いています）


```
// RRAの解像度の最も高いものを探して、それ以外を削除します。
$ rrdtool info check.rrd | grep 'rows'
rra[0].rows = 547
rra[1].rows = 157680  ←これ以外を削除
rra[2].rows = 6570
rra[3].rows = 26280

// RRAの0,2,3を削除すれば、必要なデータのみが残ります。
$ rrdtool tune check.rrd DELRRA:0 DELRRA:2 DELRRA:3

// 不要なデータを削除したRRDファイルからグラフを作成する。
$ /bin/rrdtool graph -  --imgformat=PNG \
--start='1579556940' --end='1579719840' \
DEF:a='./check.rrd':'5min_cpu':AVERAGE \
AREA:a#FF0000FF:'CPU Usage'  \
GPRINT:a:LAST:'Current\:%8.0lf'  \
GPRINT:a:AVERAGE:'Average\:%8.0lf'  \
GPRINT:a:MAX:'Maximum\:%8.0lf\n' \
> check2.png

// ハッシュを確認
$ md5sum check2.png
04cff82bb092918e896622d2b8724eeb  check2.png
```

あら、 **ダメでした** ね。

#### なぜダメだったか

元データと、ダメデータの双方のデータと、期間を絞って見てみましょう。

```
// 元のデータ
$ rrdtool fetch origin.rrd AVERAGE --start='1579673900' --end='1579675000'
                       5min_cpu

1579674000: 4.0000000000e+00
1579674300: 4.9966666667e+00
1579674600: 5.9966666667e+00
1579674900: 5.0233333333e+00
1579675200: 5.0000000000e+00
```

```
// RRAを削除したデータ
$ rrdtool fetch check.rrd AVERAGE --start='1579673900' --end='1579675000'
                       5min_cpu

1579674000: 6.9800000000e+00
1579674300: 7.0000000000e+00
1579674600: 7.0000000000e+00
1579674900: 7.0000000000e+00
1579675200: 7.0000000000e+00
```

なんか欲しいデータじゃないですね。

データの状態から推測すると、インデックス０番目を削除するときに、１番目のデータが０番目にコピーされてから、０番目の領域が拡張されているような感じです。つまり、０番目の領域にコピーされた時点で平均化されてしまうようですね。 **（ちゃんと調べてません）**
ひとまず、０番目を詰めなければ大丈夫そうですね。

```
// 元RRDファイルからRRAの2-15を削除します。
$ cp origin.rrd check2.rrd
$ rrdtool tune check2.rrd DELRRA:2 DELRRA:3 DELRRA:4 DELRRA:5 DELRRA:6 \
DELRRA:7 DELRRA:8 DELRRA:9 DELRRA:10 DELRRA:11 \
DELRRA:12 DELRRA:13 DELRRA:14 DELRRA:15

// グラフを作成する。
$ /bin/rrdtool graph -  --imgformat=PNG \
--start='1579556940' --end='1579719840' \
DEF:a='./check2.rrd':'5min_cpu':AVERAGE \
AREA:a#FF0000FF:'CPU Usage'  \
GPRINT:a:LAST:'Current\:%8.0lf'  \
GPRINT:a:AVERAGE:'Average\:%8.0lf'  \
GPRINT:a:MAX:'Maximum\:%8.0lf\n' \
> check2.png

// ハッシュを確認
$ md5sum check2.png
a72f5bc258a088443209eb9a0db0c54d  check2.png
```

OKでした。
つまり、このグラフテンプレートでは、AVERAGE以外のCFは参照されていないことがわかりました。
同様にもう一つのグラフテンプレートで検証してみましょう。

### GT - CPU Usage Windows/Linux

これはWindowsやLinuxのCPU利用率のグラフを作成するための、グラフテンプレート
[![Screenshot from Gyazo](https://gyazo.com/fee3b5af71f871cc765461b8cc750a3e/raw)](https://gyazo.com/fee3b5af71f871cc765461b8cc750a3e)

これにより実際に生成されるグラフはこれ。
[![Screenshot from Gyazo](https://gyazo.com/4077b58d44c0d1d1bff0e463ce18f156/raw)](https://gyazo.com/4077b58d44c0d1d1bff0e463ce18f156)

先ほどと同様に確認してみます。

#### 元データからグラフを作成

```
/bin/rrdtool graph - \
--imgformat=PNG \
--start='1553520300' \
--end='1554887940' \
～（省略）～
DEF:a='/usr/share/cacti/rra/esxi01_cpu_118.rrd':'cpu':AVERAGE \
DEF:b='/usr/share/cacti/rra/esxi01_cpu_118.rrd':'cpu':MAX \
AREA:a#FF00007F:'CPU Utilization'  \
GPRINT:a:LAST:'Current\:%8.0lf'  \
GPRINT:a:AVERAGE:'Average\:%8.0lf'  \
GPRINT:a:MAX:'Maximum\:%8.0lf\n'  \
LINE1:b#FF0000FF: 
```

これは、DEFにCFのMAXが使われてしまっていますね。

#### 参照されているCFも削除しRRDデータからグラフを作成

でも、考えてみると、５分毎の値を常時保管できているので、MAXの値は（に限らず、他の値も）実は利用していないのでは？

```
$ rrdtool tune check.rrd DELRRA:3 DELRRA:4 DELRRA:5 DELRRA:6 \
DELRRA:7 DELRRA:8 DELRRA:9 DELRRA:10 DELRRA:11 \
DELRRA:12 DELRRA:13 DELRRA:14 DELRRA:15

$ cp check.rrd /usr/share/cacti/rra/esxi01_cpu_118.rrd
```

[![Screenshot from Gyazo](https://gyazo.com/907efa520afb5fc9322d6941880748ff/raw)](https://gyazo.com/907efa520afb5fc9322d6941880748ff)
ピークは無くなっていますが、AREAの部分はほぼ同じ。表示されるMaximumの数値も55で同じですね。


ちなみに、このときのグラフ作成コマンドは以下になっていました。

```
/bin/rrdtool graph - \
--imgformat=PNG \
--start='1553520300' \
--end='1554887940' \
～（省略）～
DEF:a='/usr/share/cacti/rra/esxi01_cpu_118.rrd':'cpu':AVERAGE \
AREA:a#FF00007F:'CPU Utilization'  \
GPRINT:a:LAST:'Current\:%8.0lf'  \
GPRINT:a:AVERAGE:'Average\:%8.0lf'  \
GPRINT:a:MAX:'Maximum\:%8.0lf\n'  \
LINE1:a#FF0000FF: 
```

CFのMAXがないので、自動的にコマンドを削除してくれているようです。親切。
そのままだと、rrdtoolのエラーでグラフが生成されないのでしょう。
`GPRINT:a:MAX:'Maximum\:%8.0lf\n' `が同じなため、数値の表示は５５なのですね。

５分毎のデータは保管されているはずなので、拡大したら同じはずです。
見てみましょう。

**元のRRDで生成されるグラフ**
[![Screenshot from Gyazo](https://gyazo.com/56ae783af090b9ffcece76c558aa01db/raw)](https://gyazo.com/56ae783af090b9ffcece76c558aa01db)

**RRA削除したデータで生成されるグラフ**
[![Screenshot from Gyazo](https://gyazo.com/7428574ce4ea1b572f63847ce4cbb6d2/raw)](https://gyazo.com/7428574ce4ea1b572f63847ce4cbb6d2)

これは、同一といってよいですね。

まぁ、グラフテンプレートの作り方といえばそれまでなので。

### 検証結果

CFの削除について、
グラフテンプレートで利用されていないCFは削除しても大丈夫。
グラフテンプレートで利用されていても、参照先のCFが無ければ、自動的に除外され、エラーにはならない。

同一CFの解像度の異なるインデックスの削除について
最も解像度の高いRRAが残っていれば大丈夫。
それ以外を削除しても大丈夫だが、当該のRRAインデックスよりも小さなものは削除してはいけない。



## 実装

前述の検証結果に基づいて、グラフテンプレートに利用されていないCFを削除する。
調査の結果、自身の環境では、先ほどのWindows/LinuxのCPU利用率以外はすべてAVERAGEのみだった。
CPU利用率の過去グラフでMAX値はあまり必要ないので、AVERAGE以外はすべて削除してしまうことにする。
トラブル時でも調査だとしても、CPU利用率の参照は、せいぜい１週間くらいと思われる。
まぁ、あったらあったで、グラフ見てニヤニヤできる楽しさは残せるんだけど。

perlでスクリプトを作成

```perl:redrra.pl
#!/bin/perl

# 今回の保管対象は CF:AVERAGEでROWS:157680
# もともとのDataProfileではCFが４種、RRAが４種だったので、インデックスは0-15までで決め打ちしちゃう

$target_cf = "AVERAGE";
$target_rows = 157680;
$index_max = 15;

foreach $file (glob("./*.rrd")){

  # rrdtool info を実施し、保管対象のCFとROWSをgrepしておく
  $cmd = "rrdtool info $file | grep 'cf\\ =\\ \\\"$target_cf\\\"\\|rows\\ =\\ $target_rows' ";

  undef(%flg_cf); # CFのチェック用フラグ
  foreach(split(/\n/,`$cmd`)){

    # まずCFをチェックして当該のインデックス値を保管しておく
    if($_ =~ /\[(\d+)\]\.cf\ =\ \"$target_cf\"/){
      $flg_cf{$1}  = 1;
    }

    # まずROWSをチェックして当該のCFと同一のインデックス値だったら、それが保管対象のインデックスになる
    if($_ =~ /\[(\d+)\]\.rows\ =\ $target_rows/){
      $check_index = $1;
      if($flg_cf{$check_index}){
        last;
      }
    }

  }

  # 当該インデックスより小さなものは削除できないので、当該インデックスの次から最後まで削除する
  $cmd = "rrdtool tune $file ";
  foreach (($check_index+1)..$index_max){
    if($_ ne $check_index){
      $cmd .= "DELRRA:$_ ";
    }
  }

  # テスト用表示
  #print $cmd."\n";
  system($cmd);
}

exit;
```



## 確認する

```
// 自身の環境では、RRDファイルは174個ありました。
$ ls -1 | nl | tail -n1
   174  vmwlc01_traffic_in_297.rrd

// データサイズは2.5G
$ du -hs
2.5G    ./

$ ./redrra.pl

// データサイズは571MBに削減
$ du -hs ./
571M    ./
```

データサイズは2.5GB → 571MBとなり、1/5程度となりました。
あ、もちろんバックアップはとりましたよ。
あと、実施後は、RRDファイルのパーミッションがapacheから読み書きできる状態であるか、確認しておきます。


**もとのデータを利用したグラフの一覧**
[![Screenshot from Gyazo](https://gyazo.com/04aa832ae06e7d54bfb2635ba6e2e8c4/raw)](https://gyazo.com/04aa832ae06e7d54bfb2635ba6e2e8c4)


**RRAを削除したあとのRRDデータを利用したグラフの一覧**
[![Screenshot from Gyazo](https://gyazo.com/70ee87ab44e73620f30392520c797b2a/raw)](https://gyazo.com/70ee87ab44e73620f30392520c797b2a)

うーん、**つまんねぇグラフだな**

スクリーンショットのタイミングでデータ更新されたため、すこーしピーク場所などが変わっていますが、
Windows/LinuxのCPU負荷のグラフ以外はほとんど見た目は変わっていません。

CPU負荷のピークとかは拡大してみればよいわけですし。
[![Screenshot from Gyazo](https://gyazo.com/333643810708df9bbb8eda19a8693f23/raw)](https://gyazo.com/333643810708df9bbb8eda19a8693f23)


おしまい


# あとしまつ

ログも特にエラーは出ていないようです。

## DataProfileの作成

AVEのみ５分毎の値を1.5年間保管する、というプロファイルを作成し、デフォルトに設定します。
いままでは１データソースあたり約6MByteでしたが、修正後は約1.2MByteになりました。
[![Screenshot from Gyazo](https://gyazo.com/6d953aae7d698eb01045432696f4e180/raw)](https://gyazo.com/6d953aae7d698eb01045432696f4e180)


## cactiでの定義との乖離

Console -> Management -> Datasourceからデバッグ情報をみると、さっき作ったDataProfileで作成されたことになってて（ほんとは違うのに）、知らないインデックスがあるぞ、と言われていますが、まぁ大丈夫でしょう。
[![Screenshot from Gyazo](https://gyazo.com/2b2a03a2f5e3ab140f8ddff1d74b011e/raw)](https://gyazo.com/2b2a03a2f5e3ab140f8ddff1d74b011e)

# さいごに

インデックスを０まで詰めたかったので、 `rrdtool resize check_last.rrd 0 GROW 157133 `からの`rrdtool tune resize.rrd DELRRA:0`とかやってみたけど、ダメでした。
たぶん ダンプの結果を新規作成したRRDに突っ込めばいけるとは思うのですが、なんか力業すぎてちょっと避けました。

あと、rrdtoolの機能すごい。とcactiの機能すごい。全然つかいこなせませんわ。



# 出典
https://oss.oetiker.ch/rrdtool/doc/rrdtune.en.html