Skip to content
isdnetworks
Go back

설정돼 있길래 되는 줄 알았다

백업이 되고 있는 줄 알았다. crontab 에 백업 작업이 등록돼 있었는데 복구가 필요한 상황이 왔을 때 백업 파일이 3개월 전 것이었다.

Table of contents

Open Table of contents

등록만 돼 있었다

예약 작업 목록을 봤다.

0 3 * * * /home/deploy/bin/backup.sh

backup.sh 가 등록돼 있다. 그런데 결과 파일이 3개월 전 것에서 멈춰 있었고 로그를 보니 매일 실패하고 있었다.

mysqldump: Got error: 1045: Access denied for user 'backup'@'localhost'

3개월 전에 backup 계정 비밀번호를 바꿨는데 스크립트의 값을 안 바꿨다. 등록돼 있는 것과 성공하는 것은 다르다.

실패를 아무도 몰랐다

스크립트가 실패해도 아무 데도 안 알렸다.

mysqldump -u backup -p"$PASS" shopdb > "$DIR/dump-$(date +%Y%m%d).sql"
gzip "$DIR/dump-$(date +%Y%m%d).sql"

mysqldump 가 실패해도 다음 줄이 돌아서 gzip 이 빈 파일을 압축한다. 결과가 있으니 겉으로는 정상으로 보이고 파일 크기를 안 보면 모른다.

스크립트의 종료 코드는 마지막으로 실행된 명령의 것이다. 중간에서 한 줄이 실패해도 뒤에 성공하는 줄이 있으면 전체가 0으로 끝나고 cron 쪽에서는 성공으로 보인다.

파이프로 이은 자리도 같다. mysqldumpgzip 으로 넘기는 형태면 앞이 죽어도 뒤가 정상이라 전체는 0이고 앞쪽 종료 코드는 PIPESTATUS 에 따로 들어 있으니 그것을 봐야 한다.

결과를 확인하게 고쳤다

#!/bin/sh
set -e

DIR=/backup/db
F="$DIR/dump-$(date +%Y%m%d).sql"

mysqldump --defaults-file=/root/.my.cnf shopdb > "$F"

SIZE=$(stat -c%s "$F")
if [ "$SIZE" -lt 1000000 ]; then
  echo "백업 파일이 너무 작습니다: ${SIZE} bytes"
  exit 1
fi

gzip "$F"
echo "백업 완료: ${F}.gz ($(stat -c%s "${F}.gz") bytes)"

set -e 로 중간 실패에서 멈추고 stat 으로 잰 크기가 작으면 실패시킨다.

1000000 은 임의 기준이다. 실제 덤프가 400메가라 훨씬 작으면 뭔가 잘못된 것이다. 명령의 종료 코드만 보면 이런 경우를 못 잡고 결과물을 직접 확인해야 실제로 됐는지가 나온다.

실패 알림의 한계

예약 실행 결과를 메일로 받게 했다.

0 3 * * * /home/deploy/bin/backup.sh || echo "백업 실패 $(date)" | mail -s "[알림] 백업 실패" admin@example.com

mail 은 성공하면 조용하고 실패하면 온다. 이것도 문제가 있었는데 스크립트가 아예 안 돌면 아무것도 안 온다. 예약 자체가 지워졌거나 서버가 죽었으면 조용하다.

알림을 cron 에 맡기지 않은 이유도 있다. cron 은 명령이 뭔가 출력하면 그것을 메일로 보내는데 MAILTO 를 비워 두면 아무 데도 안 가고 안 적어 두면 그 crontab 의 소유자에게 간다. 아무도 안 읽는 로컬 계정으로 가면 안 보낸 것과 같다.

echo "$(date '+%Y-%m-%d %H:%M') OK $(stat -c%s "${F}.gz")" >> /backup/backup.log

성공했을 때도 기록을 남기게 했다. 다른 서버에서 backup.log 의 마지막 줄을 확인해서 어제 날짜가 없으면 알린다.

아무 소식이 없는 것을 정상으로 읽으면 안 된다.

검증 — 복구와 다른 예약 작업

백업이 되는 것을 확인했으니 복구도 확인했다. 시험 서버에 최신 덤프를 넣어 봤다.

$ gunzip -c dump-20160801.sql.gz | mysql -h test-db shopdb_restore
$ mysql -h test-db -e "SELECT COUNT(*) FROM shopdb_restore.product;"
82041

원본과 같은 수가 나왔다. 테이블 수도 봤다.

SELECT COUNT(*) FROM information_schema.TABLES WHERE TABLE_SCHEMA='shopdb_restore';

84개로 원본과 같다. 복구가 되는 것을 확인하기 전에는 백업이 있다고 말할 수 없다.

백업 하나만 이런 상태일 리 없어서 등록된 작업을 전부 뽑았다.

$ crontab -l
$ ls /etc/cron.d/

cron.d 까지 합쳐 열한 개였고 각각 마지막 성공이 언제인지 확인했다.

백업          3개월 전 실패 중  ← 이번 건
로그 정리      정상
통계 집계      2주 전부터 실패   ← 새로 발견
세션 정리      정상
발송 재시도    정상
...

stat_daily 를 채우는 통계 집계도 실패하고 있었고 화면에 옛 숫자가 계속 보이는데 아무도 몰랐다.

예약 작업마다 무엇을 보면 정상인지도 적었다.

백업       /backup/backup.log 마지막 줄이 어제 날짜
로그 정리   /var/log/app 에 7일 넘은 파일이 없음
통계 집계   stat_daily 테이블에 어제 행이 있음
세션 정리   session 테이블 행 수가 1만 미만

주 1회 이 목록을 훑고 몇 분이면 된다.

정리


Share this post on:

Previous Post
로그를 못 남기는 것을 재기
Next Post
남의 장비를 역으로 읽기