PyPy 사용하기 (with uWSGI on CentOS 6)

in English (Google Translate)

왜 PyPy가 필요한가요?

최근에 voeasy을 업데이트를 했습니다. 나만의 투자 기준을 만들고, 투자 기준을 통과한 종목을 보여주는 허들이 개편의 핵심인데, 각각의 주식 종목을 설정된 투자 기준에 맞추어 허들을 계산하는 것은 시간이 많이 필요한 작업입니다.

대한민국 상장 종목에 현재 최대 13개까지 적용할 수 있는 허들 기준을 모두 적용하면, 전체 연산작업은 단순 계산해도 2,000 (종목) x 13 (허들) = 26,000 (허들 계산)을 수행해만 합니다. 사용자가 기준을 정하면 즉시 결과를 볼 수 있도록 만들려고 했지만, 현재는 번개같은 처리를 하지 못하기 때문에 분단위로 작업을 기다려야 합니다. 허들 계산을 번개같이 처리하는 것은 또다른 도전과제입니다.

도전과제는 생각과 시간이 필요하기 때문에, 도전을 하기 전에 생각없이 허들 계산을 빠르게 할 수 있는 PyPy를 적용을 고려하기로 하였습니다.1 허들 계산이 DB bound 작업이지만 개발환경2에서 테스트해보면 코드 변경 없이도 약 13% 정도 빨라졌기 때문에 조금이라도 더 빨라지기 위해서 한번 해볼만한 작업이라 간단히 생각했습니다.

CentOS! uWSGI!! libpypy-c.so!!!

개발환경2인 MacOS에서 PyPy를 설치하는 것은 너무나 쉬웠습니다. OSX에서는 별다른 어려움 없이 brew install을 사용하면 PyPy를 동작할 수 있는 환경으로 만들 수 있습니다. 너무도 쉬웠었기 때문에 실제 운영환경인 CentOS에서도 쉽게 설치할 수 있을 것이라고 착각하고 개발을 진행했습니다.

그러나 CentOS는 달랐습니다!

실제 운영환경에 적용할 시점이 되어 CentOS에 PyPy를 설치하려니 다음과 같은 장애물이 있었습니다.

  1. yum install pypy (X) – 이게 가능했으면 이 글을 쓰지도 않았겠지?
  2. PyPy Official Binary for CentOS (X)3Ubuntu만 지원하는 PyPy Binary Download
  3. uWSGI에서 PyPy를 사용하려면 shared library build 된 PyPy 필요. – Unfortunately you still need to build/translate libpypy-c by yourself

쉽게 하려면, portable linux binary3를 사용하고 libpypyb-c.so는 uWSGI에서 제공하는 20130524 버전을 사용하는 방법도 가능할 수 있습니다. 그러나 Python 2.74을 build 할 때 binary 불일치로 segmantation fault의 경험이 있기 때문에, 구성이 확실하지 않은 binary를 사용하는 것에 대한 부담이 있습니다. 위의 조합으로 가능한지는 한번 테스트를 해봐야 겠습니다.

결국, uWSGI 에서 필요한 libpypy-c.so 때문에 PyPy를 source build 하기로 했습니다. pypy.org 에서 PyPy official binary for CentOS 를 언젠가 지원한다고 해도, shared library build를 포함하지 않으면 uWSGI를 사용하기 위해서는 직접 빌드할 수 밖에 없을 것입니다.

PyPy 빌드하기

빌드 전 준비 – 5GB memory 필요 (4Core, CPU 비례 증가)

PyPy 소스 빌드는 정말 오래 걸립니다.5

CentOS 66에서 PyPy를 소스 빌드하기 위해서는 가장 중요한 것은 메모리입니다. pypy building from source 에서 설명하고 있지만, 64bit 환경에서 필요한 ram은 4GB 이상입니다. 그런데 메모리는 option5을 주지 않으면 CPU core에 비례해서 늘어납니다.7 cc 프로세스 하나가 1GB이상을 사용하기 때문에 core가 많은 환경이라면 램이 충분하다고 생각하더라도 옵션으로 job 갯수를 조정해야 합니다. 4Core 환경에서 5GB ram/1 GB swap으로 compilation 가능합니다.

빌드 전 준비 – PyPy pre-built binary

cd /tmp
wget https://bitbucket.org/squeaky/portable-pypy/downloads/pypy-2.2.1-linux_x86_64-portable.tar.bz2
tar xf pypy-2.2.1-linux_x86_64-portable.tar.bz2
mv pypy-2.2.1-linux_x64-portable pypy
  • pypy 빌드할 때, CentOS에 기본적으로 설치된 python 2.6을 사용하면 시간이 더 오래 걸리기 때문에, portable linux binary3 을 /tmp/pypy에 설치

빌드 전 준비 – library dependency

