20131110

PHP 5.5 OPcache 도입 후기

회사 서버 PHP 5.4.17 → 5.5.5 올리면서 APC 떼고 OPcache 붙인 기록. 일단 메모.

배경부터. 5.5에선 Zend OPcache가 번들로 들어오기 시작해서 APC가 사실상 단종 수순. APC 소스는 5.5 대응을 멈췄고, apcu 라는 user-cache 전용 포크가 따로 나왔다. 지금까지는 apc.stat=0 + user cache로 섞어 썼는데 앞으로는 opcode 쪽은 OPcache, user cache는 apcu 이렇게 분리해야 함.

php.ini 세팅은 일단 이렇게.

zend_extension=opcache.so
opcache.memory_consumption=128
opcache.interned_strings_buffer=8
opcache.max_accelerated_files=4000
opcache.validate_timestamps=1
opcache.revalidate_freq=60
opcache.fast_shutdown=1
opcache.enable_cli=0
opcache.save_comments=1

max_accelerated_files=4000 — 컴포저 깔린 프로젝트는 파일 수가 금방 늘어남. find . -name "*.php" | wc -l 찍어보니 우리 레포 기준 2,900 정도 나와서 여유있게 4000. 이 값이 실제 파일 수보다 작으면 LRU로 밀려나면서 컴파일 다시 타서 TTFB 튀는 순간이 있다. opcache status에 max_cached_keys 소진되면 로그 남기는게 정석인데 일단은 수동으로 가끔 봄.

interned_strings_buffer=8MB — 문자열 인터닝(동일 리터럴 재사용) 풀 크기. 기본 4MB인데 심포니/라라벨 계열은 로케일/라우트/설정 문자열이 많아서 4로는 부족함. 이게 꽉 차면 opcache 전체 동작이 이상해지는 버그 리포트가 몇 건 있어서 약간 여유있게.

revalidate_freq=60 — 처음엔 2초로 뒀다. 근데 배포 직후 첫 히트에서 파일 stat 다 부르는게 아까워서 60초로 늘림. 대신 배포 스크립트 끝에 curl http://127.0.0.1/opcache_reset.php 찔러서 강제 flush. reset 트리거용 엔드포인트는 localhost 바인딩만 허용하도록 listen 막아둠. FPM reload 대안은 graceful하긴 한데 worker 죽을 때 마침 긴 쿼리 물려있는 요청이 애매하게 끊겨서 싫었다.

fast_shutdown=1 — 요청 끝날 때 PHP가 일반적인 destructor 해제 순서 대신 Zend MM 블록을 한번에 날리는 모드. 마이크로 벤치에서 request/sec 5~8% 정도 이득 봄. 단점이라면 shutdown 중 에러가 잘 안 보이게 되는데, 개발 서버만 0 두고 프로덕션은 1로.

성능 체감은 APC 시절 대비 비슷하거나 살짝 나음. ab로 대충 재보면 홈 페이지 requests/sec 340 → 365 정도. 극적 차이는 아님. 대신 메모리 조각화가 훨씬 덜하다. APC는 user cache랑 opcode cache가 같은 공간 공유해서 fragmentation이 40% 넘게 튀던게, 분리하고 나니 OPcache 쪽은 거의 2% 이하 유지. opcache_get_status() 찍어보면 wasted_memory가 눈에 띄게 낮다.

memory_consumption 128MB로 잡았는데 실제 점유는 48~52MB 수준. 일단 여유있게 두고 나중에 줄일 예정. 나중에 64MB로 줄이면 interned strings랑 충돌해서 warning 나올 수 있으니 그때 다시 볼것.

남은 숙제: apcu 별도 컴파일해서 user cache 복구 + opcache.huge_code_pages 실험. 얘는 Transparent Huge Pages 기반이라 커널쪽 설정이 엮여서 좀 귀찮긴 한데 TLB 미스 줄여서 2~3% 더 나온다는 얘기가 있음. 담주에.

댓글 없음: