CLOVER🍀

That was when it all began.

Prometheusのデータ(TSDB)のSnapshotを取得して、リストアまで

これは、なにをしたくて書いたもの?

Prometheusのストレージまわりがちょっと気になって、いくつか見ていくことにしました。

最初に、Snapshotについて見ていきたいと思います。

Snapshot?

TSDB Admin APIに、Snapshotを取得できるAPIが登場します。

TSDB Admin APIs

TSDBというのは、Prometheusの時系列データベースのことです。

GitHub - prometheus/tsdb: The Prometheus time series database layer.

Prometheus 2.9.2では、TSDB 0.7.1を使用しています。

https://github.com/prometheus/prometheus/blob/v2.9.2/go.mod#L82

そのフォーマットについては、こちらに記載があります。

tsdb/README.md at v0.7.1 · prometheus/tsdb · GitHub

で、SnapshotというのはTSDB Admin APIを使って取得できる、TSDBのその時点のデータをデータディレクトリの配下に
スナップショットとして作成する機能です。

Snapshot

こちらを使って、Snapshotの取得して、リストアまで行ってみたいと思います。

環境

今回の環境は、こちらです。

$ lsb_release -a
No LSB modules are available.
Distributor ID: Ubuntu
Description:    Ubuntu 18.04.2 LTS
Release:    18.04
Codename:   bionic

Prometheusのバージョンは、2.9.2を利用します。

とりあえず、起動してみる

まずは、Prometheusをデフォルト設定で起動してみます。

$ ./prometheus
level=info ts=2019-04-28T17:19:55.195Z caller=main.go:285 msg="no time or size retention was set so using the default time retention" duration=15d
level=info ts=2019-04-28T17:19:55.195Z caller=main.go:321 msg="Starting Prometheus" version="(version=2.9.2, branch=HEAD, revision=d3245f15022551c6fc8281766ea62db4d71e2747)"
level=info ts=2019-04-28T17:19:55.195Z caller=main.go:322 build_context="(go=go1.12.4, user=root@1d43b6951e8f, date=20190424-15:32:31)"
level=info ts=2019-04-28T17:19:55.195Z caller=main.go:323 host_details="(Linux 4.15.0-48-generic #51-Ubuntu SMP Wed Apr 3 08:28:49 UTC 2019 x86_64 7020bd11d63d (none))"
level=info ts=2019-04-28T17:19:55.195Z caller=main.go:324 fd_limits="(soft=1048576, hard=1048576)"
level=info ts=2019-04-28T17:19:55.195Z caller=main.go:325 vm_limits="(soft=unlimited, hard=unlimited)"
level=info ts=2019-04-28T17:19:55.196Z caller=web.go:416 component=web msg="Start listening for connections" address=0.0.0.0:9090
level=info ts=2019-04-28T17:19:55.196Z caller=main.go:640 msg="Starting TSDB ..."
level=info ts=2019-04-28T17:19:55.202Z caller=main.go:655 msg="TSDB started"
level=info ts=2019-04-28T17:19:55.202Z caller=main.go:724 msg="Loading configuration file" filename=prometheus.yml
level=info ts=2019-04-28T17:19:55.203Z caller=main.go:751 msg="Completed loading of configuration file" filename=prometheus.yml
level=info ts=2019-04-28T17:19:55.203Z caller=main.go:609 msg="Server is ready to receive web requests."

この状態でも、Prometheusのデフォルト設定で、Prometheus自身のメトリクスを取得するようになっています。

# A scrape configuration containing exactly one endpoint to scrape:
# Here it's Prometheus itself.
scrape_configs:
  # The job name is added as a label `job=<job_name>` to any timeseries scraped from this config.
  - job_name: 'prometheus'

    # metrics_path defaults to '/metrics'
    # scheme defaults to 'http'.

    static_configs:
    - targets: ['localhost:9090']

とりあえず、「process_cpu_seconds_total」でも見てみましょう。

f:id:Kazuhira:20190429023150p:plain

取得されたメトリクスが表示されています。

ここで、データディレクトリの中を見てみます。

$ find data -type f
data/wal/00000000
data/lock

現状はこうです、と。では、ドキュメントに習ってSnapshotを取ってみます。

Snapshot

すると、怒られます。

$ curl -XPOST http://localhost:9090/api/v1/admin/tsdb/snapshot
{"status":"error","errorType":"unavailable","error":"admin APIs disabled"}

ドキュメントを見返すと、起動時に「--web.enable-admin-api」オプションが必要なようです。

TSDB Admin APIs

仕方がないので、1度Prometheusを停止して、「--web.enable-admin-api」オプションを付与して起動しなおします。

$ ./prometheus --web.enable-admin-api

再起動したので、メトリクスを取得できていないタイミングができてしまいましたが…。

f:id:Kazuhira:20190429023603p:plain

気を取り直して、Snapshot取得に再チャレンジ。
※ちなみに、Prometheus 2.9からPUTでの呼び出しも可能だそうです

$ curl -XPOST http://localhost:9090/api/v1/admin/tsdb/snapshot
level=info ts=2019-04-28T17:36:41.486Z caller=compact.go:499 component=tsdb msg="write block" mint=1556472013810 maxt=1556472988810 ulid=01D9JGADE0D7D2W9A5FT93YT0N duration=77.938466ms
{"status":"success","data":{"name":"20190428T173641Z-5954bed5ce03a992"}}

今度はうまくいきました。

