Tenax
한국어
GitHub

Kubernetes용 백업 & 복원 오퍼레이터 · Postgres 15–17 · PostGIS · MySQL 8.4

모든 순간을 붙잡아 두세요.

Tenax는 Kubernetes에서 운영하는 Postgres와 MySQL 데이터베이스를 여러분의 S3 버킷에 백업하고, 모든 변경 사항을 발생하는 즉시 스트리밍합니다. 복원 가능 기간 안에서 원하는 초를 고르면 바로 그 시점의 데이터베이스를 쿼리할 수 있는 샌드박스로 되돌려 받습니다. 클러스터를 떠나기 전에 암호화되며, 크래시 전용으로 설계되어 절대 멈추지 않습니다.

바이너리 하나, 차트 하나. 버킷도 키도 여러분의 것입니다.

특정 시점 복원 가능 기간 · 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에서의 논리 전체 백업 처리량, 임시 파일 없이 스트리밍
  • 20ms데이터베이스 1,000개와 백업 100,000개가 있을 때 콘솔 개요의 p99

k3d 테스트 하네스(4 vCPU, 클러스터 내부 MinIO)에서 make test-perf와 make test-ui로 측정했습니다. 수치는 저장소의 docs/PERF.md에 있습니다.

01작동 방식다섯 단계

실시간 변경에서 쿼리할 수 있는 샌드박스까지.

데이터베이스 안에 에이전트도, 볼륨 스냅샷도 없습니다. Tenax는 네트워크로 변경 사항을 읽고, 파드를 떠나기 전에 봉인하며, 요청하면 어떤 초든 되돌려 놓습니다.

  1. 01

    스트리밍

    예약된 전체 백업과 함께 모든 변경 사항을 발생하는 즉시 받아 옵니다. Postgres는 Tenax 자체 WAL 수신기로, MySQL은 mysqlbinlog로 처리합니다.

    WAL · binlog
  2. 02

    암호화

    모든 바이트는 파드 안에서 임시 파일 없이 한 번의 스트리밍으로 zstd 압축되고 여러분의 공개 키에 맞춰 age로 암호화됩니다.

    zstd → age
  3. 03

    저장

    S3 호환 버킷 어디로든 병렬 멀티파트 업로드하고, 모든 오브젝트에 SHA-256을 붙이며, 매니페스트는 마지막에 기록합니다.

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

    초 단위로 복원

    시각이나 백업 ID를 지정하세요. Tenax가 기준 백업을 고르고 바로 그 초까지 변경 사항을 재생합니다.

    --at 14:02:37
  5. 05

    샌드박스 열기

    결과는 TTL과 읽기 전용 쿼리 콘솔을 갖춘 전용 데이터베이스에 들어갑니다. 복원이 성공으로 인정되기 전에 여러분의 검사가 실행됩니다.

    --ttl 6h

02복원네 가지 복원 위치

프로덕션은 건드리지 않고 어디로든 복원하세요.

복원은 다른 리소스와 다를 바 없는 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

덤프 파일

한 시간 뒤 만료되는 링크로 받는 복호화·압축된 논리 덤프입니다. 감사 로그에 기록됩니다.

--to download

03살펴보기1분

시작부터 복원까지 1분.

데모 카탈로그를 대상으로 실제 명령어와 실제 콘솔을 보여 줍니다. 모든 데이터베이스를 나열하고, 밤사이 무엇이 바뀌었는지 확인한 뒤, 샌드박스로 복원합니다.

0:58 · 소리 포함 · 1.9 MB

콘솔

Tenax 콘솔 개요: 데이터베이스 9개와 각각의 최근 전체 백업, 특정 시점 복원 가능 기간, 지연, 저장 크기, 상태
모든 데이터베이스를 한눈에: 최근 전체 백업, 복원 가능 기간, 스트림 지연, 크기, 상태.
Tenax 콘솔 대상 페이지: 복원 가능 타임라인, 행 수와 크기 추세, 검증 배지가 붙은 버전 목록
데이터베이스 하나: 복원 가능 타임라인, 추세, 모든 버전과 검증된 복원 표시.
두 백업 간의 Tenax 콘솔 차이 비교: 추가된 열, 비워진 테이블, 테이블별 행 수와 크기
두 버전을 나란히: 무엇이 적용되었는지, 어떤 열이 바뀌었는지, 테이블별 행 수와 크기.

두 버전 사이에 무엇이 바뀌었는지 알 수 있습니다.

