728x90

서두

Nginx에서 "emergency restart"에 대한 개념은 일반적으로 특정 상황에서 Nginx 서비스를 빠르게 재시작하거나 복구해야 할 때 사용됩니다. 하지만 Nginx 자체에는 "emergency restart"라는 특정 명령어나 기능이 명시적으로 정의되어 있지는 않습니다. 대신, 이는 운영 환경에서 특정 문제를 해결하기 위해 관리자가 수행하는 비상 조치로 해석될 수 있죠.

 

1. 비상 재시작의 의미

  • 비상 재시작은 Nginx가 비정상적으로 동작하거나, 설정 오류, 메모리 누수, 프로세스 충돌 등의 문제로 인해 즉각적인 재시작이 필요한 상황을 의미합니다.
  • 예를 들어, 웹 서버가 응답하지 않거나, 잘못된 설정 파일로 인해 서버가 제대로 작동하지 않을 때 관리자가 빠르게 조치해야 할 수 있습니다.

결론적으로 NginX 에서 의미하는 비상 재시작 조치라는 것은, 상황이 닥쳤을 때 비상 재시작을 하는 것을 의미하는데, 오늘 글에서 다룰 이야기는 이런 NginX의 수동적인 재시작의 의미가 아니라, PHP-FPM에서 제공하는 emergency_restart에 관한 이야기 입니다.

 

PHP-FPM의 emergency_restart 관련 옵션은 PHP-FPM 프로세스가 비정상적인 상황(예: 메모리 누수, 세그먼테이션 오류 등)에서 자동으로 재시작하도록 설정하는 데 사용되는 부분입니다. 이 옵션들은 주로 php-fpm.conf 파일의 [global] 섹션에서 설정된다. 아래는 관련 옵션들과 설정 방법에 대한 자세한 설명 입니다.

 

간단하게 추가 이야기를 다루자면, 대부분의 SE 사이에서는 당연한 부분일 수 있겠지만 LAMP 스택과 LEMP 스택에 관련된 이야기이다. LEMP 스택에서는 NginX + MySQL(MariaDB) + PHP-FPM 을 조합하여 쓰기 때문에 이 부분에서 NginX를 언급하지 않을 수 없는 것입니다.

 

PHP-FPM의 Emergency Restart 옵션

  1. emergency_restart_threshold
    • 설명: 지정된 시간 간격 내에 특정 수의 자식 프로세스가 SIGSEGV(세그먼테이션 오류) 또는 SIGBUS(버스 오류)로 종료되면 PHP-FPM이 자동으로 재시작됩니다. 이 설정은 비정상적인 종료가 빈번할 경우 메모리 누수나 기타 문제를 해결하는 데 유용합니다.
    • 기본값: 0 (비활성화)
    • 예시: emergency_restart_threshold = 10 (10개의 자식 프로세스가 비정상 종료 시 재시작 트리거)
    • 참고: 이 설정은 [global] 섹션에 추가해야 하며, 풀별 설정 파일(예: www.conf)에 추가하면 오류가 발생할 수 있습니다.
  2. emergency_restart_interval
    • 설명: emergency_restart_threshold에서 지정한 비정상 종료 횟수가 발생해야 하는 시간 간격을 정의합니다. 이 시간 내에 지정된 수의 자식 프로세스가 비정상 종료되면 재시작이 트리거됩니다.
    • 기본값: 0 (비활성화)
    • 사용 가능한 단위: s(초), m(분), h(시간), d(일)
    • 예시: emergency_restart_interval = 1m (1분 내에 지정된 횟수의 오류 발생 시 재시작)
    • 참고: 이 설정은 메모리 누수나 공유 메모리 손상 같은 문제를 해결하는 데 유용합니다.
  3. process_control_timeout
    • 설명: 자식 프로세스가 마스터 프로세스로부터 받은 신호(예: 종료 신호)에 반응할 때까지 대기하는 시간 제한을 설정합니다. 이 시간이 초과되면 자식 프로세스가 강제로 종료되고 새로운 프로세스로 대체됩니다. 이는 자식 프로세스가 응답하지 않는 상황에서 시스템 안정성을 보장합니다.
    • 기본값: 0 (비활성화)
    • 사용 가능한 단위: s(초), m(분), h(시간), d(일)
    • 예시: process_control_timeout = 10s (10초 동안 신호에 응답하지 않으면 강제 종료)
    • 참고: 이 설정은 자식 프로세스가 작업을 완료할 기회를 주되, 무한정 대기하지 않도록 합니다..

설정 예시

/etc/php/7.x/fpm/php-fpm.conf 파일의 [global] 섹션에 아래와 같이 추가할 수 있습니다:

[global]
emergency_restart_threshold = 10
emergency_restart_interval = 1m
process_control_timeout = 10s
  • 이 설정은 1분 내에 10개의 자식 프로세스가 SIGSEGV 또는 SIGBUS로 종료되면 PHP-FPM이 자동으로 재시작되며, 자식 프로세스가 10초 동안 마스터의 신호에 응답하지 않으면 강제로 종료됩니다.

설정 시 주의사항

  1. 설정 파일 위치:
    • emergency_restart_thresholdemergency_restart_interval[global] 섹션에 추가해야 합니다. 풀 설정 파일(예: /etc/php/7.x/fpm/pool.d/www.conf)에 추가하면 unknown entry 오류가 발생할 수 있습니다.
    • 예를 들어, Ubuntu 16.04에서 PHP 7.0을 사용할 경우 /etc/php/7.0/fpm/php-fpm.conf에 설정해야 합니다.
  2. 로그 확인:
    • 비정상 종료의 원인을 파악하려면 PHP-FPM 로그를 활성화하고 확인해보죠. 예:
      [global]
      error_log = /var/log/php-fpm.log
      log_level = notice
      로그 파일을 확인하려면:
      tail -f /var/log/php-fpm.log
  3. 적절한 값 설정:
    • emergency_restart_thresholdemergency_restart_interval은 서버 부하와 애플리케이션 특성에 따라 조정해야 합니다. 값이 너무 낮으면 불필요한 재시작이 빈번해지고, 너무 높으면 비정상 상황을 감지하지 못할 수 있습니다.
    • 예: 고트래픽 사이트에서는 emergency_restart_threshold = 10emergency_restart_interval = 1m이 적절할 수 있습니다. (고트래픽 사이트가 뭔진 모르겠지만 그렇게 번역할 것이니까 그런거로 하죠)
  4. Graceful 재시작:
    • 이 옵션들은 PHP-FPM의 재시작이 기존 요청을 중단시킬 수 있으므로, 가능하면 reload를 사용해 부드러운 재시작을 시도합시다:
      sudo systemctl reload php7.x-fpm
  5. 문제 해결:
    • emergency_restart_threshold가 작동하지 않는 경우, 자식 프로세스가 SIGSEGVSIGBUS 외의 다른 신호(예: SIGTERM)로 종료되고 있을 수 있습니다. 로그를 확인해 정확한 종료 원인을 파악합시다.

추가 팁

  • 모니터링: PHP-FPM 상태 페이지를 활성화하여 프로세스 상태를 모니터링 할 수 있습니다:
    [www]
    pm.status_path = /status
    Nginx에서 이를 설정하려면:
    location /status {
        fastcgi_pass unix:/var/run/php/php7.x-fpm.sock;
        include fastcgi_params;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
    }
     
  • 문제 원인 분석: 메모리 누수나 비정상 종료가 자주 발생한다면, slowlogrequest_slowlog_timeout을 설정해 느린 스크립트를 추적하거나, pm.max_requests를 낮춰 자식 프로세스를 자주 재생성하도록 설정할 수 있습니다.

 

결론

PHP-FPM의 emergency_restart 옵션(emergency_restart_threshold, emergency_restart_interval, process_control_timeout)은 비정상적인 상황에서 자동 재시작을 통해 시스템 안정성을 유지하는 데 유용합니다. 이 옵션들은 [global] 섹션에 설정해야 하며, 서버 환경과 트래픽 패턴에 맞게 값을 조정하는 것이 중요합니다. 설정 후에는 로그를 모니터링하여 예상대로 동작하는지 확인해야 합니다. 


근데? 우리가 살펴볼 것은 당연하게도 NginX 에서 살펴볼 PHP-FPM 의 옵션이 아니다. 정확하게는 Apache 에서 이 부분을 조질 수 없냐는 것인데, 당연하게도 없을 줄 알았지만 있다.

 

 Apache와 PHP-FPM, MySQL 환경에서도 PHP-FPM의 emergency_restart 옵션을 설정할 수 있습니다. PHP-FPM은 웹 서버(Nginx 또는 Apache)와 독립적으로 동작하는 PHP FastCGI 프로세스 매니저이기 때문에, Apache를 사용하더라도 PHP-FPM의 설정은 동일하게 적용됩니다.

emergency_restart 관련 옵션(emergency_restart_threshold, emergency_restart_interval, process_control_timeout)은 PHP-FPM의 설정 파일(php-fpm.conf)에서 관리되며, 웹 서버가 Apache이든 Nginx이든 상관없이 동일하게 작동합니다.

 

다만, Apache와 PHP-FPM을 함께 사용할 때는 몇 가지 추가적인 설정과 주의사항이 필요하다. 

