Tenax
日本語
GitHub

Kubernetes 向けバックアップ・復元オペレーター · Postgres 15–17 · PostGIS · MySQL 8.4

1秒たりとも、手放さない。

Tenax は Kubernetes で動かしている Postgres と MySQL を、あなた自身の S3 バケットにバックアップし、すべての変更を発生したそばからストリーミングします。範囲内の任意の1秒を選べば、まさにその時点のデータベースが、クエリできるサンドボックスに戻ってきます。データはクラスタを出る前に暗号化され、クラッシュオンリー設計なので決してハングしません。

バイナリひとつ、チャートひとつ。バケットも鍵もあなたのもの。

ポイントインタイム復元の範囲 · team/app · ポリシー prod 範囲 7日間 · チェーン 正常

復元時点
2026-10-10 14:02:37 UTC
ベースバックアップ
20261010T020712Z-4c1e
続けて適用
114
$ tenax restore team/app --at 2026-10-10T14:02:37Z --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 にあります。

01仕組み5つのステップ

ライブの変更から、クエリできるサンドボックスまで。

データベース内にエージェントは不要、ボリュームスナップショットも使いません。Tenax は変更をネットワーク越しに読み取り、Pod を出る前に封をして、求められればどの1秒でも取り戻します。

  1. 01

    ストリーミング

    スケジュールされたフルバックアップに加え、すべての変更を発生したそばから取り込みます。Postgres には Tenax 独自の WAL レシーバー、MySQL には mysqlbinlog。

    WAL · binlog
  2. 02

    暗号化

    すべてのバイトを zstd で圧縮し、age であなたの公開鍵宛てに暗号化します。Pod の中で、一時ファイルなしの1回のストリーミング処理で行います。

    zstd → age
  3. 03

    保存

    S3 互換の任意のバケットへ並列マルチパートでアップロード。オブジェクトごとに SHA-256 を記録し、マニフェストは最後に書き込みます。

    *.zst.age · manifest.json
  4. 04

    秒単位で復元

    時刻かバックアップ ID を指定するだけ。Tenax がベースバックアップを選び、その1秒ちょうどまで変更を適用します。

    --at 14:02:37
  5. 05

    サンドボックスを開く

    結果は TTL 付きの専用データベースと読み取り専用のクエリコンソールに届きます。復元を成功とみなす前に、あなたのチェックが実行されます。

    --ttl 6h

02復元4つの復元先

本番に触れずに、どこへでも復元。

復元はほかと同じ Kubernetes リソースです。時刻かバックアップ ID を指定し、復元先を選ぶだけ。Tenax がベースバックアップと適用する変更を計画し、検証を実行してから成功とします。

SANDBOX

使い捨てのコピー

TTL・認証情報・読み取り専用クエリコンソール付きのプライベートなデータベース。期限が来れば自動で片付きます。

--to sandbox --ttl 6h
NEW

新しいデータベース

ステージング用サンドボックスで復元・検証してから新しい名前でコピー。既存のオブジェクトは決して上書きしません。

--to new --database app_copy
IN PLACE

本当に巻き戻す

まず安全のためのバックアップを取り、対象名の入力を求めます。restorer-admin ロールが必要です。

--to in-place --confirm app
DOWNLOAD

ダンプファイル

復号・圧縮済みの論理ダンプを、1時間で失効するリンクで提供。監査されます。

--to download

03デモ1分

1分で、一覧から復元まで。

デモ用カタログに対する本物のコマンドと本物のコンソール。全データベースを一覧し、夜のあいだに何が変わったかを確認し、サンドボックスに復元します。

0:58 · 音声あり · 1.9 MB

コンソール

Tenax コンソールの概要:9つのデータベースの最新フルバックアップ、復元可能範囲、遅延、容量、状態
全データベースをひと目で:最新フルバックアップ、復元可能範囲、ストリーム遅延、容量、状態。
Tenax コンソールのデータベース画面:復元可能なタイムライン、行数と容量の推移、検証済みバッジ付きのバージョン一覧
1つのデータベース:復元可能なタイムライン、推移、すべてのバージョン。検証済みの復元に印が付きます。
Tenax コンソールの2つのバックアップの差分:追加された列、空になったテーブル、テーブルごとの行数と容量
2つのバージョンを並べて:適用されたマイグレーション、変わった列、テーブルごとの行数と容量。

2つのバージョン間で何が変わったかがわかる。

各バックアップはテーブル・行数・容量の一覧を持ち、各変更セグメントはその内容の要約を持ちます。だから復元する前に、失った行がまだ残っているバージョンがわかります。

  • 任意の2つのバックアップの差分。 スキーマ変更、行数、テーブルごとの容量。
  • WAL・binlog セグメントごとの変更要約。 テーブルごとの INSERT・UPDATE・DELETE と DDL。22:48 の TRUNCATE もすぐ見つかります。
  • マスク済みプレビュー。 指定した列をマスクしたサンプル行。データ本体は復号できないカタログ鍵で読めます。

CLI