Snapshotを含めたデータディレクトリの内容は、こんな感じになっています。

$ find data -type f
data/wal/00000000
data/lock
data/snapshots/20190428T173641Z-5954bed5ce03a992/01D9JGADE0D7D2W9A5FT93YT0N/meta.json
data/snapshots/20190428T173641Z-5954bed5ce03a992/01D9JGADE0D7D2W9A5FT93YT0N/index
data/snapshots/20190428T173641Z-5954bed5ce03a992/01D9JGADE0D7D2W9A5FT93YT0N/tombstones
data/snapshots/20190428T173641Z-5954bed5ce03a992/01D9JGADE0D7D2W9A5FT93YT0N/chunks/000001

Snapshotの「name」は、「snapshots」直下のディレクトリ名(今回は「20190428T173641Z-5954bed5ce03a992」)に
なるようですね。

Snapshot内のmeta.json以外のファイルは全部バイナリですが、中身を見てみるとこんな感じでした。
data/snapshots/20190428T173641Z-5954bed5ce03a992/01D9JGADE0D7D2W9A5FT93YT0N/meta.json

{
    "ulid": "01D9JGADE0D7D2W9A5FT93YT0N",
    "minTime": 1556472013810,
    "maxTime": 1556472988810,
    "stats": {
        "numSamples": 29418,
        "numSeries": 485,
        "numChunks": 485
    },
    "compaction": {
        "level": 1,
        "sources": [
            "01D9JGADE0D7D2W9A5FT93YT0N"
        ]
    },
    "version": 1
}

ここで、1度、Prometheusを停止してデータディレクトリをリネームしてみます。

$ mv data data-current
$ ./prometheus --web.enable-admin-api

Web UIを見てみると、当然のことながらデータがなくなっています。

f:id:Kazuhira:20190429023933p:plain

ここでもう1度Prometheusを停止させてデータディレクトリを削除し、空のデータディレクトリを作成します。

$ rm -r data
$ mkdir data

この中に、先ほど取得したSnapshotの「中身」をコピーします。

$ cp -R data-current/snapshots/20190428T173641Z-5954bed5ce03a992/* data

つまり、こういう状態になります。

$ find data -type f
data/01D9JGADE0D7D2W9A5FT93YT0N/meta.json
data/01D9JGADE0D7D2W9A5FT93YT0N/index
data/01D9JGADE0D7D2W9A5FT93YT0N/tombstones
data/01D9JGADE0D7D2W9A5FT93YT0N/chunks/000001

これでPrometheusを起動させて、Web UIを確認してみます。

$ ./prometheus --web.enable-admin-api

すると、今度はSnapshotで取得したデータが見えるようになっています。

f:id:Kazuhira:20190429024401p:plain

もちろん、止めていた間はデータがないことになってしまいます。

これで、1度起動できたからといって、Snapshotからコピーしたデータを削除すると

$ rm -rf data/01D9JGADE0D7D2W9A5FT

Snapshotを取得した時のデータがなくなります。

f:id:Kazuhira:20190429024747p:plain

Snapshotから戻したデータも、そのまま置いておきましょう、と。

Snapshotは複数回取得することができますが、

$ curl -XPOST http://localhost:9090/api/v1/admin/tsdb/snapshot
level=info ts=2019-04-28T17:50:07.527Z caller=compact.go:499 component=tsdb msg="write block" mint=1556472013810 maxt=1556473093810 ulid=01D9JH30JNEYZRKPPFA056R4BA duration=81.852128ms
{"status":"success","data":{"name":"20190428T175007Z-5a80b8f4cc055ea8"}

持っているデータ全体のSnapshotなので、どの時点のものを利用するかを選びましょう。

$ find data -type f
data/wal/00000000
data/lock
data/snapshots/20190428T173641Z-5954bed5ce03a992/01D9JGADE0D7D2W9A5FT93YT0N/meta.json
data/snapshots/20190428T173641Z-5954bed5ce03a992/01D9JGADE0D7D2W9A5FT93YT0N/index
data/snapshots/20190428T173641Z-5954bed5ce03a992/01D9JGADE0D7D2W9A5FT93YT0N/tombstones
data/snapshots/20190428T173641Z-5954bed5ce03a992/01D9JGADE0D7D2W9A5FT93YT0N/chunks/000001
data/snapshots/20190428T175007Z-5a80b8f4cc055ea8/01D9JH30JNEYZRKPPFA056R4BA/meta.json
data/snapshots/20190428T175007Z-5a80b8f4cc055ea8/01D9JH30JNEYZRKPPFA056R4BA/index
data/snapshots/20190428T175007Z-5a80b8f4cc055ea8/01D9JH30JNEYZRKPPFA056R4BA/tombstones
data/snapshots/20190428T175007Z-5a80b8f4cc055ea8/01D9JH30JNEYZRKPPFA056R4BA/chunks/000001

これで、最低限の確認はできた気がします。

ドキュメントにはSnapshotのことは書いているものの、リストアのことについてはほぼ触れられていなかったので、
どう戻したらいいのかちょっと困りました。

他にも、範囲を絞ったり、ヘッドブロックにのみ存在してまだディスクに圧縮されていないSnapshotのデータをスキップ
するようにもできるみたいですが、今回はパスです。

Prometheusのストレージについては、他にもリテンション(データの保持期間)や

Storage | Prometheus

外部ストレージのことなども考えないといけないようですが、

Remote Endpoints and Storage

これらは順次やっていくとしましょう。

今回は、こんなところで。