[global]
emergency_restart_threshold = 10
emergency_restart_interval = 1m
process_control_timeout = 10s
error_log = /var/log/php-fpm.log
log_level = notice

 

2. Apache와 PHP-FPM 연동 확인

Apache에서 PHP-FPM을 사용하려면 mod_fastcgi 또는 mod_proxy_fcgi 모듈을 통해 PHP 요청을 PHP-FPM으로 전달하도록 설정해야 합니다. emergency_restart 옵션이 제대로 작동하려면 Apache와 PHP-FPM 간의 연결이 올바르게 설정되어 있는지 확인해야 합니다.

Apache 설정 예시

Ubuntu/Debian 시스템에서 PHP-FPM과 Apache를 연동하는 일반적인 설정은 다음과 같습니다:

 

필요한 모듈을 활성화 한 이후에

sudo a2enmod proxy proxy_fcgi

 

Apache 설정 파일 수정: /etc/apache2/sites-available/000-default.conf (또는 사용 중인 가상 호스트 파일)에 다음을 추가합니다:

<VirtualHost *:80>
    ServerName example.com
    DocumentRoot /var/www/html

    <FilesMatch \.php$>
        SetHandler "proxy:unix:/var/run/php/php7.x-fpm.sock|fcgi://localhost/"
    </FilesMatch>
</VirtualHost>

- 설마 아직도 VirtualHost 기능을 모르는 사람은 없겠죠? 는 있을 수도 있다.

 

  • /var/run/php/php7.x-fpm.sock는 PHP-FPM 소켓 경로로, PHP 버전에 따라 7.x는 실제 버전(예: 7.4, 8.0)으로 대체됩니다.
  • TCP 포트를 사용하는 경우 proxy:fcgi://127.0.0.1:9000/처럼 설정할 수도 있습니다.

설정을 해 줬으면 당연하게 apache랑 php-fpm 데몬을 재시작 시켜줘야 적용된다 ^^

sudo systemctl restart apache2
sudo systemctl restart php7.x-fpm

 


중론

그런데 말입니다?

사실 제가 궁금했던 것은 망할놈의 emergency_restart를 흔하게 nginx 에서 조진다거나 apache에서 php-fpm과 함께 조지는 것에 대한 내용이 아닙니다. 그럼 무엇이냐구요? PHP-FPM이 없을 때를 이야기 하는 것 입니다.

 

왜 그러냐구요? PHP-FPM을 추가적으로 설정하고 모듈을 설정하는 것은 매우매우 귀찮은 일인 것이거든요.

 

Apache, PHP, MySQL 스택에서 PHP-FPM을 사용하지 않는다면, PHP는 일반적으로 Apache의 mod_php 모듈을 통해 실행됩니다. 이 경우 PHP-FPM의 emergency_restart 옵션(emergency_restart_threshold, emergency_restart_interval, process_control_timeout)은 사용할 수 없습니다. 이는 PHP-FPM 전용 설정이기 때문입니다.

mod_php는 Apache 프로세스 내에서 PHP를 직접 실행하므로 PHP-FPM의 프로세스 관리 기능과 같은 자동 재시작 메커니즘이 없습니다.

 

하지만 mod_php 환경에서도 비정상적인 상황(예: 메모리 누수, 세그먼테이션 오류 등)에서 Apache를 재시작하거나 문제를 완화할 수 있는 대안적인 방법이 있습니다. 아래에서 이를 자세히 설명하겠습니다.

 

1. mod_php 환경에서 비상 재시작 대안

mod_php는 Apache 워커 프로세스 내에서 PHP를 실행하므로, PHP 관련 문제를 해결하려면 Apache 자체를 관리하거나 외부 스크립트 및 모니터링 도구를 활용해야 합니다. 다음은 대안적인 접근법입니다:

(1) Apache 재시작

Apache 프로세스가 비정상적으로 동작하거나 PHP 스크립트로 인해 메모리 사용량이 비정상적으로 증가할 경우, Apache를 재시작하여 문제를 해결할 수 있습니다. (아 이런 허접한 방법 말구요)

 

Graceful Reload:

sudo apachectl graceful

 

  • 이 명령은 기존 연결을 유지하면서 Apache 워커 프로세스를 새로고침합니다.
  • 클라이언트 연결이 끊기지 않으므로 다운타임을 최소화할 수 있습니다.
  • PHP 스크립트의 메모리 누수가 워커 프로세스에 영향을 미쳤다면, 이 방법으로 새로운 워커 프로세스를 생성하여 문제를 완화할 수 있습니다.

Hard Restart:

