![[PostgreSQL] 데이터베이스 백업 및 S3 업로드](https://img1.daumcdn.net/thumb/R750x0/?scode=mtistory2&fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FocTQK%2FbtsCMoWMopZ%2FVHHTo3JYEtJve11gwMdgH1%2Fimg.png)
🧐 개요
이번 포스트는 PostgreSQL 데이터베이스의 백업본을 생성하고 S3 스토리지에 업로드하는 방법을 설명합니다.
AWS와 같은 대규모 클라우드 서비스들은 RDS 데이터베이스에 대한 백업 스냅샷을 주기적으로 생성하여 언제든지 데이터베이스를 백업할 수 있도록 기술적으로 지원하고 있습니다(해당 데이터를 외부로 이관할 수 있도록 s3 스토리지에 스냅샷을 업로드할 수도 있죠). 그런데 이러한 서비스들이 포함되어 있기 때문에 동일 사양의 인스턴스 대비 청구되는 비용이 높다는 단점이 있습니다.
다행히도, 대부분의 RDBMS 시스템은 데이터베이스를 백업 및 복원하기 위한 자체 기능을 내장하고 있습니다. 따라서 이번 포스트는 PostgreSQL의 자체 내장 기능을 사용하여 AWS의 스냅샷 기능을 벤치마킹 하도록 하겠습니다.
🖍️ 스크립트 작성하기
PostgreSQL 데이터베이스 관련 기능은 가급적이면 프로그래밍 언어를 사용하여 구현하는 것이 좋습니다. 왜냐하면 쉘 스크립트 및 터미널 환경에서 인증 비밀번호를 자동 입력할 수 없기 때문입니다(MySQL과는 다르게 기능적으로 지원하지 않습니다). 해당 포스트에서는 Python 언어를 통해 기능 구현을 수행하고 있습니다.
데이터베이스 백업 스크립트
from datetime import datetime
import subprocess
import os
..
DB_HOST = ..
DB_PORT = ..
DB_USER = ..
DB_PASSWORD = ..
dir=f'{snapshot_path}/{datetime.now().strftime("%Y-%m-%d")}'
try: os.mkdirs(dir)
except: pass
backup_dir = f'{dir}/{datetime.now().strftime("%Y-%m-%d_%H:%M:%S")}.sql'
dump_command = [
'pg_dump',
'-h', DB_HOST,
'-p', str(DB_PORT),
'-U', DB_USER,
'-F', 'c',
'-b',
'-v',
'-f', backup_dir,
'spotify'
]
os.environ['PGPASSWORD'] = DB_PASSWORD
subprocess.run(dump_command)
print(f"Backup completed. Backup file: {backup_dir}")
os.environ.pop('PGPASSWORD', None)
해당 스크립트는 데이터베이스의 백업 파일을 .sql 확장자로 생성하여 로컬 환경에 저장하고 있습니다.
- os.mkdirs() 모듈을 통해 오늘 날짜의 디렉토리 생성
- subprocess.run() 모듈을 통해 쉘 명령어 동작
- os.environ 모듈을 통해 PostgreSQL 데이터베이스의 사용자 암호를 등록 & 제거
os.environ 모듈을 사용하면 사용자 입력으로 입력해야 할 파라미터를 선행 입력할 수 있으므로 매우 유용한 기능이라고 할 수 있습니다. 다만, 등록한 파라미터는 스트립트 종료 시점에 사라지지 않고 계속 더미 데이터로 남아 있으므로 pop 로직을 통해 제거해야 합니다.
데이터베이스 업로드 스크립트
데이터베이스를 S3 스토리지에 업로드하는 기능은 모듈화를 통해 구현하였습니다.
def upload_to_s3(bucket_name:str, local_dir:str):
import boto3
import os
aws_access_key_id = ..
aws_secret_access_key = ..
s3 = boto3.client('s3',
aws_access_key_id=aws_access_key_id,
aws_secret_access_key=aws_secret_access_key)
try:
for root, dirs, files in os.walk(local_dir):
s3_root = root.split("/")[-1]
for file in files:
local_path = os.path.join(root, file)
relative_path = os.path.relpath(local_path, local_dir)
s3_path = os.path.join(s3_root, relative_path).replace("\\", "/")
s3.upload_file(local_path, bucket_name, s3_path)
print(f'Uploaded {local_path} to {s3_path}')
except Exception as E:
print(f"Upload SNAPSHOT : Error Appeared - {E}")
..
해당 스크립트는 로컬 디렉토리를 S3 버킷에 업로드하는 모듈입니다. S3 스토리지의 GUI 인터페이스에서는 지원하는 기능이지만, Python의 boto3 모듈은 폴더 디렉토리를 통채로 업로드하는 기능을 지원하지 않습니다. 따라서 로컬 디렉토리를 탐색하고 해당 파일 구조로 데이터를 업로드하는 방식을 사용해야 합니다.
- boto3.client() 모듈을 통해 AWS 관련 작업 수행 - 파일 업로드
- os.walk() 모듈을 통해 로컬 폴더의 디렉토리 구조 탐색
- os.path.join() 모듈을 통해 디렉토리 문자열 결합 (os.path.join() 기능은 문자열 + fstring 조합으로 대체할 수 있습니다)
import os, sys
lib_dir = os.path.join(os.path.dirname(os.path.abspath(__file__)), "../lib")
sys.path.append(lib_dir)
from s3_libs import upload_to_s3
from datetime import datetime
snapshot_folder_dir = os.path.join(os.path.dirname(os.path.abspath(__file__)), "../snapshot")
local_dir = f"{snapshot_folder_dir}/{datetime.now().strftime("%Y-%m-%d")}"
bucket_name = "spotify-snapshot-backup"
upload_to_s3(bucket_name=bucket_name,
local_dir=local_dir)
해당 스크립트는 앞서 작성한 모듈을 사용하여 .sql 파일을 S3 환경에 업로드합니다. 상대 경로를 통해 모듈을 호출할 수도 있지만, 만일의 오류를 대비해 아래의 로직을 사용하고 있습니다.
- os.path.abspath() 모듈을 통해 스크립트의 절대 경로를 반환
- sys.path.append() 모듈을 통해 라이브러리 경로를 입력 (해당 조치 후 모듈 import 가능)
스크립트 자동화 - Cron
데이터베이스의 백업본 생성 작업은 자동화되어 있는 것이 일반적일 것입니다. 해당 로직을 수행함에 있어 Airflow와 같은 무거운 스케줄러는 필요하지 않기 때문에 Cron 서비스를 사용하여 스케줄링을 진행하였습니다.
0 0 * * * python3 .. /backup_snapshot.py
10 0 * * * python3 .. /upload_snapshot.py
데이터베이스의 백업본을 생성한 후 데이터를 업로드해야 하기 때문에 약 10분의 차이를 두었습니다.
실행 결과
PostgreSQL 데이터베이스의 백업본 데이터는 'spotify-shapshot-backup' 버킷에 업로드되고 있습니다(레어닉이라 당연히 먹혔을 줄..).
버킷 내부에는 백업 파일들이 날짜별로 정리되어 있습니다.
폴더 내부에 .sql 파일이 정상적으로 업로드 되어 있습니다. 백업 파일의 생성 시간대가 파일명으로 기재되어 있습니다.
📝 마치며
대량의 데이터를 관리하는 파이프라인에서 데이터의 백업은 매우 중요한 작업입니다. 다행히도 대부분의 서비스들이 백업을 기능적으로 지원하고 있으므로, 이를 통해 다양한 로직들을 구성해 쉽고 간편하게 백업 및 복원 작업을 수행하고 혹여나 발생할 수 있는 시스템 에러 상황에 대비할 수 있을 것입니다.
'데이터 이모저모 > SQL' 카테고리의 다른 글
[PostgreSQL] 외부 IP로부터의 접속 관리하기(pg_hba, postgresql) (0) | 2023.12.23 |
---|---|
[PostgreSQL] 유저 생성 및 권한 설정하기 (0) | 2023.12.23 |
발자취를 로그처럼 남기고자 하는 초보 개발자의 블로그