https://github.com/xpressengine/xe-core/pull/1598


XE는 느리다는 악평이 많은 편입니다. XE는 여러가지 현대적인 프레임워크 형태이면서 트리거, 애드온 위젯 등등의 여러 인터페이스를 통해서 확장이 가능한 형태이고, 이러한 확장성때문에 느리다고 하더라도 많은 불만이 있지는 않은 것 같습니다.

그러나 XE는 몇가지만 고치더라도 클라우드플레어나 varnish 캐서서버 혹은 nginx 리버스 프록시 캐시서버를 통해서 그 성능을 획기적으로 향상시킬 수 있습니다. 그것도 아주 간단한 패치를 하는 것 만으로 이러한 성능향상이 가능합니다.

그것은 다름아닌 캐시서버의 적중률을 방해하는 대표적인 요소인 Set-Cookie: 헤더를 최소화 하는 것입니다.

Set-Cookie:헤더는 세션이 시작되는 경우 및 쿠키를 설정하는 경우에 설정이 됩니다. 웹 서비스를 하게되면 대부분의 사용자는 로그인사용자가 아닌 익명의 사용자이고, 로봇등에 의해서 상당량의 접속이 있게됩니다. 이러한 상당수의 익명의 클라이언트의 경우 세션을 세팅하지 않거나 쿠키 헤더를 쓰지 않는것 만으로도 캐시서버의 적중률을 높일 수 있습니다.

XE의 경우에도  session_start()가 무조건적으로 호출되고 있습니다. 이 경우 Set-Cookie:PHPSESSID=... 쿠키가 설정이 되며 varnish 캐시서버가 효율적으로 작동하지 않게 됩니다. 아래는 varnish 캐서서버 + XE + nginix (php5-fpm) 설정을 한 서버에 XE를 설치한 후에 살펴본 헤더입니다.

$ curl -I  http://localhost/
HTTP/1.1 200 OK
Content-Type: text/html; charset=UTF-8
X-Powered-By: PHP/5.5.9-1ubuntu4.9
Cache-Control: public, must-revalidate, post-check=0, pre-check=0
Set-Cookie: PHPSESSID=1g12hp22q8r0ojbjog7seaft26; path=/
Accept-Ranges: bytes
Date: Thu, 09 Jul 2015 00:54:35 GMT
X-Varnish: 1471733958
Age: 0
Via: 1.1 varnish
Connection: keep-alive
X-Backend-Server: nginx/1.4.x (Ubuntu)
Server: nginx/1.4.x (Ubuntu)
X-Cache-Hit: MISS

위에서 보는바와 같이 varnish 캐시서버에서 캐시가 미스가 되고 있습니다.

ab 벤치마크입니다. 1000회를, 동시에 3개 요청하는 경우입니다. 상당히 느리고 이것이 현재 XE를 nginx + varnish 캐시서버로 운용할때의 대략적 성능입니다. (혹은 단순히 nginx 혹은 아파치 웹서버만 운용하는 경우)

$ ab -n 1000 -c 3  http://localhost/
This is ApacheBench, Version 2.3 <$Revision: 1528965 $>
Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/
Licensed to The Apache Software Foundation, http://www.apache.org/

