Nginx deploy! (+uWSGI +flask)

Nginx deploy! (+uWSGI +flask)

uWSGI

uWSGI
Flask(uWSGI)를 nginx에 연결하기

uWSGI를 먼저 설치하고 실행한다.

$ pip install uwsgi

나는 ini파일을 만들어 실행했다. python path 문제 때문에 그래야만 했다. virtualenv를 안 써서 그런지 자꾸 python path를 못 찾아서, sys.path를 치면 나오는 path 들을 죄다 갖다 박아줬다. 다른 실행 옵션들은 위 링크를 참조하자.

[uwsgi]
chdir=/home/khanrc/tworoom/flask
chmod-socket=666
callable=app
module=app
socket=/tmp/uwsgi.sock
pythonpath=/usr/lib/python2.7
pythonpath=/usr/lib/python2.7/plat-linux2
pythonpath=/usr/lib/python2.7/lib-tk
pythonpath=/usr/lib/python2.7/lib-old
pythonpath=/usr/lib/python2.7/lib-dynload
pythonpath=/usr/local/lib/python2.7/dist-packages
pythonpath=/usr/lib/python2.7/dist-packages
pythonpath=/usr/lib/python2.7/dist-packages/PIL
pythonpath=/usr/lib/python2.7/dist-packages/gtk-2.0
pythonpath=/usr/lib/pymodules/python2.7

간단하게 적었지만 이 과정에서 엄청 고생했다. 이제, 실행해보자.

$ uwsgi uwsgi.ini &

당연한 얘기지만 &를 빼면 foreground로 실행된다.

Nginx

설치부터 하자.

$ apt-get install nginx-full

설치하고 나면 설정파일을 수정해서 uwsgi와 연결해 주자.

server {
    listen 80;
    server_name ip_address;

    location / {
        try_files $uri @app;
    }

    location @app {
        include uwsgi_params;
        uwsgi_pass unix:/tmp/uwsgi.sock;
    }
}

이제 nginx와 uwsgi, flask가 전부 연결되었다.

$ /etc/init.d/nginx restart

Done!

Problem

어? 다 된 줄 알았는데 request에 header가 없다. -_-;;
뭔가 하고 한참 삽질했는데, 보니까 우리 헤더 이름이 user_token인데 이게 규약에 어긋나는 것 같다. 자세한 건 여기를 참조하자: List of HTTP header fields

flask를 그냥 python app.py로 실행하면 자체 서버로 실행이 되는데, 이러면 이 자체 서버가 헤더를 바꿔주는 것 같다. user_token으로 보내도 User-Token으로 들어온다. 그런데 nginx는 그렇지 않다. user_token으로 보내면 그냥 버려버린다 -_-;;

그래서 user_token을 User-Token으로 변경하는 것으로 마무리했다.

'Server(Back-end)' 카테고리의 다른 글

Nginx deploy! (+uWSGI +flask)  (0) 2014.11.15
logstash (2) - with logrotate  (0) 2014.11.07
cron, crontab의 개념  (0) 2014.11.06
logstash with python  (0) 2014.11.05
docker global hack day에 다녀와서  (0) 2014.11.02
Docker & Vagrant  (0) 2014.11.02

decorator (2) - extension

decorator (2) - extension

확장이라고 하기에는 별 거 없지만, 실제로 적용하면서 알게된 몇가지 더 정리해 보았다.
@wraps는 항상 적용해 주는 게 좋은 것 같고, 그 이외에는 적용하면서 알아가면 되는 부분이지 미리 학습할 필요까진 없어 보인다.

@wraps

플라스크에 데코레이터를 적용하려고 찾아봤더니, @wraps라는 게 보인다. 이게 뭐지?

from functools import wraps

def without_wraps(func):
    def __wrapper(*args, **kwargs):
        return func(*args, **kwargs)
    return __wrapper

def with_wraps(func):
    @wraps(func)
    def __wrapper(*args, **kwargs):
        return func(*args, **kwargs)
    return __wrapper


@without_wraps
def my_func_a():
    """Here is my_func_a doc string text."""
    pass

@with_wraps
def my_func_b():
    """Here is my_func_b doc string text."""
    pass

'''
# Below are the results without using @wraps decorator
print my_func_a.__doc__
>>> None
print my_func_a.__name__
>>> __wrapper

# Below are the results with using @wraps decorator
print my_func_b.__doc__
>>> Here is my_func_b doc string text.
print my_func_b.__name__
>>> my_func_b
'''

위 코드를 찬찬히 읽어 보면 뭔지 알 수 있다. 기존 함수를 데코레이터로 래핑하게 되면, 데코레이트된 함수의 속성을 요청하면 기존 함수의 속성이 나오는 게 아니라 데코레이트된 래퍼의 속성이 나오는 것이다. @wraps를 사용하면 이 문제를 해결할 수 있다.

pass parameter on func

래퍼인 데코레이터에서 감싸진 원 함수로 파라메터를 넘기고 싶다면?
kwargs로 넘기면 된다.

