Popit 장애 보고서

금일 Popit 서비스의 장애가 있었습니다. 심천보님의 나만-모르고-있던-http2 글의 인기가 올라가면서 생긴 일입니다.

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

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

XML RPC 공격 방어

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

1
2
3
4
5
        location = /xmlrpc.php {
                deny all;
                access_log off;
                log_not_found off;
        }

php-fpm 로그 살펴보기

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

1
"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간의 문제로 W3 Total Cache 0.9.5.1 버전 업그레이드를 통해 해결했고, error.log 파일이 깨끗해졌습니다.

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

1
2
3
4
5
6
7
8
9
10
[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 공식 문서를 보고 바로 튜닝에 들어갔습니다.

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가 생성한 정적 파일(캐시 내용)로 서비스할 수 있습니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
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 서비스가 될 수 있게 노력하겠습니다.

감사합니다.


Popit은 페이스북 댓글만 사용하고 있습니다. 페이스북 로그인 후 글을 보시면 댓글이 나타납니다.