yum install gcc make pkgconfig libffi-devel expat-devel
zlib-devel bzip2-devel sqlite-devel ncurses-devel openssl-devel
  • CentOS 6.5 minimal install 상태6

빌드

wget https://bitbucket.org/pypy/pypy/downloads/pypy-2.2.1-src.tar.bz2
tar xf pypy-2.2.1-src.tar.bz2
cd pypy-2.2.1-src/pypy/goal
/tmp/pypy/bin/pypy ../../rpython/bin/rpython --translation-verbose
--shared --gcrootfinder=shadowstack
--opt=jit targetpypystandalone
  • /tmp/pypy 에 설치된 pre-built된 pypy를 사용하여 빌드
  • --shared: pypy shared library인 libpypy-c.so를 빌드하기 위한 옵션
  • --gcrootfinder=shadowstack: asmgcc대신 shadowstack을 사용한다는 옵션
  • --shared --gcrootfinder=shadowstack: uWSGI를 사용하기 위한 옵션. uWSGI를 사용하지 않을 것이라면 제외
  • --opt=jit targetpypystandalone: JIT를 사용한다는 일반적인 PyPy build 옵션
  • --translation-verbose: 이 옵션을 빼면 Enjoy Mandelbrot 🙂를 할 수 있을 텐데, 혹시 빌드 실패가 일어날 경우, 어떤 문제인지 확인하려면 필요

엔터치면 느긋하게 1시간 가량 놀다오면 됩니다~ 다만 중간에 에러가 나면 처음부터 다시 시작해야 하는데, 그러면 또 놀 수 있습니다. 🙂 (처음부터 시작하지말고, 실패한 이후부터 빌드하는 옵션은 없나요?)

패키징

mkdir -p /opt/pypy/bin
mv ./libpypy-c.so /opt/pypy/bin
ln -s /opt/pypy/bin/libpypy-c.so /usr/lib64/libpypy-c.so
cd ../../pypy/tool/release
../../goal/pypy-c package.py --without-tk ../../.. pypy-libpypy-c_centos65_x64
cp -r /tmp/usesession-release-2.2.x-2/build/ /opt/pypy
  • mv ./libpypy-c.so /opt/pypy/bin: 성공적으로 빌드된다면 pypy/goal/libpypy-c.so만들어지는데, libpypy-c.so를 /opt/pypy/bin으로 이동. libpypy-c.so는 패키지에 포함되지 않기때문에 따로 설치해야 함.
  • ln -s /opt/pypy/bin/libpypy-c.so /usr/lib64/libpypy-c.so: 뒤에서 직접 shared 빌드한 pypy-c가 동작하는지 확인하기 위해서 packaging을 빌드한 pypy로 진행하는데, shared build기 때문에 libpypy-c.so의 위치를 지정 (--shared 로 빌드한 경우에만 해당)
  • ../../goal/pypy-c package.py --without-tk: 직접 빌드한 pypy-c로 패키징을 하는데, tcl/tk 관련 사항이 필요없기 때문에 제외
  • /tmp/usesession-release-2.2.x-2/build/: 패키징이 완료되면, 어떤 곳에 패키징되어 있는지 알려주는데, x는 실행시마다 다름
  • /opt/pypy 에 PyPy를 설치한다고 가정

위의 과정을 거치면 패키징된 PyPy JIT를 빌드할 수 있을 것입니다.
필요한 dependency library가 많고, 개별 설치 환경마다 필요한 옵션이 다를 수 있는데 빌드옵션을 살펴보면서 진행하면 빌드할 수 있을 것입니다. (말은 쉽지. 아 힘들다…)

uWSGI 에서 PyPy 사용하기

uWSGI-PyPy plugin 빌드

cd /tmp
wget http://projects.unbit.it/downloads/uwsgi-2.0.tar.gz
tar xf uwsgi-2.0.tar.gz
cd uwsi-2.0
/opt/pypy/bin/pypy uwsgiconfig.py --build pypy
  • --build pypy: 일반적으로 --build만 사용해서 uWSGI를 인스톨 했는데, 이렇게하면 dependency가 많아서 직접 빌드하기 어려움 (CentOS 6.5 minimal install). 그래서 --build pypy 옵션으로 PyPy만 사용할 수 있는 uWSGI 빌드. uwsgi-2.0/buildconf를 참고

변경되는 .ini option