from inspect import getargspec

# decorator.
def usertoken_required(func):
    @wraps(func)
    def decorated(*args, **kwargs):
        user_token = request.headers.get('user_token')
        cur.execute("""SELECT * FROM USER WHERE user_token='%s'""" % user_token)
        user = cur.fetchone()

        if user is None:
            return Response(response="user_token is wrong!", 
                            status=401)

        argspec = getargspec(func)
        if argspec[2] is not None: # kwargs를 받는 함수에게만 전달
            kwargs['user_info'] = user

        return func(*args, **kwargs)

    return decorated


@app.route('/')
@usertoken_required
def hello_world(**kwargs):
    return 'Hello,' + request.headers.get('user_token') + ' user:' + str(kwargs['user_info'])

usertoken을 검사하는 데코레이터를 만들었는데, 데코레이터에서 찾은 유저 정보를 버리기가 아까워 kwargs에 담아 넘겼다. 감싸지는 원 함수인 func에서 kwargs를 받지 않는 경우를 대비해서, getargspec으로 args의 스펙을 받아 kwargs를 받는 경우에만 user_info를 넘기도록 했다.

more in flask: return

플라스크에서 데코레이터를 찾아보면 적절한 예제 페이지가 하나 나온다.

from functools import wraps
from flask import g, request, redirect, url_for

def login_required(f):
    @wraps(f)
    def decorated_function(*args, **kwargs):
        if g.user is None:
            return redirect(url_for('login', next=request.url))
        return f(*args, **kwargs)
    return decorated_function


@app.route('/secret_page')
@login_required
def secret_page():
    pass

이 외에도 링크에 들어가 보면 실제로 데코레이터를 어떻게 사용하는지 적절한 예제들이 잘 나와 있다. 데코레이터를 쓸 생각이면 한번 살펴보도록 하자.

위 예제에서는 return 이 있는 함수를 데코레이트 하는 방법을 볼 수 있다.

decorator with parameter

마찬가지로 위 플라스크 페이지에서 가져온 예제다. flask@app.route처럼, 데코레이터에 파라메터를 넘겨서 처리하고 싶다면?

from functools import wraps
from flask import request

def templated(template=None):
    def decorator(f):
        @wraps(f)
        def decorated_function(*args, **kwargs):
            template_name = template
            if template_name is None:
                template_name = request.endpoint \
                    .replace('.', '/') + '.html'
            ctx = f(*args, **kwargs)
            if ctx is None:
                ctx = {}
            elif not isinstance(ctx, dict):
                return ctx
            return render_template(template_name, **ctx)
        return decorated_function
    return decorator

@app.route('/')
def index():
    return render_template('index.html', value=42)

@app.route('/')
@templated('index.html')
def index():
    return dict(value=42)

@app.route('/')
@templated()
def index():
    return dict(value=42)

이렇게 한번 더 감싸줌으로써 처리할 수 있다.

참고

왜 파이썬 데코레이터를 만들때, @wraps어노테이션을 쓰는 것을 권장하는 걸까?
Flask: View Decorators

'Python' 카테고리의 다른 글

contextlib  (0) 2014.10.12
Errors and Exceptions  (0) 2014.10.09
decorator (2) - extension  (0) 2014.10.06
decorator와 closure  (2) 2014.10.04
String option - u와 r  (0) 2014.09.21
한글 in the dictionary (feat. pretty)  (0) 2014.09.17

WSGI로 보는 웹 서버의 개념

Web server, WSGI, Middleware, Application

예전에 웹 서버와 WAS, WSGI를 공부하면서 블로그에 포스팅을 몇 개 한 적이 있는데, 이제서야 개념정리가 좀 되어 한 글에 정리하기로 했다.

웹 서버

khanrc: 웹서버, WAS, CGI
웹 서버는 정적이다. 리퀘스트가 들어오면 그 리퀘스트를 분석하여 알맞는 리소스를 리턴한다.

이 웹서버를 동적으로 기능하게 하기 위해서 웹서버 위에 flask, django, rails, node.js따위의 프레임워크를 얹는다. 그게 바로 WAS 다.

웹서버의 구조

apache라고 하자.

  1. 80번 포트를 listening
  2. 리퀘스트가 들어오면 리퀘스트를 처리하기 위한 apache process를 fork
  3. 포크된 아파치 프로세스는 리퀘스트에 붙어 처리하고 원래 있던 프로세스는 그대로 listen

이게 과거의 아파치다. 문제는, 프로세스는 무겁다. nginx는 이를 쓰레드로 전환하여 속도를 향상시켰다. 지금은 아파치도 쓰레드 형태로 전환하여 비슷한 퍼포먼스가 나온다고 한다.

프로세스: code, data, heap, stack 모두 갖고 있는 하나의 덩어리. 즉 독자적인 메모리를 갖고 있다.
쓰레드: 라이트웨이트 프로세스. 하나의 프로세스 안에서 code, data, heap을 공유하고 stack만을 별도로 가지고 있다. 즉 메모리를 공유한다.

