백업과 복구

이 페이지

복구 지점은 전체 데이터베이스를 복원할 수 있어야 합니다. EmDash의 JSON 백업은 확인이나 맞춤 도구에는 유용하지만, EmDash는 이를 복원할 수 없습니다. 데이터베이스 마이그레이션이나 파괴적인 작업 전에는 원시 데이터베이스 백업 또는 특정 시점 복구를 사용하세요.

미디어 바이너리는 별도로 백업하세요. 사이트가 암호화된 플러그인 설정을 저장한다면 EMDASH_ENCRYPTION_KEY의 전체 로테이션 목록을 시크릿 관리자의 별도 백업에 보관하세요. 데이터베이스 백업과 내보내기에는 해당 파일과 키가 포함되지 않습니다.

복구 방법 선택

사이트에 구성된 데이터베이스 어댑터에 맞는 방법을 사용하세요.

데이터베이스복구 지점
Cloudflare D1Time Travel 북마크를 기록합니다. 필요한 보존 기간이 Time Travel을 넘거나 복원 리허설이 필요하면 오프사이트 SQL 덤프를 만드세요.
SQLite 또는 로컬 libSQLSQLite의 온라인 백업 명령을 사용한 다음, 복사본에 무결성 검사를 실행합니다.
PostgreSQL공급자의 특정 시점 복구를 사용하거나 pg_dump로 사용자 지정 형식 아카이브를 만듭니다.
Hyperdrive오리진에 직접 연결해 해당 PostgreSQL 오리진을 백업합니다. Hyperdrive 바인딩은 백업용 연결이 아닙니다.
Turso 호스팅 libSQLTurso의 특정 시점 복구를 사용하거나 SQL 덤프를 만들어 리허설합니다.
다른 원격 libSQL 공급자공급자의 전체 데이터베이스 내보내기 또는 특정 시점 복구 절차를 사용하고, 격리된 데이터베이스에서 테스트합니다.

복구 지점은 그에 맞는 애플리케이션 아티팩트, 미디어 백업, 키 목록 버전과 함께 보관하세요. 계획된 마이그레이션 전에 정확한 데이터베이스와 환경을 기록하고, 공급자의 절차가 요구하면 애플리케이션 쓰기를 중지하며, 의도한 롤백 기간 동안 복구 지점을 계속 사용할 수 있는지 확인하세요.

미디어 객체 백업 및 복원

R2와 S3 호환 버킷은 데이터베이스와 별도로 객체 수준 백업이 필요합니다. 다음 AWS CLI 명령은 backups/ 아래의 EmDash JSON 아카이브를 포함한 모든 객체를 로컬 백업 디렉터리로 복사합니다. AWS S3에서는 --endpoint-url을 생략하세요.

aws s3 sync s3://emdash-media ./emdash-media-backup \
  --endpoint-url https://<account-id>.r2.cloudflarestorage.com

<account-id>를 Cloudflare 계정 ID로 바꾸세요. 정기 백업 작업에는 읽기 전용 버킷 자격 증명을 사용하세요. 백업은 프로덕션 계정이나 장애 도메인 밖에 저장하고, 같은 시점에 만든 데이터베이스 복구 지점을 기록하세요.

프로덕션이 요청을 처리하는 동안 덮어쓰지 말고, 빈 복구 버킷에 복원하세요.

aws s3 sync ./emdash-media-backup s3://emdash-media-recovery \
  --endpoint-url https://<account-id>.r2.cloudflarestorage.com

복원 작업에는 복구 버킷에 대한 쓰기 권한만 부여하세요. 프로덕션이 아닌 배포가 복원된 데이터베이스와 버킷을 가리키게 하고, 알려진 미디어 URL 몇 개를 열어 본 다음, 임시 파일을 업로드했다가 삭제하세요. 데이터베이스와 미디어가 함께 검사를 통과한 뒤에만 프로덕션 바인딩이나 버킷 구성을 변경하세요.

로컬 스토리지는 local() 스토리지 어댑터에 구성된 디렉터리를 복사하세요. 복사하는 동안 미디어 쓰기를 중지하고, 복사본에서 대표적인 파일을 확인하세요.

Time Travel로 D1 데이터베이스 복구

마이그레이션 전에 Time Travel에서 데이터베이스의 현재 북마크를 조회하고, 전체 출력을 배포와 함께 기록하세요.

npx wrangler d1 time-travel info my-database