위 아래놈 중 한놈으로 조질 수 있는 것은 모두가 다 알고있는 사실일 것 입니다.

sudo systemctl restart apache2

sudo service apache2 restart

 

 

강제 종료 후 재시작:

간혹 뒤지지 않는다면 강제로 죽이는 방법도 있죠

sudo killall -9 apache2
sudo systemctl start apache2

 

 

(2) MaxRequestsPerChild 설정

Apache의 MPM(Module Processing Model) 설정에서 MaxRequestsPerChild를 조정하여 PHP 스크립트로 인한 메모리 누수를 방지할 수 있습니다. 이 설정은 각 워커 프로세스가 처리할 수 있는 최대 요청 수를 제한하여, 일정 요청 후 프로세스를 종료하고 새 프로세스로 교체합니다.

  • 설정 방법: Apache 설정 파일(예: /etc/apache2/apache2.conf 또는 /etc/httpd/conf/httpd.conf)의 MPM 모듈 설정에 추가합니다. 예를 들어, mpm_prefork를 사용하는 경우:
<IfModule mpm_prefork_module>
    StartServers          5
    MinSpareServers       5
    MaxSpareServers      10
    MaxClients          150
    MaxRequestsPerChild  500
</IfModule>

 

  • MaxRequestsPerChild 500: 각 워커 프로세스가 500개의 요청을 처리한 후 종료되고 새 프로세스로 교체됩니다.
  • 이 설정은 PHP-FPM의 pm.max_requests와 유사한 역할을 하며, 메모리 누수로 인한 문제를 줄이는 데 효과적입니다.

물론 mpm 모듈에 따라서도 다른 것을 쓰고 있는 경우도 있을겁니다. 그건 당연히 바꿔치기 술법을 써야겠죠. 여하튼 저걸 적용시키면 재시작 시키는건 당연한겁니다.

sudo apachectl configtest
sudo systemctl reload apache2

 

(3) 외부 모니터링 스크립트

PHP-FPM의 emergency_restart와 같은 자동 재시작 기능을 구현하려면 외부 스크립트나 모니터링 도구를 사용하여 Apache 프로세스의 상태를 감시하고 필요 시 재시작하도록 설정할 수 있습니다.

 

간단한 Bash 스크립트 예시:

#!/bin/bash
# /usr/local/bin/monitor_apache.sh

# 메모리 사용량 체크 (예: 1GB 초과 시 재시작)
MEMORY_USAGE=$(ps -C apache2 -o %mem | awk '{sum += $1} END {print sum}')
THRESHOLD=1000  # 메모리 사용량 임계값 (MB)

if (( $(echo "$MEMORY_USAGE > $THRESHOLD" | bc -l) )); then
    echo "Memory usage exceeded threshold: $MEMORY_USAGE MB"
    sudo systemctl restart apache2
    echo "Apache restarted at $(date)" >> /var/log/apache_monitor.log
fi

이 스크립트를 크론잡으로 주기적으로 실행하도록 설정:

crontab -e
* * * * * /bin/bash /usr/local/bin/monitor_apache.sh

 

 

모니터링 도구:

  • Monit: Apache 프로세스의 메모리 사용량이나 CPU 사용량을 감시하고, 임계값 초과 시 자동으로 재시작하도록 설정할 수 있습니다.
    • Monit 설정 예시 (/etc/monit/monitrc)
      check process apache2 with pidfile /var/run/apache2.pid
          start program = "/bin/systemctl start apache2"
          stop program = "/bin/systemctl stop apache2"
          if totalmem > 1000 MB for 5 cycles then restart
          if cpu > 80% for 5 cycles then restart


    • Monit 설치 및 실행:
      sudo apt-get install monit
      sudo systemctl enable monit
      sudo systemctl start monit

 

추가적으로 PHP 의 설정을 조정해서 memory_limit 를 기본적으로 128M 정도로 잡는 다던지 max_execution_time을 잡는다는지의 설정 방법이 있다. 문제는? 사이트가 돌아가는 메모리 자체가 크고 max_execution_time이 원래부터 길어야 한다면? 이 방법은 틀린 방법이라는 것이다.

 

특히 내 경우는 t2.micro 로 스펙을 설정해서 시작했는데, 이 경우는 셋팅을 좀 바꿔줘야 한다.

<IfModule mpm_prefork_module>
    StartServers          2
    MinSpareServers      2
    MaxSpareServers      4
    MaxClients          20
    MaxRequestsPerChild  200
</IfModule>

 

 

이후에 메모리 사용량을 확인해보자.

free -m
top

 

Monit의 경우도 t2.micro에 맞게 이렇게 설정을 고쳐줬다.