모든 백업에는 테이블, 행 수, 크기의 인벤토리가 담겨 있습니다. 모든 변경 세그먼트에는 그 세그먼트가 한 일의 요약이 담겨 있습니다. 그래서 무엇이든 복원하기 전에, 잃어버린 행이 아직 남아 있는 버전이 어느 것인지 확인할 수 있습니다.

  • 어떤 두 백업 사이든 차이 비교. 테이블별 스키마 변경, 행 수, 크기.
  • 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에도 끄떡없습니다.

백업은 밤중에 뻔한 이유로 실패합니다. 멈춘 마운트, 죽은 파드, 반쯤 쓰인 파일. Tenax는 이 중 어떤 것도 시스템을 멈추게 하거나 백업인 척할 수 없도록 만들어졌습니다.

절대 멈추지 않음

마운트도 블록 디바이스도 없고, 데이터베이스는 네트워크로만 접근합니다. 모든 스트림에는 진행 감시기가 있고, 모든 도구는 자체 프로세스 그룹에서 실행됩니다.

크래시 전용

언제든 어떤 파드든 종료해도 됩니다. 백업은 매니페스트가 마지막으로 기록된 뒤에야 존재하며, 카오스 테스트 스위트가 백업 도중 Tenax를 종료해 이를 증명합니다.

데이터는 컨트롤러가 아니라 Job에서 이동합니다.

컨트롤러는 예약만 합니다. mover와 streamer가 하나의 스트리밍 파이프라인으로 바이트를 버킷까지 곧바로 옮기고, restorer가 이를 되돌려 놓습니다.

프로덕션에 부담 없이

절대 쓰지 않는 최소 권한 데이터베이스 사용자 하나. 호스트별 슬롯, 대역폭 제한, 서버가 바쁠 때 백업을 연기하는 부하 보호 장치.

변경 스트리밍

Postgres는 복제 슬롯에 연결하는 Tenax 자체 WAL 수신기로, MySQL은 mysqlbinlog로 처리합니다. 조용한 데이터베이스에서도 복원 가능 기간이 현재까지 이어집니다.

설계부터 빠르게

임시 파일 없는 스트리밍 파이프라인, 병렬 멀티파트 업로드, 멀티스레드 zstd, 그리고 밀리초 단위로 응답하는 콘솔 뒤의 인메모리 카탈로그.

있는 줄도 모를 만큼 가볍습니다.

Tenax는 Go 바이너리 하나입니다. 컨트롤러와 API는 예약하고 읽기만 하며, 데이터는 작업이 끝나면 종료되는 단기 Job에서 이동합니다. 그래서 백업과 백업 사이에는 클러스터에 무거운 것이 남지 않습니다.

54MB정적 바이너리 하나: 컨트롤러, API와 콘솔, mover, streamer, restorer, CLI
18MB압축된 다운로드 크기, 설치할 런타임도 사이드카도 없음
21MiB데이터베이스 클러스터를 감시할 때 유휴 상태의 컨트롤러 메모리
19MiB카탈로그를 메모리에 올린 유휴 상태의 API와 콘솔 메모리

엔드투엔드 및 카오스 테스트 스위트 실행 후 k3d 테스트 클러스터(데이터베이스 12개)에서 kubelet이 보고한 워킹 세트입니다. 유휴 상태 CPU는 몇 밀리코어입니다. 특정 시점 복구를 쓰는 데이터베이스마다 streamer도 실행되며, 스트리밍 중에는 약 70 MiB를 사용합니다.

05보안원본에서 암호화

버킷이 보는 것은 언제나 암호문뿐입니다.

암호화는 업로드 전에 파드 안에서, Tenax가 갖고 있지 않은 키로 이루어집니다. 접근 권한은 역할별로 나뉘며, 모든 쓰기와 모든 쿼리는 감사 기록을 남깁니다.

버킷이 보는 것
$ head -c 200 data/part-00001.zst.age
age-encryption.org/v1
-> X25519 Qm9yZWQ/IE5vdGhpbmcgdG8gc2VlIGhlcmUu…
--- 7Hc1kS0zR2QeYc…
ÌÞ#ø§ñ¹t¤¢Ý}ϰë։ÿ]Wœ3Ð÷…

