Ubuntu 서버 초기 설정 ⑤ — 서버 모니터링과 rsync 자동 백업

Ubuntu 서버 초기 설정 ⑤ — 서버 모니터링과 rsync 자동 백업

서버는 언젠가 문제가 생긴다. 문제를 미리 감지하고, 최악의 경우 복원할 수 있는 구조를 만들어두자.

1~4편에서 SSH 보안, 방화벽, 시스템 설정, Nginx+HTTPS까지 마쳤다. 서버가 세상에 공개됐으니 이제 마지막 안전망이 필요하다. 디스크가 꽉 차기 전에 알림을 받고, 로그가 디스크를 잡아먹지 않게 관리하고, 최악의 상황에서도 복원할 수 있는 백업 체계를 갖추자.

htop, df, free로 서버 상태 확인하기

서버 상태를 확인하는 기본 명령어부터 익혀두자. 문제가 생겼을 때 가장 먼저 실행하는 도구들이다.

시스템 리소스 모니터링 — htop

htop

htoptop의 상위 호환으로, CPU·메모리·프로세스를 컬러풀한 UI로 보여준다. 3편에서 이미 설치했다.

영역 확인 항목
상단 바 CPU 코어별 사용률, 메모리/스왑 사용량
프로세스 목록 CPU/메모리 많이 쓰는 프로세스 식별
단축키 F6 정렬 기준 변경 (MEM%, CPU%)
단축키 F9 프로세스 강제 종료 (Kill)

디스크 사용량 — df

df -h
Filesystem      Size  Used Avail Use% Mounted on
/dev/vda1        50G   12G   36G  25% /

Use%80%를 넘으면 주의, 90%를 넘으면 즉시 대응이 필요하다. 디스크가 100% 차면 로그 기록이 멈추고, DB가 멈추고, 서버 전체가 다운될 수 있다.

메모리 사용량 — free

free -h
               total        used        free      shared  buff/cache   available
Mem:           1.9Gi       512Mi       256Mi        10Mi       1.2Gi       1.2Gi
Swap:          2.0Gi          0B       2.0Gi

available 값이 실제로 사용 가능한 메모리다. free가 적어도 available이 충분하면 문제없다. Linux는 빈 메모리를 캐시로 활용하기 때문이다.

최근 시스템 로그 — journalctl

# 최근 로그 50줄
sudo journalctl -n 50

# 실시간 로그 스트리밍
sudo journalctl -f

# 특정 서비스 로그 (예: nginx)
sudo journalctl -u nginx --since "1 hour ago"

logrotate로 로그 파일 관리

로그 파일은 방치하면 끝없이 커진다. logrotate는 로그 파일을 주기적으로 회전(rotate)시켜 오래된 로그를 압축하거나 삭제한다.

기본 동작 확인

Ubuntu 24.04에는 logrotate가 기본 설치되어 있다.

# 설정 파일 위치
cat /etc/logrotate.conf

Nginx 로그 회전 설정 확인

