uWSGI에서 thread 활성화

며칠 전에 웹서버 환경을 apache2, php5, mod_wsgi에서 nginx, php5-fpm, uwsgi로 바꿨는데 오늘 확인해보니 flask 앱중 하나가 계속 DB를 잡고 놔주질 않아서 Lock이 걸린 채로 서비스가 동작하질 않고 있었다.
uwsgi를 끄면 lock이 해제되는 걸 봐서 uwsgi로 돌리고 있던 게 문제인게 확실한데 테스트 해보면 잘 돌아가서 삽질을 또 엄청 했다.

확인 해보니 계산이 많이 필요한 작업은 스레드를 따로 돌려놨는데 이게 그냥 돌리면 돌아가지만 uwsgi 위에선 전혀 돌지 않고 있었다. 찾아보니 uwsgi는 기본적으로 멀티스레드가 꺼져 있었다. 실행 할 때 --enable-threads를 붙여서 실행해야 하고 ini파일에선 enable-threads = true라는 한 줄을 추가해주면 된다. 이제서야 정상적으로 돌아간다.

최근 nginx로 바꾸면서 익숙하지 않은 걸 여러 개 접하다 보니 삽질을 많이 하는 것 같다.
아직도 내가 찾지 못한 문제가 있을 것만 같아 두렵다.

Apache2에서 Nginx로 갈아탔다.

여태 개인 서버에 Apache2로 워드프레스 블로그도 돌리고 HTML 호스팅도 하고 mod_wsgi를 이용해서 Flask 앱도 올려놓고 했었다. Nginx가 뜬지는 한참 됐는데 계속 갈아타려다가 실패했었다. 그러다가 오늘 완전히 옮겼는데 삽질한 과정을 써놔야 다음에 또 삽질하지 않을 것 같다.

PHP가 실행되지 않고 그냥 원문이 보일 때

index.php에서 /wordpress/로 리다이렉트를 시켜놨는데 얘가 작동을 안하고 HTML이 출력되서 보니 PHP 소스가 그대로 나타나있었다. 그런데 이상하게도 워드프레스 블로그는 PHP인데도 잘 돌아가는 걸 보고 눈치를 챘다. PHP는 태그를 열 때 <?만 쓸 수도 있고 <?php로 쓸 수도 있는데 앞의 것은 short open, 뒤의 것을 full open이라고 한다.

원래 Apache2에서는 PHP를 그냥 사용했지만 Nginx로 갈아타면서 php-fpm을 사용하게 됐는데 이게 설정파일이 다른 위치에 있다. /etc/php5/fpm/php.ini를 열고 short_open_tag = On으로 바꿔주면 된다.

Rewrite engine

글을 쓴지 좀 지난 지금 wordpress의 RSS 피드가 작동하지 않는 걸 깨달았다. Apache2는 .htaccess 파일을 이용해 URL을 멋대로 꼬을 수 있지만 nginx는 그게 안되기 때문에 수동으로 적어줘야 한다.

location /wordpress {
    try_files $uri $uri/ /wordpress/index.php?q=$uri&$args;
}

위와 같이 적어주면 feed도 작동하고 permalink도 잘 작동 한다.

uWSGI 사용

uwsgi를 pip로 깔았다가 아주 고생했다. 참고로 pip로 설치하면 얘가 컴파일을 하기 때문에 uninstall을 해도 uwsgi 실행파일은 남는다. 수동으로 지워주자. 난 이걸로 2시간 삽질 한 것 같다.

그냥 우분투니까 편하게 sudo apt-get install uwsgi 해주면 설치는 끝난다. 설정파일은 대충 아래와 같이 써놓으면 sudo service uwsgi start 명령으로 간단하게 컨트롤 할 수도 있어서 굉장히 편하다.

[uwsgi]
chdir = /var/wsgi
uid = www-data
gid = www-data
chmod-socket = 666
socket = /tmp/ertest.sock
virtualenv = /var/wsgi/venv
plugin = python
wsgi-file = /var/wsgi/ertest.wsgi

여러 개의 가상호스트를 만들 시 duplicated listen options라는 에러가 뜰 때

ertest.kjwon15.net을 earthreader의 데모 인스턴스로 띄워놓아야 하기 때문에 아파치의 vhost처럼 설정을 했는데 얘가 자꾸 같은 포트에 바인딩하지 말라고 찡찡댄다. 왜 그런가 찾아보니 옵션 중 ipv6only=on 부분을 지워버리면 된다. 아직 고쳐지지 않은 버그인가본데 난 그냥 ipv6 부분의 한 줄을 아예 지워버렸다.

Sub-directory에 WSGI 앱 장착하기

그냥 루트 디렉터리에 WSGI 앱을 달면 문제가 없지만 서브디렉터리에 달면 아파치의 mod_wsgi와는 다르게 full-path를 넘겨줘서 문제가 생긴다. 아래와 같이 설정을 추가하면 된다. (가장 아래의 두 줄)

location /earthreader {
    include uwsgi_params;
    uwsgi_pass unix:/tmp/earthreader.sock;
    uwsgi_param SCRIPT_NAME /earthreader;
    uwsgi_modifier1 30; 
}

basic auth 걸기

엄청 간단하다. 아래와 같이 적으면 끝난다. (가장 위의 두 줄)

location /earthreader {
    auth_basic "Personal RSS reader";
    auth_basic_user_file /var/wsgi/earthreader.htpasswd;
    include uwsgi_params;
    uwsgi_pass unix:/tmp/earthreader.sock;
    uwsgi_param SCRIPT_NAME /earthreader;
    uwsgi_modifier1 30; 
}

WSGI 앱의 sub-directory에 basic auth 걸기

난 kjwon15.net/autotweet/에는 암호를 걸지 않고 kjwon15.net/autotweet/teach/에만 암호를 걸어놓고 사용했다. nginx는 nested directory를 지원한다. 아래와 같이 쓰면 된다.

P.s: nested directory 안에서도 설정을 일일히 적어줘야 한다. nginx의 구조상 그렇다는데 왜 그렇게 만들었는지는 의문이다.

location /autotweet {
    location /autotweet/teach {
        auth_basic "Restricted area";
        auth_basic_user_file /var/wsgi/earthreader.htpasswd;

        include uwsgi_params;
        uwsgi_pass unix:/tmp/autotweet.sock;
        uwsgi_param SCRIPT_NAME /autotweet;
        uwsgi_modifier1 30;
    }
    include uwsgi_params;
    uwsgi_pass unix:/tmp/autotweet.sock;
    uwsgi_param SCRIPT_NAME /autotweet;
    uwsgi_modifier1 30;
}