Litestream으로 Rails의 네 SQLite 데이터베이스를 백업하고 복구하기

네 데이터베이스는 네 개의 복구 대상이다

Rails 8에서 primary, cache, queue, cable을 별도 SQLite 파일로 운영하면 배포 구조는 단순하지만 백업 설계는 한 파일만 다룰 때보다 신중해야 한다. 글과 댓글은 primary에 있고, 비동기 삭제 작업은 queue에 있으며, cache와 cable도 각각 독립된 상태를 가진다. Litestream 설정에는 네 파일을 모두 명시해야 하고, 각각 독립된 replica 경로를 사용해야 한다.

이 복제는 하나의 분산 트랜잭션이 아니다. 같은 순간에 네 파일이 원자적으로 저장되는 것이 아니므로 복구 시점과 업무 관계를 운영자가 검증해야 한다. “Litestream 프로세스가 실행 중”이라는 상태만으로 복구 가능성을 증명할 수는 없다.

S3 경로와 credential 분리하기

백업 bucket은 Active Storage 이미지 bucket과 분리하고, 각 DB에 고유한 prefix를 부여한다. credential도 이미지 업로드 변수에서 암묵적으로 가져오지 않는다. 현재 같은 계정을 쓰더라도 Litestream 전용 환경변수에 명시해야 나중에 최소 권한 계정으로 교체하기 쉽다.

dbs:
  - path: /rails/storage/production.sqlite3
    replicas:
      - type: s3
        bucket: <%= ENV["LITESTREAM_BUCKET"] %>
        path: app/primary
        endpoint: <%= ENV["LITESTREAM_ENDPOINT"] %>
        region: us-east-1
        force-path-style: true

  - path: /rails/storage/production_queue.sqlite3
    replicas:
      - type: s3
        bucket: <%= ENV["LITESTREAM_BUCKET"] %>
        path: app/queue
        endpoint: <%= ENV["LITESTREAM_ENDPOINT"] %>
        region: us-east-1
        force-path-style: true

같은 패턴으로 cache와 cable도 추가한다. 설정 파일에는 access key와 secret을 쓰지 않고 배포 secret으로 주입한다. 보존 기간은 오래 설정할 수 있지만 복제 프로세스가 멈춘 시간을 보상하지는 못한다.

백업 상태를 트랜잭션 ID로 확인하기

정기 점검에서는 daemon 상태뿐 아니라 네 DB 각각 sync -wait를 실행해 local과 replica의 txid가 일치하는지 확인한다. 실행 중인 Litestream accessory의 control socket을 재사용해야 한다면 control 명령에 --reuse를 사용한다. 새로운 replicate 프로세스를 잘못 띄우면 lock 충돌이나 이중 실행을 만들 수 있다.

점검 결과에는 다음 항목을 남긴다.

  • DB별 마지막 replica timestamp
  • local txid와 replica txid의 일치 여부
  • 마지막 성공 이후 경과 시간
  • S3 endpoint 접근 실패와 인증 오류
  • bucket 내 예상 prefix 존재 여부

가장 최근 timestamp만 보고 정상이라고 판단하지 않는다. primary만 최신이고 queue가 오래되었다면 글 데이터는 살아 있어도 삭제 작업이나 예약 작업의 상태가 어긋날 수 있다.

동일 시점으로 안전하게 복구하기

실제 복구 때는 먼저 애플리케이션 쓰기를 중지하고 사고 직전의 UTC timestamp 하나를 결정한다. 기존 production 파일 위에 -force로 덮어쓰지 말고 uid 1000이 쓸 수 있는 비어 있는 격리 volume에 네 DB를 복구한다. 각 replica에 동일한 timestamp를 사용하되, Litestream이 선택한 실제 snapshot과 WAL 범위는 DB별로 기록한다.

복구 후에는 각 파일에서 다음을 실행한다.

PRAGMA integrity_check;
PRAGMA foreign_key_check;

WAL mode DB를 read-only mount에서 바로 열면 읽기 과정에서도 SHM 파일 생성이 필요해 실패할 수 있다. writable 격리 volume에서 검사하고, immutable=1은 모든 writer가 멈춘 고정 사본에만 사용한다. 단순한 SQLite 무결성 검사를 통과한 뒤에도 관리자 계정 존재, 발행 글과 댓글 연결, Active Storage attachment와 blob 연결, queue 실패 작업 수 같은 업무 관계를 확인해야 한다.

복구 훈련을 완료 조건으로 삼기

백업은 복구 훈련을 통과하기 전까지 미검증 데이터다. 최소한 릴리스 전과 정기 운영 점검에서 다음 절차를 자동화한다.

  • 네 DB의 replica prefix가 서로 다르다.
  • sync -wait 결과 local/replica txid가 일치한다.
  • 동일 UTC timestamp로 빈 volume에 네 파일을 복구한다.
  • integrity와 foreign key 검사가 모두 성공한다.
  • Rails production 환경에서 schema version을 읽을 수 있다.
  • 발행 글, 댓글, attachment의 업무 관계가 유지된다.
  • 복구 환경에서 테스트용 sentinel을 읽고 원본 운영 환경은 변경하지 않는다.

훈련에 사용한 prefix와 volume은 검증 후 정리하되 production backup prefix에는 recursive cleanup을 실행하지 않는다. 복구 명령, 소요 시간, 선택한 timestamp, 검증 결과를 런북에 남기면 장애 상황에서 추측을 줄일 수 있다. Litestream 운영의 핵심 지표는 백업 파일 개수가 아니라 원하는 시점의 네 DB를 일관된 애플리케이션 상태로 되돌릴 수 있는가다.

댓글

아직 댓글이 없습니다.

댓글 남기기