https://docs.nestjs.com/techniques/task-scheduling 참고
https://dev.to/yasseryka/how-to-backup-mongodb-every-night-in-nodejs-257o 참고2
이것을 참고할 만 하나 mongo를 도커로 돌리고 있기때문에 Nestjs에서 db를 백업하는게 좀 애매하다. cron으로 db를 백업하고 별도 스크립트로 firebase에 업로드하는 식으로 구현해봤다.
Cron으로 매일 특정 시간에 mongodb를 백업하고 , 클라우드에 업로드하는 스크립트를 실행한다.
https://jainsaket-1994.medium.com/installing-crontab-on-amazon-linux-2023-ec2-98cf2708b171 참고 Amazon linux2023에 기본으로 crond가 동작하지 않는 것 같으니 일단 서비스 설정을 해주고
cat /etc/crontab
[ec2-user@ip-172-31-28-80 docker]$ cat /etc/crontab
SHELL=/bin/bash
PATH=/sbin:/bin:/usr/sbin:/usr/bin
MAILTO=root
# For details see man 4 crontabs
# Example of job definition:
# .---------------- minute (0 - 59)
# | .------------- hour (0 - 23)
# | | .---------- day of month (1 - 31)
# | | | .------- month (1 - 12) OR jan,feb,mar,apr ...
# | | | | .---- day of week (0 - 6) (Sunday=0 or 7) OR sun,mon,tue,wed,thu,fri,sat
# | | | | |
# * * * * * user-name command to be executed
0 21 * * * ec2-user /home/ec2-user/docker/backup_upload.sh
ec2-user로 스크립트를 실행해도 상관없을 듯 하다. root권한이 필요한 것은 docker exec 쪽에 몰아두거나 sudo로 실행하면 되니. 시간설정시 서버는 UTC시간이므로 적당히 리전에 따라 조정한다. 편집후에는 서버 재시작
[ec2-user@ip-172-31-28-80 docker]$ sudo systemctl enable crond.service
[ec2-user@ip-172-31-28-80 docker]$
[ec2-user@ip-172-31-28-80 docker]$
[ec2-user@ip-172-31-28-80 docker]$ sudo systemctl start crond.service
[ec2-user@ip-172-31-28-80 docker]$ sudo systemctl status crond | grep Active
Active: active (running) since Tue 2024-05-21 14:22:17 UTC; 36s ago
backup_upload.sh
mongodb를 백업하는 backup.sh를 호출하고 , db압축파일을 업로드하는 mongo_upload.ts를 호출한다.
#!/bin/bash
today=$(date +"%Y%m%d")
old_date=`date -d '7 days ago' +%Y%m%d`
echo "today=$today, old_date=$old_date"
cd /home/ec2-user/docker
#backup mongodb to backup/$today.tar
sudo docker exec docker-mongo-1 sh -c "cd /app/backup; sh backup.sh $today"
zip -P mypassword1 ./backup/$today.zip ./backup/$today.tar
#upload to firebase storage
/home/ec2-user/.bun/bin/bun run /home/ec2-user/nest_server/src/mongo_upload.ts /home/ec2-user/docker/backup/$today.zip $old_date.zip
# delete files
sudo rm ./backup/$today.tar
old_file="./backup/$old_date.zip"
if [ -e $old_file ]
then
rm $old_file
fi
# /etc/crontab
# 0 6 * * * ec2-user /home/ec2-user/docker/backup_upload.sh
날짜별로 db를 tar로 묶은 파일을 zip으로 암호를 걸어서 압축한다. 이후 firebase로 업로드하고, zip 파일은 그대로 두고, tar파일과 오래된 zip파일은 삭제하는 코드다.
backup.sh
도커로 돌아가는 컨테이너에서 실행되는 backup 스크립트이다. $today 파라미터를 받아서 dump폴더를 날짜.tar로 압축한다.
#!/bin/bash
today=$1
echo "[docker]today=$today"
# mongo db backup
echo "backup db to ${today}/"
mongodump --username=user1 --password=password2 --db=db3 --out=$today
today_file=${today}.tar
# if exist $today_file delete it
if [ -e $today_file ]
then
rm $today_file
fi
echo "make $today_file"
tar cvf $today_file $today
# delete directory
rm -rf $today
mongo_upload.ts
frebase의 firestorage에 업로드하는 스크립트이다. 기본적으로 사용자 서비스에는 클라우드 스토리지는 사용하지 않으모로 필히 엑세스 권한을 read / write 모두 false로 해 두어야 한다.
import { App, initializeApp } from "firebase-admin/app";
import { getStorage } from "firebase-admin/storage";
var admin = require("firebase-admin");
const fs = require("fs");
const path = require("path");
var serviceAccount = require("../firebse-adminsdk.json");
const app:App = initializeApp({
credential: admin.credential.cert(serviceAccount),
storageBucket: "gs://yourappId.appspot.com"
});
// bun run ~/projects/leetzsche_backend/nest_server/src/mongo_upload.ts target_path old_filename
//console.log(process.argv[0]) // bun
//console.log(process.argv[1]) // .../mongo_upload.ts
//console.log(process.argv[2]) // target_path
const target = process.argv[2];
if (fs.existsSync(target)) {
console.log(`Upload ${target} to firebase`);
const filename = path.basename(target);
const storage = getStorage(app)
const metadata = {
destination: filename,
contentType: 'application/zip'
};
try {
await storage.bucket().upload(target, metadata);
console.log(`${filename} upload done`);
} catch(err) {
console.log(err);
}
//delete old file
const old_filename = process.argv[3];
if (old_filename != undefined) {
console.log(`try to delete old ${old_filename}`);
try {
await storage.bucket().file(old_filename).delete();
console.log(`${old_filename} delete done`);
} catch(err) {
if (err.code != "404") {
console.log(err);
}
}
}
} else {
console.log(`not found: ${target}`);
}