check process apache2 with pidfile /var/run/apache2.pid
    start program = "/bin/systemctl start apache2"
    stop program = "/bin/systemctl stop apache2"
    if totalmem > 600 MB for 3 cycles then restart
    if cpu > 70% for 3 cycles then restart

 

특히 t2.micro 같은 경우는 메모리를 지나치게 잡아먹는 부분이 있으므로 128MB를 64MB로 설정하고 crontab 에서 주기도 5분마다 한번 씩 돌리는 것으로 바꿔줘야 하는 이슈가 있었다. 하지만 그럼에도 다시한번 다운이 발생하는 이슈가 발생했다.

 

웹사이트가 만약 WordPress 기반이라면? 

그렇다. WordPress는 MySQL에 크게 의존하고, 이에 따라 설정해줘야 하는 값이 좀 달라진다. 특히 t2.micro 부분에서도 96MB로 설정해주고 innoDB 의 값도 설정해줘야 하는 약간의 추가 셋팅이 필요하다.

 

Information Gathering

1. 로그 분석 및 메모리 용량 추정

커널에서 수집한 로그의 일부를 살펴보자.

Aug 8 03:07:01 ip-172-31-44-66 kernel: [90625.257121] [ 13548] 33 13548 106257 5889 585728 0 0 apache2
Aug 8 03:07:01 ip-172-31-44-66 kernel: [90625.257121] [ 13551] 33 13551 105608 2870 561152 0 0 apache2
Aug 8 03:07:01 ip-172-31-44-66 kernel: [90625.257122] [ 13556] 33 13556 106257 5888 585728 0 0 apache2
Aug 8 03:07:01 ip-172-31-44-66 kernel: [90625.257123] [ 13557] 33 13557 106257 5887 585728 0 0 apache2
Aug 8 03:07:01 ip-172-31-44-66 kernel: [90625.257124] [ 13558] 33 13558 106257 5889 585728 0 0 apache2
Aug 8 03:07:01 ip-172-31-44-66 kernel: [90625.257125] [ 13559] 33 13559 105646 2841 561152 0 0 apache2
Aug 8 03:07:01 ip-172-31-44-66 kernel: [90625.257125] [ 13624] 33 13624 105646 2842 561152 0 0 apache2
Aug 8 03:07:01 ip-172-31-44-66 kernel: [90625.257126] [ 13625] 33 13625 105646 2839 561152 0 0 apache2
Aug 8 03:07:01 ip-172-31-44-66 kernel: [90625.257127] [ 13633] 33 13633 105646 2839 561152 0 0 apache2
Aug 8 03:07:01 ip-172-31-44-66 kernel: [90625.257128] [ 13634] 33 13634 105608 2870 561152 0 0 apache2
Aug 8 03:07:01 ip-172-31-44-66 kernel: [90625.257129] [ 13635] 33 13635 105608 2870 561152 0 0 apache2
Aug 8 03:07:01 ip-172-31-44-66 kernel: [90625.257129] [ 13655] 33 13655 105600 2677 561152 0 0 apache2
Aug 8 03:07:01 ip-172-31-44-66 kernel: [90625.257130] [ 13657] 33 13657 105600 2432 561152 0 0 apache2
Aug 8 03:07:01 ip-172-31-44-66 kernel: [90625.257131] [ 13680] 33 13680 105600 2760 561152 0 0 apache2
Aug 8 03:07:01 ip-172-31-44-66 kernel: [90625.257132] [ 13681] 33 13681 105600 2768 561152 0 0 apache2
Aug 8 03:07:01 ip-172-31-44-66 kernel: [90625.257132] [ 13683] 33 13683 105602 2678 561152 0 0 apache2
Aug 8 03:07:01 ip-172-31-44-66 kernel: [90625.257133] [ 13697] 33 13697 105600 2432 561152 0 0 apache2
Aug 8 03:07:01 ip-172-31-44-66 kernel: [90625.257134] [ 13711] 0 13711 14806 115 155648 0 0 cron
Aug 8 03:07:01 ip-172-31-44-66 kernel: [90625.257135] [ 13712] 33 13712 105600 2678 561152 0 0 apache2
Aug 8 03:07:01 ip-172-31-44-66 kernel: [90625.257136] [ 13714] 33 13714 105602 2673 561152 0 0 apache2
Aug 8 03:07:01 ip-172-31-44-66 kernel: [90625.257136] [ 13715] 33 13715 105602 2758 561152 0 0 apache2
Aug 8 03:07:01 ip-172-31-44-66 kernel: [90625.257137] [ 13716] 33 13716 105598 2753 552960 0 0 apache2
Aug 8 03:07:01 ip-172-31-44-66 kernel: [90625.257138] [ 13721] 0 13721 14806 116 155648 0 0 cron
Aug 8 03:07:01 ip-172-31-44-66 kernel: [90625.257139] [ 13722] 0 13722 5955 107 86016 0 0 apport
Aug 8 03:07:01 ip-172-31-44-66 kernel: [90625.257139] [ 13728] 0 13728 14806 116 155648 0 0 cron
Aug 8 03:07:01 ip-172-31-44-66 kernel: [90625.257140] [ 13731] 33 13731 105582 1632 536576 0 0 apache2
Aug 8 03:07:01 ip-172-31-44-66 kernel: [90625.257141] [ 13732] 0 13732 14806 116 155648 0 0 cron
Aug 8 03:07:01 ip-172-31-44-66 kernel: [90625.257142] [ 13733] 33 13733 105504 1339 528384 0 0 apache2
Aug 8 03:07:01 ip-172-31-44-66 kernel: [90625.257143] [ 13737] 33 13737 105586 1579 548864 0 0 apache2
Aug 8 03:07:01 ip-172-31-44-66 kernel: [90625.257143] [ 13738] 33 13738 105586 1579 548864 0 0 apache2
Aug 8 03:07:01 ip-172-31-44-66 kernel: [90625.257144] [ 13739] 33 13739 105582 1632 536576 0 0 apache2
Aug 8 03:07:01 ip-172-31-44-66 kernel: [90625.257145] [ 13741] 0 13741 14806 116 155648 0 0 cron
Aug 8 03:07:01 ip-172-31-44-66 kernel: [90625.257146] [ 13769] 1000 13769 1159 16 57344 0 0 sh
Aug 8 03:07:01 ip-172-31-44-66 kernel: [90625.257147] [ 13770] 1000 13770 3330 61 65536 0 0 bash
Aug 8 03:07:01 ip-172-31-44-66 kernel: [90625.257148] [ 13772] 1000 13772 1159 17 53248 0 0 sh
Aug 8 03:07:01 ip-172-31-44-66 kernel: [90625.257148] [ 13773] 1000 13773 1159 17 53248 0 0 sh
Aug 8 03:07:01 ip-172-31-44-66 kernel: [90625.257149] [ 13774] 1000 13774 1159 16 57344 0 0 sh
Aug 8 03:07:01 ip-172-31-44-66 kernel: [90625.257150] [ 13775] 1000 13775 1159 16 57344 0 0 sh
Aug 8 03:07:01 ip-172-31-44-66 kernel: [90625.257151] [ 13776] 1000 13776 3330 57 69632 0 0 bash
Aug 8 03:07:01 ip-172-31-44-66 kernel: [90625.257152] [ 13777] 1000 13777 3330 56 65536 0 0 bash
Aug 8 03:07:01 ip-172-31-44-66 kernel: [90625.257152] [ 13778] 1000 13778 3330 56 69632 0 0 bash
Aug 8 03:07:01 ip-172-31-44-66 kernel: [90625.257153] [ 13779] 1000 13779 3330 57 65536 0 0 bash
Aug 8 03:07:01 ip-172-31-44-66 kernel: [90625.257154] [ 13780] 0 13780 12162 93 135168 0 0 cron
Aug 8 03:07:01 ip-172-31-44-66 kernel: [90625.257155] [ 13791] 1000 13791 3330 61 61440 0 0 bash
Aug 8 03:07:01 ip-172-31-44-66 kernel: [90625.257156] [ 13792] 1000 13792 6363 45 86016 0 0 ps
Aug 8 03:07:01 ip-172-31-44-66 kernel: [90625.257156] [ 13793] 1000 13793 5651 36 77824 0 0 awk
Aug 8 03:07:01 ip-172-31-44-66 kernel: [90625.257157] [ 13795] 0 13795 105495 1308 516096 0 0 apache2
Aug 8 03:07:01 ip-172-31-44-66 kernel: [90625.257158] [ 13796] 0 13796 105495 1308 516096 0 0 apache2
Aug 8 03:07:01 ip-172-31-44-66 kernel: [90625.257159] [ 13797] 0 13797 105495 1308 516096 0 0 apache2
Aug 8 03:07:01 ip-172-31-44-66 kernel: [90625.257159] [ 13798] 0 13798 105495 1308 516096 0 0 apache2
Aug 8 03:07:01 ip-172-31-44-66 kernel: [90625.257160] [ 13799] 0 13799 105495 1308 516096 0 0 apache2
Aug 8 03:07:01 ip-172-31-44-66 kernel: [90625.257161] [ 13800] 0 13800 105495 1308 516096 0 0 apache2
Aug 8 03:07:01 ip-172-31-44-66 kernel: [90625.257161] [ 13801] 0 13801 105495 1308 516096 0 0 apache2
Aug 8 03:07:01 ip-172-31-44-66 kernel: [90625.257162] [ 13803] 0 13803 9077 79 110592 0 0 cron
Aug 8 03:07:01 ip-172-31-44-66 kernel: [90625.257163] oom-kill:constraint=CONSTRAINT_NONE,nodemask=(null),cpuset=/,mems_allowed=0,global_oom,task_memcg=/system.slice/apache2.service,task=apache2,pid=13142,uid=33
Aug 8 03:07:01 ip-172-31-44-66 kernel: [90625.257170] Out of memory: Killed process 13142 (apache2) total-vm:429472kB, anon-rss:12732kB, file-rss:0kB, shmem-rss:20288kB, UID:33 pgtables:592kB oom_score_adj:0
Aug 8 10:17:33 ip-172-31-44-66 kernel: [ 0.000000] Linux version 5.4.0-1103-aws (buildd@lcy02-amd64-028) (gcc version 7.5.0 (Ubuntu 7.5.0-3ubuntu1~18.04)) #111~18.04.1-Ubuntu SMP Tue May 23 20:04:10 UTC 2023 (Ubuntu 5.4.0-1103.111~18.04.1-aws 5.4.233)

 

