> ## Content Index
> Fetch the complete content index at: https://www.popit.kr/llms.txt
> Use this file to discover other available public pages before exploring further.

# Popit 장애 보고서
- URL: https://www.popit.kr/popit-incident-report/
- Published: 2016-12-05T06:42:08.000Z
- Updated: 2016-12-05T14:45:03.000Z
- Author: 병화 윤
- Tags: #소프트웨어, WordPress, Popit

금일 Popit 서비스의 장애가 있었습니다. 심천보님의 [나만-모르고-있던-http2](https://www.popit.kr/http2-what-i-didnt-know/) 글의 인기가 올라가면서 생긴 일입니다.

서비스가 인기가 생기면서 더이상 기본값 설정으로는 서비스를 할 수가 없게 됐습니다. 너무 기쁜일입니다. ^^

서비스가 느려지는 것을 방지하기 위해 오늘 몇가지 설정을 변경, 적용했습니다. 그 내용을 공유합니다. 참고로 Popit은 Wordpress로 운영 중이며, Nginx를 웹서버로 사용 중입니다. 대부분 기본값으로 사용했으며, 오늘 장애로 몇가지 설정을 추가 했습니다.

## XML RPC 공격 방어

서비스가 오픈하고, 한, 두달이 지나니 바로 XML RPC 공격이 들어왔습니다. XML RPC 공격이 들어오면 웹사이트가 백지 현상이 나타나며, access log에 /xmlrpc.php 주소가 많이 호출되는 것을 볼 수 있습니다. 이때는 차분하게 xmlrpc.php 주소를 deny 시켜주세요.  

```
        location = /xmlrpc.php {
                deny all;
                access_log off;
                log_not_found off;
        }

```

## php-fpm 로그 살펴보기

php-fpm의 error.log와 www-slow.log 파일을 살펴보고 php에 문제가 없는지 확인합니다. 사이트 느려짐 현상과는 무관하지만 아래와 같은 오류들이 많이 발생하는 것을 확인할 수 있었습니다.  

```
"NOTICE: PHP message: PHP Warning:  Parameter 1 to W3_Plugin_TotalCache::ob_callback() expected to be a reference, value given in /var/www/wp-includes/functions.php on line 3549"
```

이는 W3 Total Cache와 [PHP 7간의 문제](https://wordpress.org/support/topic/should-i-be-worried-warning-parameter-1-to-w3%5Fplugin%5Ftotalcacheob%5Fcallback/?ref=popit.kr)로 W3 Total Cache 0.9.5.1 버전 업그레이드를 통해 해결했고, error.log 파일이 깨끗해졌습니다.

www-slow.log 파일에는 아래와 같은 내용들이 많이 발생 했습니다. 내용으로 미뤄 볼 때, 이미지와 관련 됐을 것이다라는 유추를 했습니다. 실제로 용량이 큰 이미지 파일들이 게시물로 올라왔을 때, 사이트가 전반적으로 느려졌습니다. 바로 아래 소스들을 점검한 결과 [Image Script](http://themify.me/docs/image-script?ref=popit.kr)라는 기능 때문에 featured image를 테마 크기에 맞게 resizing 되어 이미지를 재생성했습니다. 해당 기능을 Disable 시키니 바로 서비스가 안정적으로 되었습니다.  

```
[0x00007ff76c215ff0] imagejpeg() /var/www/wp-includes/class-wp-image-editor.php:425
[0x00007ff76c215e20] make_image() /var/www/wp-includes/class-wp-image-editor-gd.php:487
[0x00007ff76c215d50] make_image() /var/www/wp-includes/class-wp-image-editor-gd.php:419
[0x00007ff76c215910] _save() /var/www/wp-includes/class-wp-image-editor-gd.php:384
[0x00007ff76c2157f0] save() /var/www/wp-includes/media.php:584
[0x00007ff76c215680] image_make_intermediate_size() /var/www/wp-content/themes/pinboard/themify/img.php:146
[0x00007ff76c2152b0] themify_make_image_size() /var/www/wp-content/themes/pinboard/themify/img.php:109
[0x00007ff76c214c90] themify_do_img() /var/www/wp-content/themes/pinboard/themify/themify-utils.php:2454
[0x00007ff76c213470] themify_get_image() /var/www/wp-content/themes/pinboard/themify/themify-utils.php:2271
[0x00007ff76c2133f0] themify_image() /var/www/wp-content/themes/pinboard/page.php:48
```

## Nginx 튜닝

Nginx에 대해서 특별히 설정 튜닝을 하지 않고 사용했습니다. [Wordpress 공식 문서](https://codex.wordpress.org/Nginx?ref=popit.kr)를 보고 바로 튜닝에 들어갔습니다.

nginx.conf 파일의 worker\_processes가 1이었습니다. ㅠ.ㅠ 그간 느린 서비스를 하게 된 원인으로 이미지의 용량이 큰 경우 이상하게 전반적으로 서비스가 느려졌습니다. 서비스가 안 되는 것은 아니고, 느려졌다가 맞는 표현 같습니다. 그래서 Image Optimization을 하고 이미지를 교체하면 정상화 되고는 했죠. 바로 이해가 됐습니다. Worker Processe가 1이니, 요청이 조금이라도 늘어나면 서비스가 급속도로 느려졌던거죠. "grep processor /proc/cpuinfo | wc -l" 명령으로 core 갯수를 확인한 후, 바로 8로 수정했습니다.

그리고 W3 Total Cache 플러그인을 예전에 설치 했고, 설치만 하면 자동으로 캐시가 적용되는 줄 알았습니다. 아니더군요. 꼭 WP Admin > Performance > Install 가이드를 보고 설정해주세요. 관련해서 nginx.conf에 추가된 내용입니다. "# BEGIN W3TC Page Cache core"부터 "# END W3TC Browser Cache"까지 추가된 내용입니다. 이 내용을 추가해야 WP3TC가 생성한 정적 파일(캐시 내용)로 서비스할 수 있습니다.  

```
server {
        listen   80;
        server_name www.popit.kr;

        root /var/www;
        index index.php index.html index.htm;
        client_max_body_size 100m;

        location / {
                try_files $uri $uri/ /index.php?$args;
        }

        location = /favicon.ico {
                log_not_found off;
                access_log off;
        }

        location = /robots.txt {
                allow all;
                log_not_found off;
                access_log off;
        }

        error_page 404 /404.html;
        error_page 500 502 503 504 /50x.html;
        location = /50x.html {
              root /usr/share/nginx/html;
        }

        location ~ \.php$ {
                try_files $uri =404;
                fastcgi_pass unix:/php-fpm/php-fpm.sock;
                fastcgi_index index.php;
                fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
                include fastcgi_params;
        }
        location = /xmlrpc.php {
                deny all;
                access_log off;
                log_not_found off;
        }
        location = /wp-config.php {
                deny all;
                access_log off;
                log_not_found off;
        }

    # BEGIN W3TC Page Cache core
    set $w3tc_rewrite 1;
    if ($request_method = POST) {
        set $w3tc_rewrite 0;
    }
    if ($query_string != "") {
        set $w3tc_rewrite 0;
    }
    if ($request_uri !~ \/$) {
        set $w3tc_rewrite 0;
    }
    if ($http_cookie ~* "(comment_author|wp\-postpass|w3tc_logged_out|wordpress_logged_in|wptouch_switch_toggle)") {
        set $w3tc_rewrite 0;
    }
    set $w3tc_preview "";
    if ($http_cookie ~* "(w3tc_preview)") {
        set $w3tc_preview _preview;
    }
    set $w3tc_enc "";
    if ($http_accept_encoding ~ gzip) {
        set $w3tc_enc _gzip;
    }
    if (!-f "$document_root/wp-content/cache/page_enhanced/$http_host/$request_uri/_index$w3tc_preview.html$w3tc_enc") {
      set $w3tc_rewrite 0;
    }
    if ($w3tc_rewrite = 1) {
        rewrite .* "/wp-content/cache/page_enhanced/$http_host/$request_uri/_index$w3tc_preview.html$w3tc_enc" last;
    }
    # END W3TC Page Cache core
    # BEGIN W3TC Page Cache cache
    location ~ /wp-content/cache/page_enhanced.*html$ {
        expires modified 600s;
        add_header X-Powered-By "W3 Total Cache/0.9.5.1";
        add_header Vary "Accept-Encoding, Cookie";
        add_header Pragma "public";
        add_header Cache-Control "max-age=600, public";
    }
    location ~ /wp-content/cache/page_enhanced.*gzip$ {
        gzip off;
        types {}
        default_type text/html;
        expires modified 600s;
        add_header X-Powered-By "W3 Total Cache/0.9.5.1";
        add_header Vary "Accept-Encoding, Cookie";
        add_header Pragma "public";
        add_header Cache-Control "max-age=600, public";
        add_header Content-Encoding gzip;
    }
    # END W3TC Page Cache cache
    # BEGIN W3TC Browser Cache
    gzip on;
    gzip_types text/css text/x-component application/x-javascript application/javascript text/javascript text/x-js text/richtext image/svg+xml text/plain text/xsd text/xsl text/xml image/bmp application/java application/msword application/vnd.ms-fontobject application/x-msdownload image/x-icon application/json application/vnd.ms-access application/vnd.ms-project application/x-font-otf application/vnd.ms-opentype application/vnd.oasis.opendocument.database application/vnd.oasis.opendocument.chart application/vnd.oasis.opendocument.formula application/vnd.oasis.opendocument.graphics application/vnd.oasis.opendocument.spreadsheet application/vnd.oasis.opendocument.text audio/ogg application/pdf application/vnd.ms-powerpoint application/x-shockwave-flash image/tiff application/x-font-ttf audio/wav application/vnd.ms-write application/font-woff application/font-woff2 application/vnd.ms-excel;
    location ~ \.(css|htc|less|js|js2|js3|js4)$ {
        expires 3600s;
        add_header Pragma "public";
        add_header Cache-Control "max-age=3600, public";
        add_header X-Powered-By "W3 Total Cache/0.9.5.1";
        try_files $uri $uri/ $uri.html /index.php?$args;
    }
    location ~ \.(html|htm|rtf|rtx|svg|svgz|txt|xsd|xsl|xml)$ {
        expires 600s;
        add_header Pragma "public";
        add_header Cache-Control "max-age=600, public";
        add_header X-Powered-By "W3 Total Cache/0.9.5.1";
        try_files $uri $uri/ $uri.html /index.php?$args;
    }
    location ~ \.(asf|asx|wax|wmv|wmx|avi|bmp|class|divx|doc|docx|eot|exe|gif|gz|gzip|ico|jpg|jpeg|jpe|json|mdb|mid|midi|mov|qt|mp3|m4a|mp4|m4v|mpeg|mpg|mpe|mpp|otf|_otf|odb|odc|odf|odg|odp|ods|odt|ogg|pdf|png|pot|pps|ppt|pptx|ra|ram|svg|svgz|swf|tar|tif|tiff|ttf|ttc|_ttf|wav|wma|wri|woff|woff2|xla|xls|xlsx|xlt|xlw|zip)$ {
        expires 31536000s;
        add_header Pragma "public";
        add_header Cache-Control "max-age=31536000, public";
        add_header X-Powered-By "W3 Total Cache/0.9.5.1";
        try_files $uri $uri/ $uri.html /index.php?$args;
    }
    # END W3TC Browser Cache
}
```

향후에는 Popit의 미디어 파일들을 S3 등의 클라우드 서비스로 옮길 계획입니다. 그러면 멀티 도메인이 갖는 몇가지 이득(쿠키 공유 방지, 브라우저 요청 쓰레드 효율화)을 얻을 수 있습니다.

이렇게 해서 오늘의 장애 상황을 모면했으며, 앞으로도 더 안정적인 Popit 서비스가 될 수 있게 노력하겠습니다.

감사합니다.