홈서버 워드프레스 유지 보수 자동화 전략 – 백업, 보안, 업데이트를 자동으로

홈서버 워드프레스 유지 보수 자동화 전략 – 백업, 보안, 업데이트를 자동으로

홈서버로 워드프레스를 운영하면서 가장 걱정되는 부분은
바로 “언제 문제가 터질지 모른다”는 불안감이다.

업데이트를 미뤘더니 보안 구멍이 생기고,
백업을 안 했더니 복구가 불가능하고,
로그를 보지 않아서 문제를 사전에 감지하지 못하는 경우도 많다.

이 모든 상황을 해결할 방법은 단 하나.
바로 “자동화”다.

이번 글에서는 홈서버 환경에서 워드프레스의 유지보수를 자동화하는 방법을 소개한다.
백업, 보안 점검, 패키지 업데이트, 모니터링 등을 스크립트나 툴로 자동화함으로써
운영 안정성은 높이고, 관리 스트레스는 낮추는 전략이다.

📊 자동화의 중요성

수동 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.sh

WEEK_START=$(date -d "7 days ago" +%Y-%m-%d)
WEEK_END=$(date +%Y-%m-%d)
REPORT_FILE=~/logs/we

댓글 남기기

댓글 남기기