5단계: 성능 최적화 및 관리

← 이전: 도메인/HTTPS 설정 메인으로

마지막 단계예요! 이제 서버를 최적화하고, 자동 배포를 설정하고, 모니터링 방법을 배워봅시다. 특히 1GB RAM 환경에서도 안정적으로 운영하는 방법을 알려드릴게요.

이 단계에서 배울 내용

  • 1GB RAM 서버 최적화 (MySQL, Redis, PHP-FPM, Nginx)
  • 스왑 메모리 설정
  • 자동 배포 스크립트 작성
  • 크론잡 설정 (Laravel 스케줄러)
  • 로그 로테이션 설정
  • 백업 설정
  • 모니터링 및 성능 체크
  • 실제 운영 환경 가이드

💡 예상 소요 시간: 30-40분 정도 걸려요.

⚠️ 중요: 1GB RAM은 학습/테스트용입니다. 실제 서비스 운영은 최소 2GB 이상을 권장해요!


5.1 1GB RAM 서버 최적화

왜 최적화가 필요한가요?

1GB RAM은 제한적이에요. MySQL, PHP-FPM, Nginx, Redis가 모두 메모리를 사용하기 때문에 최적화 없이는 서버가 느려지거나 멈출 수 있어요.

현재 메모리 사용량 확인

# 현재 메모리 사용량 확인
free -h

# 출력 예시:
#               total        used        free
# Mem:          985Mi       742Mi       89Mi
# Swap:           0B          0B          0B

프로세스별 메모리 사용량

# 메모리를 많이 사용하는 프로세스 확인
ps aux --sort=-%mem | head -10

# 서비스별 메모리 사용량
systemctl status mysql redis-server nginx php8.4-fpm --lines=0 | grep Memory

5.2 MySQL 최적화 (1GB RAM용)

MySQL 설정 파일 편집

# MySQL 설정 파일 편집
sudo nano /etc/mysql/mysql.conf.d/mysqld.cnf

다음 설정을 [mysqld] 섹션에 추가하거나 수정:

[mysqld]
# === 메모리 사용량 최소화 ===
innodb_buffer_pool_size = 128M          # 가장 중요한 설정
key_buffer_size = 32M                   # MyISAM 키 캐시
table_open_cache = 64                   # 테이블 캐시
sort_buffer_size = 512K                 # 정렬 버퍼
read_buffer_size = 256K                 # 순차 읽기 버퍼
read_rnd_buffer_size = 512K             # 랜덤 읽기 버퍼

# === 연결 설정 ===
max_connections = 10                    # 최대 동시 연결 수
thread_cache_size = 8                   # 스레드 캐시

# === 쿼리 캐시 (MySQL 5.7 이하) ===
# MySQL 8.x에서는 제거됨
# query_cache_size = 16M
# query_cache_type = 1

# === 임시 테이블 ===
tmp_table_size = 32M                    # 임시 테이블 크기
max_heap_table_size = 32M               # 힙 테이블 크기

# === 로깅 (프로덕션에서는 필요 시만) ===
slow_query_log = 1
slow_query_log_file = /var/log/mysql/mysql-slow.log
long_query_time = 2

# === InnoDB 최적화 ===
innodb_log_file_size = 64M              # 로그 파일 크기
innodb_log_buffer_size = 8M             # 로그 버퍼
innodb_flush_method = O_DIRECT          # 더블 버퍼링 방지

💡 innodb_buffer_pool_size: 가장 중요한 설정이에요. RAM의 약 12-15% 정도가 적당합니다.

MySQL 재시작

# MySQL 재시작
sudo systemctl restart mysql

# 상태 확인
sudo systemctl status mysql

# 설정 적용 확인
mysql -u root -p -e "SHOW VARIABLES LIKE 'innodb_buffer_pool_size';"
mysql -u root -p -e "SHOW VARIABLES LIKE 'max_connections';"

MySQL 메모리 사용량 재확인

# MySQL 메모리 사용량
ps aux | grep mysql | grep -v grep

5.3 Redis 최적화 (1GB RAM용)

Redis 설정 편집

# Redis 설정 파일 편집
sudo nano /etc/redis/redis.conf

다음 설정들을 찾아서 수정:

# === 메모리 제한 ===
maxmemory 64mb                          # 최대 메모리 64MB
maxmemory-policy allkeys-lru            # 메모리 가득 차면 LRU로 제거

# === 데이터 지속성 (가벼운 설정) ===
save 900 1                              # 15분에 1개 이상 변경
save 300 10                             # 5분에 10개 이상 변경
save 60 10000                           # 1분에 10000개 이상 변경

# === 네트워크 최적화 ===
timeout 300                             # 클라이언트 타임아웃
tcp-keepalive 300                       # TCP 연결 유지

# === 로깅 ===
loglevel notice
logfile /var/log/redis/redis-server.log

# === 보안 (이미 설정했다면 확인) ===
requirepass your_redis_password_here
bind 127.0.0.1 ::1

Redis 재시작

# Redis 재시작
sudo systemctl restart redis-server

# 상태 확인
sudo systemctl status redis-server

# 메모리 사용량 확인
redis-cli -a your_redis_password_here INFO memory

5.4 PHP-FPM 최적화 (1GB RAM용)

PHP-FPM 풀 설정 편집

# PHP-FPM 풀 설정 파일 편집
sudo nano /etc/php/8.4/fpm/pool.d/www.conf

다음 설정들을 찾아서 수정:

[www]
# === 프로세스 관리자 ===
pm = dynamic                            # 동적 프로세스 관리
pm.max_children = 8                     # 최대 자식 프로세스 (중요!)
pm.start_servers = 2                    # 시작 시 프로세스 수
pm.min_spare_servers = 1                # 최소 대기 프로세스
pm.max_spare_servers = 3                # 최대 대기 프로세스
pm.process_idle_timeout = 10s           # 유휴 프로세스 타임아웃
pm.max_requests = 200                   # 프로세스당 최대 요청 수

# === 상태 모니터링 (선택사항) ===
pm.status_path = /status

# === 슬로우 로그 ===
slowlog = /var/log/php8.4-fpm.slow.log
request_slowlog_timeout = 5s

💡 pm.max_children 계산법:

  • 각 PHP-FPM 프로세스: 약 50-80MB
  • 1GB RAM에서: 8개면 충분 (640MB 정도)

PHP-FPM 재시작

# PHP-FPM 재시작
sudo systemctl restart php8.4-fpm

# 상태 확인
sudo systemctl status php8.4-fpm

# 프로세스 수 확인
ps aux | grep php-fpm | wc -l

5.5 Nginx 최적화 (1GB RAM용)

Nginx 메인 설정 확인

# Nginx 설정 파일 편집 (이미 4단계에서 했을 수도 있음)
sudo nano /etc/nginx/nginx.conf

다음 설정들을 확인:

# === 워커 프로세스 ===
worker_processes 1;                     # 1GB RAM에서는 1개
worker_rlimit_nofile 1024;

events {
    worker_connections 512;             # 동시 연결 수 제한
    use epoll;                          # 효율적인 이벤트 처리
    multi_accept on;
}