WAS

웹 서버 위에 서버 어플리케이션을 얹은 것이 바로 WAS.

WSGI

khanrc: WSGI와 CGI의 차이
ko.wiki: WSGI
Web Server Gateway Interface. 파이썬에서 어플리케이션, 즉 파이썬 스크립트가 웹 서버와 통신하기 위한 명세다. 프로토콜 개념으로 이해하면 될 듯.

WSGI는 서버와 앱 양단으로 나뉘어져 있다. WSGI 리퀘스트를 처리하려면 서버에서 환경정보와 콜백함수를 앱에 제공해야 한다. 앱은 그 요청을 처리하고 콜백함수를 통해 서버에 응답한다.

Middleware

이러한 커뮤니케이션을 WSGI 미들웨어가 보충한다. 이 미들웨어는 서버의 관점에서는 앱으로, 앱의 관점에서는 서버로 행동한다. 이 미들웨어는 아래와 같은 기능을 가진다.

  • 환경변수가 바뀌면 타겟 URL에 따라서 리퀘스트의 경로를 지정해준다.
  • 같은 프로세스에서 여러 애플리케이션과 프레임워크가 실행되게 한다.
  • XSLT 스타일시트를 적용하는 것과 같이 전처리를 한다.

미들웨어에는 mod_wsgi, uwsgi, gunicorn, twisted.web, tornado 등등이 있다.

어플리케이션의 관점에서는 이 미들웨어를 통해 앱이 실행되므로 앱을 실행시켜주는 어플리케이션 컨테이너(Application Container)라고도 할 수 있다.

Framework

서버 어플리케이션을 만들기 위해 사용하는 최상단 웹 프레임워크. Flask로 대표되는 Micro 프레임워크와 Django로 대표되는 Full-stack 프레임워크로 나눌 수 있다.

spoqa tech blog: Flask로 만들어 보는 WSGI 어플리케이션

종합

  1. http 리퀘스트가 들어오면
  2. 웹 서버가 그 리퀘스트를 받고
    • 서버사이드 처리가 필요 없으면 리스폰스를 리턴(static한 웹 서버)
  3. 서버사이드 처리가 필요하면 wsgi 미들웨어를 통해 파이썬 어플리케이션으로 리퀘스트 전달
  4. 파이썬 어플리케이션이 리퀘스트를 받아 처리 후 wsgi 미들웨어 - 웹서버를 통해 리스폰스 리턴.

의 구조라고 할 수 있다.

정리하자면, 상식대로 웹 서버 위에 서버 어플리케이션이 올라가는데 이 어플리케이션과 웹 서버간의 커뮤니케이션을 위해 wsgi 미들웨어가 존재하는 것. 이 미들웨어를 어플리케이션을 적재하는 어플리케이션 컨테이너라고도 할 수 있다. 자바는 잘 모르지만, 자바 서블릿 컨테이너도 동일한 개념으로 보인다.

String option - u와 r

String option - u와 r

http://stackoverflow.com/questions/2081640/what-exactly-do-u-and-r-string-flags-do-in-python-and-what-are-raw-string-l

그냥 str : 8-bits ascii
u : unicode
r : raw string

r은 escape character로 사용되는 백슬래쉬 \를 문자 그대로 쓰일 수 있게 해준다.
ur도 사용할 수 있는데, 마찬가지로 유니코드에서 \를 문자 그대로 쓸 수 있게 해줌.

Test

>>> s1 = "안녕\n방가"
>>> s2 = r"안녕\n방가"
>>> s3 = u"안녕\n방가"
>>> s4 = ur"안녕\n방가"
>>> s1
'\xec\x95\x88\xeb\x85\x95\n\xeb\xb0\xa9\xea\xb0\x80'
>>> s2
'\xec\x95\x88\xeb\x85\x95\\n\xeb\xb0\xa9\xea\xb0\x80'
>>> s3
u'\uc548\ub155\n\ubc29\uac00'
>>> s4
u'\uc548\ub155\\n\ubc29\uac00'

네 문자열은 전부 다르다. 찬찬히 살펴보면 그 차이를 알 수 있다.

'Python' 카테고리의 다른 글

decorator (2) - extension  (0) 2014.10.06
decorator와 closure  (2) 2014.10.04
String option - u와 r  (0) 2014.09.21
한글 in the dictionary (feat. pretty)  (0) 2014.09.17
Python 2.x 한글 인코딩  (0) 2014.09.10
다양한 Python들!  (2) 2014.08.19

SharedCount: 소셜 인기도 확인하기

SharedCount: Facebook, Twitter 공유 개수 확인하기

SharedCount.com이라는 서비스가 있다.
들어가보면 바로 확인할 수 있는데, URL을 넣으면 Facebook, Twitter, Google+, Pinterest, LinkedIn 등에서 이 URL이 얼마나 공유되었는지 알려준다.

