InfluxDB v1 から重複データを3TiB削除してEBSコストを減らした
Axelspaceソフトウェアエンジニアのrharaです。
今回社内で利用しているInfluxDB OSS v1で重複データを除去し、EBSボリュームを5.9TiBから2.9TiBに縮小しました。
取り組む際にあまり情報が見つからなかったので、ログをかねて記します。
InfluxDBとは?
InfluxDBとは、 時系列データの保存に特化した時系列データベースの1つです。
ログの集約のような、書き込みと読み込み性能が重要な用途に特化しています。 (公式ドキュメント)
AxelspaceでのInfluxDB
Axelspaceでは、衛星の運用ログ(消費電力データなど)の記録にInfluxDBを利用しています。
現在InfluxDBのバージョンはv2が主流ですが、衛星の打ち上げ当初からv1のOSS版を使っており、EC2インスタンス上でDockerを使って動かしています。
2018年のGRUS-1Aの打ち上げ以降すべての運用ログが保存されているのですが、一部データが重複して蓄積されており、データを保存しているEBSボリュームのサイズが肥大化してしまいました。
そこで重複データのクリーンアップをして、小容量のEBSボリュームに交換することでコスト削減を行いました。
InfluxDBのクリーンアップ
EBSのコスト削減
EBSでは容量に応じてコストがかかります。ボリューム縮小によってコスト削減を狙いましたが、EBSを直接ボリュームダウンさせることはできないようです。
そこでこのQ&Aに従い、新たに小さなボリュームを用意し既存のものを置き換えるという手順を踏みました。
InfluxDBでの重複データクリーンアップ
はじめに検討したのが、既存のボリュームからInfluxDBのDELETE
機能を用いて重複データを削除し、クリーンアップ後のデータを新しいボリュームにコピーすることです。
InfluxQLのDELETE
キーワードはデータの削除を可能としますが、機能は限られています。(参考)
DELETE
は削除対象の指定方法が限られており、今回対象となる重複データのみを削除することは不可能と判明しました。
参考までに、削除対象を指定する主要な方法は、タイムスタンプか、InfluxDBのtagの値となっています。(公式ドキュメント)
そこで、DELETE
に代わるいくつかのクリーンアップの手法を検討しました。
- InfluxQLが提供する
SELECT * INTO
句を用いる - Fluxクエリを用いる
- Telegrafを利用して一旦データを取り出した後、重複データを削除して、新たに書き込む
結果的には、1. InfluxQL
のSELECT * INTO
を利用しました。
SELECT * INTO
はデータをあるdatabase からもう一つのdatabaseにコピーするときなどに用いるクエリで、一度データを読み出してこれを他のdatabaseに書き込むという処理を行います。
読み出す際に対象とするデータを柔軟に指定できるため、フィールドの削除や重複データの削除などが可能になり、今回のクリーンアップの目的にかなうものでした。
2. は一部のクエリが実装されていないこと、3. は一旦データを展開する必要があるため選択しませんでした。
SELECT * INTO
を用いるときの障壁
SELECT * INTO
句は、削除されないデータのコピーを作るので一時的にデータ量が増えてしまいます。
今回、既存のEBSボリュームを一時的に大きくはしたくないという要望があったので、データのコピー先を新たなボリュームとするようにしました。
また、巨大なデータを扱うときはデータを時間単位で区切ってメモリ使用量が急増しないように注意する必要があります。
クリーンアップ中にも衛星のテレメトリーデータは追加されていくため、InfluxDBが停止しないように気を付ける必要がありました。
1日, 3日など適当な区間で処理を区切り、タイムウィンドウをずらしながら処理していきました。
手順
具体的手順について説明します。
繰り返しになりますが、クリーンアップのアイデアは、小さな新しいEBSボリュームにSELECT * INTO
を用いてデータを削除しながらコピーしていき、最後にEBSボリュームを入れ替えるというものです。
これには2つのボリュームを1つのInfluxDBプロセスが利用することが必要であり、多少のwork around が必要でした。
以下の4ステップで実行しました。
- サイズの小さなEBSボリュームを用意する
- 新旧2つのEBSボリュームをInfluxDBが利用できるようシンボリックリンクを張る
-
SELECT * INTO
の実行 - 旧ボリュームから新ボリュームに他データのコピー
- 新旧ボリュームを入れ替える
さらに詳しく説明します。
1. EBS ボリュームの準備
クリーンアップ後にデータを保持するEBSボリュームを用意しました。
これを新ボリュームと呼び、現在利用しているEBSボリュームを旧ボリュームと呼びます。
EBSの制約から旧ボリュームの容量縮小はできないので、旧ボリュームから容量の小さい新ボリュームに入れ替えることがゴールになります。
実際のEBSボリュームの準備の説明は公式ドキュメントに譲ります。
2. InfluxDBで新旧ボリュームの同時利用
InfluxDB v1 OSSでは、複数ボリュームにデータを分散して保存することはおそらく想定されておりません。
そこで公式ドキュメントにはない作業が必要でした。
2.1 シンボリックリンクの準備
InfluxDBはデフォルトでは/var/lib/influxdb/
にwal
, meta
, data
という3つのディレクトリのサブディレクトリにデータを保存します。(参考)
/var/lib/influxdb/wal
/var/lib/influxdb/data
の下に、databaseごとにディレクトリができることを利用して, シンボリックリンクを活用して乗り切りました。
/var/lib/influxdb/wal/new_db
--> <新ボリュームのマウントポイント>/wal/new_db
, /var/lib/influxdb/data/new_db
--> <新ボリュームのマウントポイント>/data/new_db
のようにリンクを張ります。
以降では簡単のため、<新ボリュームのマウントポイント>を /mnt/new_ebs
として説明します。
シンボリックリンクはInfluxDBでは完全にはうまく動かない(これはv2のポストですが)ので、書き込んだデータが一時的に存在しないように見えることがあります。
それでも書き込みは行われており、最後にシンボリックリンクを経由せずにInfluxDBを設定するとすべてのデータが確認できます。
2.2 InfluxQLでdatabase作成
シンボリックリンクの作成後、InfluxQLにて、CREATE DATABASE new_db
とすることで、InfluxDBが new_db
というdatabaseを認識するようになります。
SELECT * INTO
句を用いてデータのコピーと重複削除
3. SELECT * INTO new_db FROM old_db WHERE ...
のようにしてデータのコピーと重複削除を行います。
*を用いずにtagやフィールドを指定することもできます。
このステップが最も時間がかかり、数年分の5TiB程度のデータすべてを処理するのに数カ月かかりました。
4. 旧ボリュームから新ボリュームに他データのコピー
旧ボリュームにある必要なデータを新ボリュームにコピーします。
まず、/var/lib/influxdb/meta/meta.db/
を /mnt/new_ebs/meta/meta.db
にコピーします。
加えてInfluxDBに old_db
以外のdatabase等がある場合、これを新ボリュームの対応する場所にコピーします。
cp -avi /var/lib/influxdb/data/other_db /mnt/new_ebs/data
や
cp -avi /var/lib/influxdb/wal/other_db /mnt/new_ebs/wal
などのようにします。
rsync
も一つの手です。
5. 新旧ボリュームを入れ替える
InfluxDBが利用するボリュームを新ボリュームにします。
これには、マウントポイントを入れ替えたり、必要に応じてInfluxDBの設定を変更することで行います。
これは、設定ファイルを変更するか、環境変数によって設定できます。(参考)
さいごに
今回の削除対象は主に重複データでしたが、SELECT * INTO
の機能を利用すれば、削除対象を柔軟に指定することが可能になります。
繰り返しになりますが、今回のInfluxQLのSELECT * INTO
句を利用する方法では、databaseの名前を維持できないという欠点があります。
InfluxDBにはdatabaseのリネームを簡単に行うことができないので、もしdatabaseの名前を保ちたいのであれば、同じ名前のdatabaseを再作成しデータをSELECT * INTO
を行って戻す必要があります。
-
例えば
SELECT * INTO
句によってdatabaseの名前を戻す操作。 ↩︎
Discussion