Time Travel 복구 지점은 만료됩니다. Cloudflare는 현재 Workers Free 플랜에서 7일, Workers Paid 플랜에서 30일 동안 보관합니다. 롤백 기간이 더 길어야 한다면 오프사이트 D1 덤프를 만드세요.

복구가 필요하면 사이트에 대한 쓰기를 중지하고, 마이그레이션 전에 기록해 둔 정확한 북마크로 복원하세요.

npx wrangler d1 time-travel restore my-database \
  --bookmark="<pre-migration-bookmark>"

<pre-migration-bookmark>를 기록한 북마크로 바꾸세요. 이 명령은 데이터베이스를 제자리에서 덮어쓰고 진행 중인 쿼리를 취소합니다. 복원 명령이 출력하는 이전 북마크를 저장해 두면 복원 자체를 되돌릴 수 있습니다.

Time Travel은 콘텐츠, 사용자, 설정, 플러그인 데이터, 마이그레이션 기록을 복원합니다. R2 미디어 객체와 EMDASH_ENCRYPTION_KEY는 복원하지 않습니다. 트래픽을 다시 열기 전에 그에 맞는 애플리케이션 아티팩트, 미디어, 키를 복원하세요. 로그인, 콘텐츠 읽기, 스키마, 암호화된 플러그인 설정, 쓰기를 확인하세요.

현재의 보존 기간, 요구 사항, 복원 동작은 D1 Time Travel 문서를 참고하세요.

오프사이트 D1 덤프 만들기

원시 데이터베이스(사용자 및 인증 테이블 포함)의 SQL 덤프에는 Wrangler를 사용하세요. EmDash는 검색 인덱스를 FTS5 가상 테이블에 보관하므로, 어떤 컬렉션에서든 검색을 활성화하면 단순한 wrangler d1 export my-database --remote는 D1 Export error: cannot export databases with Virtual Tables (fts5) 오류로 실패합니다. 나머지 테이블을 이름으로 지정해 내보내고 검색 인덱스는 제외하세요. 복원된 사이트가 인덱스를 다시 만듭니다.

복원이 성공하도록 덤프를 네 개의 파일로 나눕니다.

  • backup-schema.sql은 테이블을 만듭니다.
  • backup-folders.sql은 미디어 폴더를 담고 있습니다. 큰 가져오기는 여러 부분으로 나뉘어 실행되므로, 그렇지 않으면 미디어 항목이 소속 폴더보다 먼저 삽입되어 외래 키 검사에 실패합니다.
  • backup-data.sql은 나머지 모든 행을 담고 있습니다.
  • backup-indexes.sql은 테이블 이름으로 내보낼 때 빠지는 인덱스와 트리거를 담고 있습니다. 행을 가져오는 동안 트리거가 실행되지 않도록 마지막에 생성합니다.

이 명령에는 jq가 필요합니다. 다음 명령이 네 개의 파일을 작성합니다.

npx wrangler d1 execute my-database --remote --json \
  --command "SELECT name FROM sqlite_master WHERE type = 'table' AND name NOT GLOB '_emdash_fts_*' AND name NOT GLOB 'sqlite_*' AND name NOT GLOB '_cf_*' AND name != 'media_folders'" \
  | jq -r '.[0].results[] | "--table=" + .name' > backup-tables.txt

npx wrangler d1 export my-database --remote --no-data --output=backup-schema.sql \
  --table=media_folders $(cat backup-tables.txt)
npx wrangler d1 export my-database --remote --no-schema --output=backup-folders.sql \
  --table=media_folders
npx wrangler d1 export my-database --remote --no-schema --output=backup-data.sql \
  $(cat backup-tables.txt)

npx wrangler d1 execute my-database --remote --json \
  --command "SELECT sql FROM sqlite_master WHERE type IN ('index', 'trigger') AND sql IS NOT NULL AND name NOT GLOB '_emdash_fts_*' AND tbl_name NOT GLOB '_emdash_fts_*' AND tbl_name NOT GLOB '_cf_*'" \
  | jq -r '.[0].results[] | .sql + ";"' > backup-indexes.sql

Wrangler의 내보내기 옵션과 제한은 D1 가져오기 및 내보내기 문서를 참고하세요.