Pricing에 가 보면 가격 정책을 볼 수 있다. 공짜로는 하루에 1만 번까지. 한달에 40달러만 내면 매일 20만번의 요청이 가능하니, 귀찮으면 이 서비스를 사용하는 것도 나쁘지 않다.

내가 하는 프로젝트에서는 Facebook과 Twitter만 체크하기 때문에, SharedCount를 사용하기보단 직접 만들기로 했다.

http://www.sharedcount.com/documentation.php 에 보면 SharedCount가 공유 데이터를 어떤 API를 사용해서 가져오는지 상세하게 잘 나와 있다.

Facebook

https://api.facebook.com/method/links.getStats?urls=%%URL%%&format=json
SharedCount에서 사용하는 URL인데, 이게 최신이 아닌 걸로 보인다.

http://www.local-pc-guy.com/web-dev/getting-number-facebook-shares-likes-url
이 글을 참조하자. 근데 이 글에서 제시하는 fql도 deprecated 되었다 -_-

https://developers.facebook.com/docs/graph-api/reference/v2.1/link
이게 최신의 graph-api다.

지금은 다 잘 작동하는걸로 보인다.

on Python

urllib를 이용해서 간단하게 구현했다.
딱히 api key도 필요 없는 것 같고, 별로 어려울 거 없지만 한 가지 짚고 넘어간다면
urlencoding을 해주어야 한다.

params = urllib.urlencode({"url": link_url})
url = "http://urls.api.twitter.com/1/urls/count.json?" + params

이런 식으로.

참고로 urlencode에 다른 여러가지 키밸류를 넣으면 &로 묶어 준다.

Python 2.x 한글 인코딩

파이썬 2는 아스키가 디폴트다. 그래서 한글이 골치아픔. 파이썬 3은 유니코드가 디폴트.

일단 코드 내에서 한글을 쓰려면 그것부터 에러가 난다. 심지어 주석조차도 에러가 남. 유니코드로 바꿔 주자.

#coding: utf-8

근데 이건 스크립트에서 한글을 쓸 수 있게 해 주는 것 뿐이다. 즉 스크립트를 유니코드로 작성하는 것. 이것만 해 주면 라이브러리를 가져다 쓴다거나 함수를 사용할 때 문제가 생긴다. file write시에도 문제가 있었다. sys.setdefaultencoding을 사용하자.

#coding: utf-8

import sys
reload(sys)
sys.setdefaultencoding('utf-8')

s = '한글'
print str(unicode(s))


Python 2.x 한글 인코딩 관련 정리
여기에 정리가 잘 되어 있다. 참고하자.

'Python' 카테고리의 다른 글

decorator와 closure  (2) 2014.10.04
String option - u와 r  (0) 2014.09.21
한글 in the dictionary (feat. pretty)  (0) 2014.09.17
Python 2.x 한글 인코딩  (0) 2014.09.10
다양한 Python들!  (2) 2014.08.19
pyenv와 virtualenv를 통한 파이썬 개발 환경 구축  (0) 2014.08.13

다양한 Python들!

다양한 Python들!

전부터 컴파일러 수업을 듣고 싶었는데 학교에서는 시간표가 겹쳐서 못 듣고, 콜세라에서 하길래 들어야지 했는데 영 잘 안 듣게 된다. 올해의 목표로 삼아야 겠다.

Pythons

파이썬은 귀도 반 로썸(Guido van Rossum)에 의해서 1991년 만들어졌다. 이 귀도가 만든게 바로 CPython.
공식 홈페이지는 python.org.

  • CPython
  • Cython
  • PyPy
    • RPython
  • Stackless Python
  • JPython
  • Jython
  • IronPython
  • IPython

참고로 루비 또한 JRuby, IronRuby, MacRuby, Rubinius, Cardinal 등등 다양한 구현체가 존재한다.

CPython

C로 짜여진 파이썬. 우리가 쓰는 파이썬 인터프리터가 바로 이 CPython이다. 바꿔 말하면 파이썬은 C로 짜여졌다 라고 할 수도.

GIL1 때문에 멀티쓰레딩 이슈가 있으나 일반적으로 파이썬은 prototyping용으로 사용하기 때문에 실질적으로 이게 문제가 되는 경우는 거의 없다고 한다. 이브온라인 같이 파이썬을 좀 더 적극적으로 사용하게 되면 Stackless Python등 다양한 시도를 하게 되는 듯.

Cython

4세대 컴퓨팅 언어, 싸이쎤(Cython)
싸이썬은 파이썬의 수퍼셋(superset)이다. 파이썬이 동적으로 결정되는 부분(변수의 타입이라던가)이 있어서 인터프리팅을 해야 하고, 그래서 느리다면, 그걸 정적으로 결정하고 컴파일을 하면 되지 않는가? 라는 게 바로 싸이썬이다.