이는 커널에서 수집한 로그의 일부이지만, 대충 살펴보자면 결과적으로 OOM Killer 로 인해 발생한 것이다. 

 

  • 각 줄은 Apache 프로세스(apache2)의 메모리 사용량을 나타냅니다. 예를 들어, 프로세스 ID 13548의 경우:
    • total-vm: 106257 (약 426MB)
    • anon-rss: 5889 (약 24MB)
    • file-rss: 0, shmem-rss: 585728 (약 572MB)
  • 다수의 Apache 프로세스가 각각 400~500MB 이상의 가상 메모리(total-vm)를 사용하고 있으며, 공유 메모리(shmem-rss)가 상당히 큽니다. 이는 WordPress의 PHP 스크립트(mod_php)가 Apache 워커 프로세스 내에서 실행되면서 메모리를 많이 소모하고 있음을 나타냅니다.

 

여러 부분에서 t2.micro 로는 부족한 부분이 있어, T3.medium 으로 인스턴스 스펙을 변경해줬다.

T4g 시리즈를 고려할 수도 있었고 AWS Graviton2(ARM) 프로세서로 40% 더 나은 가격/성능 비율을 자랑하지만, 기존에 사용하던 것에서 아키텍쳐를 변경해야 하고 플러그인 다수 사용시 OOM위험이나 플러그인 호환성을 확인해야 하기 때문에 안정성 선택에서는 배제하고 5$가 비싼 T3.Medium으로 선택했다. 

 

