Skip to content
isdnetworks
Go back

스크립트가 중간에 멈췄고, 백업본이 사라져 있었다

백업 스크립트를 새로 짜면서 맨 위에 set -e를 넣었다. 어디서 실패하면 거기서 멈추니까 안전하다고 생각했다. 새벽에 돌았고 아침에 보니 어제 백업본이 없었다. 새 백업도 없었다.

Table of contents

Open Table of contents

지운 뒤에 멈췄다

문제의 부분은 이랬다.

#!/bin/bash
set -e

rm -f $BACKUP_DIR/dump-*.sql
mysqldump -u backup -p"$PASS" shopdb > $BACKUP_DIR/dump-$DATE.sql
gzip $BACKUP_DIR/dump-$DATE.sql

mysqldump 가 실패했고 그 앞의 rm -f 는 성공했다. set -e 는 실패 지점에서 멈췄지만 멈추기 전에 지운 것은 그대로 지워진 상태로 남는다. 옛 백업도 없고 새 백업도 없는 자리가 그렇게 만들어졌다.

set -e 는 명령이 실패하면 스크립트를 종료한다. 그게 전부다. 실패 지점 이후를 실행하지 않을 뿐 이미 실행한 것은 건드리지 않는다.

이름 때문에 안전장치처럼 느껴지는데 실제로 하는 일은 불완전한 상태에서 손을 떼는 것이다. ROLLBACK 같은 것이 파일과 시스템 설정에는 없다. 지운 것은 지워진 것이다.

안 잡는 자리도 여럿이다. 직접 돌려 보니 파이프라인 왼쪽이 실패해도 안 멈추고 if 조건과 || 왼쪽과 함수 안에서 난 실패도 그냥 지나간다. set -o pipefail 을 함께 켜야 파이프라인 실패가 잡힌다.

백업 스크립트가 정확히 그 모양이 되기 쉽다. mysqldump ... | gzip > $FILE 로 쓰면 mysqldump 가 죽어도 gzip 이 0 으로 끝나므로 파이프라인 전체는 성공이다.

더 위험한 경우

같은 함정이 네트워크 설정에서 더 크게 터진다.

set -e
route del default
route add default gw 10.0.0.1

앞이 성공하고 뒤가 실패하면 그 서버는 기본 경로가 없는 상태로 남는다. 밖으로 나가는 통신이 전부 끊긴다. ssh 로 접속해 이걸 돌리고 있었다면 그 순간 연결이 끊기고 다시 못 들어간다. 안전하게 멈춘 결과가 서버를 못 쓰게 만든다.

부분 실패가 시스템을 불완전한 상태로 만드는 작업에서는 이 설정에 기대지 않기로 했다. routermchmodmount 가 거기 들어간다.

지우는 자리를 뒤로 보냈다

백업 스크립트는 명령마다 결과를 보는 방식으로 고쳤다.

mysqldump -u backup -p"$PASS" shopdb > "$NEW.tmp"
if [ $? -ne 0 ]; then
    echo "덤프 실패, 기존 백업 유지" >&2
    rm -f "$NEW.tmp"; exit 1
fi

if [ ! -s "$NEW.tmp" ]; then
    echo "덤프가 비었다, 기존 백업 유지" >&2
    rm -f "$NEW.tmp"; exit 1
fi

mv "$NEW.tmp" "$NEW"
gzip "$NEW"
find $BACKUP_DIR -name 'dump-*.sql.gz' -mtime +7 -delete

바뀐 것이 셋이다. rm 을 뒤로 보내 새 것이 확보되기 전에는 옛 것을 안 건드린다. .tmp 로 받고 마지막에 mv 하므로 받다가 죽으면 임시 파일만 남는다. 그리고 test -s 로 파일이 비었는지를 따로 본다. 오래된 것 정리는 find -mtime +7 -delete 로 맨 끝에 뒀다.

종료 코드가 성공이어도 실패일 수 있다

세 번째가 이 일에서 제일 오래 남았다. > $FILE 은 셸이 명령보다 먼저 처리하므로 mysqldump 가 한 글자도 못 쓰고 죽어도 빈 파일은 이미 만들어져 있다. 종료 코드만 보면 실패를 성공으로 읽는다.

명령이 0 으로 끝났다는 것은 그 명령이 자기 일을 마쳤다는 뜻이지 내가 원한 상태가 됐다는 뜻이 아니다. cp 도 잘못된 파일을 성실히 복사하고 0 으로 끝난다. mkdir -p 도 이미 있는 디렉터리에 0 을 준다. ln -s 는 대상이 없어도 링크를 만든다.

그래서 명령의 성공이 아니라 결과를 확인해야 한다. 백업이면 test -s 로 크기를 보고 gzip -t 로 압축이 풀리는지 본다. 복사면 양쪽 md5sum 을 본다.

확인이 한 단계 더 있다. 백업 파일이 있는 것과 그 파일로 복구가 되는 것은 다른 얘기라 월 1회 다른 서버에서 실제로 풀어 넣어 보기로 했다.

정리


Share this post on:

Previous Post
로그인을 하나로 할지 둘로 할지 정했다
Next Post
파일 이름이 사용자에게서 왔다