홈서버 워드프레스 유지 보수 자동화 전략 – 백업, 보안, 업데이트를 자동으로
홈서버로 워드프레스를 운영하면서 가장 걱정되는 부분은
바로 “언제 문제가 터질지 모른다”는 불안감이다.
업데이트를 미뤘더니 보안 구멍이 생기고,
백업을 안 했더니 복구가 불가능하고,
로그를 보지 않아서 문제를 사전에 감지하지 못하는 경우도 많다.
이 모든 상황을 해결할 방법은 단 하나.
바로 “자동화”다.
이번 글에서는 홈서버 환경에서 워드프레스의 유지보수를 자동화하는 방법을 소개한다.
백업, 보안 점검, 패키지 업데이트, 모니터링 등을 스크립트나 툴로 자동화함으로써
운영 안정성은 높이고, 관리 스트레스는 낮추는 전략이다.
📊 자동화의 중요성
수동 vs 자동 운영 비교
| 작업 | 수동 (시간/주) | 자동 (시간/주) | 절감 |
|---|---|---|---|
| 백업 | 2시간 | 10분 (검증) | -92% |
| 보안 점검 | 1시간 | 5분 (리뷰) | -92% |
| 시스템 업데이트 | 1시간 | 10분 (확인) | -83% |
| 모니터링 | 1시간 | 0분 (알림만) | -100% |
| 로그 분석 | 1시간 | 5분 (요약) | -92% |
| 총 | 6시간 | 30분 | -92% |
장애 발생 시 복구 시간
| 상황 | 백업 없음 | 수동 백업 | 자동 백업 |
|---|---|---|---|
| DB 손상 | 복구 불가 | 2~4시간 | 10분 |
| 파일 삭제 | 복구 불가 | 1~2시간 | 5분 |
| 해킹 | 새로 구축 | 4~8시간 | 30분 |
| 서버 고장 | 모두 손실 | 8~12시간 | 1시간 |
핵심: 자동화 = 시간 절약 92% + 복구 시간 95% 단축
🗃️ Level 1: 백업 자동화 시스템
1-1. 완벽한 백업 스크립트
#!/bin/bash
# ~/scripts/wordpress-backup.sh
# 설정
DATE=$(date +%Y%m%d_%H%M%S)
BACKUP_DIR=~/backups/wordpress
RETENTION_DAYS=30
EMAIL="[email protected]"
# WordPress 정보
WP_DIR=/var/www/html
DB_NAME=wordpress
DB_USER=root
DB_PASS=your_password
DB_HOST=localhost
# 로그
LOG_FILE=~/logs/backup-${DATE}.log
exec 1>$LOG_FILE 2>&1
echo "=== WordPress Backup Started: $DATE ==="
# 디렉토리 생성
mkdir -p $BACKUP_DIR
# 1. 파일 백업
echo "1. Backing up WordPress files..."
docker exec wordpress tar czf /tmp/wp-files-${DATE}.tar.gz \
-C /var/www/html \
--exclude='wp-content/cache' \
--exclude='wp-content/uploads/cache' \
.
docker cp wordpress:/tmp/wp-files-${DATE}.tar.gz $BACKUP_DIR/
docker exec wordpress rm /tmp/wp-files-${DATE}.tar.gz
FILE_SIZE=$(du -h $BACKUP_DIR/wp-files-${DATE}.tar.gz | cut -f1)
echo " ✓ Files backed up: $FILE_SIZE"
# 2. 데이터베이스 백업 (mysqldump)
echo "2. Backing up database (mysqldump)..."
docker exec wordpress-db mysqldump \
-u$DB_USER \
-p$DB_PASS \
--single-transaction \
--routines \
--triggers \
--events \
$DB_NAME \
> $BACKUP_DIR/db-${DATE}.sql
gzip $BACKUP_DIR/db-${DATE}.sql
DB_SIZE=$(du -h $BACKUP_DIR/db-${DATE}.sql.gz | cut -f1)
echo " ✓ Database backed up: $DB_SIZE"
# 3. Docker 볼륨 백업 (추가 안전장치)
echo "3. Backing up Docker volumes..."
docker run --rm \
-v wordpress-project_wordpress_data:/source:ro \
-v $BACKUP_DIR:/backup \
alpine \
tar czf /backup/volume-wordpress-${DATE}.tar.gz -C /source .
docker run --rm \
-v wordpress-project_db_data:/source:ro \
-v $BACKUP_DIR:/backup \
alpine \
tar czf /backup/volume-db-${DATE}.tar.gz -C /source .
VOL_SIZE=$(du -h $BACKUP_DIR/volume-*.tar.gz | awk '{sum+=$1} END {print sum}')
echo " ✓ Volumes backed up"
# 4. 백업 검증
echo "4. Verifying backups..."
for file in $BACKUP_DIR/*-${DATE}*; do
if [ -f "$file" ]; then
size=$(stat -f%z "$file" 2>/dev/null || stat -c%s "$file" 2>/dev/null)
if [ $size -gt 1024 ]; then
echo " ✓ $file OK ($(du -h $file | cut -f1))"
else
echo " ✗ $file FAILED (too small)"
echo "Backup verification failed!" | mail -s "Backup Error" $EMAIL
exit 1
fi
fi
done
# 5. 오래된 백업 삭제
echo "5. Cleaning old backups (>$RETENTION_DAYS days)..."
find $BACKUP_DIR -name "*-*" -mtime +$RETENTION_DAYS -delete
REMAINING=$(ls -1 $BACKUP_DIR | wc -l)
echo " ✓ Remaining backups: $REMAINING"
# 6. Google Drive 업로드 (rclone)
if command -v rclone &> /dev/null; then
echo "6. Uploading to Google Drive..."
rclone copy $BACKUP_DIR gdrive:Backups/WordPress \
--include "*-${DATE}*" \
--progress \
--log-file=~/logs/rclone-${DATE}.log
echo " ✓ Uploaded to cloud"
fi
# 7. 요약 이메일
TOTAL_SIZE=$(du -sh $BACKUP_DIR | cut -f1)
cat <1-2. Cron 스케줄 설정
# crontab -e
# 매일 새벽 3시 백업
0 3 * * * ~/scripts/wordpress-backup.sh
# 주간 백업 (일요일 새벽 4시 - 추가 안전장치)
0 4 * * 0 ~/scripts/wordpress-backup.sh
# 월간 백업 (매월 1일 새벽 5시)
0 5 1 * * cp -r ~/backups/wordpress ~/backups/monthly/$(date +\%Y\%m)
# 백업 로그 정리 (30일 이상)
0 6 * * * find ~/logs -name "backup-*.log" -mtime +30 -delete
1-3. 복원 스크립트
#!/bin/bash
# ~/scripts/wordpress-restore.sh
# 사용법: ./wordpress-restore.sh 20251111_030000
if [ -z "$1" ]; then
echo "Usage: $0 "
echo "Available backups:"
ls -1 ~/backups/wordpress/ | grep -E '^(wp-files|db)-' | cut -d- -f2-3 | sort -u
exit 1
fi
TIMESTAMP=$1
BACKUP_DIR=~/backups/wordpress
echo "⚠️ WARNING: This will restore WordPress to backup: $TIMESTAMP"
read -p "Are you sure? (yes/no): " confirm
if [ "$confirm" != "yes" ]; then
echo "Restore cancelled"
exit 0
fi
# 1. 현재 상태 백업 (롤백용)
echo "Creating rollback backup..."
~/scripts/wordpress-backup.sh
# 2. 컨테이너 중지
echo "Stopping containers..."
docker-compose down
# 3. 파일 복원
echo "Restoring files..."
docker run --rm \
-v wordpress-project_wordpress_data:/target \
-v $BACKUP_DIR:/backup:ro \
alpine \
sh -c "rm -rf /target/* && tar xzf /backup/wp-files-${TIMESTAMP}.tar.gz -C /target"
# 4. DB 복원
echo "Restoring database..."
gunzip < $BACKUP_DIR/db-${TIMESTAMP}.sql.gz | \
docker exec -i wordpress-db mysql -uroot -p$MYSQL_ROOT_PASSWORD wordpress
# 5. 컨테이너 재시작
echo "Starting containers..."
docker-compose up -d
# 6. 검증
sleep 10
HTTP_STATUS=$(curl -o /dev/null -s -w "%{http_code}" http://localhost)
if [ "$HTTP_STATUS" = "200" ]; then
echo "✓ Restore completed successfully!"
else
echo "✗ Restore failed (HTTP: $HTTP_STATUS)"
echo "Rolling back..."
# 롤백 로직
fi
🛡️ Level 2: 보안 자동화
2-1. WordPress 자동 업데이트
# wp-config.php에 추가
// 핵심 파일 자동 업데이트 (보안 패치)
define('WP_AUTO_UPDATE_CORE', 'minor');
// 플러그인 자동 업데이트 (선택)
add_filter('auto_update_plugin', '__return_true');
// 테마 자동 업데이트 (비권장 - 디자인 깨질 수 있음)
add_filter('auto_update_theme', '__return_false');
// 번역 자동 업데이트
add_filter('auto_update_translation', '__return_true');
// 업데이트 이메일 알림
add_filter('auto_core_update_send_email', '__return_true');
// 개발 버전 제외
add_filter('allow_dev_auto_core_updates', '__return_false');
2-2. 보안 스캔 자동화
#!/bin/bash
# ~/scripts/security-scan.sh
DATE=$(date +%Y%m%d)
REPORT_FILE=~/logs/security-scan-${DATE}.log
EMAIL="[email protected]"
echo "=== Security Scan: $DATE ===" > $REPORT_FILE
# 1. Wordfence CLI 스캔
echo "\n# Wordfence Scan" >> $REPORT_FILE
wp wordfence scan --path=/var/www/html >> $REPORT_FILE 2>&1
# 2. 의심스러운 파일 검색
echo "\n# Suspicious Files" >> $REPORT_FILE
find /var/www/html -name "*.php" -mtime -1 >> $REPORT_FILE
# 3. 권한 검사
echo "\n# File Permissions" >> $REPORT_FILE
find /var/www/html -type f -perm 0777 >> $REPORT_FILE
# 4. 최근 로그인 시도
echo "\n# Recent Login Attempts" >> $REPORT_FILE
wp db query "SELECT * FROM wp_users ORDER BY user_registered DESC LIMIT 10" \
--path=/var/www/html >> $REPORT_FILE
# 5. 플러그인 취약점 체크
echo "\n# Plugin Vulnerabilities" >> $REPORT_FILE
wp plugin list --path=/var/www/html --format=csv | \
while IFS=, read name status update version; do
if [ "$update" = "available" ]; then
echo "UPDATE AVAILABLE: $name ($version)" >> $REPORT_FILE
fi
done
# 6. SSL 인증서 만료일
echo "\n# SSL Certificate" >> $REPORT_FILE
echo | openssl s_client -connect localhost:443 2>/dev/null | \
openssl x509 -noout -dates >> $REPORT_FILE
# 7. 이메일 발송 (문제 발견 시)
if grep -qi "threat\|vulnerable\|suspicious" $REPORT_FILE; then
mail -s "⚠️ Security Alert: $DATE" $EMAIL < $REPORT_FILE
fi
# Cron: 매일 새벽 4시
# 0 4 * * * ~/scripts/security-scan.sh
2-3. Fail2ban 자동 차단
# /etc/fail2ban/jail.local
[wordpress]
enabled = true
filter = wordpress
logpath = /var/log/nginx/access.log
maxretry = 5
findtime = 600
bantime = 3600
[wordpress-xmlrpc]
enabled = true
filter = wordpress-xmlrpc
logpath = /var/log/nginx/access.log
maxretry = 3
findtime = 600
bantime = 86400
# /etc/fail2ban/filter.d/wordpress.conf
[Definition]
failregex = ^ .* "POST /wp-login.php
^ .* "POST /xmlrpc.php
# 재시작
sudo systemctl restart fail2ban
# 상태 확인
sudo fail2ban-client status wordpress
🔄 Level 3: 시스템 업데이트 자동화
3-1. Ubuntu 자동 업데이트
# 설치
sudo apt install unattended-upgrades update-notifier-common -y
# 설정
sudo dpkg-reconfigure --priority=low unattended-upgrades
# /etc/apt/apt.conf.d/50unattended-upgrades 편집
Unattended-Upgrade::Allowed-Origins {
"${distro_id}:${distro_codename}-security";
"${distro_id}ESMApps:${distro_codename}-apps-security";
};
Unattended-Upgrade::AutoFixInterruptedDpkg "true";
Unattended-Upgrade::MinimalSteps "true";
Unattended-Upgrade::Remove-Unused-Dependencies "true";
Unattended-Upgrade::Automatic-Reboot "false"; # 수동 재부팅
Unattended-Upgrade::Automatic-Reboot-Time "03:00";
Unattended-Upgrade::Mail "[email protected]";
Unattended-Upgrade::MailReport "on-change";
# 테스트
sudo unattended-upgrades --dry-run --debug
3-2. Docker 자동 업데이트 (Watchtower)
# docker-compose.yml에 추가
services:
watchtower:
image: containrrr/watchtower
container_name: watchtower
volumes:
- /var/run/docker.sock:/var/run/docker.sock
environment:
- WATCHTOWER_CLEANUP=true # 이전 이미지 삭제
- WATCHTOWER_POLL_INTERVAL=86400 # 24시간마다 체크
- WATCHTOWER_NOTIFICATIONS=email
- [email protected]
- [email protected]
- WATCHTOWER_NOTIFICATION_EMAIL_SERVER=smtp.gmail.com
- WATCHTOWER_NOTIFICATION_EMAIL_SERVER_PORT=587
- [email protected]
- WATCHTOWER_NOTIFICATION_EMAIL_SERVER_PASSWORD=your_password
- WATCHTOWER_SCHEDULE=0 0 3 * * * # 매일 새벽 3시
restart: unless-stopped
# 특정 컨테이너만 업데이트
watchtower:
command: wordpress wordpress-db nginx-proxy
# 특정 컨테이너 제외
critical-app:
labels:
- "com.centurylinklabs.watchtower.enable=false"
# 시작
docker-compose up -d watchtower
3-3. 수동 확인 스크립트
#!/bin/bash
# ~/scripts/check-updates.sh
echo "=== Available Updates ==="
# 1. System packages
echo "\n# System Packages:"
apt list --upgradable 2>/dev/null | grep -v "Listing"
# 2. Docker images
echo "\n# Docker Images:"
docker images --format "{{.Repository}}:{{.Tag}}" | \
while read image; do
docker pull $image --quiet
done
docker images
# 3. WordPress core
echo "\n# WordPress Core:"
wp core check-update --path=/var/www/html
# 4. WordPress plugins
echo "\n# WordPress Plugins:"
wp plugin list --update=available --path=/var/www/html
# 5. WordPress themes
echo "\n# WordPress Themes:"
wp theme list --update=available --path=/var/www/html
# Cron: 매주 월요일 오전 9시
# 0 9 * * 1 ~/scripts/check-updates.sh | mail -s "Weekly Update Report" [email protected]
📈 Level 4: 모니터링 자동화
4-1. Uptime Kuma 설정
# docker-compose.yml에 추가
services:
uptime-kuma:
image: louislam/uptime-kuma:1
container_name: uptime-kuma
volumes:
- ./uptime-kuma:/app/data
ports:
- "3001:3001"
restart: unless-stopped
# 시작
docker-compose up -d uptime-kuma
# 접속: http://서버IP:3001
# 모니터 설정:
1. WordPress 사이트 (HTTP, 60초)
2. MySQL (TCP, 3306포트, 60초)
3. SSH (TCP, 22포트, 300초)
4. Disk Space (Script, 600초)
# 알림:
- Email
- Slack
- Discord
- Telegram
4-2. 리소스 모니터링 스크립트
#!/bin/bash
# ~/scripts/resource-monitor.sh
DATE=$(date +"%Y-%m-%d %H:%M:%S")
LOG_FILE=~/logs/resources.csv
THRESHOLD_DISK=85
THRESHOLD_MEM=90
EMAIL="[email protected]"
# CSV 헤더 (최초 실행시)
if [ ! -f $LOG_FILE ]; then
echo "Date,CPU_Load,Memory_%,Disk_%,Docker_Containers" > $LOG_FILE
fi
# 데이터 수집
CPU_LOAD=$(uptime | awk -F'load average:' '{print $2}' | awk '{print $1}' | tr -d ',')
MEM_PERCENT=$(free | awk 'NR==2 {printf "%.0f", $3/$2 * 100}')
DISK_PERCENT=$(df -h / | awk 'NR==2 {print $5}' | tr -d '%')
DOCKER_COUNT=$(docker ps -q | wc -l)
# CSV 기록
echo "$DATE,$CPU_LOAD,$MEM_PERCENT,$DISK_PERCENT,$DOCKER_COUNT" >> $LOG_FILE
# 임계값 체크
ALERT=""
if [ $DISK_PERCENT -gt $THRESHOLD_DISK ]; then
ALERT="${ALERT}\n⚠️ Disk usage: ${DISK_PERCENT}%"
fi
if [ $MEM_PERCENT -gt $THRESHOLD_MEM ]; then
ALERT="${ALERT}\n⚠️ Memory usage: ${MEM_PERCENT}%"
fi
# 알림 발송
if [ ! -z "$ALERT" ]; then
echo -e "Resource Alert at $DATE\n$ALERT" | \
mail -s "⚠️ Resource Alert" $EMAIL
fi
# Cron: 5분마다
# */5 * * * * ~/scripts/resource-monitor.sh
4-3. 로그 집계 및 리포팅
#!/bin/bash
# ~/scripts/weekly-report.shWEEK_START=$(date -d "7 days ago" +%Y-%m-%d)
WEEK_END=$(date +%Y-%m-%d)
REPORT_FILE=~/logs/we
댓글 남기기