파이썬 컴파일러 ‘Cython’ 기본 원리 소개
인터프리팅이 느리다, 그래서 컴파일이 필요하다. 근데 생각해 보면 그렇다고 전부 다 컴파일 해야 할 필요는 없다. 속도에 치명적인 영향을 주는 부분만 컴파일 하면 되는 것이다. 이 아이디어로부터 나온 것이 C-API이며, Cython이다. 이 외에 MIT의 Julia, 구글의 Go, 자바의 Scala도 이러한 하이브리드 방법을 적용한 언어에 해당한다.

PyPy

PyPy와 함께 인터프리터 작성하기
PyPy가 CPython보다 빠를 수 있는 이유, 홍민희

메아리 : meta-tracing JIT에 대해 좀더 자세한 글, 강성훈

파이파이는 파이썬으로 작성된 파이썬 인터프리터다. 그리고 거기에 JIT2이라는 기술을 도입하여 CPython보다 빠르게 만들었다.

현재 파이썬 구현체중 가장 각광받고 있으며 Stackless Python도 파이파이가 거의 잡아먹은 듯.

RPython

이 PyPy를 구현하기 위해 나온것이 바로 RPython. Restricted Python이란 의미인데 정적 컴파일이 가능하도록 Python에 제약을 가한 서브셋(subset) 언어다. 컴파일하는 데에 장애가 되는 동적인 기능을 제거한 것.

Stackless Python

CPython을 구현할 때 파이썬의 함수 호출 스택을 C의 스택에 그대로 얹어버렸기 때문에 파이썬에서 얼마나 메모리를 쓸 수 있느냐와 관련없이 C 스택을 꽉 채우면 그대로 스택 오버플로 에러가 뜨게 된다. 뿐만 아니라 파이썬 프로그램의 호출 스택, 즉 프로그램의 실행 흐름을 CPython 스스로 제어할 수 없게 되어 코루틴 등 실행 흐름을 제어하는 언어 기능을 쓸 수 없다.

이 문제를 해결하기 위해서 CPython 소스를 수정해서 C스택을 쓰는 부분을 전부 들어내고 새로 호출 스택을 짜 넣은것이 Stackless Python이다.

현재는 PyPy에 밀려 사향길에 접어든 것으로 보인다. 자세히는 위 링크 참조.

JPython, Jython, IronPython

자이썬(Jython) 완벽 안내서
JPython은 Jython의 조상으로 10년 전 문서들에서나 찾아볼 수 있는 단어다. Jython은 자바, IronPython은 닷넷 위에서 돌아간다.
파이썬은 GIL1때문에 쓰레드를 쓰기 힘든데, Jython과 IronPython은 그런 문제가 없다.

IPython

IPython은 지금까지 살펴본 파이썬들과는 달리 새로운 파이썬 인터프리터 혹은 컴파일러가 아니다. IPython은 강력한 추가 기능들을 제공하는 interactive shell이다. 즉, 파이썬 기본 인터프리터의 업그레이드 버전이라고 보면 된다.

IPython Notebook

IPython의 강력한 기능 중 하나로, 웹 기반 쉘 환경을 제공한다. 파이썬 코드, 텍스트, 수학식, 그래프 그리고 다양한 미디어들을 하나의 도큐먼트로 만들 수 있다.

wakari.io : 웹에 IPython Notebook을 올리고 공유할 수 있다.

결론

Python Compatibility Table
기본적으론 CPython
퍼포먼스가 필요할 때는 PyPy
호환성이 필요할 때는 Jython이나 IronPython


  1. Global Interpreter Lock. coarse-grained-lock중에서도 극단적인 개념이 GIL이다. 즉, 어떤 시점에서든 단 한개의 바이트코드만이 실행된다. Python에서 thread를 사용하지 마세요? 참조.

  2. Just-In-Time 컴파일러. 파이썬 바이트코드를 인터프리팅 하다가, 자주 사용되는 부분은 컴파일한다. 즉, 처음에는 느리지만 코드가 돌아갈수록 점점 빨라진다.

'Python' 카테고리의 다른 글

decorator와 closure  (2) 2014.10.04
String option - u와 r  (0) 2014.09.21
한글 in the dictionary (feat. pretty)  (0) 2014.09.17
Python 2.x 한글 인코딩  (0) 2014.09.10
다양한 Python들!  (2) 2014.08.19
pyenv와 virtualenv를 통한 파이썬 개발 환경 구축  (0) 2014.08.13

인터프리터와 바이트코드

인터프리터와 바이트코드

컴파일 과정
C 언어 컴파일 과정
C나 C++은 컴파일 언어다. 즉, 코딩을 하고 실행을 하면 컴파일러가 소스코드를 컴파일해서 어셈블리로 변환하고 어셈블러가 이걸 기계어로 변환한다. 자세히는 위 링크를 참조.

반면 스크립트 언어, 즉 인터프리터 언어1는 변수의 타입 등이 동적으로 결정되기 때문에 한줄한줄 실행한다. 따라서 컴파일 언어에 비해 속도가 느리다.