모든 데이터 오브젝트는 여러분의 수신자(X25519)에 맞춰 암호화된 age 파일입니다. 매니페스트만 평문 JSON이며, 비밀 정보는 담겨 있지 않습니다.

  • 떠나기 전에 암호화

    모든 오브젝트는 클라이언트 측에서 age로 여러분의 키에 맞춰 암호화됩니다. 버킷은 평문을 절대 보지 못하며, 콘솔의 카탈로그 키로는 데이터를 읽을 수 없습니다.

  • 데이터를 읽을 수 없는 카탈로그 키

    콘솔은 별도의 카탈로그 ID로 인벤토리와 마스킹된 미리 보기를 읽습니다. 덤프, 기준 백업, WAL, binlog는 복호화할 수 없습니다.

  • 처음부터 끝까지 검사

    매니페스트에는 저장된 모든 오브젝트의 SHA-256이 담겨 있어 매번 읽기가 끝날 때 검사되며, 매니페스트 자체는 조건부 PUT으로 마지막에 기록됩니다.

  • 비밀 정보는 보이지 않게

    데이터베이스 자격 증명은 argv나 환경 변수가 아니라 봉인된 인메모리 파일을 통해 하위 도구에 전달됩니다. 로그는 비밀처럼 보이는 것을 모두 가립니다.

  • 역할과 감사 기록

    Cloudflare Access로 로그인하며, viewer부터 restorer-admin까지 네 가지 역할이 있습니다. 제자리 복원에는 최상위 역할과 직접 입력하는 확인이 필요합니다.

  • 잠금된 파드

    non-root, 읽기 전용 루트 파일시스템, 모든 capability 제거, RuntimeDefault seccomp. 데이터 경로 파드는 emptyDir만 마운트합니다.

06비교cron 작업과

버킷에 쌓이는 야간 덤프 그 이상.

이럴 때…cron + pg_dump 스크립트Tenax
09:41:27로 되돌리기어젯밤 덤프, 최대 24시간 손실기간 안의 어떤 초든
백업이 복원되는지 증명하기장애가 난 뒤에야 알게 됨여러분의 검사를 포함한 예약 검증 복원
사라진 행이 있는 버전 찾기여러 개를 복원해서 들여다보기콘솔의 차이 비교와 변경 요약
위험 없이 살펴보기공유 스테이징 데이터베이스 위에 복원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.run/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.run/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는 어떤 데이터베이스를 지원하나요?

PostGIS를 포함한 Postgres 15, 16, 17을 물리 또는 논리 전체 백업과 WAL 스트리밍으로 지원합니다. MySQL 8.4는 논리 전체 백업과 binlog 스트리밍으로 지원합니다.

얼마나 이전까지, 얼마나 정밀하게 복원할 수 있나요?

보존 티어가 유지하는 모든 전체 백업, 그리고 정책이 정한 특정 시점 복원 기간 안의 어떤 초든 가능합니다. 예시 prod 정책은 7일 기간과 함께 일일 14개, 주간 8개, 월간 12개, 연간 5개의 전체 백업을 보존합니다.

Tenax나 스토리지 제공업체가 제 데이터를 읽을 수 있나요?

버킷은 읽을 수 없습니다. 모든 오브젝트는 업로드 전에 여러분의 수신자에 맞춰 age로 암호화됩니다. 콘솔은 데이터 자체는 복호화할 수 없는 카탈로그 키로 인벤토리를 읽습니다. 복원은 여러분이 제공한 age ID로 복호화하므로, 그 오프라인 사본을 보관해 두세요.

백업 도중 파드가 죽으면 어떻게 되나요?

인정되는 것은 아무것도 없습니다. 백업은 모든 오브젝트가 업로드되고 체크섬이 확인된 뒤 매니페스트가 기록되어야 비로소 존재합니다. 중간에 끝난 실행에는 매니페스트가 없으므로 복원 대상으로 제시되지 않습니다. 카오스 테스트 스위트가 백업 도중 Tenax를 종료해 이를 증명합니다.

Tenax는 볼륨을 마운트하거나 스냅샷을 사용하나요?

아니요. 데이터베이스에는 네트워크로만 접근하며, 파드는 emptyDir 외에는 아무것도 마운트하지 않습니다. hostPath, FUSE, iSCSI, 블록 디바이스를 쓰지 않으며 기본적으로 PVC도 없습니다. 멈춘 스토리지 드라이버가 백업을 멈추게 할 수 없습니다.

Tenax가 데이터베이스에 요구하는 것은 무엇인가요?

데이터베이스마다 절대 쓰지 않는 최소 권한 tenax 사용자 하나로, docs/sql/의 SQL로 만듭니다. 복제 권한이 없어도 Tenax는 논리 백업을 수행하며, 어떤 초로든 복원하려면 무엇을 부여해야 하는지 알려 줍니다.

Tenax는 오픈 소스인가요?

The Republic Lex Aequa License 1.0에 따라 소스가 공개되어 있습니다. 자신의 데이터를 보호하기 위해서라면 상업적 기업의 프로덕션 환경을 포함해 누구나 무료로 사용, 수정, 재배포할 수 있습니다. 제3자를 위해 판매하거나 서비스로 호스팅하는 권리는 유보되어 있습니다.

필요해지기 전에 복원을 테스트하세요.