docs:백업 자동화 문서 보강 — 크론 설정, sam_stage 동기화 절차 추가

- 10-backup-recovery.md: CI/CD 자동 백업 상세화 (스크립트, 크론, 복원 절차), sam→sam_stage 동기화 절차 신규
- 11-server-setup.md: CI/CD ⑪ 백업 자동화 설치 가이드, ⑫ 최종 점검 추가, PM2 stage 메모리 실제값 반영

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-02-24 22:14:32 +09:00
parent 47fdf147c3
commit 50899c6a0e
2 changed files with 267 additions and 17 deletions

View File

@@ -165,28 +165,161 @@ sudo tar czf /home/hskwon/backups/grafana/grafana-data-$(date +%Y%m%d).tar.gz /v
## [CI/CD] MySQL 자동 백업 (운영 DB)
**자동 백업:** crontab 매일 03:00 실행
### 개요
- 스크립트: /home/hskwon/scripts/backup-db.sh
- 인증 정보: /home/hskwon/.sam_backup.cnf (chmod 600)
- 저장소: /home/hskwon/backups/mysql/
- 보존: 14일
CI/CD 서버(sam-cicd)에서 운영 서버(sam-prod)의 MySQL DB를 원격으로 백업합니다.
**백업 대상:**
| 항목 | 값 |
|------|-----|
| 스케줄 | **매일 03:00** (crontab) |
| 스크립트 | `/home/hskwon/scripts/backup-db.sh` |
| 인증 정보 | `/home/hskwon/.sam_backup.cnf` (chmod 600) |
| 저장소 | `/home/hskwon/backups/mysql/` |
| 보존 기간 | **14일** (자동 삭제) |
| 로그 | `/home/hskwon/backups/mysql/backup.log` |
| DB | 서버 | 사용자 | 비고 |
|----|------|--------|------|
| gitea | localhost | root (auth_socket) | Gitea DB |
| sam | 211.117.60.189 (운영) | sam_backup | 운영 메인 DB |
| sam_stat | 211.117.60.189 (운영) | sam_backup | 통계 DB |
### 백업 대상
**수동 백업 실행:**
| DB | 서버 | 사용자 | 크기 (gzip) | 비고 |
|----|------|--------|------------|------|
| gitea | localhost | root (auth_socket) | ~50KB | Gitea DB |
| sam | 211.117.60.189 (운영) | sam_backup | ~9.3MB | 운영 메인 DB (295 테이블) |
| sam_stat | 211.117.60.189 (운영) | sam_backup | ~184KB | 통계 DB (20 테이블) |
### 백업 스크립트
```bash
# 전체 백업 (Gitea + 운영 DB)
/home/hskwon/scripts/backup-db.sh
# /home/hskwon/scripts/backup-db.sh
#!/bin/bash
set -e
BACKUP_DIR="/home/hskwon/backups/mysql"
BACKUP_CNF="/home/hskwon/.sam_backup.cnf"
DATE=$(date +%Y%m%d_%H%M%S)
RETENTION_DAYS=14
mkdir -p $BACKUP_DIR
# Gitea DB 백업 (로컬, auth_socket)
mysqldump --single-transaction --routines --triggers gitea | gzip > $BACKUP_DIR/gitea_$DATE.sql.gz
# 운영 DB 원격 백업 (sam_backup 사용자)
if [ -f "$BACKUP_CNF" ]; then
mysqldump --defaults-extra-file=$BACKUP_CNF -h 211.117.60.189 --single-transaction --routines --triggers --no-tablespaces sam | gzip > $BACKUP_DIR/sam_production_$DATE.sql.gz
mysqldump --defaults-extra-file=$BACKUP_CNF -h 211.117.60.189 --single-transaction --routines --triggers --no-tablespaces sam_stat | gzip > $BACKUP_DIR/sam_stat_production_$DATE.sql.gz
echo "$(date '+%Y-%m-%d %H:%M:%S'): Backup completed (gitea + sam + sam_stat)" >> $BACKUP_DIR/backup.log
else
echo "$(date '+%Y-%m-%d %H:%M:%S'): Backup completed (gitea only - $BACKUP_CNF not found)" >> $BACKUP_DIR/backup.log
fi
# 오래된 백업 삭제
find $BACKUP_DIR -name '*.sql.gz' -mtime +$RETENTION_DAYS -delete
```
### 인증 설정
```ini
# /home/hskwon/.sam_backup.cnf (chmod 600)
[client]
user=sam_backup
password=<백업용_비밀번호>
```
### 크론탭 (sam-cicd 서버, hskwon 유저)
```crontab
# SAM DB 백업 (매일 새벽 3시)
0 3 * * * /home/hskwon/scripts/backup-db.sh >> /home/hskwon/backups/mysql/backup.log 2>&1
```
### 수동 실행 및 확인
```bash
# 수동 백업 실행
/home/hskwon/scripts/backup-db.sh
# 백업 파일 확인
ls -lht /home/hskwon/backups/mysql/
# 백업 로그 확인
tail -10 /home/hskwon/backups/mysql/backup.log
# 크론 스케줄 확인
crontab -l
```
### 백업 복원 (CI/CD → 운영)
```bash
# sam DB 복원 (운영 서버에서 실행)
gunzip -c /path/to/sam_production_YYYYMMDD_HHMMSS.sql.gz | mysql -ucodebridge -p sam
# sam_stat DB 복원
gunzip -c /path/to/sam_stat_production_YYYYMMDD_HHMMSS.sql.gz | mysql -ucodebridge -p sam_stat
```
### 운영 MySQL 백업 사용자 (운영 서버 설정)
```sql
-- 운영 서버(sam-prod)에서 실행
CREATE USER 'sam_backup'@'110.10.147.46' IDENTIFIED BY '<백업용_비밀번호>';
GRANT SELECT, LOCK TABLES, SHOW VIEW, EVENT, TRIGGER ON sam.* TO 'sam_backup'@'110.10.147.46';
GRANT SELECT, LOCK TABLES, SHOW VIEW, EVENT, TRIGGER ON sam_stat.* TO 'sam_backup'@'110.10.147.46';
FLUSH PRIVILEGES;
```
UFW에서 CI/CD IP의 MySQL 접근이 허용되어 있어야 합니다:
```bash
# 운영 서버 UFW 규칙 확인
sudo ufw status | grep 3306
# → 110.10.147.46 ALLOW (CI/CD 백업용)
```
---
## [운영] sam → sam_stage 동기화
Stage 환경(stage-api.sam.it.kr)은 `sam_stage` DB를 사용합니다. 운영 `sam` DB와 **자동 동기화는 없으며**, 필요 시 수동으로 동기화합니다.
### 스키마만 동기화 (테이블 구조)
운영 DB에 테이블/컬럼 변경이 있을 때 실행합니다.
```bash
# 운영 서버(sam-prod)에서 실행
# 1. sam_stage 초기화
mysql -ucodebridge -p -e "DROP DATABASE IF EXISTS sam_stage; CREATE DATABASE sam_stage CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;"
# 2. 스키마 복사 (구조만, 데이터 없음)
mysqldump -ucodebridge -p --single-transaction --no-data --no-tablespaces --skip-triggers --skip-routines sam | mysql -ucodebridge -p sam_stage
# 3. 데이터 복사 (필요시)
mysqldump -ucodebridge -p --single-transaction --no-create-info --no-tablespaces --skip-triggers --skip-routines sam | mysql -ucodebridge -p sam_stage
# 4. Laravel 캐시 갱신
cd /home/webservice/api-stage/current
php artisan config:cache
php artisan cache:clear
```
### 전체 동기화 (스키마 + 데이터)
Stage 환경을 운영과 동일한 상태로 리셋할 때 실행합니다.
```bash
# 운영 서버(sam-prod)에서 실행
mysql -ucodebridge -p -e "DROP DATABASE IF EXISTS sam_stage; CREATE DATABASE sam_stage CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;"
mysqldump -ucodebridge -p --single-transaction --no-tablespaces --skip-triggers --skip-routines sam | mysql -ucodebridge -p sam_stage
cd /home/webservice/api-stage/current && php artisan config:cache && php artisan cache:clear
```
### 주의사항
- `codebridge` 사용자에 `SUPER`, `PROCESS` 권한이 없으므로 `--no-tablespaces --skip-triggers --skip-routines` 옵션 필수
- sam_stage의 `.env`는 별도 관리 (`APP_URL=https://stage-api.sam.it.kr`, `APP_ENV=staging`)
- Jenkins 파이프라인(api)의 Stage 배포 시 `php artisan migrate --force`로 스키마 자동 반영
---
## 전체 서버 복구 절차

