서버가 죽으면 글도 사라진다 — mysqldump + tar 자동 백업 실전

지난 세 편으로 사이트를 빠르게 만들었다. 이제 방향을 바꾼다. 안정성이다. 티스토리를 쓸 때는 백업을 신경 쓴 적이 없었다. 플랫폼이 알아서 해줬으니까. 하지만 서버를 직접 빌린 순간부터, 백업은 온전히 내 몫이다. 서버가 한 번 죽으면 그동안 쓴 글과 설정이 통째로 사라진다. 이번 편에서는 DB와 파일을 자동으로, 그리고 복원이 검증되는 백업 체계를 만든다. 플러그인 없이, 리눅스 기본 도구만으로.

결론부터. mysqldump + tar + cron 세 가지로 매일 새벽 자동 백업을 세웠다. 백업 한 번은 약 60MB, 3.5초. DB 18개 테이블은 605KB로, 파일 131MB는 59MB로 압축됐다. 7개를 회전 보관해도 420MB로, 디스크 여유의 1%도 안 된다. 만들자마자 gzip 무결성과 테이블 수까지 검증했다. 단, 이 백업은 아직 같은 서버 안에 있다 — 진짜 재해 대비는 다음 편에서 원격으로 보낸다.

무엇을 백업하나 — 사라지면 복구 불가능한 것

워드프레스에서 잃으면 되돌릴 수 없는 건 딱 둘이다. DB(글, 댓글, 설정 전부)와 wp-content(업로드 이미지, 테마, 플러그인). 워드프레스 코어 파일은 언제든 공식 사이트에서 다시 받으면 되니 백업 대상이 아니다. wp-config.php는 DB 접속 정보와 보안 키가 들었으니 꼭 포함한다.

대상별 크기를 먼저 쟀다. 여기서 예상 밖의 그림이 나왔다.

대상크기비고
DB (/var/lib/mysql)148MB글·설정
wp-content 전체131MB
└ plugins73MB가장 큰 덩어리
└ uploads (이미지)25MBWebP로 이미 최적화됨
└ themes25MB
wp-config.php3.7KB접속·보안 정보

보통 이미지가 제일 클 거라 생각한다. 그런데 plugins(73MB)가 uploads(25MB)보다 훨씬 컸다. 8편에서 이미지를 WebP로 줄여둔 덕분에 업로드 폴더가 가벼워졌고, 결과적으로 플러그인 코드가 백업의 최대 덩어리가 된 것이다. 과거의 최적화가 여기서도 이득으로 돌아왔다.

DB 백업 — mysqldump, 비밀번호 없이

DB는 mysqldump로 뜬다. 두 가지가 핵심이다.

time sudo bash -c 'mysqldump --single-transaction --quick ddlabx_wp | gzip > /var/backups/ddlabx/db_test.sql.gz'

--single-transaction은 사이트를 멈추지 않고도 일관된 스냅샷을 뜬다(테이블을 잠그지 않는다). 그리고 우분투 MariaDB의 root는 sudo(소켓 인증)로 접속되므로, 스크립트 어디에도 DB 비밀번호를 적지 않는다. 비밀번호를 파일에 박아두는 것 자체가 보안 구멍인데, 그걸 원천 차단하는 방식이다.

파이프라인에 sudo 걸 때 주의. sudo mysqldump ... > 파일은 실패한다. sudomysqldump에만 붙고, 파일에 쓰는 리다이렉트(>)는 일반 사용자 권한으로 실행되기 때문이다. 백업 폴더를 chmod 700으로 잠갔다면 여기서 Permission denied가 난다. 전체를 sudo bash -c '...'로 감싸야 리다이렉트까지 root로 실행된다. (실제로 이걸로 한 번 막혔다.)

결과: 148MB DB가 605KB로, 0.16초 만에 압축됐다. gzip이 SQL 텍스트를 250배 넘게 줄인 건, DB 용량 대부분이 인덱스·오버헤드이고 순수 데이터는 작기 때문이다.

파일 백업(tar 한 줄)

time sudo tar czf /var/backups/ddlabx/files_test.tar.gz -C /var/www/ddlabx wp-content wp-config.php

131MB의 wp-content가 59MB로(55% 감소), 3.34초에 압축됐다. 이건 tar czf 파일명 구조라 tar가 직접 파일을 쓰므로, DB와 달리 sudo bash -c로 감쌀 필요가 없다(리다이렉트가 아니라서).

스크립트 + 보관 회전(자동화)

날짜별로 백업하고 오래된 건 자동으로 지우는(최근 7개 보관) 스크립트를 만들었다.