SQL 파일은 그에 맞는 애플리케이션 버전, 같은 시점에 만든 미디어 백업, 별도로 저장한 EMDASH_ENCRYPTION_KEY 로테이션 목록 사본과 함께 보관하세요. 새로 프로비저닝한 빈 D1 데이터베이스에 파일을 가져오고, 런타임 키를 복원하고, 프로덕션이 아닌 바인딩이 그 데이터베이스를 가리키도록 업데이트한 뒤 사이트를 확인하는 방식으로 복구를 테스트하세요.

빈 복구 데이터베이스에 다음 순서로 파일을 가져오세요.

npx wrangler d1 execute my-recovery-database --remote --file=backup-schema.sql
npx wrangler d1 execute my-recovery-database --remote --file=backup-folders.sql
npx wrangler d1 execute my-recovery-database --remote --file=backup-data.sql
npx wrangler d1 execute my-recovery-database --remote --file=backup-indexes.sql

복원된 데이터베이스에는 검색 인덱스가 없습니다. EmDash는 복원된 사이트에서 검색 API(/_emdash/api/search)가 처음 호출될 때 인덱스를 다시 만듭니다. 예를 들어 관리자 명령 팔레트나 API를 사용하는 검색 상자에서 호출됩니다. 그때까지는 search()를 직접 호출하는 페이지가 결과를 반환하지 않습니다. 바로 다시 만들려면 사이트가 실행된 후 /_emdash/api/search?q=a를 한 번 요청하세요.

SQLite 및 로컬 libSQL 백업

오프라인 백업은 데이터베이스에 쓰는 모든 프로세스를 중지하고, 데이터베이스 파일과 그 옆의 -wal, -shm 파일을 함께 복사합니다. -wal 파일에는 아직 주 파일에 반영되지 않은 커밋된 변경이 들어 있을 수 있습니다.

일관된 온라인 백업은 데이터베이스 어댑터에 구성된 경로에 대해 SQLite의 백업 명령을 사용하세요.

sqlite3 emdash.db ".backup backup.db"

복사한 데이터베이스를 검사합니다. 명령은 ok를 출력해야 합니다.

sqlite3 backup.db "PRAGMA integrity_check"

프로덕션이 아닌 환경에서 그에 맞는 애플리케이션 버전으로 복사본을 열고 로그인, 공개 콘텐츠, 암호화된 플러그인 설정, 편집, 미디어 읽기를 확인하세요.

복구하려면 데이터베이스에 쓰는 모든 프로세스를 중지하고, 손상된 데이터베이스와 그 -wal, -shm 파일의 사본을 보관한 다음, 검증된 백업으로 교체하세요. 트래픽을 다시 열기 전에 그에 맞는 애플리케이션 아티팩트, 미디어, 키를 복원하세요.

PostgreSQL 및 Hyperdrive 백업

PostgreSQL 공급자가 프로덕션을 변경하기 전에 테스트할 수 있는 격리된 복원을 제공한다면, 해당 공급자의 특정 시점 복구나 스냅샷 기능을 사용하세요. 정확한 타임스탬프나 스냅샷 ID와 보존 기간을 기록하세요.

이식 가능한 백업은 PostgreSQL 오리진에 직접 연결해 사용자 지정 형식 아카이브를 만드세요.

pg_dump --format=custom --file=emdash.dump "$DATABASE_URL"

pg_dump는 데이터베이스를 계속 사용하는 동안에도 트랜잭션 측면에서 일관된 아카이브를 만듭니다. 아카이브를 신뢰하기 전에 목차를 확인하세요.

pg_restore --list emdash.dump > emdash.dump.list

빈 복구 데이터베이스를 프로비저닝하세요. 표준 EmDash 애플리케이션 역할로 연결하고, 원본의 소유권을 복구 데이터베이스로 가져오지 않고 복원하세요.

pg_restore --exit-on-error --no-owner \
  --dbname="$RECOVERY_DATABASE_URL" emdash.dump

RECOVERY_DATABASE_URL의 역할이 복원된 EmDash 객체의 소유자가 됩니다. 복구된 애플리케이션이 사용할 것과 같은 안정적인 역할을 사용하세요. 프로덕션이 아닌 배포가 복구 데이터베이스를 가리키게 하고 로그인, 콘텐츠 읽기, 스키마, 암호화된 플러그인 설정, 쓰기를 확인하세요.

Hyperdrive의 경우 DATABASE_URL은 migrationConnectionStringEnv가 지정하는 PostgreSQL 오리진 직접 연결이어야 합니다. Worker 바인딩은 PostgreSQL 백업 도구에 전달할 수 없습니다. 오리진 공급자의 복구 절차를 따르고 Hyperdrive 구성을 애플리케이션 아티팩트와 함께 보관하세요.