Benchmarking localhost (be patient)
Completed 100 requests
Completed 200 requests
Completed 300 requests
Completed 400 requests
....
Requests per second:    74.85 [#/sec] (mean)
Time per request:       40.079 [ms] (mean)
Time per request:       13.360 [ms] (mean, across all concurrent requests)
Transfer rate:          2533.88 [Kbytes/sec] received
...

그러면 이번에는 XE 성능을 100배 향상시키는 속도 패치를 적용한 후에 테스트를 해보겠습니다. 즉 session_start()가 기본적으로 호출이 안되고 Set-Cookie 헤더가 붙지 않게 되며, 캐시 적중률이 올라가게 됩니다.

$ curl -I  http://localhost/
HTTP/1.1 200 OK
Content-Type: text/html; charset=UTF-8
X-Powered-By: PHP/5.5.9-1ubuntu4.9
Cache-Control: public, must-revalidate, post-check=0, pre-check=0
X-Backend-Status: normal
Date: Thu, 09 Jul 2015 00:59:14 GMT
X-Varnish: 1471735403 1471735392
Age: 6
Via: 1.1 varnish
Connection: keep-alive
X-Backend-Server: nginx/1.4.x (Ubuntu)
Server: nginx/1.4.x (Ubuntu)
X-Cache-Hit: HIT

(몇차례 curl -I를 실행하면 Cache-Hit가 HIT 상태임을 나타내고 있습니다.)

ab 벤치마크 측정

$ ab -n 1000 -c 3  http://localhost/
This is ApacheBench, Version 2.3 <$Revision: 1528965 $>
Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/
Licensed to The Apache Software Foundation, http://www.apache.org/

Benchmarking localhost (be patient)
Completed 100 requests
Completed 200 requests
Completed 300 requests
....
Requests per second:    8018.35 [#/sec] (mean)
Time per request:       0.374 [ms] (mean)
Time per request:       0.125 [ms] (mean, across all concurrent requests)
Transfer rate:          269428.95 [Kbytes/sec] received
...

8000 RPS로 아파치 벤치값으로 비교하여 무려 ~100배가 향상되었습니다.

동시에 200개 요청을 하고 20000회 요청을 해면 다음과 같습니다.

$ ab -n 20000 -c 200  http://localhost/
...
Requests per second:    14025.98 [#/sec] (mean)
Time per request:       14.259 [ms] (mean)
Time per request:       0.071 [ms] (mean, across all concurrent requests)
Transfer rate:          473883.72 [Kbytes/sec] received

무려 14000 RPS나 나옵니다. 초당 470MB/s
동시에 500개 요청을 해도 결과는 비슷합니다. (ab -n 20000 -c 500)

더 놀라운 것은 시스템의 부하 상태입니다. (ab -n 20000 -c 500을 처리하고 난 이후)

$ w
 10:04:25 up xx days, 17:28,  x user,  load average: 0.25, 0.19, 0.18

그러면 nginx만의 성능은 어떨까요 ? ab -n 10000 -c 50 http://localhost:8000/ 1만회 50개 요청을 동시에 해보았습니다.

$ ab -n 10000 -c 50  http://localhost:8000/ (ngix가 8000 포트로 설정되어 있음)
This is ApacheBench, Version 2.3 <$Revision: 1528965 $>
Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/
Licensed to The Apache Software Foundation, http://www.apache.org/

Benchmarking localhost (be patient)
Completed 1000 requests
Completed 2000 requests
Completed 3000 requests
Completed 4000 requests
....
Requests per second:    106.64 [#/sec] (mean)
Time per request:       468.850 [ms] (mean)
Time per request:       9.377 [ms] (mean, across all concurrent requests)
Transfer rate:          3570.91 [Kbytes/sec] received
....

~100RPS 수준이고 3MB/s 밖에 안나옵니다.

그리고 시스템 부하상태입니다.

$ w
 10:09:01 up xx days, 17:33,  x user,  load average: 3.64, 1.45, 0.63

현재 이 패치는 https://github.com/xpressengine/xe-core/pull/1598 PR로 등록되어 있으며 반영되기를 기다리고 있습니다. 또한 XE 포럼에도 글을 등록하였습니다. https://www.xpressengine.com/forum/23040664

XE3 개발때문인지 그다지 반응이 시근둥한 편입니다만 많은 관심가져주시기 바랍니다~

참고로 현재 이 패치는 리그베다위키 BBS에 적용되어 운용중입니다.

by dumpcookie 2015. 7. 19. 04:00

세션($_SESSION쿠키)의 사용은 캐시 서버의 캐시 적중률에 방해를 일으켜서 캐시서버 성능을 저하시키는 원인이 됩니다.

https://github.com/gnuboard/gnuboard5/pull/15

이 패치는 그누보드에서 무조건적으로 session_start()가 호출되는 부분을 변경하여. session_start()가 선택적으로 호출되게 하여 캐시서버의 캐시 적중률을 높여줍니다.

이 패치를 적용하고 난 후에 다음과 같은 줄을 커맨트아웃시킨 후에 아파치벤치의 성능을 살펴보겠습니다.

// common.php의 다음 줄을 찾아서 커맨트아웃
// 방문자수의 접속을 남김
#include_once(G5_BBS_PATH.'/visit_insert.inc.php');

위 visit_insert.inc.php는 중복 접속이 카운팅되는 것을 막기위해 쿠키를 사용하고 있는데, 쿠키를 사용하는 부분때문에 캐시 적중률을 떨어뜨리고 있습니다. 이 부분은 다른 방식으로 고쳐야만 그누보드가 좀 더 캐시서버 친화적이 될 수 있습니다.

패치 적용하지 않은 경우

$ ab -n 1000 -c 30 "http://g4.xxxxx.xxx"

Requests per second:    1854.10 [#/sec] (mean)
Time per request:       16.180 [ms] (mean)
Time per request:       0.539 [ms] (mean, across all concurrent requests)
Transfer rate:          21510.47 [Kbytes/sec] received
...

~1800RPS 및 21MB/s가 나오고 있습니다.

패치 적용 후

이 패치를 적용한 후에 중복 접속 카운팅 부분을 커맨트아웃 시키고
varnish 캐시서버 + 그누보드5 + nginx (php4-fpm 세팅) 성능을 아파치벤치마크 성능이 다음과 같이 나옵니다.

$ ab -n 1000 -c 30 "http://g4.xxxxxx.xxxx/"
...
Requests per second:    12976.40 [#/sec] (mean)
Time per request:       2.312 [ms] (mean)
Time per request:       0.077 [ms] (mean, across all concurrent requests)
Transfer rate:          148354.17 [Kbytes/sec] received
...

~12000RPS 및 148MB/s 속도가 나오고 있습니다.

접속회수 1만회 동시접속을 300까지 늘려보면

$ ab -n 10000 -c 300 "http://g4.xxxxxx.xxxx/"
...
Requests per second:    17307.54 [#/sec] (mean)
Time per request:       17.333 [ms] (mean)
Time per request:       0.058 [ms] (mean, across all concurrent requests)
Transfer rate:          197871.10 [Kbytes/sec] received
...

서버 로드상태

$ w
 01:30:28 up xx days,  8:54,  x users,  load average: 0.13, 0.08, 0.11

단순히 아파치벤치 성능상으로 최대 ~8배의 성능 향상이 있음을 볼 수 있습니다.


이 패치는 현재 https://github.com/gnuboard/gnuboard5/pull/15 에 등록시켜 반영을 기다리고 있는 중입니다. (http://sir.co.kr/g5_tip/2964 글도 같이 올렸습니다.)

by dumpcookie 2015. 7. 19. 03:38

예전에 엔하위키 시절 모니위키가 감당못할정도로 느려졌을 무렵, 모니위키는 텍스트기반이라서 DB기반과는 다르기때문에 느리다는 주장을 인터넷에서 종종 보아왔다. 그 당시 엔하위키가 느렸던 것은 버그로 밝혀졌고, 그것을 고친 이후에도 그와 관련된 버그가 여전히 말끔하게 고쳐지지 않아서, 20만 페이지가 되었을 무렵에도 특정 병목 현상때문에 모니위키가 느려지는 현상이 있었고, 이러한 문제가 발생하면 어김없이 이런 얘기가 나왔다. 모니위키는 텍스트 기반이라서 느리다.


이미 이와 관련된 이야기를 여기서 한차례 언급했지만, 이번에는 좀 더 구체적으로 모니위키가 왜 당시에 엔하위키에서 느렸었는지 그 이유를 좀 더 자세히 밝혀보고, 현재 30만 페이지의 규모인 리그베다위키로 그 당시 불가능해 보였던 모니위키가 현재 어떻게 사용되고 있는지 등등을 써보려고 한다.


이 이상은 보여주지 않는다구 엔하위키 시절에 서버가 터졌을때에 올라오곤 했던 짤방



페이지 개수 매크로의 문제

그 당시 구버전의 모니위키의 경우 페이지 개수를 세는 매크로가 호출되면 디렉토리의 파일 개수를 모두 세었다. 사실 요즈음 파일 시스템 성능이 워낙 좋아져서, 30만 페이지가 가까이 되는 경우 ls 명령으로 시간을 재면 성능이 별로인 서버에서는 10초 미만으로 든다. 성능이 괜찮은 서버라면 훨씬 더 빠르다. (time ls text |wc) ls 명령을 쓰지 않고 간단히 PHP로 짜면 몇초가 들까? 놀랍게도 대폭 줄어들어 1초 수준이 된다. 게다가 이게 캐싱도 된다. 한번 명령을 내리면 1초라면 다음 한번 더 명령을 내리면 0.5초 미만으로 대폭 줄어든다. 정확한 지표는 아니겠지만 이정도로 빠르다.


그런데 모니위키의 속도 문제를 겪었던 구 버전의 경우에 페이지 개수만 세는 방식이 아니였다. 모든 페이지 목록을 가져오고 난 다음에 그 개수를 세었다. 5천 페이지 미만에서는 이 방식도 아주 빠르고 문제 없었지만 1만 페이지 이상 넘어가니 이게 문제가 되었던 것이다. 그래서 페이지 목록을 가져오지 않고 단순히 파일 개수를 세는 방식으로 바꾸었던 것이다. (변경 내역은 https://github.com/wkpark/moniwiki/commit/0ee14c6362ae945e8363ea975fb5721364f2d3eb#diff-10732b52856680bd57ef6fcab3abbbcf 참조)

구 엔하위키의 경우 예전 방식이 문제가 될 수 밖에 없었던 것은, 페이지 오른쪽 상단에 페이지 개수를 항상 보여주고 있었기 때문인데, 페이지 개수 세는 매크로가 테마에서 호출되어졌고, 테마는 페이지를 보여줄때마다 매번 페이지 세는 매크로를 호출했던 것이고, 더군다나 해당 매크로는 한번 갯수를 센 결과를 저장하지 않았기때문에 지속적인 CPU 부하의 원인이 되었던 것이다.


랜덤 페이지의 문제

아무튼 이렇게 페이지 개수를 세는 방법을 고치고나니 속도가 대폭 개선되었다. 그런데 또 다른 문제가 있었는데 그것은 랜덤페이지의 문제였다. 엔하위키시절부터 랜덤페이지는 꽤 인기있는 기능이어서 모니위키의 핫키 기능을 사용하여, 키보드의 "a"를 눌러서 랜덤페이지를 보며 여행하고는 했다. 그런데 이 랜덤페이지도 페이지 목록을 전부 가져와서 그중에 랜덤 페이지 하나를 고르는 방식이였다. 랜덤페이지 기능 자체가 이렇게 리소스를 차지하고 무거운 기능인데, 이 기능을 많이 이용하니 전체적으로 시스템의 로드가 올라갈 수밖에 없었다. 눈치챈 분이라면 페이지 개수를 세던 방식과 동일하게 페이지 전체 목록을 가져오는 문제가 있었다. 즉, 두 문제는 사실상 같은 문제였던 것이다.


그래서 랜덤페이지 기능을 개선하기 위해서 페이지 이름만을 따로 목록을 만드는 방식을 쓰게끔 고쳤다. 매번 페이지 목록을 만들어 읽어들이는것이 아니라, 페이지 목록을 미리 만들어서 텍스트로 저장하고, 랜덤으로 페이지를 고를 때는 전체 페이지 개수에 대해 랜덤으로 페이지 몇개를 고르고 정해진 개수의 페이지만을 리턴한다. 그런데 이런 단순 무식한 방법을 써도 꽤 빨랐고 문제점이 비로소 해결된 듯 보였다. 전페 페이지 목록을 미리 만들어두니, 20만 페이지가 되어도 전체 페이지 목록은 이미 만들어둔 파일을 읽기만 하면 된다. 30여만 페이지의 페이지 목록 파일은 5메가 수준이지만, 별 걱정할 정도로 큰게 아니였고, 랜덤 페이지 기능은 매우 빨랐다. 그 이후로는 이 무식한 방식이 리소스를 많이 쓰는 것을 보완하기 위해서 페이지 목록에 대한 작은 인덱스를 미리 만들어서, 전체 페이지 목록을 모두 읽어들일 필요가 없도록 고쳤다.


리소스를 최소로 사용하기

그리고 한동안 속도문제는 크게 문제가 되지 않다가 20여만 페이지 규모가 되었을 2013년 당시에도 상당히 사이트가 느려져 있었다. 그래서 원인을 점검해보니 역시나 페이지 개수를 세는 문제와 비슷한 문제로 인해 리소스를 많이 쓰고 있던 것이 문제였다. 그래서 고친 것은 다음과 같았다.

  • 페이지가 업데이트되면 매번 페이지 개수를 다시 세는 방식 => 페이지가 추가되거나 삭제되면 페이지 카운팅만 가감.
  • 페이지가 업데이트되면 매번 페이지 목록을 갱신하던 방식 => 페이지가 추가/삭제되면 목록에서 해당 페이지만 추가/삭제.

별것도 아닌 고침이였지만 리소스 사용률이 줄어서 훨씬 나아졌다. (https://github.com/wkpark/moniwiki/commit/91bb73078e7e2ac7f6092d315b93942855062167 참고) 그리고 뒤를 이은 일련의 고침들은 리소스 사용률과 CPU 부담을 최소로 하면서 최소한의 검색 기능이 작동하도록 하는 고침이였다.



검색의 문제

우선 그동안 제대로 지원하지 않던 검색을 보완했다. 제목 검색은 의외로 매우 단순한 방식으로 해결하였다. 모니위키의 경우에는 페이지 개수가 작은 경우에는 별 문제 없이 제목 검색에 regex를 쓸 수 있었다. 그런데 20여만 페이지 규모가 되니 MySQL같은 DBMS에 20만여개의 페이지에 대한 제목을 넣고 index를 생성한다고 할지라도 regex 검색을 하기는 어렵다. regex 검색을 하면 row를 모두 타야 검색이 되는 것이다. 그래서 간단한 Like 검색밖에 쓸 수 없다. 그러나 그 대신에 20여만 페이지의 파일 이름을 텍스트 파일로 만든 후에, 이 텍스트 파일을 적당히 잘라내어 읽어서 regex로 검색하면 놀라울 정도로 빠르게 20만 페이지의 제목을 모두 검색해낸다. 자모 검색을 할때에도 굳이 별도로 한글 자모 인덱스를 만들 필요조차 없었다. (리그베다위키는 현재 이 방식을 사용중이다)


하지만 본문 검색은 이 방식을 쓸 수 없었고 DBMS 없이 쉽게 지원하기는 어려웠다. 일단 개인 사용자라면 MySQL이나 DBMS에 익숙하지 않을 것을 가정하고 PHP 자체적으로 지원하고 있는 SQLite를 쓰거나 버클리DB를 이용하기로 하였다. 버클리DB를 이용한 간단한 n-gram PHP 검색 엔진이 있었기때문에 이를 고쳐서 모니위키에 적용했는데, 5만여 페이지일 경우에는 꽤 빠르게 검색이 되었는데, 20만 페이지 이상이 되니 이것도 그리 빠르지가 않았다. 그래서 일단은 아예 작동이 안되던 본문검색을 제한적으로나마 작동이 되도록 먼저 고쳤다. 검색을 하는 경우에는 5천여개의 페이지 목록만 가져와서 5천여개의 페이지의 본문만 검색하도록 하는 것이였다. 이러한 제한이 있는 대신에 regex를 사용할 수 있는 유연함을 가지고 있다. 물론 5천개의 검색 결과에서 못 찾으면 그 다음의 5천 페이지에 대한 검색을 하기 위해서, 다음 그 다음 버튼을 눌러야 하는 불편한 방식이다. 사용자가 검색어를 너무 간단한 것을 넣으면 검색 결과가 너무 많이 나오기때문에 사용자는 알아서 검색어를 수정할 것이며, 운 좋게 사용자가 원하는 페이지가 나올 수도 있다. (하지만 이런 방식으로는 부족했기때문에 Elastic Search 엔진을 달아서 테스트해보았다. Elastic Search엔진은 매우 유연하고 모니위키에 적용하기도 쉬웠고 속도도 적당히 빠른 수준이였지만, 결국엔 리그베다위키에 적용되지는 않았고, Elastic Search 엔진은 아직 실험중인 상태에 머물고 있다.)


이렇게 페이지를 몇천 페이지 단위로 잘라서 검색하는 간단한 방식을 쓰니 리소스도 적게 들고 원하는 반응 속도를 얻기 위한 적절한 타협점을 찾을 수 있었다. 속도를 얻기 위해 검색의 완전한 기능은 포기한 셈이다. dba를 사용한 tri-gram 검색엔진도 같이 만들었고 꽤 쓸만했지만 이는 모니위키에 아직 적용하지 못했으며, 그 대신에 이번에 모니위키 1.2.5 게발버전이 되어서야 MySQL을 통한 fulltext 서치를 본문 검색을 옵션을 제공하게 되었으며 30여만 페이지에 대한 검색을 비로서 대응할 수 있게 되었다. (미디어위키 역시 MySQL의 MYISAM 엔진의 FULLTEXT 인덱스를 사용함. 보다 최신의 MySQL은 InnoDB엔진에서도 FULLTEXT 지원)


개인위키를 위한 위키엔진과 RCS 버전 관리

모니위키는 사용자가 별로 좋지 않은 서버나 열악한 호스팅 환경을 가정으로 한다. 최신 PHP도 설치되어 있지 않고, MySQL 지원도 없는 값싼 호스팅 환경, 혹은 PC에서도 문제 없이 사용할 수 있는 개인 사용자에게 초점이 맞춰져 있다.

이러한 상황에서 MySQL지원은 당연히 첫번째 단계에서부터 제외된다. 모니위키가 텍스트기반이 될 수 밖에 없었던 첫번째 이유가 바로 모니위키는 개인위키를 목표로 하고있다는 것이다.

그런데 이러한 부분은 적은 리소스밖에 없는 상황에서도 굴러갈 수 있게 만드는 장점이 있다. 엔하위키 초창기에 모니위키를 선택했던 이유는 다름아닌 소규모 위키를 운영하려고 시작했던 것이 그 원인일 수 있다. 과거 트래픽이 부족해서 엔하위키를 오후에는 쓸 수 없는 경우가 많았다고 하니 어느정도 소규모로 처음에 운영했는지 알만하기는 하다.


두번째로 고려할 수 밖에 없는 부분은 위키엔진의 핵심적인 부분이기도 한 버전 컨트롤 시스템의 선택이다. 그 당시에 많은 위키엔진들은 텍스트 기반이 상당수가 있었고, 그중에 중대규모 위키엔진으로는 TWiki라는 둘째 가라하면 서러워할 유명한 위키엔진이 있다. 트위키는 RCS를 버전 컨트롤 시스템으로 채택하였다. 비단 TWiki뿐만 아니다. 상당수의 위키엔진은 RCS를 쓰고 있었는데 이것이 RCS를 자연스럽게 선택하게된 이유였다.

게다가 파이썬(Python)으로 만든 모니위키의 모체가 된 모인모인(MoinMoin)의 가장 큰 단점을 익히 알고있기도 했다. 모인모인은 자체적인 버전 컨트롤을 하고 있었는데, 모인모인은 과거 버전의 기록을 모두 완전한 텍스트로 저장하고 있었다. 과거 변경 이력을 보려면 과거 히스토리 파일을 모두 뒤져서 보여준다. 이게 소규모일 경우에는 별 문제 없지만, 몇천 페이지만 되어도 문제가 발생하게 된다. 히스토리 보기는 파일의 히스토리가 쌓일 수록 점점 느려졌고, 계속 거의 비슷한 복사본이 쌓이게 되는 구조였다. (노스모크는 이러한 문제점을 해결하기 위해서 주기적으로 과거의 히스토리를 지워버리고는 했다;;; 심지어 사용자의 편집 기록이 별도로 저장되는 editlog마져도 주기적으로 날려버렸다...) 최종버전 텍스트가 1GB라고 하고, 과거 변경 이력이 평균잡아 10개라고만 해도 벌써 10GB에 이르는 용량이 된다. 느리고 리소스도 많이 잡아먹는 이러한 자체적 버전관리 방식의 단점을 알고 있었기때문에 모니위키는 자체적인 버전관리 시스템을 사용하지 않고, RCS 버전관리 시스템을 당연한 듯 사용하게 되었다.


이번에 모니위키 1.2.5를 개발하면서 리그베다위키 서버를 잠시 살펴볼 수 있는 좋은 기회를 가지게 되었는데, 리그베다위키의 경우에는 28만 페이지의 규모에 최종 텍스트 버전이 2.8GB 수준이였으며 (tar.gz으로 압축하니 880MB), RCS 히스토리 파일의 개수는 32만개 정도 되고 그 용량은 12GB가 되었고, tar.gz로 압축하니 2.5GB가 되었다. 단순히 산술적으로 평균 버전 개수가 10개일 경우에 28GB 용량이 될 것이라는 예상을 깨고 그 절반도 안되는 12GB라는 것은 RCS 히스토리 파일의 효율성을 잘 보여준다. RCS는 최종 버전을 고스란히 가지고 있으며, 편집 로그도 자체적으로 보관하고, 각 버전간의 변경분인 delta만을 보존하고 있다. 뿐만아니라 리그베다위키는 editlog도 잘 보존하고 있었다. 2012년 이전의 기록은 안타깝게도 시스템 이전을 하다가 날려버리는 불상사를 겪었으나, 그 이후의 editlog는 모두 보존하고 있었으며 그 기록에는 2012년부터 2015년 5월까지의 편집 기록이 무려 7백4십만건이나 되었다. (editlog 파일은 아파치 로그처럼 일반 텍스트로 저장되므로 유닉스의 wc -l 명령으로 간단히 그 편집 항목수를 알 수 있다. 리그베다의 이러한 기록들을 분석한 결과는 다른 글로 보고할 기회가 있으면 한다)


RCS를 쓰지 않고 자체적으로 버전관리 방식을 쓰는 도쿠위키의 경우에는 과거의 버전을 온전한 텍스트 형태로 보존하되 압축해서 저장하고 있고, 미디어위키 역시 자체적인 버전관리방식을 쓰는데, 과거 버전이 모두 온전한 텍스트로 저장된다. @ditto님의 wikistat (http://sapzil.org/wikistat/)을 살펴보면 한국어 위키백과의 경우 사용자/토론 등등의 페이지를 모두 합쳐서 리그베다위키에 비해 3배 이상의 100백만 페이지를 가지고 있고, 이를 단순히 산술적으로 계산해도 10GB가 되며, 온전한 텍스트로 과거 이력을 보관하고 있기때문에 평균잡아 페이지당 버전이 10개만 되어도 전체 페이지 용량이 100기가 바이트를 넘기고 있다.



리소스 부족리소스가 부족합니다



문제는 리소스

위키엔진은 사실 매우 단순한 형태라서 DB schema가 복잡할 이유가 전혀 없다. 검색도 제목검색, 본문검색 수준의 기본적인 작업만 된다면 문제될 것이 별로 없다. 제목은 UNIQ하고 index타기에 좋아 검색에 매우 유리하고, 가장 리소스를 잡아먹을 부분은 본문 검색 뿐이다. 본문검색의 경우 기능도 별로 없는 MySQL을 쓸것이 아니라 전문적인 검색엔진인 ElasticSearch을 쓰면 루씬과 같은 고급 형태소분석 엔진을 같이 사용하는게 가능하다. 위키엔진 그 자체는 사실 DBMS보다는 오히려 key-value 형태의 NoSQL이 유리하다 할 수 있다.

미디어위키처럼 DBMS를 써야하는 이유는 다른 것이 원인일 수는 있다. 수많은 사용자들에 대한 데이터베이스가 필요하고, 반달러의 어뷰징에 대비하고 IP를 블럭시키고 사용자에 대한 적절한 대응을 하려면 사용자의 패턴 및 과거 페이지 변경 이력에 대한 추적이 필요할 것이고, 이를 위해서라면 DBMS를 당연히 써야할 수밖에 없어 보인다.

그러나 모니위키는 개인위키를 목표로 하기때문에 이러한 사용자 추적이나 관리 기능이 사실상 전무하고 지원하고 있지도 않았다.


문제는 리소스와의 싸움이다. 모니위키가 DBMS 기반이 아니기때문에 모니위키가 느리다는 말은 전혀 잘못된 말이다. 리소스를 잘 활용하면 DBMS가 전혀 없는 상황에서도 대규모 위키를 지원하는 것이 가능하고, DBMS가 필요한 경우가 있고 그렇지 않은 경우가 있으며, DBMS를 쓰더라도 리소스 관리를 잘못하면 적은 자원으로는 서비스 하기가 곤란한 상황이 된다. 위키백과 같은 경우에는 리소스 부족하면 서버 더 사고, 하드디스크 싸니까 부족하면 붙이고, 메모리 늘리면 된다는 식으로 쉽게 말할 수 있지만 그게 그리 쉬운 말인가? 최소의 자원으로 최대의 효과를 내려는 노력은 당연한 것이고, 모니위키가 지금까지 DBMS를 쓸 필요를 느끼지 않았던 것은 이것만으로도 제 효과를 충분히 내었다고 판단했기 때문이다. 앞으로 MySQL과 같은 검색엔진의 지원이 추가되었으니, 규모에 맞춰서 선택할 수 있는 폭도 넓어졌고, 모니위키는 앞으로도 개인위키를 목표로 하고, 더 사용하기 쉬운 간단한 위키엔진을 추구할 것이다.


여전히 파일시스템 기반의 기조를 지킬생각이라고 누군가가 질문한다면 그렇다고 대답하겠다. 사실 지금의 파일시스템 성능은 과거 10년과 비교가 안될 정도로 좋아진 성능을 자랑한다. 리눅스의 파일 시스템 자체가 개속 개선되고 있는 상황이라는 것이다. 과거에는 DBMS만의 장점으로 꼽히던 일부 기능을 파일시스템 자체기능으로 포함시켜버리려는 시도도 있다. DBMS만 개선되는게 아니라 그만큼 파일시스템 그 자체도 진화하고 있다는 것이다. 그러한 와중에도 역시 리소스를 적게 차지하는 쪽은 파일시스템쪽이며, 특히나 버전관리 시스템의 대부분은 파일시스템을 사용한다. git / SVN / CVS / mecurial 등등의 버전관리 시스템은 모두 파일시스템을 사용한다. SVN은 약간 특이한 케이스인데, 구 버전의 SVN은 버클리DB기반의 버전 컨트롤을 사용했고, 이것이 자주 깨지는 등의 문제가 발생하자 FSFS라는 파일시스템 기반의 버전관리를 지원하게 되었다. (자세한 내용은 http://web.mit.edu/ghudson/info/fsfs를 참고) 그밖에 파일시스템이 더 유리한 경우는 https://www.eldos.com/solfs/articles/7853.php?page=all 문서를 참고하라. RCS보다 더 훌륭한 파일기반이 아닌 버전관리 시스템이 있다면 상황이 달라질 수 있겠지만, 위키위키에서만큼은 아직은 RCS가 버전관리 시스템으로 가장 훌륭해 보인다. (사실 git을 가지고 실험해본 적이 있다. 하나의 파일에 대해서 1000개의 버전을 생성시켜서 저장했더니 RCS이 경우에는 최종 텍스트 크기는 46KB, RCS 히스토리 파일은 820KB정도밖에 안되는 반면, git의 경우에는 46KB 최종 본문 크기 동일하지만 .git 크기가 19MB나 되었다...)


리그베다 위키와 모니위키의 개발 방향은..

모니위키처럼 개인위키를 표방하는 위키엔진이 30여만 페이지를 운영하고 있는 예는 사실 거의 없다. 도쿠위키는 본인의 실험에 의하면 5천 페이지에서 조차도 본문 검색이 너무 느렸다. (못하다는 뜻으로 말하는게 아니다. 그러한 대응이 필요했다면 이미 도쿠위키는 고쳤을 것이다. 다만 아직 필요하지 않아서 고칠 필요성을 못느끼는 것이다.) 유명한 중대규모 위키인 트위키조차도 이런 큰 규모의 위키를 운영하고 있지 않고, 트위키 개발 사이트조차도 사실 너무 느리다. 트위키 역시 대규모 위키에 대한 적절한 대응책을 마련하지 않은 것이다. 이것은 DBMS를 쓰느냐 쓰지 않느냐의 문제가 전혀 아니다.


모니위키는 이번 리그베다위키 사건으로 말미암아 기로에 서게되었지만 다른 한편으로 모니위키 및 모니위키 사용자에게 소식도 있다. 리그베다측에서는 본인의 개발에 필요할 수 있는 리그베다 운영시에 겪었던 여러 문제점을 알려주고, 서버를 개방하여서 본인이 직접 문제를 테스트할 수 있는 테스트 베드를 제공함과 동시에, 모니위키의 안정화를 위한 테스트과정에 참가한 테스터들의 협력을 앞으로도 이어나가며 안정화된 기능을 점진적으로 개방해 나아갈 것이라는 것이다.

뿐만 아니라 그간의 운영을 위해 자체적으로 개발하던 소스와 운영 노하우를 모니위키의 적용할 수 있도록 제공하고 적극 협조해 준다고 하였다. 이는 수많은 모니위키 개인 사용자에게 환영받을 소식임과 동시에 모니위키를 사용하고 있는 커뮤니티위키에도 좋은 소식이 된다.


이러한 것이 모니위키의 코드를 더 복잡하게 만들것이라 생각하는 분들도 계실것이다. 물론 단기적으로 봤을때에는 그렇게 보일 수도 있겠다. 그러나 모니위키는 그 코어 코드를 점점 줄이고 특정 기능을 확장하기 쉬운 구조로 개선하게 될 것이며, 작은 규모의 위키위키에서 고려하지 않았던 부분이 커다란 위키가 되었을 때에 리소스 문제가 발생할 수 있는 부분을 해결해 나아가며, 보안에 관련된 부분은 점점 보강하게 될 것이다.


그간 모니위키는 리그베다와 지극히 한정적인 형태의 협업을 하고 일년에 한두번 할까 말까했던 소통을 하였으나, 이번에 예정된 모니위키 1.2.5 개발판의 경우 리그베다에 100% 적용되었으며, 이제 리그베다위키의 소스는 99.9%가 모니위키와 일치하고, 그 나머지 부분도 모니위키에 반영되거나 더 간단한 방식이 적용될 것이다. 리그베다는 더이상 모니위키 소스를 별도로 관리할 필요가 없어지게 된 것이고, 보안에 대한 문제가 발생하였을 경우에 신속한 대처를 할 수 있게 되었다. 모니위키 1.2.5의 베타테스팅은 리그베다위키와 함께 진행되며, 테스터들과 직접 소통을 하며 의견을 조율하여 모니위키 소스에도 직접 반영할 수 있다는 것을 의미한다.


마치며

리그베다위키 사건으로 시작되었던 일련의 일들이 사실 상당히 혼란스럽고 보는 사람 입장에서도 당혹스럽지만, 이것을 일반 사용자 측면에서 본다면, 리그베다측이 그간 사용자의 요구를 제대로 반영하지 못했다는 점이 분명한 것 같다. 저작권 부분이 가장 이슈화 되었지만, 어디 저작권 뿐이겠는가. 위키백과의 경우에도 아무런 문제 없는 듯이 보이지만, 각 개별 기여자들이 당연히 누려야할 부분은 누리지 못하고 소외된 느낌이다. 위키백과는 누가 기여했는지 그 기여목록을 보려면 특수 기능을 눌러야 한다. 레퍼런스를 달려고 하면 기여자의 이름은 보이지 않고 Wikipedia contributors 라고만 나온다. 히스토리를 봐야 기여자의 이름을 볼 수 있을 뿐이다. 기여자의 링크나 연락처는 쉽게 알 방법이 없다. 하다못해 홈페이지나 이메일 주소도 일일히 찾아봐야 한다. 기여자에게 모든 저작권이 있다고 말하지만 정작 기여자에게 돌아가는 것은 없어보인다.

모니위키는 앞으로 리그베다와 기술적인 협력을 통해서 리그베다위키의 기여자들에게 돌아가야 할 마땅한 권리를 보장하려고 노력하고, 기술적인 것 뿐만 아니라 오픈소스/자유소프트웨어의 가치실현 및 위키위키의 공유정신을 실현해 나아가는 모습을 보여줄 수 있게 되기를 바란다.

by dumpcookie 2015. 6. 12. 11:48

PHP 경험이 많은 분들이라면 한번정도는 PHP 확장 모듈을 만들어 보았을 것이다. 본인의 경우는 비로서 최근에 PHP 확장 모듈을 만들어 보았는데 다음의 내용이 게중에 가장 쉬웠다. 뚝딱 테스트 해보는데에 20분이면 충분했다.

http://devzone.zend.com/303/extension-writing-part-i-introduction-to-php-and-zend/

위 사이트에서 복사해서 붙이기만 하고 최초 작동 테스트 완료 하는데에 20분 걸렸으니, 더 간단하고 쉬운 문서를 만들면 좋겠다 싶어서 문서를 더 찾아보았다. 그래서 찾은 문서는 exman으로 유명한 박준철님이 작성하신 다음 문서이다. (박준철님의 사이트는 닫혀있었으나 문서는 다른 곳에서 찾을 수 있었다)

http://srctalk.imfree.co.kr/view.ife?seq=268


이 문서의 특징은 PHP로 확장모듈을 만드는 프로그래머가 실제로 확장 모듈을 만들게 될 상황에 맞춰서 구성되었다는 점이다.

그리고 또 찾은 문서는 다음과 같은데, 문서가 길지 않고 매우 짧고 쉬워서 그대로 따라하기 쉽다.

http://eddmann.com/posts/introduction-to-creating-a-basic-php-extension/


이정도 문서만 참고하더라도 10분안에 나만의 PHP 확장을 만들고 최소한의 테스트를 완료할 수 있을 것 같은데, 열거한 문서를 읽어본 사람이라면 다음의 내용을 따라해서 좀 더 빨리 확장 모듈을 만들어보고 테스트 할 수도 있다.


스크립트 실행 준비

먼저 원하는 디렉토리로 위치로 가서 새로 디렉토리를 만든다음 그곳에서 스크립트를 실행시킬 준비를 한다. 이때에 적절한 프로젝트 이름을 생각해둔다. 여기서는 그 이름을 foobar로 하겠다.

cd ~/home
mkdir php-foobar
cd php-foobar

스크립트 받기

디렉토리에 들어가서 (여기서는 php-foobar) 다음과 같이, 복사하고 붙이기를 최소화 하기 위하여 미리 만들어둔 쉘 스크립트를 다운로드 받아서 압축을 푼다.

php_ext.sh.gz


이 스크립트는 PHP 소스에 포함되어있는 ext_skel 스크립트와 하는 기능은 거의 같지만, 더 단순한 기능만 하고, 실행 즉시 곧바로 아무것도 건드리지 않고 phpize를 통해 컴파일 할 수 있다는 점이 다르며, skeleton 디렉토리에 의존하는 스크립트가 아니기때문에 아무데서나 사용할 수 있다.

스크립트 압축 풀기

스크립트의 압축을 푼다.

gunzip php_ext.sh.gz

스크립트 실행

디렉토리에 들어가서 (여기서는 php-foobar) 다음과 같이 스크립트를 실행시킨다.

sh php_ext.sh foobar

그러면 config.m4, foobar.c, php_foobar.h 세개의 파일이 생성된다.


컴파일하기

콘솔에서 phpize를 실행하면 각종 파일이 자동으로 생성되며, configure를 실행하고 난 후에 make 한다.

phpize
sh configure
make
...

테스트하기

콘솔에서 다음의 명령을 실행시켜보자

php -dextension=.libs/foobar.so -r "foobar_world('hello');"

화면에 "Hello hello"라고 뜨면 정상 작동한 것이다. 테스트 종료.


아마 이대로만 정확히 따라했다면 5분안에도 만들고 테스트 완료 했을 것이다. 그러나 원하는 기능을 만들기 위해서는 이제 몇십배 더 많은 시간을 투자해야 할지도 모른다!!

by dumpcookie 2015. 5. 2. 10:36

그간 몇차례 그누보드4를 설치하고 사용자 모듈을 만들어보려고 시도를 했었습니다. 그런데 그누보드4가 short open 태그여서 수정하기 귀찮음으로 그만두고는 하다가, 이번에 간단한 스크립트를 돌려서 short open 태그를 모두 고친 후에 설치를 하고 사용자 모듈을 만들어 봤습니다.


사용자 모듈은 다음과 같은 형태입니다.

<?php

class User_g4 extends WikiUser {

    function g4_init() {
        global $g4, $member, $g4_root_dir;

        $g4_root_dir = !empty($DBInfo->g4_root_dir) ?
                $DBInfo->g4_root_dir : __DIR__.'/../../../gb4';

        require_once('g4.common.php');
        $member = g4_get_member();
    }

    function User_g4($id = '') {

여기서 g4.common.php는 그누보드의 루트 디렉토리에 있는 common.php를 간소화시킨 버전으로 다음과 같은 형태입니다.


<?php
/**
 * simplified common.php by wkpark @ gmail.com
 * 2015/04/29
 */

function g4_get_member() {
    global $g4, $g4_root_dir;
...
include_once("$g4_root_dir/lib/constant.php");  // 상수 정의
include_once("$g4_root_dir/config.php");  // 설정 파일
include_once("$g4_root_dir/lib/common.lib.php"); // 공통 라이브러리
include_once("$g4_root_dir/dbconfig.php");

$connect_db = sql_connect($mysql_host, $mysql_user, $mysql_password);
$select_db = sql_select_db($mysql_db, $connect_db);
...

이런 식으로 연결을 한 후에 그누보드 함수 get_menber()로 사용자를 가져오게 됩니다. 자세한 내용은 소스를 참고하시기 바라며, 소스 다운로드 및 사용 설명은 http://moniwiki.kldp.net/wiki.php/G4UserPlugin.를 참조하시기 바랍니다~

by dumpcookie 2015. 4. 29. 18:36

리그베다위키의 전신 엔하위키를 본인이 처음 봤을때가 아마도 2008년정도 였을것이다. 그때는 "아... 이런 희안한 사이트도 있구나"라는 정도로 스쳐지나갔다가, 한 2년 후에 다시 보게 되었을때는 거의 ~8만여 페이지 규모의 국내 최대 모니위키 사이트가 되어있었다. 규모에도 놀랐지만 너무 너무 느려서 더 놀랬던 것으로 기억한다. 검색은 당연히 먹통이고, 반응 속도가 너무 느려서 이대로 두면 안되겠다 싶어서 위키 게시판에 글을 올렸고, 병목현상을 일으키던 부분을 찾아내어 수정하고 모니위키 1.1.5 개발자판으로 판올림을 했던 것으로 기억한다.(2010년 10월) 그 당시 엔하위키는 모니위키 렌더러에 자잘한 수정을 하고 (버그 수정 포함), 일부 사용자 관리/필터링 등등의 일부 기능을 추가 했을 뿐이라서, 엔하위키측에서 제공한 모니위키 변경분에 대해서 선별적으로 적용하고 추가적으로 여러 수정을 해서 모니위키 1.1.5 버전이 나왔고, 엔하위키 관리/운영진은 이를 바탕으로 어렵지 않게 판올림을 하였고 정상적인 속도도 나오게 되었다. 그 뒤에 엔하위키 사용자의 피드백을 받아서 모니위키 1.1.6CVS 개발판이 나왔다.


https://kldp.org/node/118619 참조

1.1.5 판올림 당시 구 엔하위키게시판에 올라온 짤방



그러다가 다시 15만~20만 페이지 규모가 되었던 시점이었을때에 또 다시 엔하위키는 느려져 있었고, 또 다시 병목현상의 원인을 찾아 수정하고 모니위키 1.2.0 판올림도 같이 병행하였다. (2013년 8월)


당시 병목 현상의 원인은?

2010년 당시 5만여 페이지 규모였을때에, 병목현상의 원인은 사실 별거 없었다. 매크로중에 페이지 개수를 카운팅하는 기능이 너무 느렸던 것이었다. 엔하위키의 경우 페이지 개수 표시가 오른쪽 사이드바에 항상 표시가 되었다. 그런데 카운팅 매크로는 5만여 페이지의 목록을 모두 가져오고 그것의 개수를 매번 세었던 것이였다. 그래서 이 부분에 대해서 페이지 개수를 보다 효율적으로 세도록 고치고, 페이지 개수 정보를 캐싱하게 만들었다. 그밖에 여러 개선과 수정이 물론 있었지만 이 변경이 구 엔하위키의 속도를 원상복구 시키는 주요한 고침이였다. 그밖에 5만여 페이지 규모에서도 문제없도록 간단한 n-gram 인덱서를 추가해서 5만여 페이지 규모의 위키에서도 본문 검색을 할 수 있도록 만들었다.


2013년 8월의 고침도 사실 따지고 보면 대동소이했다. 페이지 개수 세는 부분을 보다 영리하게 만들고, 전체 페이지 목록을 매번 가져올 것이 아니라 주기적으로 업데이트 하고, 페이지 삭제/추가시 페이지 개수를 부분 업데이트 하도록 고쳤다. 물론 1.2.0 판올림의 경우에는 모니위키의 캐싱 방법을 더 뜯어고쳐서 대규모 위키에 문제 없도록 하는 등등의 여러 고침도 함께 포함하고 있었다.


DBMS vs 파일 시스템

많은 사람들이 말하기를 모니위키가 DBMS 기반이 아니라서 대규모 위키 대응이 불가능할 것이라 하지만, 사실 위키의 경우에는 flat한 key-value 구조라서 DBMS보다는 NoSQL이 더 유리하고, key-value구조는 파일 시스템 친화적이다. git/cvs/rcs같은 훌륭한 버전컨트롤 시스템이 모두 파일 기반이며 이를 그대로 써먹을 수 있다. 특히 RCS의 경우는 각 페이지별로 버전 히스토리가 별도로 저장되므로 위키엔진의 버전 컨트롤용으로 적합하다. 미디어위키처럼 DBMS로 구현하는 경우에는 버전 컨트롤을 다시 구현해야 한다. (또한 미디어위키는 여러 이미지 자료들을 DBMS로 저장하거나 하지는 않으며 이미지 자료는 파일시스템에 저장하는 방식을 여전히 쓰고 있다.) 그러나 모니위키의 경우처럼 파일기반인 경우에는 버전컨트롤을 RCS와 같은 외부프로그램이 별도로 담당하고 있어서 따로 구현할 필요가 없다. 위키엔진은 페이지 이름에 해당하는 페이지의 최종 버전을 가져와서 렌더링해서 뿌려주면 그만이다. 페이지 이름이 고유한 key-value구조라서 NoSQL에 대응하기 어렵지 않다. 위키 규모가 100만페이지 이상이 되더라고 대응이 그리 어려운 일이 아니라는 얘기다. 리눅스의 경우 파일시스템의 성능이 비약적으로 좋아졌다는 사실도 한몫 하고 있다. 버전 정보는 파일시스템으로 저장하고, 프론트엔드 단에서는 NoSQL을 써서 최종 버전을 저장하고, 검색은 elastic 검색 엔진을 쓰면된다. (모니위키는 이미 개발판에 elastic 검색 엔진을 실험적으로 넣었다. 가까운 미래에 NoSQL 백엔드를 만들게 될지도 모르겠다)


미디어위키는 훌륭한 위키엔진이다. 그러나 이것을 개인사용자가 사용하기 적합할까? 미디어위키를 처음 설치해보고는 그 속도가 생각보다 느리다는 사실에 놀랬었다. 미디어위키의 경우 자신의 위키 규모에 맞춰서 적절하게 설정을 해주어야 한다. (미디어위키의 속도를 향상시키는 법은 다른 글 #1 #2 참조.) 도쿠위키도 아주 좋은 위키엔진임에 분명하다. 파일기반이라서 단촐하고 개인사용자에게 적합하다. 그런데 본인의 테스트에 의하면 5천 페이지 정도만 되어도 쉽게 대응할 방법을 찾지 못하였다. 물론 개인위키 사용자라면 1천 페이지 만들기도 어렵기때문에 사용하는데 지장이 없을 것이다.


엔하 미러의 속도와 비교

종종 리그베다 본관의 속도와 엔하미러의 속도가 비교대상이 되고는 하였다. 사실 엔하미러의 속도는 상상을 초월할 정도로 빠르다. 엔하미러가 얼마나 빠르냐 하면, 로컬에서 아파치 벤치로 속도를 측정한다고 했을때에 거의 static html 페이지 속도급으로 빠르다. 이정도로 빠르기때문에 사실 엔하미러 속도를 다른 위키엔진과 비교하는 것은 무의미할 수 있다. 모니위키의 속도는 여러 최적화 옵션을 키면 엔하미러의 속도의 1/10에 해당한다 보면 된다. 사양에 따라 다르겠지만 로컬에서 static html 속도가 ~2000RPS 정도라고 한다면 모니위키의 경우에는 ~200RPS이다. (만약 모니위키를 아파치서버 + varnish 캐싱 서버와 함께 사용하면 ~7000RPS 정도로 올라가게 된다.)


그렇다면 미디어위키의 속도는 어떨까? 기본 설정으로 설치가 끝난 상태에서는 서버사양에 따라 달라지겠지만 위에서 언급한 것처럼 static html이 ~2000RPS정도 나온다고 했을때에 미디어위키는 ~5RPS도 안된다. 최적화 옵션을 켜면 ~10~15RPS 수준으로 올라간다. (이 경우도 아파치 + varnish캐시서버를 사용하면 비약적으로 빨라지게 되는데 자세한 내용은 미디어위키의 문서를 참고하라)


마치며

지금은 상당히 좋은 위키 엔진이 많이 있기때문에 모니위키의 인기가 많이 사라진 편이지만, 모니위키가 최초 나왔을 무렵 노스모크의 위키 엔진이었던 python으로 구현된 모인모인의 클론을 표방하고, 웹 호스팅에 좀 더 유리했던 PHP로 새롭게 작성했었다. 모니위키는 기존의 모인모인을 금방 대신하여 자리를 차지하게 되었고, 한동안 모니위키는 개인위키 사용자들의 대세였던 적도 있었다. 뿐만 아니라 지인의 증언(?)에 의하면 모니위키는 많은 회사 인트라넷에서도 사용되어졌고 처음 위키위키를 사용하는 사용자들에게 영향을 끼쳤다.


모니위키는 여전히 개인위키를 지향하며, 코어 코드를 좀더 줄이려고 노력하고 있다. 그러면서도 리그베다위키의 규모에 대응하기 위해서 확장이 보다 손쉽도록 개선하고 있으며, 최근에는 마크다운의 문법과 거의 유사한 것에 착안하여 마크다운 문법을 섞어서 쓰더라도 문제가 없도록 MixDown 파서를 개발하고 테스트하였고 한편으로는 기존 모니위키 문법 파서를 재작성 및 테스트 하였다.


개인 사정으로 2014년 거의 개발 중단 상태였다가 다시 모니위키 소스를 건드리고 있는데, 모니위키가 앞으로 어떠한 방향으로 개발되게 될지는 나도 장담하지 못하겠지만, 리그베다위키가 사라지지 않는한, 일부 개인 사용자의 피드백이 지속되고 있는 이상 계속 개발하게 되지 않을까 생각한다.


다음에는 리그베다위키와 그 클론들에 대한 이야기와 전망에 대해 써보려 한다.

by dumpcookie 2015. 4. 28. 14:44

최근 리그베타위키 발 모니위키 보안 문제가 이슈였습니다. 2003년에 처음 만들어져서 벌써 13년째 장수(?) 자유소프트웨어 프로젝트인 모니위키에서 이정도 심각했던 보안 이슈는 지금까지 없었습니다. 그런데 이번에 발견된 보안문제는 문제가 심각할뿐만 아니라 PHP 관련 보안문제중에 가장 기초적인 부분중에 한가지라서 그 충격이 컸던 것도 사실입니다.

그러면 어찌해서 이런 기초적인 보안오류가 최초 모니위키가 릴리스된 2003년 이후 10년이 지난 시점에서야 발견된 것일까요?





이번에 발견된 보안 문제는 모니위키를 사용하기위해 필수적으로 필요한 RCS 버전관리 프로그램을 popen()으로 호출하기 위해 만들어진 RCS 버전관리 모듈에서 발견되었습니다. 최초로 발견하신 분은 강성훈님이며, 이 이슈는 2013년에 만들어진 모니위키 1.2.0 버전부터 1.2.3 버전까지 영향을 받습니다. 모니위키 1.2.0 버전 이전에는 다음과 같았기때문에 쉘코드 인젝션이 불가합니다.

wiki.php의 savePage() method에서 RCS 모듈 호출 if (strlen($comment)>$this->anonymous_log_maxlen) $comment=''; // restrict comment le $REMOTE_ADDR=$_SERVER['REMOTE_ADDR']; $comment=escapeshellcmd($comment); // 이 부분때문에 문제가 없음 $myid=$user->id; if (!empty($user->info['nick'])) {


그러던 것이 모니위키 1.2.0부터 다음과 같이 바뀌었습니다.

--- a/lib/version.RCS.php
+++ b/lib/version.RCS.php
@@ -63,7 +63,9 @@ class Version_RCS {
       _mkdir_p($dir.'/RCS', 2777);
       umask($om);
     }
-    $fp=@popen("ci -l -x,v/ -q -t-\"".$key."\" -m\"".$log."\" ".$key.$this->NULL,"r");
+    // $log = escapeshellarg($log); // win32 not work
+    $log = '"'.preg_replace('/([\\\"])/', "\\\\\\1", $log).'"';
+    $fp = @popen("ci -l -x,v/ -q -t-\"".$key."\" -m".$log." ".$key.$this->NULL,"r");
     if (is_resource($fp)) pclose($fp);
   }

diff --git a/wiki.php b/wiki.php
index 536b2a1..43826be 100644
--- a/wiki.php
+++ b/wiki.php
@@ -1020,7 +1020,6 @@ class WikiDB {
       if (strlen($comment)>$this->anonymous_log_maxlen) $comment=''; // restrict comment le

     $REMOTE_ADDR=$_SERVER['REMOTE_ADDR'];
-    $comment=escapeshellcmd($comment);

     $myid=$user->id;
     if (!empty($user->info['nick'])) {

잘 보시면 escapeshellcmd()가 제거되었으나 RCS모듈에서는 win32 문제때문에 escaheshellarg()를 사용하지 않고, 어찌된 영문인지 쌍따옴표만 escape하는 실수가 보입니다. (커멘트에도 나와있듯이)

그리고 곧 바로 다음과 같이 수정하여 win32에서 문제점을 수정했으나 escapeshellarg()를 다시 적용하지 않는 실수를 저지르고 있습니다.

--- a/lib/version.RCS.php
+++ b/lib/version.RCS.php
@@ -63,10 +63,27 @@ class Version_RCS {
       _mkdir_p($dir.'/RCS', 2777);
       umask($om);
     }
-    // $log = escapeshellarg($log); // win32 not work
-    $log = '"'.preg_replace('/([\\\"])/', "\\\\\\1", $log).'"';
-    $fp = @popen("ci -l -x,v/ -q -t-\"".$key."\" -m".$log." ".$key.$this->NULL,"r");
+    $mlog = '';
+    $plog = '';
+    if (getenv('OS') == 'Windows_NT' and isset($log[0])) {
+      // win32 cmd.exe arguments do not accept UTF-8 charset correctly.
+      // just use the stdin commit msg method instead of using -m"log" argument.
+      $logfile = tempnam($this->DB->vartmp_dir, 'COMMIT_LOG');
+      $fp = fopen($logfile, 'w');
+      if (is_resource($fp)) {
+        fwrite($fp, $log);
+        fclose($fp);
+        $plog = ' < '.$logfile;
+      }
+    }
+    if (empty($plog)) {
+      // $log = escapeshellarg($log); // win32 does not work correctly
+      $log = '"'.preg_replace('/([\\\"])/', "\\\\\\1", $log).'"'; // 이미 win32 문제가 해결되었으므로 이 줄을 지우고 윗 줄을 써야 했음
+      $mlog = ' -m'.$log;
+    }
+    $fp = @popen("ci -l -x,v/ -q -t-\"".$key."\" ".$mlog." ".$key.$plog.$this->NULL,"r");
     if (is_resource($fp)) pclose($fp);
+    if (isset($plog[0])) unlink($logfile);
   }

   function rlog($pagename,$rev='',$opt='',$oldopt='') {

이렇게 해서 2013년 버전 1.2.0부터 1.2.3버전까지 보안 이슈가 영향을 받게 된 것이고, 리그베타위키발 보안이슈는 2015년에 비로소 터지게 됩니다. 





개인위키 사용자는 안전한가?

그러면 모니위키를 사용하는 대다수 개인위키 사용자들에게 미치는 영향은 어느정도 일까요?


모니위키는 개발 초기부터 개인위키를 지향하였으며 이에 대응하기 위해 개인 사용자들을 위한 보안모듈을 손쉽게 만들 수 있게 하였고, 모니위키를 설치한 개인 사용자들은 대게 자신만이 편집하고 사용할 수 있도록 모니위키를 설정하였습니다. 예를 들어 모니위키를 최초 설치하면 비로그인 사용자를 포함한 모든 사람들이 편집할 수 있도록 설정이 되지만, 개인 사용자들은 다음과 같이 로그인을 한 사용자, 특히 관리자이면서 소유자인 개인만 편집을 하게끔 설정을 바꾸어 사용하는 패턴이었습니다. 게다가 일부 사용자들은 자신만 사용자 등록을 마친 후에 다른 사용자들은 사용자 등록을 하지 못하도록 하는 옵션을 만들어 달라고 제안하기까지 하였습니다. 그래서 만들어진 옵션이 $no_register 옵션인데, 개인위키 사용자들은 다음과 같은 식의 설정으로 모니위키를 운영하고 있습니다.

.... $security_class="needtologin"; // 로그인해야 일반적인 기능을 쓸 수 있도록 설정 $no_register=1; ...

혹은 다음과 같이

.... $security_class="mustlogin"; // 반드시 로그인하도록 설정. $no_register=1; ...


그런데 이러한 설정을 해놓으면 비로그인 사용자는 모니위키의 edit/diff 기능등을 사용할 수 없고, 모니위키의 보안 이슈가 된 RCS모듈은 아무런 영향력을 발휘하지 못하게 되어 위와 같은 식으로 설정된 개인위키는 RCS 보안문제와 상관 없게 됩니다.


이번 보안 이슈가 드러나게된 또다른 원인으로는 리그베타위키는 엔하위키 시절부터 익명사용자의 편집을 장려하는 정책을 펼치고 있었기 때문입니다. 익명사용자가 편집(edit) 및 변경점(diff)을 볼 수 있기때문에 RCS 모듈의 보안버그가 노출되게 되었던 것입니다.


개인위키 사용자의 대처 방법은?

위에서 기술했다시피 이번에 발견된 보안 이슈를 회피하려면 다음의 설정으로 config.php에서 개인위키 설정을 먼저 바꿔주시기 바랍니다. 그리고 최대한 빠른 시일 안에 모니위키를 백업하고 모니위키 최신 1.2.4p2로 업그레이드 하시길 권장해 드립니다.

.... $security_class="mustlogin"; // 반드시 로그인하도록 설정. $no_register=1; ...


다른 위키엔진은 문제 없나?

그렇다면 모니위키 이외의 다른 PHP 위키 엔진들은 이러한 이슈가 많은 편일까요? 개발자들은 실수를 하게 마련이며, 방대한 기능의 미디어위키는 외부프로그램에 의존하는 경우가 많아서 이런 이슈에 더 자주 노출되게 됩니다.


http://www.cvedetails.com/version/129227/Mediawiki-Mediawiki-1.19.0.html 쉘 인젝션 버그 있음.

https://gerrit.wikimedia.org/r/#/c/110069/2/includes/media/Bitmap.php 쉘 인젝션 버그가 있던 파일을 고치는 패치

(이 경우 미디어위키 1.19.0이 2012-05-02에 나왔고 문제 해결은 2014년 1월)


도쿠위키의 경우도 찾아보면 다음과 같이 나옵니다.

http://www.opennet.ru/base/linux/1159548011_6882.txt.html 2006년 쉘 인젝션 버그

https://www.freelists.org/post/dokuwiki/SECURITY-ALERT-problems-in-fetchphp

https://bugs.dokuwiki.org/index.php?do=details&task_id=926 코드 패치


개발자가 완벽한 천재가 아닌이상 그것이 쌍팔년도 보안 취약점이라고 할지라도 보안버그가 아예 없을수만은 없는 것입니다. 모니위키 코드는 지난 10여년간 꽤 지속적으로 관리되었고, 코드가 방대하지 않고 단촐하기때문에 이러한 버그에 대응이 더 쉽습니다.


모니위키 1.2.4p2

이번에 강성훈님이 발견하신 보안버그를 수정한 버전 1.2.4를 내놓았다가, 패키징 실수 및 RCS 보안버그를 추가로 수정한 패치 릴리스 1.2.4p2를 내놓았습니다. 모니위키 개인 위키 사용자분들은 위 내용을 참조하시어 config.php에서 개인위키 전용으로 편집을 할 수 있도록 설정을 바꾼 후에 모니위키를 업그레이드하시기 바랍니다~


http://dev.naver.com/projects/moniwiki/download/note/7020


P.S.

본인은 이번 리그베다위키 사건에 관련된 보안버그 이슈를 과거 토끼군으로 알려졌던 강성훈님이 버그리포팅하신 25일에 처음 알았으며, 버그가 심각한 만큼 이 문제를 해결한 모니위키 1.2.4 버전을 곧바로 내놓았습니다. 또한 이자리를 빌어 본인은 리그베다위키의 운영에 전혀 관여하지 않고 있다는 사실을 다시한번 밝힙니다.


물론 저는 리그베다위키 관리자 청동님과 종종 모니위키 문제점에 대한 이메일을 주고받고 있으며, 이번 보안 패치도 리그베다위키에 개인메일로 가장 먼저 알리고 제공하였습니다. 또한 리그베다위키 미러관리자로 알려진 퍼즐릿정님에게도 모니위키 버그 및 패치에 대해 알려드렸는데, 이는 다름이 아니라 퍼즐릿정님은 과거 국내 최대 위키였던 노스모크 위키의 관리자이기도 하기 때문입니다.


마지막으로 이번 보안버그를 알려주신 강성훈님에게 다시한번 감사를 드리는 바입니다~

by dumpcookie 2015. 4. 27. 10:57

앞의 일련의 포스트를 통해 mod_disk_cache를 사용하면 엄청난 성능의 향상이 가능하다는 것을 알 수 있습니다.

물론 mod_disk_cache를 제대로 지원하기위해서는 몇가지 제한사항이 있기는 하지만 특히 위키위키와 같은 경우에는 큰 변경 없이도 mod_disk_cache가 어렵지 않게 적용 가능한 경우입니다.

이번에는 보다 전문적이고 사실상 표준으로 자리 잡았다 해도 과언이 아닌 웹 가속기인 Varnish를 모니위키에서 사용해도 문제가 없도록 고쳐보았습니다.

참고로 Varnish는 FreeBSD 커널 해커가 만든 엄청난 성능의 웹가속기입니다. 2006년도에 첫 릴리스가 나온 후에 꾸준히 개발되어 현재 버전 3.0.4가 되었으며, 트위터, facebook을 비롯해서 수많은 사이트가 사이트의 속도를 높이고 동시에 부하를 낮춰 사이트 서비스를 안정적으로 할 수 있도록 도와주는, 미디어위키에서도 사용할 수 있는 유명한 웹 가속기입니다.

Varnish는 보통 아파치/nginx 앞단에 reverse proxy 방식으로 붙여두어 웹서버의 동적/정적 페이지를 캐싱하여 서비스를 하게 되는 방식을 주로 취하게 되는데, Varnish의 성능이 어느 정도냐면 정적 페이지에 대해서는 아파치 속도를 능가하며, php와 같은 동적 웹페이지에 대해서는 아파치는 물론 nginx보다 빠른 속도 및 낮은 시스템 로드로 웹 서비스를 가능하게 해줍니다. php같은 동적 웹페이지에 대해서 최소 10배 최대 1000배의 속도를 내게끔 해주게 됩니다.

다음은 약 1.4 kB 크기의 정적 페이지에 대한 로컬 서버에서의 간단한 아파치벤치 성능 테스트입니다.

 데몬 구성

 속도 (ab -n 10000 -c 10)

 아파치

 ~4000 RPS (편차가 심한 편)

 nginx

 ~8000 RPS

 Varnish + 아파치

 ~5000 RPS

 데몬 구성

 속도 (ab -n 10000 -c 3)

 아파치

 ~4000 RPS (편차가 심한 편)

 nginx

 ~8000 RPS

 Varnish + 아파치

 ~7000 RPS

그런데 동적 웹페이지에선 그 성능 향상이 가히 비약적입니다. (Varnish가 적용된 모니위키의 경우)

 데몬 구성

 속도 (ab -n 10000 -c 3)

 아파치

 ~300 RPS

 Varnish + 아파치

  ~7000 RPS

즉 Varnish를 통해 캐싱을 하게 되면 정적 웹페이지와 거의 같은 속도로 서비스를 하게 된다는 것을 확인할 수 있습니다.

모니위키에 Varnish 지원 추가

모니위키는 이러한 웹 가속기를 통해서 성능이 최대한 발휘될 수 있도록 그간 꾸준하게 작업을 하였습니다. HTTP Conditional GET를 지원하기 위해서 Last-Modified 헤더 및 ETag를 지원하고 있으며, 동적으로 변화할 수 있는 매크로를 자바스크립트를 통해 부분 업데이트가 가능하도록 하는 등등의 작업이 이루어졌습니다. (ESI방식은 mod_disk_cache를 사용할 수 없으므로 모두 사용 가능하도록 자바스크립트/ajax 방식을 사용)

일부 위키 매크로는 동적으로 컨텐츠가 변동이 있으나, 상당수의 매크로는 정적인 컨텐츠였기 때문에 이 둘을 구분하여 처리하였으며, 페이지가 추가되거나 삭제되면 ETag 및 Last-Modified가 변경되도록 처리하여 Varnish에서 특별히 purge를 통하지 않아도 자동으로 캐시가 업데이트 되도록 하였습니다.

또한 Cache-Control을 익명과 로그인사용자를 구별하였고, 위키 문서를 읽는 액션(action=show 혹은 action 쿼리 없는 경우)에 대해서 프록시 캐시 옵션 s-maxage=1을 붙여주었습니다.

마지막으로 익명 사용자의 경우에는 Cookie를 제한적으로 사용하여 Varnish에 의해 캐싱이 가능하도록 고쳤습니다.

이러한 일련의 변경을 mod_disk_cache를 통해서 그 가능성을 확인하였고 Varnish를 성공적으로 지원할 수 있게 되었습니다.

속도 비교

다음은 개략적인 속도 비교입니다.

- 모니위키 (캐싱 없음) - 50 RPS 수준

- 모니위키 + 내장 캐싱 사용 - 200 RPS

- 모니위키 + mod_disk_cache - 1500 ~ 2000 RPS (아파치 정적 웹페이지 서비스 속도에 준하는 속도)

- 모니위키 + Varnish - 5000 ~ 7000 RPS (Varnish의 정적 웹페이지 서비스 속도에 준하는 속도)



참고사이트

https://www.varnish-cache.org/

http://en.wikipedia.org/wiki/Varnish_(software)

http://phk.freebsd.dk/

by dumpcookie 2013. 11. 23. 20:00

앞에서 설명한 내용은 미디어위키 설정에 관련된 검색을 해보면 상당히 잘 설명되어 있는 부분입니다. 간단한 설정을 통해서 어느정도 미디어위키의 속도를 손쉽게 향상시킬 수 있지요.

그러나 이렇게 설정을 하더라도 성능이 원하는 만큼 손쉽게 향상이 되지 않고 있습니다. 그래서 미디어위키의 프로파일링 옵션을 켜고 어떤 부분이 느린지 살펴보았습니다.

미디어위키는 프로파일링 방식을 자체 내장하고 있는데 다음과 같은 설정 변경을 통해서 프로파일링을 활성화시킬 수 있습니다.

1. StartProfiler.php 파일을 만들어 최상위 디렉토리(index.php가 있는 디렉토리)에 넣는다.

2. StartProfiler.php에 다음과 같은 내용을 넣는다.

$wgProfiler['class'] = 'ProfilerSimpleText'; $wgProfiling = true; $wgDebugComments = true;

이렇게 한 후에 임의의 문서를 열고나서 문서의 html 소스를 보면 다음과 같은 내용을 볼 수 있습니다. 다음은 위키의 최근 바뀜(RecentChanges)문서를 본 것입니다.

최근 바뀜(RecentChanges) 문서 말고도 몇가지 문서를 열어보면 "MediaWiki::doJobs-exec"라는 항목이 공통적으로 상당한 시간을 잡아먹고 있다는 사실을 알 수 있습니다.

이것을 역추적해서 살펴보니 위키 메인함수는 MediaWiki::run()에 의해 호출되며

        /**
         * Run the current MediaWiki instance
         * index.php just calls this
         */
        public function run() {
                try {
                        $this->checkMaxLag();
                        $this->main(); // 메인 루틴
                        $this->restInPeace(); // 여기서 doJobs()가 호출되는데 이것이 매우 느림.
                } catch ( Exception $e ) {
                        MWExceptionHandler::handle( $e );
                }
        }

MediaWiki::restInPeace()에서 호출되고있는 doJobs() 메쏘드가 매우 느린 문제가 있었습니다.

단지 doJobs()가 실행되고 있는 줄을 커멘트 아웃 시키니 전체적인 성능이 ~70 RPS 수준으로, 기본 ~6 RPS 정도로 형편없이 느리던 속도가 10배 이상의 향상이 있는 것이였습니다.

        /**
         * Ends this task peacefully
         */
        public function restInPeace() {
                // Do any deferred jobs
                DeferredUpdates::doUpdates( 'commit' );

                // Execute a job from the queue
                $this->doJobs(); // 매우 느림
....

        /**
         * Do a job from the job queue
         */
        private function doJobs() {
                global $wgJobRunRate, $wgPhpCli, $IP;

                if ( $wgJobRunRate <= 0 || wfReadOnly() ) {
                        return;
                }
...
                if ( !wfShellExecDisabled() && is_executable( $wgPhpCli ) ) {
                        // Start a background process to run some of the jobs.
                        // This will be asynchronous on *nix though not on Windows.
                        wfProfileIn( __METHOD__ . '-exec' );
                        $retVal = 1;
                        $cmd = wfShellWikiCmd( "$IP/maintenance/runJobs.php", array( '--maxjobs', $n ) );
                        wfShellExec( "$cmd &", $retVal ); // 이 부분.
                        wfProfileOut( __METHOD__ . '-exec' );
                } else {
...


위에서 볼 수 있는 것처럼 doJobs()는 문서를 출력하고(main()이 실행된 다음) 그 이후에 실행되는데, shell을 사용하여 maintenance/runJobs.php 스크립트를 실행시키고 있었습니다. 이러니 아파치벤치같은 속도측정 방식으로는 형편없는 속도가 나올 수 밖에 없는 것이지요.

그래서 이 옵션에 대한 문서를 검색해보니 다음과 같은 문서를 찾을 수 있었습니다.

http://www.mediawiki.org/wiki/Manual:$wgJobRunRate

이 문서 내용에 의하면 $wgJobRunRate = 0.01; 옵션을 추가하여 doJobs() 실행회수를 줄여주면 성능향상을 기대할 수 있게 되며, 문서에 나와있는데로 $wgJobRunRate 값을 0.01로 조정해보니 ~60 RPS 수준으로 속도가 향상되었습니다.

주의해야 할 점은 $wgJobRunRate를 조절하고 난 후에는 job queue가 너무 많아지는 것을 방지하기 위해서 링크된 문서에서 설명되어있는 것처럼 cron을 통해서 주기적으로 doJobs()를 실행하도록 설정해야 한다는 점입니다. 단, 개인 위키로 사용할 경우에는 아래 설정을 굳이 하지 않아도 되리라 예상할 수 있습니다. 자세한 내용은 아래 문서를 참조하시기 바랍니다.

http://www.mediawiki.org/wiki/Manual:Job_queue


정리하면 다음과 같은 옵션을 LocalSettings.php에 추가하면 약 10배의 속도 향상을 얻을 수 있습니다.

$wgMainCacheType = CACHE_ACCEL;
$wgCacheDirectory = "$IP/cache";
$wgFileCacheDirectory="$IP/cache";

$wgEnableSidebarCache=true;
$wgUseFileCache=true;

$wgDisableCounters = true;

$wgJobRunRate = 0.01;

이 설정으로 변경하고 난 후에 앞에서 잠깐 설명했던 미디어위키 + mod_disk_cache 패치를 적용하면 static html 속도에 준하는 비약적인 성능 향상을 제대로 얻을 수 있었습니다. 즉 로컬로 ~6PRS 수준이던 속도가 캐시 관련 설정 + mod_disk_cache 패치를 하면 ~2000 RPS ~ 3000 RPS에 달하는 static html의 속도를 얻을 수 있게 됩니다~!

참고사이트

http://www.mediawiki.org/wiki/Manual:Performance_tuning

by dumpcookie 2013. 11. 19. 17:49

미디어위키를 처음 설치하고 난 후에 기본 설정을 그대로 두는 경우에는 미디어위키의 느린 성능에 많은 실망을 할 수도 있습니다. 이것은 미디어위키의 기본 옵션은 성능 향상을 위한 설정이 모두 비활성화 되어있는 상태라서 그렇습니다. 약간 손이 가기는 하지만 캐싱에 관련된 기능을 몇가지 활성화시키면 미디어 위키의 성능을 어느정도 향상시킬 수 있습니다.

기본 캐시 타입을 APC로

미디어위키를 처음 설치하면 기본 캐시 타입이 CACHE_NONE이 됩니다. 기본 캐시 타입을 APC 캐시로 설정해주려면 다음과 같은 내용을 LocalSettings.php에 넣어줍니다.

$wgMainCacheType = CACHE_ACCEL;

내장 캐시를 켜라

미디어위키에서는 몇가지 캐싱을 통해서 그 속도를 좀 더 끌어올릴 수 있습니다. 첫번째는 기본 캐시 옵션을 켜는 것이고, 두번째로는 파일 캐시 옵션(모든 문서의 랜더링 결과를 파일로 저장)을 활성화 시키는 것입니다. 이 두가지 캐시 옵션을 활성화시키면 전체적으로 두배가까운 성능으로 끌어올릴 수 있습니다.

캐시 디렉토리 설정

LocalSettings.php 세팅 파일을 열어서 다음 줄을 추가해주면 cache 디렉토리가 설정이 되며

$wgCacheDirectory = "$IP/cache";

미디어위키가 설치된 최상위 디렉토리($IP) 아래에 있는 cache 디렉토리의 퍼미션을 조정하는 최소 설정만으로도 약간의 성능향상을 얻을 수 있습니다. 이 경우 지역화 메시지가 자동으로 캐싱이 되어 cache 디렉토리 아래에 저장되게 됩니다.

주의할 점은 cache 디렉토리의 퍼미션을 적절히 맞춰주어야 한다는 것입니다. 예를 들어 chmod 777 cache 혹은 chown apache.apache cache 등등으로 적절히 조절해야만 캐시 파일이 cache 디렉토리 아래에 저장되게끔 됩니다.

파일 캐시 설정

LocalSettings.php 세팅 파일을 열어서 다음 줄을 추가해주면 위키 문서의 랜더링 결과를 파일로 저장하여 캐싱하게됩니다.

$wgUseFileCache=true;
$wgFileCacheDirectory="$IP/cache";

그밖에 추가적인 성능향상 옵션

다음의 추가적인 옵션을 통해 약간의 성능향상을 끌어올릴 수 있습니다.

$wgDisableCounters = true; // 카운터 비활성화 (약간의 성능 향상)
$wgEnableSidebarCache = true; // 사이드바 캐시 켜기

여기까지 설명한 캐시 옵션을 모두 활성화시키면 기존에 아파치벤치(ab -kt3 http://localhost/mediawiki/index.php/Main_Page) 속도측정으로 ~5RPS 정도 되던 속도가 ~10 RPS 정도로 성능이 향상이 됩니다.

여기까지의 내용은 사실 미디어위키 속도 향상에 관심을 가졌던 분들이라면 검색을 통해서 이미 이 옵션을 켜고 사용중이실 것입니다. 그러나 이것만 가지고는 예상한 것 만큼의 성능향상이 나오지 않아서 미디어위키에서 자체 내장하고 있는 프로파일링을 활성화 시키고 원인을 살펴보았습니다.

계속되는 내용은 다음 문서에서 이어집니다.

참고 사이트

http://www.mediawiki.org/wiki/Manual:Performance_tuning

by dumpcookie 2013. 11. 19. 17:25
| 1 2 3 |