#!/bin/bash
set -euo pipefail
STAMP=$(date +%F_%H%M)
DEST=/var/backups/ddlabx
KEEP=7
SITE=/var/www/ddlabx
DB=ddlabx_wp
mkdir -p "$DEST"

# DB 덤프 (무중단, 압축)
mysqldump --single-transaction --quick "$DB" | gzip > "$DEST/db_$STAMP.sql.gz"
# 파일 압축
tar czf "$DEST/files_$STAMP.tar.gz" -C "$SITE" wp-content wp-config.php
# 보관 회전 — 최근 KEEP개만
ls -1t "$DEST"/db_*.sql.gz    | tail -n +$((KEEP+1)) | xargs -r rm -f
ls -1t "$DEST"/files_*.tar.gz | tail -n +$((KEEP+1)) | xargs -r rm -f
echo "$(date '+%F %T') OK db_$STAMP files_$STAMP" >> "$DEST/backup.log"

set -euo pipefail은 중간에 하나라도 실패하면 즉시 멈추게 한다(반쪽짜리 백업 방지). 스크립트 안에서는 root로 실행되니 앞서의 리다이렉트 권한 문제도 없다. 파일명에 날짜·시각(_1621)이 붙어 매일 돌려도 안 겹친다.

백업은 복원되어야 백업이다(핵심 단계)

백업 파일이 조용히 깨진 채 쌓이고 있는데 모르는 것, 그게 최악이다. 정작 서버가 죽은 날 열어보니 복원이 안 되면 백업은 없는 것과 같다. 그래서 만들자마자 검증한다.

sudo bash -c '
cd /var/backups/ddlabx
DB=$(ls -1t db_*.sql.gz | head -1)
FILES=$(ls -1t files_*.tar.gz | head -1)
gzip -t "$DB"    && echo "DB gzip OK"
gzip -t "$FILES" && echo "FILES gzip OK"
zcat "$DB" | grep -c "CREATE TABLE"
tar -tzf "$FILES" | grep -E "wp-config.php|wp-content/(uploads|themes|plugins)/" | head
'

세 가지를 확인한다. gzip 무결성(압축이 안 깨졌나), DB 덤프 안의 테이블 수(내용이 실제로 들었나), 아카이브 안의 핵심 폴더(빠진 게 없나). 결과는 gzip OK 2개, 테이블 18개, wp-content 폴더 정상. 복원 가능한 백업임이 확인됐다.

이 폴더는 chmod 700으로 잠가 root만 접근할 수 있다. 백업엔 DB 내용과 wp-config.php의 보안 키가 들어가므로, 일반 사용자가 못 들여다보게 막는 것이 맞다. 그래서 검증·조회 명령도 전부 sudo로 실행한다.

매일 자동 실행 (cron) + 용량 계산

( sudo crontab -l 2>/dev/null; echo "0 3 * * * /usr/local/bin/ddlabx-backup.sh" ) | sudo crontab -

매일 새벽 3시에 자동 실행된다. 전체 그림을 표로 정리하면 이렇다.

항목원본백업(압축)시간
DB (18개 테이블)148MB605KB0.16초
파일 (wp-content+config)131MB59MB3.34초
합계 (1회)279MB약 60MB약 3.5초
7개 회전 보관약 420MB

7일치를 다 보관해도 420MB. 디스크 여유(46GB)의 1%도 안 된다. 백업이 서버에 주는 부담은 사실상 없다는 게 숫자로 확인됐다. 무겁다는 핑계로 백업을 미룰 이유가 전혀 없는 것이다.

솔직한 한계 — 이건 아직 재해복구가 아니다

여기까지 하면 뿌듯하지만, 냉정하게 짚어야 한다. 이 백업은 전부 같은 서버 안(/var/backups)에 있다. 실수로 파일을 지웠거나 DB가 꼬였을 때 되돌리는 데는 충분하다. 하지만 서버가 통째로 죽는 진짜 재해 — 디스크 장애, 계정 정지, 랜섬웨어 — 앞에서는 백업도 서버와 함께 사라진다.

즉 이번 편은 “복원의 재료를, 자동으로, 검증된 상태로 갖추는” 단계다. 이것만으로도 안 하는 것보다 압도적으로 낫지만, 절반이다. 나머지 절반은 이 백업을 서버 바깥으로 내보내는 것이다.

다음 편 예고 — 백업과 재해복구 2편
다음 편에서는 rclone으로 이 백업을 원격 오브젝트 스토리지에 자동으로 올린다. 서버가 죽어도 백업은 다른 곳에 살아 있게 만드는, 진짜 오프사이트 백업이다.

댓글 남기기