물론 T3.Medium의 경우는 최신 AWS Nitro 시스템, t2.micro보다 나은 CPU 성능(최대 3.1GHz Intel Xeon). Unlimited 모드 기본 활성화로 CPU 크레딧 고갈 시 추가 요금($0.04/vCPU-시간)으로 버스팅 가능. 기능이 달려있다.


추가적으로 전체 커널로그를 다시 분석한 이후에 아래와 같은 결론을 내렸다.

전체 메모리 상태

 

Aug 8 03:07:01 ip-172-31-44-66 kernel: [90625.256965] 262045 pages RAM
Aug 8 03:07:01 ip-172-31-44-66 kernel: [90625.256956] Node 0 DMA: ... = 4456kB
Aug 8 03:07:01 ip-172-31-44-66 kernel: [90625.256959] Node 0 DMA32: ... = 44100kB
Aug 8 03:07:01 ip-172-31-44-66 kernel: [90625.256964] Free swap = 0kB
Aug 8 03:07:01 ip-172-31-44-66 kernel: [90625.256964] Total swap = 0kB

 

  • 총 RAM: 262045 pages * 4kB = 약 1,048,180kB ≈ 1GB (t2.micro의 기본 메모리).
  • 사용 가능한 메모리: 4456kB(DMA) + 44100kB(DMA32) = 약 48.5MB로, 거의 모든 메모리가 소진됨.
  • 스왑 없음: Free swap = 0kB, Total swap = 0kB. 스왑 공간이 없어 OOM Killer가 즉시 작동.

Apache 프로세스의 메모리 사용

