- 출처 / 참고: Apache HTTP Server Documentation (mpm_common, event MPM, worker MPM)
명령어:
apachectl,httpd,top,ps,free,vmstat
키워드: ServerLimit, MaxRequestWorkers, MaxClients, MPM Event, MPM Worker, Prefork, High Load, Memory Exhaustion
사용처: 웹 서버 동시 접속자 급증에 따른 응답 지연 해결, 아파치 MPM 프로세스 스레드 튜닝, 메모리 고갈(OOM) 방지
실행예제
아파치 에러 로그에서 WARNING: ServerLimit of ... exceeds compile-time limit of ... 또는 server reached MaxRequestWorkers setting 경고가 발생할 때 현재 프로세스 수, 메모리 점유율, MPM 동작 모드를 파악합니다.
# 1. 아파치 에러 로그에서 ServerLimit 및 MaxRequestWorkers 관련 경고 확인
$ sudo tail -n 50 /var/log/apache2/error.log # Ubuntu/Debian
# 또는
$ sudo tail -n 50 /var/log/httpd/error_log # RHEL/CentOS/Rocky
# [출력 예시]
# [mpm_event:error] [pid 1234:tid 1402] AH00484: server reached MaxRequestWorkers setting, consider raising the MaxRequestWorkers setting
# [core:warn] [pid 1234:tid 1402] AH00018: WARNING: ServerLimit of 2000 exceeds compile-time limit of 20000 servers, decreasing to ...
# 2. 현재 컴파일/활성화된 MPM(Multi-Processing Module) 모드 점검 (event, worker, prefork 확인)
$ apachectl -V | grep -i 'Server MPM'
# Server MPM: event
# 또는
$ httpd -V | grep -i 'Server MPM'
# 3. 현재 실행 중인 아파치 자식 프로세스 수 및 스레드 수 확인
$ ps -ef | grep -E 'apache2|httpd' | grep -v grep | wc -l
# 스레드 전체 카운트 (MPM worker/event 환경)
$ ps -eLf | grep -E 'apache2|httpd' | grep -v grep | wc -l
# 4. 아파치 프로세스당 평균 메모리 사용량(RSS) 계산 (MB 단위)
$ ps -ylC apache2 --sort:rss 2>/dev/null | awk '{sum+=$8; ++n} END {if(n>1) print "Average RSS: " sum/(n-1)/1024 " MB"; else print "N/A"}'
# RHEL 계열의 경우:
$ ps -ylC httpd --sort:rss 2>/dev/null | awk '{sum+=$8; ++n} END {if(n>1) print "Average RSS: " sum/(n-1)/1024 " MB"; else print "N/A"}'
스크립트
현재 시스템의 가용 RAM 용량과 아파치 프로세스당 평균 메모리 소비량을 계산하여, 시스템 OOM(Out of Memory) 크래시를 유발하지 않는 최적의 ServerLimit 및 MaxRequestWorkers 값을 자동 산출해 주는 쉘 스크립트입니다.
#!/bin/bash
# tune_apache_mpm.sh - Diagnose Apache MPM limits and calculate safe ServerLimit/MaxRequestWorkers
echo "=========================================="
echo " [아파치 MPM 및 메모리 부하 진단]"
echo "=========================================="
# 1. 아파치 데몬 감지
HTTPD_BIN="apachectl"
HTTPD_NAME="apache2"
if ! command -v apachectl &>/dev/null; then
HTTPD_BIN="httpd"
HTTPD_NAME="httpd"
fi
if ! command -v "$HTTPD_BIN" &>/dev/null; then
echo " [오류] 아파치 실행 파일을 찾을 수 없습니다."
exit 1
fi
MPM_TYPE=$($HTTPD_BIN -V | grep -i 'Server MPM' | awk '{print $3}')
echo " - 감지된 웹 서버 데몬 : ${HTTPD_NAME}"
echo " - 활성화된 MPM 모드 : ${MPM_TYPE}"
# 2. 시스템 메모리 현황
TOTAL_MEM_MB=$(free -m | awk '/Mem:/ {print $2}')
AVAIL_MEM_MB=$(free -m | awk '/Mem:/ {print $7}')
echo -e "\n[1] 시스템 메모리 상태:"
echo " - 전체 RAM: ${TOTAL_MEM_MB} MB"
echo " - 여유 RAM: ${AVAIL_MEM_MB} MB"
# 3. 아파치 프로세스 평균 메모리 소비량 측정
PROC_COUNT=$(pgrep -c "$HTTPD_NAME")
if [ "$PROC_COUNT" -gt 0 ]; then
AVG_PROC_MB=$(ps -ylC "$HTTPD_NAME" --sort:rss 2>/dev/null | awk '{sum+=$8; ++n} END {if(n>1) printf "%.0f", sum/(n-1)/1024; else print 0}')
[ -z "$AVG_PROC_MB" ] || [ "$AVG_PROC_MB" -eq 0 ] && AVG_PROC_MB=50
else
AVG_PROC_MB=50
fi
echo -e "\n[2] 아파치 프로세스 통계:"
echo " - 실행 중인 자식 프로세스 수 : ${PROC_COUNT}개"
echo " - 프로세스당 평균 점유 메모리 : 약 ${AVG_PROC_MB} MB"
# 4. 안전 가이드라인 연산 (웹 전용 기준: 총 메모리의 70% 할당)
DEDICATED_MEM=$(( TOTAL_MEM_MB * 70 / 100 ))
SAFE_MAX_CLIENTS=$(( DEDICATED_MEM / AVG_PROC_MB ))
echo -e "\n[3] MPM 모드별 추천 튜닝 가이드 (메모리 70% 안전 기준):"
if [ "$MPM_TYPE" == "prefork" ]; then
echo " * Prefork MPM (1 프로세스 = 1 연결):"
echo " - ServerLimit = ${SAFE_MAX_CLIENTS}"
echo " - MaxRequestWorkers = ${SAFE_MAX_CLIENTS}"
else
# worker or event (기본 ThreadsPerChild = 25 가정)
THREADS_PER_CHILD=25
SAFE_SERVER_LIMIT=$(( SAFE_MAX_CLIENTS / THREADS_PER_CHILD ))
[ "$SAFE_SERVER_LIMIT" -lt 1 ] && SAFE_SERVER_LIMIT=1
CALC_WORKERS=$(( SAFE_SERVER_LIMIT * THREADS_PER_CHILD ))
echo " * ${MPM_TYPE} MPM (프로세스 기반 멀티 스레드):"
echo " - ThreadsPerChild = ${THREADS_PER_CHILD} (기본값)"
echo " - ServerLimit = ${SAFE_SERVER_LIMIT}"
echo " - MaxRequestWorkers = ${CALC_WORKERS}"
fi
echo -e "\n=========================================="
echo " [진단 완료]"
echo "=========================================="
해설
아파치 웹 서버에서 발생하는 ServerLimit directive exceeds maximum 경고와 대량 트래픽 인입 시의 고부하(High Load) 현상은 서버의 가용 하드웨어 자원(CPU/RAM)과 MPM(Multi-Processing Module) 설정 간의 불균형 때문에 발생합니다.
1. ServerLimit과 MaxRequestWorkers의 관계 및 지시어 순서
- MaxRequestWorkers (과거 MaxClients):
- 아파치가 동시에 처리할 수 있는 최대 동시 요청(동시 접속자) 수입니다.
- 이 제한에 도달하면 신규 연결 요청은
ListenBacklog큐에 대기하게 되며, 큐마저 차면 클라이언트에 연결 거절(Connection refused또는 지연 후 타임아웃)이 발생합니다.
- ServerLimit:
- 생성 가능한 최대 아파치 자식 프로세스 수의 상한선입니다.
- 지시어 순서의 중요성: 아파치는 설정 파일을 위에서 아래로 파싱합니다. 반드시
ServerLimit을MaxRequestWorkers보다 먼저 선언해야 합니다. 만약MaxRequestWorkers를 올렸는데ServerLimit이 그 아래에 있거나 누락되면, 아파치는 기존 기본ServerLimit상한선에 맞춰MaxRequestWorkers값을 강제로 깎아버리며 에러 로그를 남깁니다.
- MPM 모드별 계산 공식:
- MPM Prefork: 스레드를 쓰지 않으므로
MaxRequestWorkers = ServerLimit공식이 성립합니다. - MPM Event / Worker: 멀티 프로세스 멀티 스레드 방식이므로 아래 공식이 성립합니다. \(\text{MaxRequestWorkers} = \text{ServerLimit} \times \text{ThreadsPerChild}\)
- MPM Prefork: 스레드를 쓰지 않으므로
2. 고부하 및 OOM 방지를 위한 설정 가이드
서버 메모리가 16GB이고 데이터베이스와 분리된 전용 웹 서버 환경에서 MPM Event를 사용할 때의 모범 설정 예시입니다.
# /etc/apache2/mods-available/mpm_event.conf (Ubuntu)
# 또는 /etc/httpd/conf.modules.d/00-mpm.conf (RHEL)
<IfModule mpm_event_module>
# 반드시 ServerLimit이 MaxRequestWorkers보다 위에 위치해야 함
ServerLimit 32
StartServers 4
MinSpareThreads 75
MaxSpareThreads 250
ThreadsPerChild 25
MaxRequestWorkers 800 # 32 * 25 = 800
MaxConnectionsPerChild 5000 # 메모리 누수 방지를 위한 자식 프로세스 재활용 수
</IfModule>
주의사항
- 컴파일 타임 하드 리밋(Hard Limit) 초과 주의:
- 아파치 소스 코드에 정의된 컴파일 상한치(Event/Worker는 기본 20,000, Prefork는 200,000)를 넘는 비현실적인 값을
ServerLimit에 기재하면 아파치가 구동 시 컴파일 상한값으로 강제 하향 조정합니다.
- 아파치 소스 코드에 정의된 컴파일 상한치(Event/Worker는 기본 20,000, Prefork는 200,000)를 넘는 비현실적인 값을
- Prefork MPM 사용 시 메모리 폭발 위험:
- PHP 구형 모듈(
mod_php) 연동을 위해 여전히prefork를 사용하는 환경이 많습니다. Prefork는 프로세스당 수십~수백 MB의 메모리를 먹기 때문에 동시 접속자를 감당하려고ServerLimit을 1,000 이상 올리는 순간 시스템 스왑(Swap)이 급증하고 커널 OOM Killer가 동작하여 아파치나 DB 데몬이 강제 종료됩니다. 동시 접속 처리가 많은 환경이라면 반드시 PHP-FPM + MPM Event 조합으로 전환해야 합니다.
- PHP 구형 모듈(
- MaxConnectionsPerChild를 0(무한)으로 두는 위험:
- C/C++ 확장 모듈이나 PHP 코드에서 발생하는 미세한 메모리 누수(Memory Leak)가 자식 프로세스에 누적되어 프로세스 점유 메모리가 계속 불어날 수 있습니다. 프로덕션 환경에서는
MaxConnectionsPerChild를5000~10000정도로 지정하여 프로세스가 주기적으로 자동 재생성되도록 유도하는 것이 안전합니다.
- C/C++ 확장 모듈이나 PHP 코드에서 발생하는 미세한 메모리 누수(Memory Leak)가 자식 프로세스에 누적되어 프로세스 점유 메모리가 계속 불어날 수 있습니다. 프로덕션 환경에서는