여기까지가 기본적인 상식일텐데, 여기서 애매하게 중간에 들어있는게 있으니 바로 바이트코드다. 자바를 다루는 사람이라면 익숙한 개념일 테고, 요즘엔 자바가 C를 넘어서 국민 랭귀지에 가까우니 다들 잘 알겠지만 나는 자바를 잘 몰라서 이참에 정리해 보기로 했다.

바이트코드

어셈블리는 하드웨어 종속적이다. CPU마다 기계어가 다르고, 따라서 기계어에 대응하는 어셈블리도 CPU마다 달라진다. 즉, 컴파일러를 만든다는 것은 CPU 종류별로 대응해야 한다는 것을 의미한다2. 당연히 이건 매우 귀찮고 오랜 시간이 필요한 일이다. 그래서 등장한 것이 어셈블리와 소스코드의 중간 코드인 바이트코드다.

자바 바이트코드의 이해
바이트코드는 인터프리터 언어다. 그리고 소스코드에서 바이트코드로 변환하는 과정은 컴파일이다. 자바를 말할 때 컴파일 언어다 인터프리터 언어다 왔다갔다 하는 것은 이 때문이다. 바이트코드로 한번 변환하기 때문에 인터프리터 언어보다 퍼포먼스가 우수하면서도 인터프리터 언어의 특징을 그대로 가져갈 수 있다.

여기에 퍼포먼스 향상을 위해 한 발짝 더 나아간 개념이 JIT 컴파일러다. Just-In-Time의 줄임말로 인터프리터의 실행 과정에서 반복적으로 실행되는 부분을 컴파일하여 퍼포먼스를 향상시킨다.

이러한 일련의 과정들을 통해 최근 비약적으로 향상되어 C보다 빠르다는 소리도 나오는 자바 퍼포먼스의 형태를 어렴풋이 알 수 있겠다.

파이썬과 루비

파이썬도 바이트코드를 생성한다. pyc파일이 바로 파이썬 바이트코드 파일이다. 맥 파일 종류에도 Python Bytecode Document라고 되어 있다.

루비도 마찬가지. 루비 1.9.0버전에 YARV(Yet Another Ruby VM)이 포함되어 있다. 기존 루비의 퍼포먼스 개선을 위해 만들어진 바이트코드 인터프리터.


  1. 컴파일 언어와 스크립트 언어, 인터프리터 언어라는 명칭은 좀 애매하다. 컴파일러 언어라고 하지 않고 컴파일 언어라고 하는데 인터프리트 언어라고 하지 않고 인터프리터 언어라고 한다. 스크립트와 컴파일도 대비되는 개념이 아니며 스크립트와 인터프리터도 상등하는 개념이 아니다. 하지만 우리나라에서는 대부분 컴파일 언어 / 스크립트 언어, 인터프리터 언어로 구분하는 듯 하며 이 포스팅에서도 그렇게 사용하기로 한다.

  2. 사실 잘 모른다 -_-. 아마 그럴 거다. gcc를 다운받을 때 cpu마다 종류가 다르다고 한다.

'그 외/Tech' 카테고리의 다른 글

Git!  (0) 2014.10.03
cURL!  (0) 2014.09.22
검색엔진 (2) - 라이브러리: Lucene, Solr, Elasticsearch  (1) 2014.08.26
검색엔진 (1) - 검색엔진의 이해  (2) 2014.08.26
인터프리터와 바이트코드  (0) 2014.08.19
공인 IP와 사설 IP 그리고 고정/유동 IP  (0) 2014.08.13

Ubuntu에 개발환경 셋팅하기 (2)

Ubuntu에 개발환경 셋팅하기 (2)

Redis

레디스를 깔자.

$ apt-get install redis-server

이것만 하면 깔린다.

cp /etc/redis/redis.conf /etc/redis/redis.conf.default

환경설정은 미리미리 백업해 두도록 하자.

redis-server /etc/redis/redis.conf

레디스 서버를 실행시켜 보면 아무 반응도 없는 것 같지만 ps aux로 프로세스를 확인해보면

$ ps aux | grep redis
redis    12649  0.0  0.0  10680  1420 ?        Ss   17:28   0:00 /usr/bin/redis-server /etc/redis/redis.conf

레디스가 동작하고 있는 것을 확인할 수 있다.

redis-cli로 레디스 클라이언트도 정상 작동한다.

$ redis-cli
redis 127.0.0.1:6379>

위에 링크로 걸어둔 문서는 내용이 알찬 것 같으니 읽어보자.

Flask

플라스크 한글 문서
플라스크든 뭐든, 파이썬 환경설정은 virtualenv를 추천하지만 우리는 하나의 프로젝트를 진행할 뿐이므로 System-wide로 설치한다.

$ sudo pip install Flask

그냥 이러면 설치 끝!

SSH