Aug 8 03:07:01 ip-172-31-44-66 kernel: [90625.257013] [ 13142] 33 13142 107368 8255 606208 0 0 apache2
...
Aug 8 03:07:01 ip-172-31-44-66 kernel: [90625.257120] [ 13546] 33 13546 105646 2839 561152 0 0 apache2

 

 

 

  • Apache 프로세스(약 70개 이상)가 실행 중이며, 각 프로세스의 메모리 사용량:
    • total-vm: 약 105,600~124,777 pages (420~500MB).
    • rss (Resident Set Size): 약 1,339~8,255 pages (5~33MB).
    • pgtables_bytes: 약 516,096~606,208kB (500~600kB).
  • 평균적으로 각 Apache 프로세스가 약 20~30MB RSS를 사용하며, 70개 프로세스는 약 1.4~2.1GB의 실제 메모리를 소모. 이는 t2.micro의 1GB 메모리를 초과.

기타 프로세스:

  • MySQL (mysqld는 로그에 없지만 WordPress에 필수)과 기타 서비스(systemd, cron, varnishd 등)가 추가 메모리를 사용.
  • 예: amazon-ssm-agen (PID 791, 약 8MB RSS), cache-main (PID 993, 약 3MB RSS).
  • 총 기타 프로세스 메모리: 약 50~100MB 추정.

 

(2) 필요한 메모리 용량 추정

  • Apache 워커 프로세스:
    • 로그에서 약 70개의 Apache 프로세스가 실행 중이었으나, 이는 MaxClients 10 설정이 반영되지 않은 상태로 보임 (설정 미적용 또는 트래픽 급증).
    • WordPress 사이트의 일반적인 동시 접속을 10~20명으로 가정하면, MaxClients 15~20으로 설정 시 각 프로세스가 평균 25MB RSS를 사용한다고 가정:
      • 20 프로세스 × 25MB = 약 500MB.
  • MySQL:
    • WordPress의 MySQL은 innodb_buffer_pool_size=100M, query_cache_size=4M, max_connections=30 설정 기준 약 150~200MB 사용 추정.
  • 기타 서비스:
    • systemd, cron, varnishd, amazon-ssm-agent 등 약 100MB.
  • 시스템 예약 및 여유:
    • 안정적인 운영을 위해 최소 200~300MB의 여유 메모리 필요.
  • 총 필요 메모리:
    • Apache(500MB) + MySQL(200MB) + 기타(100MB) + 여유(300MB) = 약 1.1~1.3GB.
    • 트래픽 급증(예: 50 동시 접속) 시 Apache 프로세스 증가로 최대 2~3GB 필요.
    • WordPress의 플러그인(예: WooCommerce)이나 복잡한 테마 사용 시 추가로 500MB~1GB 필요 가능.

OOM 재발 방지 설정

네이버 웹툰 - 호랑이 형님 中 "어멋 이건 꼭 해야해!"

(1) Apache 설정

<IfModule mpm_prefork_module>
    StartServers          2
    MinSpareServers      2
    MaxSpareServers      4
    MaxClients          15
    MaxRequestsPerChild  100
</IfModule>

 

 

  • MaxClients 15: 각 프로세스 약 25MB RSS 가정 시 15 × 25MB = 375MB. 4GB 메모리에서 충분한 여유 제공.
  • MaxRequestsPerChild 100: 메모리 누수 방지.
sudo nano /etc/apache2/apache2.conf
sudo apachectl configtest
sudo systemctl reload apache2

 

 

(2) PHP 설정

memory_limit = 96M
max_execution_time = 30
error_log = /var/log/php_errors.log
log_errors = On
error_reporting = E_ALL

 

적용하기 위한 데몬 재시

sudo nano /etc/php/7.x/apache2/php.ini
sudo systemctl restart apache2

 

(3) MySQL 설정

[mysqld]
innodb_buffer_pool_size = 128M
query_cache_size = 8M
tmp_table_size = 8M
max_connections = 40

 

  • innodb_buffer_pool_size = 128M: 4GB 메모리에서 적절한 데이터베이스 성능 제공.

(4) 스왑 설정

로그에서 스왑이 없었으므로, 1GB 스왑 파일 추가:

sudo fallocate -l 1G /swapfile
sudo chmod 600 /swapfile
sudo mkswap /swapfile
sudo swapon /swapfile
echo '/swapfile none swap sw 0 0' | sudo tee -a /etc/fstab

 

 

(5) WordPress 최적화

wp-cron 을 꺼주고 플러그인 정리를 해주었다.

 

(6) 모니터링

Monit 설정:

check process apache2 with pidfile /var/run/apache2.pid
    start program = "/bin/systemctl start apache2"
    stop program = "/bin/systemctl stop apache2"
    if totalmem > 2500 MB for 2 cycles then restart
    if cpu > 60% for 2 cycles then restart

 

재시작

sudo nano /etc/monit/monitrc
sudo systemctl restart monit

+ Recent posts