CLI
$ tenax 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
CLI
$ tenax 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
CLI
$ tenax 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

$ tenax 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  tenax-sbx-sbx-7f3a.tenax-sandboxes.svc:5432
sbx-c210  mysql     billing/invoices  dev@acme.example    Provisioning  -

リポジトリのデモサーバーの出力です:go run ./hack/demo を実行し、CLI かブラウザを向けてください。

コンソール、CLI、API

OpenAPI で記述された JSON API と、Cloudflare Access によるロール。すべての書き込みとクエリが監査されます。

GitOps ネイティブ

ストア、ポリシー、ターゲット、復元はすべて CRD。保持階層、アラート、Grafana ダッシュボードがチャートに含まれます。

願うのではなく、検証する

定期的な検証用復元が実際の復元に対して SQL チェックを実行し、バックアップを検証済みにします。復元時間はメトリクスとして出力されます。

04信頼性クラッシュオンリー

決してハングしない。kill -9 にも動じない。

バックアップは夜中に、ありふれた理由で失敗します。固まったマウント、死んだ Pod、書きかけのファイル。Tenax は、そのどれにも止められず、どれもバックアップとして紛れ込まないように作られています。

決して固まらない

マウントもブロックデバイスもなし、データベースへはネットワーク経由のみ。すべてのストリームに進捗ウォッチドッグがあり、各ツールは独自のプロセスグループで動きます。

クラッシュオンリー

どの Pod をいつ止めても大丈夫。バックアップはマニフェストが最後に書かれて初めて成立し、カオステストがバックアップ中に Tenax を止めてそれを証明します。

データは Job で運ばれ、コントローラーは通らない。

コントローラーは計画するだけ。mover と streamer がひとつのストリーミングパイプラインでバイトをバケットへ直接運び、restorer が取り戻します。

本番にやさしい

書き込みを行わない、最小権限のデータベースユーザー 1 つ。ホストごとの枠、帯域制限、サーバーが混んでいればバックアップを延期する負荷ガード。

変更ストリーミング

Postgres はレプリケーションスロット上の Tenax 独自の WAL レシーバー、MySQL は mysqlbinlog。静かなデータベースでも範囲は現在まで届きます。

設計からして速い

一時ファイルなしのストリーミングパイプライン、並列マルチパートアップロード、マルチスレッド zstd、そしてミリ秒で応答するコンソールを支えるインメモリカタログ。

存在を忘れるほど軽い。

Tenax は Go のバイナリひとつです。コントローラーと API は計画と読み取りだけを行い、データは処理が終われば終了する短命な Job で運ばれます。バックアップの合間にクラスタに重いものは残りません。

54MB単一の静的バイナリ:コントローラー、API とコンソール、mover、streamer、restorer、CLI
18MB圧縮ダウンロード。ランタイムもサイドカーも不要
21MiB多数のデータベースを監視中のコントローラーの待機時メモリ
19MiBカタログをメモリに載せた API とコンソールの待機時メモリ

k3d テストクラスタ(12 データベース)で E2E とカオスのスイート後に kubelet が報告したワーキングセット。待機時の CPU は数ミリコア程度です。ポイントインタイム復元を有効にした各データベースでは、ストリーミング中に約 70 MiB の streamer も動きます。

05セキュリティ送信元で暗号化

バケットに見えるのは暗号文だけ。

暗号化はアップロード前に Pod の中で、Tenax が保持しない鍵に対して行われます。アクセスはロールで分けられ、すべての書き込みとクエリが監査記録に残ります。

バケットから見える中身
$ head -c 200 data/part-00001.zst.age
age-encryption.org/v1
-> X25519 Qm9yZWQ/IE5vdGhpbmcgdG8gc2VlIGhlcmUu…
--- 7Hc1kS0zR2QeYc…
ÌÞ#ø§ñ¹t¤¢Ý}ϰë։ÿ]Wœ3Ð÷…

すべてのデータオブジェクトは、受信者宛てに暗号化された age ファイル(X25519)です。平文の JSON はマニフェストだけで、秘密情報は含みません。

  • 出ていく前に暗号化

    すべてのオブジェクトはクライアント側で age によりあなたの鍵へ暗号化されます。バケットは平文を見ず、コンソールのカタログ鍵ではデータを読めません。

  • データを読めないカタログ鍵

    コンソールはインベントリとマスク済みプレビューを、別のカタログ用 ID で読み取ります。ダンプ、ベースバックアップ、WAL、binlog は復号できません。

  • 端から端まで検証

    マニフェストは保存した全オブジェクトの SHA-256 を持ち、読み取りのたびに最後に照合されます。マニフェスト自体は条件付き PUT で最後に書き込まれます。

  • 秘密情報は見えないところに

    認証情報は封印されたメモリ上のファイルで子プロセスに渡され、argv や環境変数には決して載りません。ログは秘密らしき値をすべて伏せます。

  • ロールと監査証跡

    Cloudflare Access でサインインし、viewer から restorer-admin までの4つのロールで管理します。インプレース復元には最上位ロールと確認入力が必要です。

  • 固く閉じた Pod

    非 root、読み取り専用のルートファイルシステム、全 capability を削除、seccomp は RuntimeDefault。データ経路の Pod がマウントするのは emptyDir だけです。