uWSGI PyPy-plugin 을 적용하면 uWSGI setup 을 변경해야 하는 사항이 있는데, voeasy는 다음 사항을 변경해야 했습니다. pypy-plugin option 참고
pypy-home: 빌드한 pypy binary 위치를 지정. 위의 설치 과정이라면 /opt/pypy가 되고, virtualenv를 적용한 경우, 해당 env 환경을 지정.
pypy-wsgi-file: 기존에는 module=django.core.handlers.wsgi:WSGIHandler()을 사용해서 wsgi.py를 별도로 만들지 않고 로딩했는데, pypy-plugin 에서는 기존의 module 로딩방법으로는 로딩되지 못함. pypy-wsgimodule과 동일한 역할이라 생각되는데, 단순히 변경만 해서는 동작하지 않음. (더 시도해보면 방법이 있을 수도 있는데, pypy-wsgi-file로 동작해서 더 이상 진행하지 않음)
The server will report “dynamic mode” on startup even if the app has been successfully loaded. uWSGI에서도 언급한 버그인데, app이 성공적으로 로딩되어도 로그에는 dynamic mode로 표시되니 log만 보고 겁먹지 말고 실제로 동작하는지 직접 확인해 볼 것. (제대로 동작하고 있었는데, 로그에서 dynamic mode로 표시되어 한참을 더 삽질…)

Tip: PyPy 적용 후 관찰해야 하는 사항

위의 과정을 거치면 CentOS/PyPy/uWSGI 에서 서비스를 동작시킬 수 있는데, PyPy compatibility 이슈는 계속 신경써서 살펴봐야 합니다. voeasy가 사용중인 라이브러리 중에서 아래의 널리 사용되는 것들은 역시나 별 문제 없었습니다.

django
mysql-python
python-memcached
Pillow

다만 lxml-3.3.0을 적용하면 segmentation fault8 가 발생했는데, uWSGI가 crash 할 때마다, 계속 reloading 해서 로그를 살펴보지 않으면 문제가 있는지 조차 파악할 수 없었습니다. 이런 점이 실제 운영 환경에서 uWSGI의 강점인데, 이와 같은 이유로 C를 사용하는 라이브러리의 python wrapper 를 사용하는 경우, PyPy 적용 후 segmentation fault 등의 변경으로 인한 문제가 발생하는지 계속 지켜볼 필요가 있습니다.

PyPy를 적용하면서 시행착오를 많이 했는데, 실제 PyPy 적용을 하고 싶은 분들에게 저희의 시행착오가 작은 도움이 되었으면 합니다.


PyPy 어디에 사용하냐고요?
여행의 시작 – Arrangy.com 에 사용합니다!
개발에 지친 몸 잠시 들러 쉬어가세요;;;

여행의 시작 – Arrangy.com | Santorini, Greece

 

 


Fez

모로코 Fez 여행 계획,  Arrangy

왜 Arrangy 를 사용해야 할까요?  ‘여행의 시작 – Arrangy’ 가 궁금하지 않으세요? (클릭)


  1. voeasy는 CentOS/Nginx/uWSGI/Python/MySQl 환경에서 운영되고 있습니다. 
  2. Intel i7/OSX Mavericks/PyPy 2.2.1 (BREW install) 
  3. PyPy binrary for portable linux는 있는데, shared library build 가 아님 (libpypy-c.so 없음) 
  4. CentOS, yum install Python 2.6 only. 
  5. i7-980x 3.33Ghz 4Core 사용/6GB Ram/SSD RAID 10, 60분 소요 – 동일 환경에서 램만 3GB로 조정하여 시도한 경우, 4시간이 지나도 완료되지 못함. 
  6. CentOS 5 에서도 시도했지만, dependecy를 맞추기 어려워서 결국 포기. yum 으로 설치되지 않는 라이브러리가 있고, dependency도 소스 빌드 시도하였으나 처리해야하는 것이 많음. 그래서 CentOS 5를 사용하던 일부 서버는 CentOS 6로 동반 업그레이드. 
  7. 5GB/4core 환경에서 몇 번의 시도 후, 더 빠르게 작업하기 위해서 8GB RAM의 6core 환경에서 build를 시도했습니다. 8GB/6core 환경에서 빌드과정에서 로그없이 crash 를 몇 번 경험하고 터득한 것은 후반 컴파일 과정에서 CPU core에 비례하여 cc를 돌리는데 이 과정에서 램/스왑을 모두 사용하면 크래쉬합니다. 
  8. lxml-3.3.1pre에서도 해결되지 않았는데, 글을 쓸 때 살펴보니 ‘lxml-3.3.1’이 2월12일 버전으로 변경. 해당 사항 변경되었는지는 테스트 필요. 
Advertisements

답글 남기기

아래 항목을 채우거나 오른쪽 아이콘 중 하나를 클릭하여 로그 인 하세요:

WordPress.com 로고

WordPress.com의 계정을 사용하여 댓글을 남깁니다. 로그아웃 / 변경 )

Twitter 사진

Twitter의 계정을 사용하여 댓글을 남깁니다. 로그아웃 / 변경 )

Facebook 사진

Facebook의 계정을 사용하여 댓글을 남깁니다. 로그아웃 / 변경 )

Google+ photo

Google+의 계정을 사용하여 댓글을 남깁니다. 로그아웃 / 변경 )

%s에 연결하는 중