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:
@@ -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`로 스키마 자동 반영
|
||||
|
||||
---
|
||||
|
||||
## 전체 서버 복구 절차
|
||||
|
||||
@@ -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
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 보안 체크리스트
|
||||
|
||||
Reference in New Issue
Block a user