NAS Docker 컨테이너 데이터 자동 백업 및 복구 가이드
NAS에서 Docker 컨테이너를 운영하다 보면 언젠가 반드시 이 질문을 만나게 됩니다. "컨테이너를 지우거나 이미지를 새로 받으면, 안에 있던 데이터는 어떻게 되나?" 설정 파일, 데이터베이스, 업로드한 파일이 한순간에 사라지는 상황은 생각보다 흔합니다.
해결책은 단순합니다. 데이터가 실제로 저장된 호스트 경로를 찾아내고, 그 폴더를 주기적으로 압축해 다른 곳에 보관하는 것입니다. 이 글에서는 그 경로를 확인하는 방법부터 셸 스크립트 작성, 크론탭 자동화, 그리고 실제 복구 절차까지 순서대로 다룹니다.
끝까지 따라 하면 매일 새벽 자동으로 백업이 쌓이고, 오래된 백업은 스스로 정리되며, 사고가 났을 때 몇 분 안에 되돌릴 수 있는 상태가 됩니다.
목차
- 왜 Docker 데이터 백업이 필요한가
- 내 데이터가 어디 있는지 찾기
- 수동으로 한 번 백업해 보기
- 자동 백업 스크립트 작성
- 크론탭으로 자동화
- 데이터 복구 절차
가정 및 전제조건
이 글은 NAS에 Docker가 이미 설치되어 있고, SSH로 접속할 수 있으며, 컨테이너의 데이터가 바인드 마운트(Bind Mount)로 호스트 경로에 연결되어 있다고 가정합니다. 아직 Docker를 설치하지 않았다면 NAS에 Docker 설치하기를 먼저 참고하세요.
본문의 경로(/volume1/...)는 예시입니다. 실제 경로는 3번 단계에서 직접 확인합니다.
1. 왜 Docker 데이터 백업이 필요한가
컨테이너는 언제든 지우고 다시 만들 수 있도록 설계되어 있습니다. 이 성질이 장점이자 위험입니다. 이미지를 업데이트하거나 docker compose down을 실행하는 것만으로도, 컨테이너 내부에만 있던 데이터는 함께 사라집니다.
반대로 데이터가 호스트 경로에 저장되어 있다면 컨테이너를 몇 번을 지우든 데이터는 남습니다. 이 원리를 다룬 사례로 Docker 재시작 후 PostgreSQL 데이터가 사라지는 이유와 영구 저장 방법을 함께 읽어보면 이해가 빠릅니다.
다만 호스트 경로에 있다는 것만으로는 부족합니다. 디스크 고장, 실수로 인한 삭제, 랜섬웨어는 호스트 경로도 똑같이 파괴합니다. 백업은 "다른 위치에 있는 또 하나의 사본"이어야 의미가 있습니다.
2. 내 데이터가 어디 있는지 찾기
Docker가 데이터를 두는 방식은 크게 두 가지입니다.
- 볼륨(Volume) — Docker가 관리하는 영역에 저장됩니다. 리눅스 기본 설치에서는 보통
/var/lib/docker/volumes아래입니다. - 바인드 마운트(Bind Mount) — 호스트의 특정 폴더를 컨테이너 안에 그대로 연결합니다. NAS에서는 파일을 직접 열어볼 수 있어 이 방식을 많이 씁니다.
어느 쪽인지, 실제 경로가 어디인지는 docker inspect로 확인합니다. 아래 명령은 Mounts 정보만 뽑아 보여줍니다.
sudo docker inspect --format '{{ range .Mounts }}{{ .Type }} {{ .Source }} -> {{ .Destination }}{{ println }}{{ end }}' 컨테이너이름
출력의 Source가 호스트에 실제로 존재하는 경로이고, 이것이 백업 대상입니다. Type이 bind면 그 경로를 그대로 압축하면 되고, volume이면 Source에 표시된 볼륨 경로를 대상으로 삼습니다.
3. 수동으로 한 번 백업해 보기
자동화하기 전에 손으로 한 번 해보는 편이 안전합니다. 스크립트가 무엇을 하는지 알고 나서 자동화해야, 문제가 생겼을 때 원인을 찾을 수 있기 때문입니다.
아래 명령은 /volume1/docker/mydata 폴더를 통째로 압축해 /volume1/backups에 날짜가 붙은 파일로 저장합니다.
sudo tar -czpf /volume1/backups/mydata_$(date +%Y%m%d_%H%M%S).tar.gz -C /volume1/docker mydata
각 옵션의 의미는 다음과 같습니다.
-c새 아카이브를 만든다 ·-zgzip으로 압축한다 ·-f뒤에 오는 이름으로 저장한다-p파일의 권한과 소유권을 보존한다. 데이터베이스처럼 권한에 민감한 컨테이너에서는 이 옵션이 없으면 복구 후 기동에 실패할 수 있습니다.-C /volume1/docker mydata— 해당 폴더로 이동한 뒤mydata만 담는다는 뜻입니다. 아카이브 안에 전체 경로가 들어가지 않아 복구할 때 훨씬 다루기 쉽습니다.
만든 직후에 tar -tzf 파일명으로 목록을 한 번 열어보세요. 목록이 정상적으로 나오면 아카이브가 깨지지 않았다는 뜻입니다.
4. 자동 백업 스크립트 작성
이제 위 과정을 스크립트로 옮깁니다. 스크립트가 하는 일은 네 가지입니다.
- 백업 폴더가 없으면 만든다
- 대상 폴더를 날짜가 붙은 이름으로 압축한다
- 압축이 성공했는지 확인한다
- 보관 개수를 넘긴 오래된 백업을 지운다
/volume1/scripts/backup_docker_data.sh로 저장합니다.
#!/bin/bash
set -euo pipefail
# --- 설정 ---
SOURCE_PARENT="/volume1/docker" # 대상 폴더의 상위 경로
SOURCE_NAME="mydata" # 백업할 폴더 이름
BACKUP_DIR="/volume1/backups/docker"
MAX_BACKUPS=7 # 보관할 백업 개수
# --- 실행 ---
mkdir -p "${BACKUP_DIR}"
TIMESTAMP=$(date +%Y%m%d_%H%M%S)
BACKUP_FILE="${BACKUP_DIR}/${SOURCE_NAME}_${TIMESTAMP}.tar.gz"
tar -czpf "${BACKUP_FILE}" -C "${SOURCE_PARENT}" "${SOURCE_NAME}"
# 아카이브가 실제로 열리는지 확인한다. 깨진 백업은 없는 백업보다 위험하다.
tar -tzf "${BACKUP_FILE}" > /dev/null
echo "backup ok: ${BACKUP_FILE}"
# 오래된 백업 정리 — 최신 MAX_BACKUPS개만 남긴다.
ls -1t "${BACKUP_DIR}"/${SOURCE_NAME}_*.tar.gz 2>/dev/null \
| tail -n +$((MAX_BACKUPS + 1)) \
| xargs -r rm --
set -euo pipefail은 명령이 하나라도 실패하면 스크립트를 즉시 멈춥니다. 이 줄이 없으면 압축이 실패해도 다음 줄의 "오래된 백업 삭제"가 그대로 실행되어, 새 백업은 없는데 옛 백업만 사라지는 최악의 상황이 생길 수 있습니다.
작성한 뒤 실행 권한을 주고, 반드시 한 번 손으로 실행해 봅니다.
sudo chmod +x /volume1/scripts/backup_docker_data.sh
sudo /volume1/scripts/backup_docker_data.sh
5. 크론탭으로 자동화
크론탭은 정해진 시각에 명령을 실행해 주는 리눅스 스케줄러입니다. crontab -e로 편집기를 열고 아래 두 줄을 추가합니다.
MAILTO=""
0 3 * * * /bin/bash /volume1/scripts/backup_docker_data.sh >> /volume1/backups/backup.log 2>&1
0 3 * * *는 앞에서부터 분, 시, 일, 월, 요일을 뜻하며 매일 새벽 3시 0분을 가리킵니다. MAILTO=""는 실행 결과를 메일로 보내지 않겠다는 설정입니다.
뒤쪽의 >> ... 2>&1은 정상 출력과 오류 출력을 모두 로그 파일에 이어 붙입니다. 크론은 조용히 실패합니다. 로그를 남겨두지 않으면 몇 달 동안 백업이 안 되고 있어도 알아채지 못합니다. 며칠 뒤 tail /volume1/backups/backup.log로 backup ok가 쌓이는지 꼭 확인하세요.
6. 데이터 복구 절차
복구는 백업보다 위험한 작업입니다. 잘못하면 살아 있는 데이터를 옛날 데이터로 덮어씁니다. 시작하기 전에 아래 세 가지를 먼저 확인하세요.
- 지금 상태를 먼저 백업합니다. 복구가 잘못됐을 때 돌아올 지점이 필요합니다. 3번의 수동 백업 명령을 한 번 실행하면 됩니다.
- 어떤 백업 파일을 쓸지 정합니다. 무조건 최신이 정답은 아닙니다. 데이터가 언제부터 잘못됐는지 짚어보고, 문제가 생기기 이전 시점의 파일을 고릅니다.
- 고른 파일이 멀쩡한지 확인합니다.
tar -tzf 파일명으로 목록이 정상적으로 출력되어야 합니다.
1단계 — 컨테이너를 멈춥니다. 실행 중인 컨테이너의 데이터 폴더를 건드리면 파일이 섞이거나 데이터베이스가 손상됩니다.
sudo docker stop 컨테이너이름
2단계 — 백업 파일의 내용을 확인합니다. 아카이브 최상위에 어떤 폴더가 들어 있는지 봐야 어디에 풀지 결정할 수 있습니다.
tar -tzf /volume1/backups/docker/mydata_20260709_030000.tar.gz | head
3번에서 -C /volume1/docker mydata로 만들었다면 목록은 mydata/...로 시작합니다. 이 경우 부모 폴더에 풀어야 원래 자리로 돌아갑니다.
3단계 — 복원합니다. 압축을 푸는 위치는 -C로 지정합니다.
sudo tar -xzpf /volume1/backups/docker/mydata_20260709_030000.tar.gz -C /volume1/docker
여기서 -p는 백업할 때와 마찬가지로 권한과 소유권을 되살립니다. 기존 폴더의 파일 위에 덮어쓰기 때문에, 백업에는 없고 현재 폴더에만 있는 파일은 그대로 남습니다. 완전히 백업 시점 상태로 되돌리려면 기존 폴더를 먼저 다른 이름으로 옮겨두고 푸는 편이 확실합니다.
4단계 — 컨테이너를 다시 시작하고 확인합니다.
sudo docker start 컨테이너이름
sudo docker logs --tail 50 컨테이너이름
로그에 권한 오류나 파일 손상 메시지가 없는지 살펴봅니다. 여러 컨테이너를 한꺼번에 다루고 있다면 NAS Docker Compose 설치 및 사용 가이드의 방식대로 docker compose 단위로 멈추고 올리는 편이 실수가 적습니다.
마무리
정리하면 세 가지입니다. 데이터의 실제 경로를 알고, 그 폴더를 검증까지 포함해 자동으로 압축해 두고, 복구 절차를 한 번은 직접 해보는 것입니다.
특히 마지막이 중요합니다. 한 번도 복구해 본 적 없는 백업은 백업이 아니라 압축 파일일 뿐입니다. 지금 만든 스크립트로 백업을 하나 만들고, 시험용 폴더에 복구해 보는 것으로 오늘 작업을 마무리하시길 권합니다.
댓글
댓글 쓰기