공급자 관리형 복구, 대용량 데이터베이스, 연속 아카이빙은 PostgreSQL 백업 문서를 참고하세요.

Turso 호스팅 libSQL 백업

Turso의 특정 시점 복구는 이전 타임스탬프에서 별도의 데이터베이스를 만듭니다. 마이그레이션 직전에 RFC 3339 타임스탬프를 기록하세요. 복구가 필요하면 그 시점에서 격리된 데이터베이스를 만드세요.

turso db create emdash-recovery \
  --from-db emdash-production \
  --timestamp <pre-migration-rfc3339-timestamp>

<pre-migration-rfc3339-timestamp>를 기록한 타임스탬프로 바꾸세요. 복구된 데이터베이스용 토큰을 만들고, 프로덕션이 아닌 배포가 그 데이터베이스를 가리키게 한 뒤, 프로덕션 연결을 바꾸기 전에 사이트를 확인하세요. 사이트의 플랜에서 제공하는 보존 기간은 Turso 특정 시점 복구에서 확인하세요.

오프사이트 사본은 전체 데이터베이스를 덤프하세요.

turso db shell emdash-production .dump > emdash.sql

덤프로 격리된 데이터베이스를 만들어 덤프를 테스트하세요.

turso db create emdash-recovery --from-dump ./emdash.sql

다른 원격 libSQL 공급자는 해당 공급자의 전체 데이터베이스 내보내기 또는 특정 시점 복구 절차를 따르세요. 그 절차로 복구 지점을 만들고 격리된 복원을 테스트하기 전에는 원격 마이그레이션을 적용하지 마세요.

JSON 백업을 확인용으로 사용

JSON 백업에는 다음이 포함됩니다.

  • 초안, 예약된 게시물, 휴지통 항목을 포함한 모든 콘텐츠 항목
  • 콘텐츠 모델을 이루는 컬렉션과 필드 정의
  • 분류 체계 정의, 용어, 각 항목에 할당된 용어
  • 메뉴와 메뉴 항목, 섹션, 위젯 영역과 위젯, SEO 레코드, 리비전 기록, 미디어 메타데이터, 데이터베이스 마이그레이션 기록
  • site: 설정 그룹과 emdash:site_title, emdash:site_tagline, emdash:locale 설정에 있는 제목, 태그라인, URL, 로케일, 로고, 표시 환경설정, 소셜 프로필, SEO 기본값 같은 사이트 설정

다음은 포함되지 않습니다.

  • 사용자 계정, 세션, 패스키, OAuth 데이터, API 토큰, 기타 인증 데이터
  • 플러그인 스토리지와 플러그인 설정(플러그인 시크릿 포함)
  • 댓글과 반응, 리디렉션과 404 로그, 바이라인, 콘텐츠 관계와 참조, 감사 로그, 속도 제한, 예약된 작업의 상태
  • 미디어 폴더, 미디어가 사용되는 위치의 기록, 완료되지 않은 업로드, 미디어 파일 자체

Settings → Backups에서 Download backup은 새 JSON 파일을 생성합니다. 스토리지가 구성되어 있으면 Back up now가 backups/ 접두사 아래에 하나를 저장합니다. 이 파일들은 EmDash 미리보기와 같은 버전 관리되는 스냅샷 형식을 사용합니다.

JSON 백업을 예약하려면 Settings → Backups에서 Daily automatic backups를 활성화하고 보존 개수를 1~30개 중에서 선택하세요. 백업은 예약된 유지 관리 중에 실행됩니다. Cloudflare에서는 Worker에 cron 트리거가 필요하고, Node에서는 내장 스케줄러가 작업을 실행합니다.

버킷에 공개 R2 도메인이나 콘텐츠 전송 네트워크 오리진이 있다면, 그 오리진을 미디어 객체로만 제한하거나 EmDash를 통해 미디어를 제공하세요. EmDash 미디어 라우트는 backups/를 제공하지 않지만, 공개 버킷 오리진은 URL로 모든 객체를 노출할 수 있습니다.

콘텐츠, 설정, 미디어를 새 EmDash 사이트로 복사하려면 사이트 패키지를 사용하세요. 사이트 패키지는 사용자, API 토큰, 플러그인 데이터, 시크릿을 복원하지 않으므로 데이터베이스 복구 지점을 대신하지 못합니다.