cat /etc/logrotate.d/nginx
/var/log/nginx/*.log {
    daily
    missingok
    rotate 14
    compress
    delaycompress
    notifempty
    create 0640 www-data adm
    sharedscripts
    postrotate
        [ ! -f /var/run/nginx.pid ] || kill -USR1 `cat /var/run/nginx.pid`
    endscript
}
옵션 의미
daily 매일 회전
rotate 14 최근 14개 파일 보관
compress 이전 로그를 gzip 압축
delaycompress 직전 1개는 압축하지 않음 (디버깅 편의)

커스텀 앱 로그에 logrotate 추가

자체 애플리케이션의 로그 파일도 logrotate로 관리할 수 있다.

sudo nano /etc/logrotate.d/myapp
/var/log/myapp/*.log {
    weekly
    rotate 4
    compress
    missingok
    notifempty
    create 0644 deploy deploy
}

journald 디스크 제한

journald도 방치하면 수 GB를 차지할 수 있다.

sudo nano /etc/systemd/journald.conf
[Journal]
SystemMaxUse=500M
SystemMaxFileSize=50M
sudo systemctl restart systemd-journald

# 현재 저널 크기 확인
journalctl --disk-usage

디스크/메모리 경고 bash 스크립트 만들기

매번 수동으로 확인할 수 없으니, 임계치를 넘으면 자동으로 알려주는 스크립트를 만든다.

모니터링 스크립트 작성

sudo nano /usr/local/bin/server-alert.sh
#!/bin/bash
# 서버 리소스 모니터링 — 임계치 초과 시 알림

HOSTNAME=$(hostname)
ALERT_FILE="/tmp/server-alert.log"

# === 디스크 사용량 체크 (90% 이상 경고) ===
DISK_USAGE=$(df / --output=pcent | tail -1 | tr -d ' %')
if [ "$DISK_USAGE" -ge 90 ]; then
    echo "[$(date)] ⚠️ 디스크 경고: ${DISK_USAGE}% 사용 중 (${HOSTNAME})" >> "$ALERT_FILE"
fi

# === 메모리 사용량 체크 (available 200MB 미만 경고) ===
AVAIL_MB=$(free -m | awk '/Mem:/ {print $7}')
if [ "$AVAIL_MB" -lt 200 ]; then
    echo "[$(date)] ⚠️ 메모리 경고: 가용 ${AVAIL_MB}MB (${HOSTNAME})" >> "$ALERT_FILE"
fi

# === 스왑 사용량 체크 (50% 이상 경고) ===
SWAP_TOTAL=$(free -m | awk '/Swap:/ {print $2}')
SWAP_USED=$(free -m | awk '/Swap:/ {print $3}')
if [ "$SWAP_TOTAL" -gt 0 ]; then
    SWAP_PCT=$((SWAP_USED * 100 / SWAP_TOTAL))
    if [ "$SWAP_PCT" -ge 50 ]; then
        echo "[$(date)] ⚠️ 스왑 경고: ${SWAP_PCT}% 사용 중 (${HOSTNAME})" >> "$ALERT_FILE"
    fi
fi

# === 알림 파일에 내용이 있으면 출력 ===
if [ -f "$ALERT_FILE" ] && [ -s "$ALERT_FILE" ]; then
    cat "$ALERT_FILE"
    # 이메일 알림을 원하면 아래 주석 해제 (mailutils 설치 필요)
    # mail -s "서버 경고: ${HOSTNAME}" [email protected] < "$ALERT_FILE"
    rm "$ALERT_FILE"
fi
# 실행 권한 부여
sudo chmod +x /usr/local/bin/server-alert.sh

# 테스트 실행
sudo /usr/local/bin/server-alert.sh

cron으로 자동 실행

sudo crontab -e
# 매 10분마다 서버 상태 체크
*/10 * * * * /usr/local/bin/server-alert.sh

💡 슬랙/디스코드 알림: curl로 Webhook URL에 POST 요청을 보내면 채팅 알림으로 전환할 수 있다. bash curl -X POST -H 'Content-type: application/json' \ --data "{\"text\":\"$(cat $ALERT_FILE)\"}" \ https://hooks.slack.com/services/YOUR/WEBHOOK/URL


rsync를 이용한 자동 백업 전략

백업 대상을 먼저 정리하자. 모든 파일을 백업할 필요는 없다.

대상 경로 예시 이유
서비스 설정 /etc/nginx/, /etc/ssh/ 서버 재구축 시 필수
SSL 인증서 /etc/letsencrypt/ 재발급 가능하지만 시간 절약
앱 데이터 /var/www/, /opt/app/ 사용자 데이터, 코드
DB 덤프 스크립트로 생성 별도 덤프 후 백업
cron 설정 /var/spool/cron/ 스케줄 작업 보존

rsync란?

rsync는 파일을 증분(incremental) 복사하는 도구다. 변경된 파일만 전송하므로 전체 복사보다 훨씬 빠르고 효율적이다.

# 기본 사용법
rsync -avz 원본/ 대상/

# 원격 서버로 백업
rsync -avz -e "ssh -p 2222" /data/ user@backup-server:/backups/
옵션 의미
-a 아카이브 모드 (권한, 시간, 심볼릭 링크 보존)
-v 상세 출력
-z 전송 시 압축
-e "ssh -p 2222" SSH 포트 지정

cron으로 백업 스크립트 자동 실행

백업 스크립트 작성

sudo nano /usr/local/bin/backup.sh
#!/bin/bash
# 서버 자동 백업 스크립트

# === 설정 ===
BACKUP_DIR="/backup"
DATE=$(date +%Y-%m-%d_%H%M)
RETENTION_DAYS=7

# === 디렉토리 생성 ===
mkdir -p "${BACKUP_DIR}/${DATE}"

# === 1. 설정 파일 백업 ===
rsync -a /etc/nginx/ "${BACKUP_DIR}/${DATE}/nginx/"
rsync -a /etc/ssh/sshd_config "${BACKUP_DIR}/${DATE}/"
rsync -a /etc/letsencrypt/ "${BACKUP_DIR}/${DATE}/letsencrypt/"
rsync -a /etc/fail2ban/jail.local "${BACKUP_DIR}/${DATE}/" 2>/dev/null

# === 2. 웹 데이터 백업 ===
rsync -a /var/www/ "${BACKUP_DIR}/${DATE}/www/"

# === 3. cron 설정 백업 ===
crontab -l > "${BACKUP_DIR}/${DATE}/crontab.txt" 2>/dev/null

# === 4. 설치된 패키지 목록 ===
dpkg --get-selections > "${BACKUP_DIR}/${DATE}/packages.txt"