06比較cron ジョブと

バケットに置いた夜間ダンプ以上のもの。

こんなとき…cron + pg_dump スクリプトTenax
09:41:27 に戻したい昨夜のダンプ。最大24時間分を失う範囲内の任意の1秒
バックアップが戻せると証明したい障害の最中に判明するチェック付きの定期検証用復元
消えた行が残るバージョンを探したいいくつも復元して確かめるコンソールの差分と変更要約
リスクなしで中身を見たい共有ステージングに上書き復元TTL 付きのプライベートなサンドボックス
バケットに中身を見せたくない設定していればサーバー側暗号化あなたの鍵によるクライアント側 age 暗号化
バックアップ中のノード障害に耐えたい完全に見える書きかけのファイルマニフェストが最後に書かれるまで何も成立しない
動き続けていると知りたい沈黙遅延バックアップ、チェーン切れ、遅延へのアラート

07インストール約10分

ゼロから復元可能へ。

  1. チャートをインストール

    CRD、コントローラー、API とコンソール、RBAC、アラート、ダッシュボード。

    helm install tenax charts/tenax -n tenax-system --create-namespace \
      --set image.repository=registry.example.com/tenax/tenax --set image.tag=v0.1.0 \
      --set 'targetNamespaces={team-a}'
  2. バケットと鍵を指定

    S3 互換ストレージならどれでも。age-keygen で age 鍵を作り、オフラインに控えを保管してください。

    apiVersion: tenax.republic.global/v1alpha1
    kind: BackupStore
    metadata: { name: main }
    spec:
      s3:
        endpoint: https://s3.example.com
        region: us-east-1
        bucket: db-backups
        credentialsSecret: { namespace: tenax-system, name: tenax-s3 }
      encryption:
        ageRecipients: [age1…]   # 公開鍵のみ。秘密鍵はオフラインに
  3. データベースの隣にターゲットを宣言

    docs/sql/ の SQL で tenax ユーザーを作り、ターゲットを git にコミット。

    apiVersion: tenax.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: tenax-postgres }
  4. あえて何かを復元してみる

    最初のフルバックアップは自動で始まります。あとは、使わずに済むことを願うあの操作を試してください。

    tenax targets
    tenax restore team-a/app --at 2026-10-07T09:41:27Z --wait
    tenax query <sandbox> -e "select count(*) from orders"

完全なガイド、すべての CRD フィールド、REST API はドキュメント(英語)にあります。

08FAQ率直な答え

エンジニアが最初に聞くこと。

Tenax が対応しているデータベースは?

Postgres 15・16・17(PostGIS を含む)は、物理または論理のフルバックアップと WAL ストリーミングに対応。MySQL 8.4 は論理フルバックアップと binlog ストリーミングに対応しています。

どこまで遡って、どれくらいの精度で復元できますか?

保持ティアが残しているフルバックアップならどれでも、さらにポリシーで決めたポイントインタイム範囲内なら任意の1秒に復元できます。サンプルの prod ポリシーは7日間の範囲と、日次14・週次8・月次12・年次5のフルバックアップを保持します。

Tenax やストレージ事業者にデータを読まれませんか?

バケットからは読めません。すべてのオブジェクトはアップロード前に age で受信者宛てに暗号化されます。コンソールはデータ本体を復号できないカタログ鍵でインベントリを読みます。復元時はあなたが渡す age の ID で復号するので、オフラインのコピーを保管してください。

バックアップの途中で Pod が落ちたら?

問題になることは何も起きません。バックアップは、すべてのオブジェクトのアップロードと検証が終わり、マニフェストが書かれて初めて存在します。途中で止まった実行にはマニフェストがないため、復元候補に出ることはありません。カオステストはバックアップ中の Tenax を強制終了して、これを確かめています。

Tenax はボリュームをマウントしたりスナップショットを使ったりしますか?

いいえ。データベースにはネットワーク越しにだけ接続し、Pod がマウントするのは emptyDir だけです。hostPath、FUSE、iSCSI、ブロックデバイスは使わず、デフォルトでは PVC もありません。ストレージドライバーが固まってもバックアップは止まりません。

データベース側に何が必要ですか?

データベースごとに、書き込みを一切しない最小権限の tenax ユーザーを1つ。docs/sql/ の SQL で作成します。レプリケーション権限がなくても論理バックアップは取得でき、秒単位の復元に何を付与すればよいかを Tenax が教えてくれます。

Tenax はオープンソースですか?

The Republic Lex Aequa License 1.0 のもとでソースを公開しています。自分のデータを守るためなら、本番環境や営利企業の中も含め、誰でも無料で利用・改変・再配布できます。第三者への販売やサービスとしての提供は認められていません。

必要になる前に、復元を試しておこう。