설치 하는데 자꾸 접속이 끊겨서 불편했다. 그래서 타임아웃 시간을 늘려주기로 함.
타임아웃 시간을 컨트롤하는건 크게 두가지 방법이 있는데, 서버에서 타임아웃 시간을 늘려주는 방법과 클라이언트에서 ssh가 알아서 주기적으로 서버에 ack을 보내 스스로가 살아있음을 알리는 방법이다.

우리 팀원들도 분명 긴걸 좋아하리라 굳게 믿고 서버의 타임아웃 시간을 수정하였다.

$ cd /etc/ssh/
$ sudo vim sshd_config

ssh와 sshd가 있는데 ssh는 ssh-client고 sshd는 ssh daemon1으로 ssh-server역할을 하는 것 같다.

ClientAliveInterval 600
ClientAliveCountMax 3

그리고 sshd_config에 이 내용을 추가. 이렇게 설정해 두면 반응이 없는 클라이언트가 살아있는지 600초마다 한번씩 최대 3번 클라이언트에게 데이터 전송을 요청한다.

$ sudo service ssh restart

로 마무리. /etc/init.d/ssh restart도 가능하지만 써보니까 service를 사용하라고 권고하더라.

클라이언트에서 타임아웃 시간을 늘리는 방법은 위 링크를 참조하자.

참고

ps aux

리눅스에서 프로세스들을 보여주는 명령어
http://superuser.com/questions/117913/ps-aux-output-meaning

$ ps aux  
USER       PID  %CPU %MEM  VSZ RSS     TTY   STAT START   TIME COMMAND
timothy  29217  0.0  0.0 11916 4560 pts/21   S+   08:15   0:00 pine  
root     29505  0.0  0.0 38196 2728 ?        Ss   Mar07   0:00 sshd: can [priv]   
can      29529  0.0  0.0 38332 1904 ?        S    Mar07   0:00 sshd: can@notty

USER = user owning the process
PID = process ID of the process
%CPU = It is the CPU time used divided by the time the process has been running.
%MEM = ratio of the process’s resident set size to the physical memory on the machine
VSZ = virtual memory usage of entire process
RSS = resident set size, the non-swapped physical memory that a task has used
TTY = controlling tty (terminal)
STAT = multi-character process state
START = starting time or date of the process
TIME = cumulative CPU time
COMMAND = command with all its arguments

특정 프로세스를 찾을 때에는 ps aux | grep <string> 보다는 pgrep <string> | xargs ps 을 활용하자.

kill

kill PIDkill -9 ProcessName 으로 프로세스를 죽일 수 있다.


  1. 멀티태스킹 운영 체제에서 데몬은 사용자가 직접적으로 제어하지 않고, 백그라운드에서 돌면서 여러 작업을 하는 프로그램을 말한다. 원래 데몬이란 도깨비나 유령을 의미함.

Ubuntu에 개발환경 셋팅하기 (1)

Ubuntu에 개발환경 셋팅하기

소프트웨어 마에스트로 프로젝트 서버를 셋팅한다. 서버는 uCloud에서 구매하였다. 서버 스펙은 나중에 추가.

Server spec

Ubuntu 12.04

Setting

  • Python
    • pip
    • virtualenv
  • Python libs
    • numpy
    • scipy
    • scikit-learn
    • matplotlib
    • nltk
    • newspaper
    • goose
    • boilerpipe
    • feedparser
  • Flask
  • Redis

libs는 개발하면서 그때그때 설치하는게 맞다고 생각하나, 몇가지는 미리 깔아둠.

Python

기본적으로 2.7.3이 깔려 있다.

$ python -V
Python 2.7.3

파이썬 최신버전은 2.7.8.

맥이나 리눅스에는 기본적으로 파이썬이 설치되어 있는데, 그건 OS에서 파이썬을 쓰기 때문이다. 많은 부분들이 파이썬으로 코딩되어 있음. 따라서 이 시스템에 설치되어있는 파이썬(이하 시스템 파이썬)을 함부로 건드렸다간 시스템이 망가진다.

특히, 파이썬의 경우 메이저 버전이 다르면 호환이 안되기 때문에 함부로 업그레이드 해서는 안된다. 여기서 말하는 메이저 버전이란 소수점 둘째자리를 의미. 즉 2.6과 2.7은 호환이 안된다.

그러나 나는 최신버전을 쓰고 싶었고 현재 깔려있는 파이썬은 2.7.3으로 메이저 버전은 같았기에 업그레이드를 시도해 보기로 했다.

1차 시도

$ apt-get update
$ apt-get upgrade
$ apt-get install python2.7

실패. 이미 python2.7이 설치되어 있다고 나온다.

2차 시도

리눅스 Python 2.7 컴파일 설치
이걸 시도해보자. 이건 2.4 -> 2.7이므로 이것보다 수월하게 되리라 기대한다.

다운로드 및 설치

처음부터 막힘. 서버 세팅 문젠지는 몰라도 그냥 wget으로 받아오려면 에러가 난다. 아래처럼 —no-check-certificate 옵션을 추가해 줘야 함. 참고로 -N은 로컬에 있는 파일보다 최신버전만 받는 옵션이다.

