Nginx, PHP-fpm, userdir

동아리에서 실습 서버를 구축하는데 아이들이 각자 웹페이지를 만들 수 있도록 userdir를 설정하고 싶었다.
userdir을 설정하면 계정마다 ~/public_html 디렉터리 밑에 파일들을 만들어 두면 server-name/~username 같은 URL로 접속할 수 있게 해 주어서 학교에서 자주 쓰인다.

아파치에서는 userdir 모듈을 로드하고 PHP도 주석 하나만 풀면 되는데 Nginx에서는 좀 어려웠다. 아무리 설정파일들을 찾아 봐도 PHP는 실행이 안 되었다. 그러다가 되는 설정을 찾았고 역시나 location 구문을 두 번 적고 중복되는 부분도 참 많다. Nginx에서 이 부분은 얼른 개선해 주면 좋겠는데 일부러 안 하는 듯 하다.

        # Userdir - php
        location ~ ^/~([^/]+)/(.+\.php)$ {
            if (!-f /home/$1/public_html/$2) {
                rewrite ^ 404;
            }
            alias /home/$1/public_html/$2;
            fastcgi_pass unix:/var/run/php/php7.0-fpm.sock;
            fastcgi_index index.php;
            fastcgi_param SCRIPT_FILENAME $request_filename;
            include fastcgi_params;
        }

        # Userdir - static
        location ~ ^/~([^/]+)(/.*)?$ {
            alias /home/$1/public_html$2;
            index index.php index.html;
            autoindex on;
        }

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;
}