View File

@@ -354,10 +354,10 @@ module.exports = {
args: 'start -p 3100',
instances: 1,
exec_mode: 'fork',
max_memory_restart: '200M',
max_memory_restart: '512M',
env: {
NODE_ENV: 'production',
NODE_OPTIONS: '--max-old-space-size=128'
NODE_OPTIONS: '--max-old-space-size=384'
}
}
]
@@ -588,7 +588,7 @@ stage-api.sam.it.kr은 api.sam.it.kr과 동일 구조 (소켓: php8.4-fpm-api-st
| ⑧ | Prometheus + node_exporter | ① |
| ⑨ | Grafana | ⑧ |
| ⑩ | Jenkins 파이프라인 + Webhook | ⑥⑦ |
| ⑪ | 백업 자동화 | ② |
| ⑪ | 백업 자동화 (운영 DB 원격 백업) | ② |
| ⑫ | fail2ban + 최종 점검 | 전체 |
---
@@ -1024,6 +1024,123 @@ sudo systemctl start grafana-server
**초기 설정:** Data Source: Prometheus (http://localhost:9090) → 대시보드 임포트: Node Exporter Full (ID: 1860)
### ⑪ 백업 자동화 (운영 DB 원격 백업)
CI/CD 서버에서 운영 서버의 MySQL DB를 매일 자동 백업합니다.
**1. 운영 서버 — 백업 사용자 생성 (운영 MySQL에서 실행):**
```sql
CREATE USER 'sam_backup'@'110.10.147.46' IDENTIFIED BY '<백업용_비밀번호>';
GRANT SELECT, LOCK TABLES, SHOW VIEW, EVENT, TRIGGER ON sam.* TO 'sam_backup'@'110.10.147.46';
GRANT SELECT, LOCK TABLES, SHOW VIEW, EVENT, TRIGGER ON sam_stat.* TO 'sam_backup'@'110.10.147.46';
FLUSH PRIVILEGES;
```
**2. CI/CD 서버 — MySQL 인증 파일:**
```bash
cat > /home/hskwon/.sam_backup.cnf << 'EOF'
[client]
user=sam_backup
password=<백업용_비밀번호>
EOF
chmod 600 /home/hskwon/.sam_backup.cnf
```
**3. CI/CD 서버 — 백업 스크립트:**
```bash
mkdir -p /home/hskwon/scripts /home/hskwon/backups/mysql
cat > /home/hskwon/scripts/backup-db.sh << 'SCRIPT'
#!/bin/bash
set -e
BACKUP_DIR="/home/hskwon/backups/mysql"
BACKUP_CNF="/home/hskwon/.sam_backup.cnf"
DATE=$(date +%Y%m%d_%H%M%S)
RETENTION_DAYS=14
mkdir -p $BACKUP_DIR
# Gitea DB 백업 (로컬, auth_socket)
mysqldump --single-transaction --routines --triggers gitea | gzip > $BACKUP_DIR/gitea_$DATE.sql.gz
# 운영 DB 원격 백업 (sam_backup 사용자)
if [ -f "$BACKUP_CNF" ]; then
mysqldump --defaults-extra-file=$BACKUP_CNF -h 211.117.60.189 --single-transaction --routines --triggers --no-tablespaces sam | gzip > $BACKUP_DIR/sam_production_$DATE.sql.gz
mysqldump --defaults-extra-file=$BACKUP_CNF -h 211.117.60.189 --single-transaction --routines --triggers --no-tablespaces sam_stat | gzip > $BACKUP_DIR/sam_stat_production_$DATE.sql.gz
echo "$(date '+%Y-%m-%d %H:%M:%S'): Backup completed (gitea + sam + sam_stat)" >> $BACKUP_DIR/backup.log
else
echo "$(date '+%Y-%m-%d %H:%M:%S'): Backup completed (gitea only - $BACKUP_CNF not found)" >> $BACKUP_DIR/backup.log
fi
# 오래된 백업 삭제
find $BACKUP_DIR -name '*.sql.gz' -mtime +$RETENTION_DAYS -delete
SCRIPT
chmod +x /home/hskwon/scripts/backup-db.sh
```
**4. CI/CD 서버 — 크론탭 등록:**
```bash
# hskwon이 crontab 사용 가능해야 함
sudo sh -c "echo hskwon > /etc/cron.allow"
sudo chmod 644 /etc/cron.allow
# 크론 등록 (매일 새벽 3시)
(crontab -l 2>/dev/null; echo "# SAM DB 백업 (매일 새벽 3시)"; echo "0 3 * * * /home/hskwon/scripts/backup-db.sh >> /home/hskwon/backups/mysql/backup.log 2>&1") | crontab -
# 등록 확인
crontab -l
```
**5. 테스트:**
```bash
# 수동 실행
/home/hskwon/scripts/backup-db.sh
# 결과 확인
ls -lht /home/hskwon/backups/mysql/ | head -5
tail -3 /home/hskwon/backups/mysql/backup.log
```
> 상세 복원 절차 및 sam→sam_stage 동기화는 [백업/복구/재부팅](./10-backup-recovery.md) 참조.
### ⑫ fail2ban + 최종 점검
```bash
sudo apt install -y fail2ban
sudo systemctl enable fail2ban
sudo systemctl start fail2ban
```
**최종 점검:**
```bash
# 전체 서비스 상태
sudo systemctl status nginx jenkins jenkins-agent gitea mysql prometheus grafana-server node_exporter fail2ban
# 포트 확인
sudo ss -tlnp | grep -E '(80|443|3000|3100|8080|9090|9100|3306)'
# 웹 서비스
curl -sI https://ci.sam.it.kr | head -3
curl -sI https://git.sam.it.kr | head -3
curl -sI https://monitor.sam.it.kr | head -3
# 백업 크론 확인
crontab -l
# 자동 시작 등록 확인
for svc in nginx jenkins jenkins-agent gitea mysql prometheus grafana-server node_exporter fail2ban; do
echo -n "$svc: "; systemctl is-enabled $svc 2>/dev/null || echo "NOT FOUND"
done
```
---
## 보안 체크리스트