운영 · 서버·인프라

서버 CPU 100% 원인 진단: 재부팅 전에 확인할 프로세스·로그·WP-Cron

CPU 과부하를 공격이나 플러그인 탓으로 단정하지 않고 같은 시각의 프로세스와 로그를 교차 확인한 뒤 원인별로 조치하는 운영 가이드입니다.

INXSEO 편집팀SEO·웹 운영 검수팀3분검토 2026년 7월 26일

CPU 100%는 원인이 아니라 결과입니다. 재부팅하면 잠시 정상으로 돌아올 수 있지만, 과도한 요청·느린 PHP 작업·데이터베이스 쿼리·예약 작업·메모리 부족 중 무엇이 문제였는지 남지 않으면 같은 장애가 반복됩니다.

아래 순서는 Ubuntu 계열의 소형 워드프레스 서버를 기준으로 한 일반적인 진단 흐름입니다. 운영체제, 웹서버와 관리 패널에 따라 명령과 서비스 이름이 다를 수 있으므로 현재 SSH 연결을 유지하고, 변경 전에 설정 파일과 데이터베이스 백업 및 복구 콘솔을 확보한 뒤 적용해야 합니다.

1. 장애 시각과 사용자 영향을 먼저 기록합니다

CPU 그래프만 캡처하지 말고 장애가 시작된 시각, 지속 시간, 응답 코드, 관리자 화면과 일반 페이지의 차이를 기록합니다. 같은 시각의 로그를 비교하려면 서버 시간대와 모니터링 시간대도 확인해야 합니다.

  • 사이트 전체가 느린지, 특정 URL만 느린지
  • 502·503·504 또는 데이터베이스 연결 오류가 있었는지
  • CPU뿐 아니라 메모리, 스왑, 디스크 I/O와 저장 공간도 부족했는지
  • 플러그인 업데이트, 배포, 백업, 크롤링 작업 직후인지

2. 어떤 프로세스가 CPU를 사용하는지 확인합니다

먼저 전체 부하와 상위 프로세스를 봅니다. 패키지 설치가 부담되거나 장애 중이라면 기본 제공되는 topps만으로도 시작할 수 있습니다.

uptime
free -h
df -h
ps -eo pid,ppid,user,%cpu,%mem,etime,cmd --sort=-%cpu | head -30

php-fpm이 높다면 PHP 요청이 많거나 오래 걸리는 것입니다. mysqld 또는 mariadbd가 높다면 느린 쿼리, 테이블 잠금, 과도한 자동 저장이나 옵션 테이블 문제를 의심할 수 있습니다. 압축, 백업, 이미지 변환 프로세스가 높다면 예약 작업의 실행 시각과 겹쳤는지 확인합니다.

프로세스 이름만 보고 원인을 확정하면 안 됩니다. PHP-FPM은 워드프레스 요청을 처리하는 실행 계층일 뿐이며, 실제 원인은 특정 URL·플러그인·봇 요청·예약 작업일 수 있습니다.

3. 같은 시각의 웹 요청 로그를 대조합니다

웹서버 access log에서 요청 수가 급증한 IP, URL, 응답 코드와 User-Agent를 확인합니다. 단순히 요청이 많은 IP를 차단하기 전에 정상 검색봇, 모니터링, 결제 콜백이나 CDN 원본 요청인지 검증해야 합니다.

# 예시: Nginx 기본 로그 경로는 환경에 따라 다릅니다.
tail -n 300 /var/log/nginx/access.log
awk '{print $1}' /var/log/nginx/access.log | sort | uniq -c | sort -nr | head

특정 URL이 반복된다면 해당 경로가 로그인, XML-RPC, 검색, 장바구니, API 또는 크론 호출인지 확인합니다. CDN이나 리버스 프록시를 사용하면 로그의 원격 주소가 실제 방문자 IP가 아닐 수 있으므로 신뢰할 프록시 설정부터 점검해야 합니다.

4. PHP-FPM과 데이터베이스 병목을 구분합니다

PHP-FPM 로그에서 최대 자식 프로세스 도달, 느린 요청, 프로세스 종료와 재시작 흔적을 확인합니다. 데이터베이스는 현재 실행 중인 쿼리와 느린 쿼리 로그를 확인하되, 운영 DB에서 무거운 진단 쿼리를 무작정 실행하지 않습니다.

  • PHP 요청 폭주: 특정 URL·봇·캐시 미적용·플러그인 루프를 확인합니다.
  • DB 과부하: 느린 쿼리, 잠금, 비대해진 옵션·세션·로그 테이블을 확인합니다.
  • 메모리 부족: 스왑 사용량과 OOM 로그를 확인합니다. 스왑 추가는 완화책이지 원인 해결이 아닙니다.
  • 디스크 병목: 백업·로그 폭증·저장 공간 부족이 CPU 대기처럼 보일 수 있습니다.

5. WP-Cron은 증거가 있을 때만 시스템 Cron으로 전환합니다

워드프레스의 WP-Cron은 페이지 요청을 계기로 예약 작업을 확인합니다. 트래픽이 많거나 예약 작업이 무거운 환경에서는 반복 호출이 부담이 될 수 있지만, CPU가 높다는 이유만으로 WP-Cron을 바로 끄면 예약 발행, 백업, 이메일과 플러그인 정리 작업이 멈출 수 있습니다.

크론 호출이 실제 원인인지 로그와 예약 작업 목록으로 확인한 뒤, 시스템 Cron으로 대체할 때만 DISABLE_WP_CRON을 사용합니다. WordPress 개발자 문서도 이 상수가 설정되면 기본 크론 실행 경로가 중단되는 동작을 명시합니다.

// wp-config.php: 시스템 Cron 대체가 준비된 뒤에만 적용
 define('DISABLE_WP_CRON', true);

외부 Cron은 사이트의 wp-cron.php를 주기적으로 호출하거나 WP-CLI로 예약 이벤트를 실행할 수 있습니다. 실행 간격은 작업 중요도와 서버 용량에 맞춰 결정하고, 인증·타임아웃·중복 실행 방지와 실패 로그를 남겨야 합니다.

6. 조치는 한 번에 하나씩 적용하고 전후를 비교합니다

  1. 장애 당시 프로세스와 로그를 보존합니다.
  2. 명확한 공격 요청이면 CDN·WAF·웹서버 제한부터 적용합니다.
  3. 특정 플러그인이나 작업이면 스테이징에서 재현하거나 안전하게 비활성화합니다.
  4. 캐시, PHP-FPM, DB 설정은 현재 메모리와 트래픽에 맞춰 변경합니다.
  5. CPU, 응답 시간, 오류율과 재발 여부를 같은 기간으로 비교합니다.

IP 블랙리스트, 서비스 재시작, 스왑 추가는 응급조치일 수 있지만 근본 원인 분석을 대신하지 않습니다. 장애 보고서에는 “CPU가 내려갔다”가 아니라 어떤 요청 또는 작업이 부하를 만들었고 어떤 증거로 확인했으며 재발 방지를 어떻게 검증할지 남겨야 합니다.

공식 자료