$ cd /usr/local/src
$ wget -N --no-check-certificate https://www.python.org/ftp/python/2.7.8/Python-2.7.8.tgz
$ sudo tar xzf Python-2.7.8.tgz
$ cd Python-2.7.8/
$ sudo ./configure
$ sudo make
$ sudo make altinstall

그러고 나면

$ python -V
Python 2.7.3
$ /usr/bin/python -V
Python 2.7.3
$ /usr/local/bin/python2.7 -V
Python 2.7.8

이렇게 된다.

바꿔치기

자 그럼 대망의 바꿔치기!

$ mv /usr/bin/python /usr/bin/python_old
$ cp /usr/local/bin/python2.7 /usr/bin/python
$ python -V
Python 2.7.8

잘 되긴 하는데 yum이 안 됨(yum도 파이썬 스크립트임) -> yum이 안 되는줄 알았는데 알고보니 yum은 안 깔려있었다. 기존의 시스템 파이썬 일때는 yum을 깔라고 나오는데 2.7.8에서는 뭐라뭐라 파이썬 에러가 남. 암튼 이것도 뭔가 안된다는거니 문제다. 고치는 방법이 나와있긴 한데 저렇게 다 고칠수는 없는 노릇… 나 혼자 하는 프로젝트도 아니고 팀플젝이니 여기까지만 하도록 한다 -_-

pip, virtualenv

자 그럼 필요한거나 하자.
How to install pip on Ubuntu

$ sudo apt-get install python-pip python-dev build-essential 
$ sudo pip install --upgrade pip 
$ sudo pip install --upgrade virtualenv

virtualenv는 우리 프로젝트에서 안 쓸 것 같지만 필수적인 패키지 중 하나니 설치해 둔다.

python libs

numpy, scipy, scikit-learn

$ sudo pip install -U numpy scipy scikit-learn

에러 -_-

Command /usr/bin/python -c "import setuptools, tokenize;__file__='/tmp/pip_build_root/scipy/setup.py';exec(compile(getattr(tokenize, 'open', open)(__file__).read().replace('\r\n', '\n'), __file__, 'exec'))" install --record /tmp/pip-XQ2E58-record/install-record.txt --single-version-externally-managed --compile failed with error code 1 in /tmp/pip_build_root/scipy
Traceback (most recent call last):
  File "/usr/local/bin/pip", line 9, in <module>
    load_entry_point('pip==1.5.6', 'console_scripts', 'pip')()
  File "/usr/local/lib/python2.7/dist-packages/pip/__init__.py", line 185, in main
    return command.main(cmd_args)
  File "/usr/local/lib/python2.7/dist-packages/pip/basecommand.py", line 161, in main
    text = '\n'.join(complete_log)
UnicodeDecodeError: 'ascii' codec can't decode byte 0xe2 in position 72: ordinal not in range(128)

scipy를 설치하다 에러가 난 듯.

한참 삽질하다 보니 이것보다 윗단에 걸 봐야되는거 같다. 저 에러 위에 이게 있었음

numpy.distutils.system_info.BlasNotFoundError:
    Blas (http://www.netlib.org/blas/) libraries not found.
    Directories to search for the libraries can be specified in the
    numpy/distutils/site.cfg file (section [blas]) or by setting
    the BLAS environment variable.

즉 blas라이브러리를 깔아야 한댄다. 이거 깔고 나면 lapack 도 깔라고 함.

$ sudo apt-get install libblas-dev
$ sudo apt-get install liblapack-dev

로 해결. apt-get python-numpy python-scipy 로 하면 쉽게 된다는 거 같은데 여기까지 한 이상 pip로 해보기로 결심함. pip가 더 좋다는 소리도 있다. 왠진 모름 -_-

building 'dfftpack' library
error: library dfftpack has Fortran sources but no Fortran compiler found

이젠 별별 에러가 다 난다. 포트란 컴파일러를 깔아주자.

$ sudo apt-get install libamd2.2.0 libblas3gf libc6 libgcc1 \
libgfortran3 liblapack3gf libumfpack5.4.0 libstdc++6 \
build-essential gfortran python-all-dev \
libatlas-base-dev

뭘 이렇게 많이 설치해야 되는지…

감동. 이러고 설치하면 다 깔린다. 한참 걸리니 터미널을 쳐다보고 있지 말 것을 추천한다. import해서 정상적으로 임포트되는것도 확인.


오늘의 교훈 : 에러메시지를 꼼꼼히 읽자.

오늘의 과제 : pip와 apt-get의 차이는 무엇인가. apt-get은 리눅스 패키지 관리 툴 아닌가? 그렇다면 왜 pip, 즉 파이썬 패키지까지 얘가 관리하는것인가. 만약 둘다 특정 패키지를 설치할 수 있다면, numpy나 scipy같이, 둘중 아무거나로 깔아도 상관 없는 것인가. 상관 있다면 왜 그런가.