Kubernetes 向けバックアップ・復元オペレーター · Postgres 15–17 · PostGIS · MySQL 8.4
データベースのすべての1秒を、復元可能に。
Keeper は Kubernetes で動かしているデータベースを任意の S3 バケットにバックアップし、変更を発生したそばからストリーミングします。過去数週間の任意の時点を選べば、そのときのデータベースが使い捨てのサンドボックスに、検証まで済んだ状態で戻ってきます。チケットを起票するより速く。
- 復元時点
- 2026-10-07 09:41:27 UTC
- ベースバックアップ
- 20261007T020712Z-4c1e
- 続けて適用
- 114
$ keeper restore team/app --at 2026-10-07T09:41:27Z --wait
範囲の上をドラッグしてください。琥珀色の目盛りが毎晩のフルバックアップ、青い帯がその間の変更ストリームです。prod ポリシー(毎晩フル、7日間)のイメージです。
- 9s644 MiB の Postgres をサンドボックスに復元する時間(取得・復号・リカバリ・検証まで)
- 4.2×バケット上で小さく:683 MiB の物理バックアップが 161 MiB に
- 72MiB/s4 vCPU での論理フルバックアップのスループット(一時ファイルなしのストリーミング)
- 20ms1,000 データベース・10万バックアップでのコンソール概要画面の p99
k3d テスト環境(4 vCPU、クラスタ内 MinIO)で make test-perf と make test-ui により計測。数値はリポジトリの docs/PERF.md にあります。
1分で、一覧から復元まで。
デモ用カタログに対する本物のコマンドと本物のコンソール。全データベースを一覧し、夜のあいだに何が変わったかを確認し、サンドボックスに復元します。
コンソール
CLI
$ keeper targets TARGET ENGINE ORG/PROJECT/ENV LAST FULL PITR WINDOW STORED HEALTH analytics/events postgres acme/data/staging 2026-10-07 02:11:47Z - 19.1GiB ok auth/users postgres acme/platform/prod 2026-10-07 02:11:06Z 2026-09-23 02:11:06Z → 2026-10-07 18:50:52Z 1.5GiB ok billing/invoices mysql acme/billing/prod 2026-10-07 02:09:03Z 2026-09-23 02:09:03Z → 2026-10-07 18:50:52Z 6.7GiB ok billing/ledger postgres acme/billing/prod 2026-10-07 02:08:22Z 2026-09-23 02:08:22Z → 2026-10-07 18:50:52Z 14.3GiB ok maps/geo postgres acme/maps/prod 2026-10-07 02:10:25Z 2026-09-23 02:10:25Z → 2026-10-07 18:50:52Z 23.3GiB ok shop-dev/orders-db postgres acme/shop/dev 2026-10-07 02:12:28Z - 17.4MiB ok shop/catalog-db postgres acme/shop/prod 2026-10-07 02:07:41Z 2026-09-23 02:07:41Z → 2026-10-07 18:50:52Z 731.0MiB ok shop/orders-db postgres acme/shop/prod 2026-10-07 02:07:00Z 2026-09-23 02:07:00Z → 2026-10-07 18:50:52Z 9.6GiB ok web/wordpress mysql acme/marketing/prod 2026-10-07 02:09:44Z 2026-09-23 02:09:44Z → 2026-10-07 18:50:52Z 607.4MiB ok
$ keeper diff 20261006T020700Z-bd79 20261007T020700Z-6da5 from 20261006T020700Z-bd79 (2026-10-06 02:07:00Z) to 20261007T020700Z-6da5 (2026-10-07 02:07:00Z) 20261007 applied (was 20261006) column orders.gift_note added table audit_log +158,175 rows (+2%) table order_items +62,923 rows (+1%) table orders +15,655 rows (+1%) table coupons −3,120 rows (−100%) table customers +531 rows (+1%) 14 segments in between: 00000001000000A100000000 18:51:32–19:20:32 · 800 commits · audit_log +3348 / ~1116 / −83 · order_items +1327 / ~442 / −33 · orders +330 / ~110 / −8 · customers +11 / ~3 / −0 00000001000000A100000001 19:21:32–19:50:32 · 813 commits · audit_log +3352 / ~1116 / −83 · order_items +1328 / ~442 / −33 · orders +330 / ~110 / −8 · customers +13 / ~3 / −0 00000001000000A100000002 19:51:32–20:20:32 · 826 commits · audit_log +3356 / ~1116 / −83 · order_items +1329 / ~442 / −33 · orders +330 / ~110 / −8 · customers +15 / ~3 / −0
$ keeper restore shop/orders-db --at 20261006T020700Z-bd79 --ttl 6h --reason "coupons truncated" restore orders-db-sandbox-60414a15fc created (sandbox from 20261006T020700Z-bd79, 0 segments) restore shop/orders-db-sandbox-60414a15fc $ keeper sandbox list NAME ENGINE SOURCE OWNER PHASE EXPIRES IN-CLUSTER sbx-7f3a postgres shop/orders-db maria@acme.example Ready 2026-10-08 00:37:32Z keeper-sbx-sbx-7f3a.keeper-sandboxes.svc:5432 sbx-c210 mysql billing/invoices dev@acme.example Provisioning -
リポジトリのデモサーバーの出力です:go run ./hack/demo を実行し、CLI かブラウザを向けてください。
本番に触れずに、どこへでも復元。
復元はほかと同じ Kubernetes リソースです。時刻かバックアップ ID を指定し、復元先を選ぶだけ。Keeper がベースバックアップと適用する変更を計画し、検証を実行してから成功とします。
使い捨てのコピー
TTL・認証情報・読み取り専用クエリコンソール付きのプライベートなデータベース。期限が来れば自動で片付きます。
--to sandbox --ttl 6h新しいデータベース
ステージング用サンドボックスで復元・検証してから新しい名前でコピー。既存のオブジェクトは決して上書きしません。
--to new --database app_copy本当に巻き戻す
まず安全のためのバックアップを取り、対象名の入力を求めます。restorer-admin ロールが必要です。
--to in-place --confirm appダンプファイル
復号・圧縮済みの論理ダンプを、1時間で失効するリンクで提供。監査されます。
--to download存在を忘れるほど軽い。
Keeper は Go のバイナリひとつです。コントローラーと API は計画と読み取りだけを行い、データは処理が終われば終了する短命な Job で運ばれます。バックアップの合間にクラスタに重いものは残りません。
k3d テストクラスタ(12 データベース)で E2E とカオスのスイート後に kubelet が報告したワーキングセット。待機時の CPU は数ミリコア程度です。ポイントインタイム復元を有効にした各データベースでは、ストリーミング中に約 70 MiB の streamer も動きます。
設計からして速い
一時ファイルなしのストリーミングパイプライン、並列マルチパートアップロード、マルチスレッド zstd、そしてミリ秒で応答するコンソールを支えるインメモリカタログ。
設計からして堅牢
クラッシュオンリー設計:どの Pod もいつ止まってもかまいません。状態は CRD と S3 にあり、バックアップはマニフェストが最後に書かれた時点で初めて成立し、無期限に待つ処理はありません。
2つのバージョン間で何が変わったかがわかる。
各バックアップはテーブル・行数・容量の一覧を持ち、各変更セグメントはその内容の要約を持ちます。だから復元する前に、失った行がまだ残っているバージョンがわかります。
- 任意の2つのバックアップの差分。 スキーマ変更、行数、テーブルごとの容量。
- WAL・binlog セグメントごとの変更要約。 テーブルごとの INSERT・UPDATE・DELETE と DDL。22:48 の TRUNCATE もすぐ見つかります。
- マスク済みプレビュー。 指定した列をマスクしたサンプル行。データ本体は復号できないカタログ鍵で読めます。
何かが起きた夜のために。
変更ストリーミング
Postgres はレプリケーションスロット上の Keeper 独自の WAL レシーバー、MySQL は mysqlbinlog。静かなデータベースでも範囲は現在まで届きます。
願うのではなく、検証する
定期的な検証用復元が実際の復元に対して SQL チェックを実行し、バックアップを検証済みにします。復元時間はメトリクスとして出力されます。
出ていく前に暗号化
すべてのオブジェクトはクライアント側で age によりあなたの鍵へ暗号化されます。バケットは平文を見ず、コンソールのカタログ鍵ではデータを読めません。
標準で圧縮
ポリシーごとに選べるレベルの zstd を、パイプラインで一度だけ適用。ダンプツールは非圧縮で動き、データベースサーバーの CPU は使いません。
決して固まらない
マウントもブロックデバイスもなし、データベースへはネットワーク経由のみ。すべてのストリームに進捗ウォッチドッグがあり、各ツールは独自のプロセスグループで動きます。
クラッシュオンリー
どの Pod をいつ止めても大丈夫。バックアップはマニフェストが最後に書かれて初めて成立し、カオステストがバックアップ中に Keeper を止めてそれを証明します。
本番にやさしい
最小権限の読み取り専用ユーザー。ホストごとの枠、帯域制限、サーバーが混んでいればバックアップを延期する負荷ガード。
コンソール、CLI、API
OpenAPI で記述された JSON API と、Cloudflare Access によるロール。すべての書き込みとクエリが監査されます。
GitOps ネイティブ
ストア、ポリシー、ターゲット、復元はすべて CRD。保持階層、アラート、Grafana ダッシュボードがチャートに含まれます。
データは Job で運ばれ、コントローラーは通らない。
コントローラーは計画するだけ。mover と streamer がひとつのストリーミングパイプラインでバイトをバケットへ直接運び、restorer が取り戻します。
バケットに置いた夜間ダンプ以上のもの。
| こんなとき… | cron + pg_dump スクリプト | Keeper |
|---|---|---|
| 09:41:27 に戻したい | 昨夜のダンプ。最大24時間分を失う | 範囲内の任意の1秒 |
| バックアップが戻せると証明したい | 障害の最中に判明する | チェック付きの定期検証用復元 |
| 消えた行が残るバージョンを探したい | いくつも復元して確かめる | コンソールの差分と変更要約 |
| リスクなしで中身を見たい | 共有ステージングに上書き復元 | TTL 付きのプライベートなサンドボックス |
| バケットに中身を見せたくない | 設定していればサーバー側暗号化 | あなたの鍵によるクライアント側 age 暗号化 |
| バックアップ中のノード障害に耐えたい | 完全に見える書きかけのファイル | マニフェストが最後に書かれるまで何も成立しない |
| 動き続けていると知りたい | 沈黙 | 遅延バックアップ、チェーン切れ、遅延へのアラート |
ゼロから復元可能へ。
チャートをインストール
CRD、コントローラー、API とコンソール、RBAC、アラート、ダッシュボード。
helm install keeper charts/keeper -n keeper-system --create-namespace \ --set image.repository=registry.example.com/keeper/keeper --set image.tag=v0.1.0 \ --set 'targetNamespaces={team-a}'バケットと鍵を指定
S3 互換ストレージならどれでも。age-keygen で age 鍵を作り、オフラインに控えを保管してください。
apiVersion: keeper.republic.global/v1alpha1 kind: BackupStore metadata: { name: main } spec: s3: endpoint: https://s3.example.com region: us-east-1 bucket: db-backups credentialsSecret: { namespace: keeper-system, name: keeper-s3 } encryption: ageRecipients: [age1…] # 公開鍵のみ。秘密鍵はオフラインに
データベースの隣にターゲットを宣言
docs/sql/ の SQL で読み取り専用ユーザーを作り、ターゲットを git にコミット。
apiVersion: keeper.republic.global/v1alpha1 kind: BackupTarget metadata: { name: app, namespace: team-a } spec: engine: postgres endpoint: { host: postgres, port: 5432 } databases: [app] policy: prod # 毎晩フル、7日間のポイントインタイム範囲 credentials: secretRef: { name: keeper-backup-postgres } replicationSecretRef: { name: keeper-repl-postgres }
あえて何かを復元してみる
最初のフルバックアップは自動で始まります。あとは、使わずに済むことを願うあの操作を試してください。
keeper targets keeper restore team-a/app --at 2026-10-07T09:41:27Z --wait keeper query <sandbox> -e "select count(*) from orders"
完全なガイド、すべての CRD フィールド、REST API はドキュメント(英語)にあります。