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 프로세스 죽음
- 서버 전체가 느려짐
해결 방법:
- 서버 재시작:
sudo reboot - 메모리 확인:
free -h - 스왑 확인:
swapon --show - 서버 업그레이드 고려 (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 애플리케이션이 실제로 운영되고 있어요! 다음과 같은 것들을 고려해보세요:
- 기능 개발: 새로운 기능 추가하기
- 사용자 피드백: 실제 사용자의 의견 수렴
- 성능 모니터링: 실제 트래픽에서의 성능 확인
- 서버 업그레이드: 사용자가 늘어나면 서버 사양 올리기
- 추가 최적화: CDN, 로드 밸런서 등 고급 기능
추가 학습 자료
문제가 생겼을 때
- 로그 확인: 항상 로그를 먼저 확인하세요
- 구글 검색: 에러 메시지를 검색하면 대부분 답이 있어요
- Stack Overflow: 영어로 검색하면 더 많은 정보를 얻을 수 있어요
- 커뮤니티 질문: Laravel 커뮤니티에 질문하기
마지막으로
이 가이드가 도움이 되었나요? 배포는 처음엔 어려워 보이지만, 한 번 해보면 생각보다 어렵지 않다는 걸 느끼셨을 거예요.
여러분의 Laravel 애플리케이션이 많은 사용자들에게 사랑받는 서비스가 되길 바랍니다!
행운을 빕니다! 🍀✨
| ← 이전: 도메인/HTTPS 설정 | 메인으로 |