# === 5. 압축 ===
cd "${BACKUP_DIR}"
tar -czf "${DATE}.tar.gz" "${DATE}/"
rm -rf "${DATE}/"

# === 6. 오래된 백업 삭제 ===
find "${BACKUP_DIR}" -name "*.tar.gz" -mtime +${RETENTION_DAYS} -delete

# === 로그 ===
echo "[$(date)] 백업 완료: ${BACKUP_DIR}/${DATE}.tar.gz" >> /var/log/backup.log
# 실행 권한 부여
sudo chmod +x /usr/local/bin/backup.sh

# 백업 디렉토리 생성
sudo mkdir -p /backup

# 테스트 실행
sudo /usr/local/bin/backup.sh

cron 등록

sudo crontab -e
# 매일 새벽 3시에 백업 실행
0 3 * * * /usr/local/bin/backup.sh

원격 서버로 백업 전송 (선택)

로컬 백업만으로는 서버 자체가 죽으면 백업도 함께 날아간다. 별도의 백업 서버나 오브젝트 스토리지로 전송하는 것을 강력히 권장한다.

# rsync로 원격 서버에 전송
rsync -avz -e "ssh -p 2222" /backup/ user@backup-server:/remote-backup/

# rclone으로 S3, Cloudflare R2 등에 전송
rclone sync /backup/ r2:my-backup-bucket/server-name/

💡 rclone 설정법: curl https://rclone.org/install.sh | sudo bashrclone config로 클라우드 스토리지 연결.


백업 복원 테스트 방법

테스트하지 않은 백업은 백업이 아니다. 이것은 서버 운영의 철칙이다. 실제로 백업 파일에서 데이터를 꺼내 복원해보지 않으면, 정작 필요할 때 백업이 손상되어 있거나 중요한 파일이 빠져 있을 수 있다.

복원 테스트 절차

# 1. 백업 파일 목록 확인
ls -la /backup/

# 2. 임시 디렉토리에 복원
mkdir /tmp/restore-test
cd /tmp/restore-test

# 3. 최신 백업 해제
tar -xzf /backup/2026-05-30_0300.tar.gz

# 4. 파일 확인
ls -la 2026-05-30_0300/

체크리스트

확인 항목 명령어
Nginx 설정이 있는가? ls nginx/
SSL 인증서가 있는가? ls letsencrypt/
웹 데이터가 있는가? ls www/
패키지 목록이 있는가? cat packages.txt | head
sshd_config가 있는가? cat sshd_config

실제 복원 예시

# Nginx 설정 복원
sudo rsync -a /tmp/restore-test/2026-05-30_0300/nginx/ /etc/nginx/
sudo nginx -t && sudo systemctl reload nginx

# 웹 데이터 복원
sudo rsync -a /tmp/restore-test/2026-05-30_0300/www/ /var/www/

💡 분기별 복원 훈련: 3개월에 한 번은 백업에서 실제 복원을 시도하라. 스테이징 서버가 있다면 거기서 테스트하는 것이 가장 좋다.

# 테스트 완료 후 정리
rm -rf /tmp/restore-test

📝 정리

  • [x] 시스템 모니터링htop, df -h, free -h, journalctl로 상태 파악
  • [x] logrotate — Nginx, 앱 로그를 자동 회전·압축·삭제
  • [x] journald 제한 — 저널 디스크 사용량을 500MB로 제한
  • [x] 모니터링 스크립트 — 디스크/메모리/스왑 임계치 초과 시 자동 경고 (cron 10분 주기)
  • [x] rsync 자동 백업 — 설정, 데이터, 인증서를 매일 새벽 자동 백업 + 7일 보관
  • [x] 복원 테스트 — 테스트하지 않은 백업은 백업이 아니다. 분기별 복원 훈련 권장

시리즈를 마치며

5편에 걸쳐 Ubuntu 서버 초기 설정을 처음부터 끝까지 다뤘다. 전체 흐름을 다시 정리하면 이렇다.

주제 핵심
1편 SSH 키 인증과 root 비활성화 문 잠그기 — 비밀번호 끄고 SSH 키로 전환
2편 SSH 포트 변경, UFW, fail2ban 벽 세우기 — 방화벽 + 자동 차단
3편 타임존, 자동 업데이트, 스왑 기본기 — 시간, 보안 패치, 메모리 안전망
4편 Nginx + Let's Encrypt HTTPS 세상에 공개 — 웹 서버 + SSL 인증서
5편 모니터링 + rsync 백업 안전망 — 감시 + 백업 + 복원 테스트

이 과정을 한 번 거치면, 이후에 새 서버를 세팅할 때 30분 이내에 동일한 수준의 보안과 기본 설정을 재현할 수 있다. 반복되는 설정은 스크립트로 자동화하는 것도 좋은 다음 단계다.