http {
    # === 기본 설정 ===
    sendfile on;
    tcp_nopush on;
    tcp_nodelay on;
    keepalive_timeout 15;               # 짧게 설정 (메모리 절약)
    types_hash_max_size 2048;
    server_tokens off;

    # === 버퍼 크기 (메모리 절약) ===
    client_body_buffer_size 1K;
    client_header_buffer_size 1k;
    large_client_header_buffers 2 1k;
    client_max_body_size 64M;

    # === Gzip 압축 ===
    gzip on;
    gzip_vary on;
    gzip_proxied any;
    gzip_comp_level 6;
    gzip_min_length 1000;
    gzip_types
        application/javascript
        application/json
        text/css
        text/plain;

    # === 로깅 ===
    access_log /var/log/nginx/access.log combined buffer=16k;
    error_log /var/log/nginx/error.log warn;

    # === 기타 설정 포함 ===
    include /etc/nginx/mime.types;
    include /etc/nginx/conf.d/*.conf;
    include /etc/nginx/sites-enabled/*;
}

Nginx 재시작

# 설정 문법 확인
sudo nginx -t

# Nginx 재시작
sudo systemctl reload nginx

5.6 스왑 메모리 설정 (필수!)

스왑이 뭔가요?

RAM이 부족할 때 디스크의 일부를 메모리처럼 사용하는 거예요. 속도는 느리지만 서버가 멈추는 걸 방지해요.

스왑 파일 생성

# 2GB 스왑 파일 생성
sudo fallocate -l 2G /swapfile

# 권한 설정 (보안)
sudo chmod 600 /swapfile

# 스왑 파일로 설정
sudo mkswap /swapfile

# 스왑 활성화
sudo swapon /swapfile

# 확인
free -h

영구적으로 설정

# 부팅 시 자동 마운트 설정
echo '/swapfile none swap sw 0 0' | sudo tee -a /etc/fstab

# 스왑 사용 빈도 조정 (낮을수록 RAM 우선)
echo 'vm.swappiness=10' | sudo tee -a /etc/sysctl.conf

# 설정 적용
sudo sysctl -p

스왑 사용량 확인

# 스왑 사용 상태
free -h

# 스왑 사용 빈도 확인
cat /proc/sys/vm/swappiness

5.7 Laravel 앱 최적화

캐시 최적화

cd /var/www/jinyerp_demo_01

# 모든 캐시 재생성
sudo -u www-data php artisan config:cache
sudo -u www-data php artisan route:cache
sudo -u www-data php artisan view:cache
sudo -u www-data php artisan event:cache

# OPcache 상태 확인
php -i | grep opcache

불필요한 패키지 제거

프로덕션 환경에서는 개발용 패키지가 필요 없어요:

# 개발 의존성 제거
cd /var/www/jinyerp_demo_01
sudo -u www-data composer install --no-dev --optimize-autoloader

5.8 자동 배포 스크립트

배포 스크립트 생성

코드를 업데이트할 때마다 수동으로 하는 건 번거로워요. 스크립트를 만들어봅시다:

# 배포 스크립트 생성
sudo nano /home/deploy/deploy.sh

다음 내용을 작성:

#!/bin/bash
# Laravel 자동 배포 스크립트
# 사용법: bash /home/deploy/deploy.sh

set -e  # 에러 발생 시 중단

echo "🚀 Laravel 배포를 시작합니다..."

# 프로젝트 디렉토리로 이동
cd /var/www/jinyerp_demo_01

# 1. 유지보수 모드 활성화
echo "📝 유지보수 모드 활성화..."
sudo -u www-data php artisan down

# 2. Git에서 최신 코드 받기
echo "📦 최신 코드 받기..."
sudo -u www-data git pull origin main

# 3. Composer 의존성 업데이트
echo "📦 Composer 의존성 업데이트..."
sudo -u www-data composer install --optimize-autoloader --no-dev

# 4. NPM 빌드 (프론트엔드가 있다면)
if [ -f "package.json" ]; then
    echo "🎨 프론트엔드 빌드..."
    sudo -u www-data npm ci
    sudo -u www-data npm run build
fi

# 5. 데이터베이스 마이그레이션
echo "🗄️ 데이터베이스 마이그레이션..."
sudo -u www-data php artisan migrate --force

# 6. 캐시 갱신
echo "⚡ 캐시 갱신..."
sudo -u www-data php artisan config:cache
sudo -u www-data php artisan route:cache
sudo -u www-data php artisan view:cache

# 7. 유지보수 모드 해제
echo "✅ 유지보수 모드 해제..."
sudo -u www-data php artisan up

echo "🎉 배포가 완료되었습니다!"

실행 권한 부여

# 실행 권한 부여
sudo chmod +x /home/deploy/deploy.sh

# 소유권 설정
sudo chown deploy:deploy /home/deploy/deploy.sh

배포 스크립트 사용

# 배포 실행
bash /home/deploy/deploy.sh

5.9 Laravel 스케줄러 (크론잡 설정)

Laravel 스케줄러가 뭔가요?

Laravel의 예약 작업(매일 실행, 매시간 실행 등)을 관리하는 기능이에요.

크론탭 설정

# www-data 사용자의 크론탭 편집
sudo crontab -u www-data -e

다음 줄을 추가:

# Laravel 스케줄러 (매분 실행)
* * * * * cd /var/www/jinyerp_demo_01 && php artisan schedule:run >> /dev/null 2>&1

저장하고 종료: Ctrl + X, Y, Enter

크론잡 확인

# www-data 크론탭 확인
sudo crontab -u www-data -l

# 크론 서비스 상태 확인
sudo systemctl status cron

Laravel 스케줄 작업 확인

# 예약된 작업 목록 확인
sudo -u www-data php artisan schedule:list

5.10 로그 로테이션 설정

로그가 계속 쌓이면 디스크가 가득 차요

자동으로 오래된 로그를 삭제하도록 설정해봅시다:

# Laravel 로그 로테이션 설정 파일 생성
sudo nano /etc/logrotate.d/laravel

다음 내용 작성:

/var/www/jinyerp_demo_01/storage/logs/*.log {
    daily                               # 매일 로테이션
    missingok                           # 파일 없어도 에러 안냄
    rotate 14                           # 14일치 보관
    compress                            # 압축 저장
    delaycompress                       # 최신 로그는 압축 안함
    notifempty                          # 비어있으면 로테이션 안함
    create 644 www-data www-data        # 새 파일 생성 권한
    sharedscripts                       # 스크립트 한 번만 실행
}

저장하고 종료

Nginx 로그 로테이션 확인

# Nginx 로그 로테이션 설정 확인
cat /etc/logrotate.d/nginx

로그 로테이션 테스트

# 로그 로테이션 테스트 (실제 실행 안함)
sudo logrotate -d /etc/logrotate.d/laravel

# 강제 실행 (테스트)
sudo logrotate -f /etc/logrotate.d/laravel

5.11 백업 설정

데이터베이스 백업 스크립트

# 백업 스크립트 생성
sudo nano /home/deploy/backup.sh

다음 내용 작성:

#!/bin/bash
# MySQL 데이터베이스 백업 스크립트

# 설정
DB_NAME="jinyerp_demo"
DB_USER="jinyerp"
DB_PASS="your_mysql_password_here"
BACKUP_DIR="/home/deploy/backups"
DATE=$(date +%Y%m%d_%H%M%S)
FILENAME="backup_${DB_NAME}_${DATE}.sql.gz"

# 백업 디렉토리 생성
mkdir -p $BACKUP_DIR

# 데이터베이스 백업
echo "📦 데이터베이스 백업 시작..."
mysqldump -u $DB_USER -p$DB_PASS $DB_NAME | gzip > "${BACKUP_DIR}/${FILENAME}"

# 30일 이상 된 백업 삭제
echo "🗑️ 오래된 백업 삭제..."
find $BACKUP_DIR -name "backup_*.sql.gz" -mtime +30 -delete

echo "✅ 백업 완료: ${FILENAME}"
echo "📊 백업 크기: $(du -h ${BACKUP_DIR}/${FILENAME})"

실행 권한 부여

# 실행 권한
sudo chmod +x /home/deploy/backup.sh

# 소유권 설정
sudo chown deploy:deploy /home/deploy/backup.sh

# 백업 디렉토리 생성
mkdir -p /home/deploy/backups

자동 백업 크론잡 설정

# deploy 사용자 크론탭 편집
crontab -e

다음 줄 추가:

# 매일 새벽 2시 데이터베이스 백업
0 2 * * * /home/deploy/backup.sh >> /home/deploy/backups/backup.log 2>&1

백업 테스트

# 백업 스크립트 실행
bash /home/deploy/backup.sh

# 백업 파일 확인
ls -lh /home/deploy/backups/

5.12 모니터링 및 성능 체크

실시간 메모리 모니터링

# 메모리 사용량 실시간 모니터링
watch -n 1 'free -h && echo "=== TOP 5 MEMORY USERS ===" && ps aux --sort=-%mem | head -6'

Ctrl + C로 종료

서비스별 메모리 사용량

# 서비스별 메모리 확인
echo "=== MySQL ===" && ps aux | grep mysql | grep -v grep
echo "=== Redis ===" && ps aux | grep redis | grep -v grep
echo "=== PHP-FPM ===" && ps aux | grep php-fpm | grep -v grep
echo "=== Nginx ===" && ps aux | grep nginx | grep -v grep

디스크 사용량 확인

# 디스크 사용량
df -h

# Laravel 로그 크기
du -sh /var/www/jinyerp_demo_01/storage/logs/

# 전체 프로젝트 크기
du -sh /var/www/jinyerp_demo_01/

Laravel 성능 확인

cd /var/www/jinyerp_demo_01

# 캐시 상태 확인
sudo -u www-data php artisan config:show

# 라우트 캐시 확인
ls -lh bootstrap/cache/

# OPcache 통계
php -i | grep -A 10 opcache

간단한 모니터링 스크립트

# 모니터링 스크립트 생성
sudo nano /home/deploy/monitor.sh

다음 내용:

#!/bin/bash
# 서버 상태 모니터링 스크립트

echo "======================================"
echo "서버 상태 리포트 - $(date)"
echo "======================================"

echo ""
echo "=== 메모리 사용량 ==="
free -h

echo ""
echo "=== 디스크 사용량 ==="
df -h /

echo ""
echo "=== CPU 로드 ==="
uptime

echo ""
echo "=== 서비스 상태 ==="
systemctl is-active mysql && echo "✅ MySQL: Running" || echo "❌ MySQL: Stopped"
systemctl is-active redis-server && echo "✅ Redis: Running" || echo "❌ Redis: Stopped"
systemctl is-active nginx && echo "✅ Nginx: Running" || echo "❌ Nginx: Stopped"
systemctl is-active php8.4-fpm && echo "✅ PHP-FPM: Running" || echo "❌ PHP-FPM: Stopped"

echo ""
echo "=== 최근 Laravel 에러 (최근 10줄) ==="
tail -n 10 /var/www/jinyerp_demo_01/storage/logs/laravel.log 2>/dev/null || echo "로그 없음"

echo ""
echo "======================================"

실행 권한:

sudo chmod +x /home/deploy/monitor.sh

사용:

bash /home/deploy/monitor.sh

5.13 1GB RAM 운영 팁 및 주의사항

해야 할 것 ✅

  • 스왑 메모리 필수 설정 (2GB 권장)
  • 로그 로테이션 설정 (디스크 공간 절약)
  • 캐시 적극 활용 (Redis, OPcache)
  • 불필요한 서비스 비활성화
  • 정기적인 메모리 모니터링

하지 말아야 할 것 ❌

  • 개발 도구 설치 금지 (PhpMyAdmin, Node modules 등)
  • 대용량 파일 업로드 제한 (64MB 이하)
  • 동시 접속자 과다 (10-20명 이하)
  • 백그라운드 작업 과다 (큐 워커 1개만)
  • 디버그 모드 켜두기 (프로덕션에서 APP_DEBUG=false)

메모리 부족 증상

이런 증상이 보이면 메모리 부족이에요:

- 웹사이트 응답 느림
- 502 Bad Gateway 에러
- MySQL 연결 실패
- PHP-FPM 프로세스 죽음
- 서버 전체가 느려짐

해결 방법:

  1. 서버 재시작: sudo reboot
  2. 메모리 확인: free -h
  3. 스왑 확인: swapon --show
  4. 서버 업그레이드 고려 (2GB+)

5.14 실제 운영 환경 최소 제한 사항

하드웨어 최소 제한

소규모 서비스 (1-50 동시 사용자)

CPU: 2 vCPU (최소)
RAM: 4GB (권장 8GB)
Storage: 40GB SSD
Network: 2TB/월
예상 비용: $20-40/월

중규모 서비스 (50-200 동시 사용자)

CPU: 4 vCPU
RAM: 8GB (권장 16GB)
Storage: 80GB SSD + 백업
Network: 5TB/월
Load Balancer: 고려 필요
예상 비용: $50-100/월

대규모 서비스 (200+ 동시 사용자)

CPU: 8+ vCPU (멀티 서버)
RAM: 16GB+ per server
Storage: 분산 스토리지 + CDN
Database: 전용 DB 서버
Cache: 전용 Redis 클러스터
예상 비용: $200+/월

보안 및 안정성 필수 사항

# SSL 인증서 (필수)
✅ Let's Encrypt (무료) 또는 상용 SSL
✅ HTTPS 강제 리다이렉트
✅ HSTS 설정

# 백업 시스템 (필수)
✅ 데이터베이스 자동 백업 (일 1회 이상)
✅ 파일 시스템 백업
✅ 오프사이트 백업 저장

# 모니터링 (권장)
✅ 서버 리소스 모니터링
✅ 애플리케이션 성능 모니터링 (APM)
✅ 에러 로깅 및 알림 시스템

성능 벤치마크 기준

# 운영 환경 성능 목표
평균 응답 시간: < 500ms
99% 응답 시간: < 2초
서버 가용률: > 99.9%
동시 접속자: 목표의 2-3배 여유

5.15 유용한 명령어 모음

서비스 관리

# 모든 서비스 재시작
sudo systemctl restart mysql redis-server nginx php8.4-fpm

# 모든 서비스 상태 확인
sudo systemctl status mysql redis-server nginx php8.4-fpm --no-pager

# 서비스 로그 확인
sudo journalctl -u nginx -f
sudo journalctl -u php8.4-fpm -f

Laravel 관리

# 모든 캐시 클리어
sudo -u www-data php artisan cache:clear
sudo -u www-data php artisan config:clear
sudo -u www-data php artisan route:clear
sudo -u www-data php artisan view:clear

# 캐시 재생성
sudo -u www-data php artisan config:cache
sudo -u www-data php artisan route:cache
sudo -u www-data php artisan view:cache

# 애플리케이션 최적화
sudo -u www-data php artisan optimize

로그 확인

# Laravel 로그
sudo tail -f /var/www/jinyerp_demo_01/storage/logs/laravel.log

# Nginx 로그
sudo tail -f /var/log/nginx/demo.jinyerp.com.access.log
sudo tail -f /var/log/nginx/demo.jinyerp.com.error.log

# PHP-FPM 로그
sudo tail -f /var/log/php8.4-fpm.log

# MySQL 로그
sudo tail -f /var/log/mysql/error.log

✅ 5단계 완료 체크리스트

모든 단계를 완료하셨나요? 최종 체크리스트입니다!

성능 최적화

  • MySQL 1GB RAM용 최적화 설정
  • Redis 메모리 제한 설정
  • PHP-FPM 프로세스 수 조정
  • Nginx 워커 설정 최적화
  • 스왑 메모리 생성 및 활성화
  • Laravel 앱 캐시 최적화

자동화

  • 자동 배포 스크립트 작성
  • Laravel 스케줄러 크론잡 설정
  • 로그 로테이션 설정
  • 데이터베이스 백업 스크립트 작성
  • 자동 백업 크론잡 설정

모니터링

  • 메모리 사용량 모니터링 방법 숙지
  • 서비스 상태 확인 방법 숙지
  • 로그 확인 방법 숙지
  • 모니터링 스크립트 작성

🎉 축하합니다! 전체 배포 완료!

정말 고생 많으셨어요! 이제 여러분은 Laravel 애플리케이션을 처음부터 끝까지 배포할 수 있는 능력을 갖추게 되었습니다.

지금까지 배운 것들

1단계: Vultr 클라우드 서버 생성 및 초기 보안 설정 ✅ 2단계: PHP 8.4, MySQL 8.4, Redis, Nginx 설치 및 설정 ✅ 3단계: GitHub에서 Laravel 프로젝트 배포 ✅ 4단계: 도메인 연결 및 무료 HTTPS 설정 ✅ 5단계: 성능 최적화 및 자동화 설정

다음 단계는?

이제 여러분의 Laravel 애플리케이션이 실제로 운영되고 있어요! 다음과 같은 것들을 고려해보세요:

  1. 기능 개발: 새로운 기능 추가하기
  2. 사용자 피드백: 실제 사용자의 의견 수렴
  3. 성능 모니터링: 실제 트래픽에서의 성능 확인
  4. 서버 업그레이드: 사용자가 늘어나면 서버 사양 올리기
  5. 추가 최적화: CDN, 로드 밸런서 등 고급 기능

추가 학습 자료

문제가 생겼을 때

  1. 로그 확인: 항상 로그를 먼저 확인하세요
  2. 구글 검색: 에러 메시지를 검색하면 대부분 답이 있어요
  3. Stack Overflow: 영어로 검색하면 더 많은 정보를 얻을 수 있어요
  4. 커뮤니티 질문: Laravel 커뮤니티에 질문하기

마지막으로

이 가이드가 도움이 되었나요? 배포는 처음엔 어려워 보이지만, 한 번 해보면 생각보다 어렵지 않다는 걸 느끼셨을 거예요.

여러분의 Laravel 애플리케이션이 많은 사용자들에게 사랑받는 서비스가 되길 바랍니다!

행운을 빕니다! 🍀✨


← 이전: 도메인/HTTPS 설정 메인으로