2010/03/13

Oleg: 安裝網頁伺服器 (thttpd, lighttpd, nginx) 和 PHP FastCGI

上一篇:Oleg: 安裝 ipkg Optware 套件管理系統

先前如果有完成 Optware 的設定後,現在就可以裝許多套件來玩了。這一篇主要著重於架設網路伺服器 (Web/HTTP Server) 的部分,查看套件列表可以發現也是有許多選擇,但是大家都不推薦安裝最常見的 Apache,理由非常簡單:跑不動。Apache 對於嵌入式系統的硬體來說實在是負擔過大,取而代之仍有許多輕量級的選擇: cherokee, lighttpd, nginx, thttpd, minihttpd 等。

thttpd - tiny/turbo/throttling HTTP server

官方網站:http://acme.com/software/thttpd/

thttpd 在 Abin 的網誌「Abin's Tech Note: 支援 PHP 的網頁伺服器 (thttpd) 安裝設定」已經介紹過,我僅簡單介紹安裝過程:

ipkg install php-thttpd

ipkg install thttpd

這兩個最大的不同是有 php 前綴的是內建 PHP 支援的版本,而後者僅為單純的 HTTP Server。

thttpd 的設定檔位置在 /opt/etc/thttpd.conf,網頁根目錄則在 /opt/share/www。另外經過 Abin 等人的實際測試發現 php-thttpd 運作時似乎會有不穩的情形發生,Process 常常無緣無故消失而必須手動重新啟動。

lighttpd - fly light.

官方網站:http://www.lighttpd.net/

LightTPD (發音同: Lighty) 是一套輕量級的 Web Server,正如副標 fly light. 它非常輕盈,運用在非常多知名的 Web 2.0 網站,大部份改機的人都推薦安裝此套件,加上 PHP FastCGI 來達成對 PHP 的支援。安裝指令:
ipkg install lighttpd php-fcgi

這個指令會安裝好 lighttpd 和 PHP FastCGI 環境,因為 PHP 的相依函式庫有很多所以需要一些時間。lighttpd 的設定檔位於 /opt/etc/lighttpd/lighttpd.conf。網頁根目錄則在 /opt/share/www。

修改設定檔

記得要先打開 /opt/etc/lighttpd/lighttpd.conf 修改 server.event-handler,可以改成:
server.event-handler = "linux-rtsig"
預設是註解的狀態,而且設定值也不對,沒有修改的話在嵌入式系統 Linux 2.4 是無法順利運作的!其實按照 Wiki 說明 poll 也可以,但 Kernel 2.4 版支援的 linux-rtsig 效果應該比較好。

由於剛才已經一併安裝了 php-fcgi,/opt/etc/lighttpd/conf.d/ 內會有 10-php-fcgi.conf,所以 Lighttpd 啟動後會一併自動啟動 PHP FastCGI,不必再做額外的設定,非常方便。但是我自己使用 lighttpd 遇到了一些問題。

自身測試遇到的問題

首先是 lighttpd 不能以 nobody 使用者運作。預設使用者是 admin 這非常危險。雖然設定檔內有 server.usernameserver.groupname 這兩個選項可以設定啟動的使用者和群組,但是兩個都設定成 nobody 後反而無法成功啟動,最多只能 server.groupname = nobody,變成使用者是 admin / nobody 群組的怪異現象。但如果不考慮安全性問題的話倒是無妨,但其實還有另一個大問題。

第二個讓我決定移除 lighttpd 的原因是我只要放超過 512KB 的檔案,以瀏覽器要求檔案,進度會卡在 512KB 而無法完成要求。這是一個很奇怪的問題,我查遍各大網站都沒有像我一樣的案例,實在是鬼打牆。經過測試後發現應該是 lighttpd 本身的問題,因為我的 vsftpd 或者是等下會介紹的 nginx 傳輸完全正常,於是決定忍痛移除 lighttpd 了。如果你安裝完、測試下載超過 512KB 大小的檔案都沒問題,那恭喜你可以安心繼續使用了。不然就跟我一樣,使用接著會介紹的 nginx。

nginx

官方網站:http://nginx.org/

nginx (發音同 Engine X) 是俄國程式設計師 Igor Sysoev 設計的輕量網頁伺服器/反向代理伺服器。噗浪 Plurk就是其中一個使用的知名 Web 2.0 網站。其效能常常跟 lighttpd 拿來比較,不相上下。唯一明朗的一點就是它們兩個都比 Apache 的效能要好。很奇怪網路上似乎找不到 Oleg 版韌體安裝 nginx 的說明,經過摸索並安裝成功後,我想我就來當第一人吧。

ipkg install nginx php-fcgi spawn-fcgi
安裝 nginx、PHP FastCGI 和 FastCGI 管理器 spawn-fcgi。最後這個套件在安裝 lighttpd 的時候會自動安裝,但是 nginx 則不會,需要手動安裝。nginx 的設定檔位於 /opt/etc/nginx/nginx.conf。網頁根目錄則在 /opt/share/nginx/html。

修改 nginx 設定檔

打開 /opt/etc/nginx/nginx.conf,我們修改的目的有 1. "nobody" 使用者執行 2. 啟用 access.log 和 error.log 3. 設定 PHP FastCGI。

user nobody;
將前面 # 刪除以取消註解 (1. "nobody" 使用者執行)

error_log logs/error.log;
將前面 # 刪除以取消註解 (2. 啟用 error.log)


log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
                  '$status $body_bytes_sent "$http_referer" '
                  '"$http_user_agent" "$http_x_forwarded_for"';
access_log  logs/access.log  main;

將前面 # 刪除以取消註解 (2. 啟用 access.log)


location ~ \.php$ {
    root           html;
    try_files      $uri @404
    fastcgi_pass   unix:/tmp/php-fcgi.sock;
    fastcgi_index  index.php;
    fastcgi_param  SCRIPT_FILENAME  $document_root$fastcgi_script_name;
    include        fastcgi_params;
}
location @404 { return 404; break; }

請改成上面的配置。(3. 設定 PHP FastCGI)

try_files 一段是先判斷 PHP 檔案是否存在才送給 PHP 執行,不然你要求一個不存在的 PHP 其 404 錯誤訊息並不是預設畫面而是奇怪的 "No input file specified." 錯誤。(FYI: Google 搜尋 FastCGI "No input file specified.")

fastcgi_pass 則是指定 PHP FastCGI 的路徑,這邊我們先設定好,後面還會再設定 FastCGI。

fastcgi_param 則是設定 PHP 檔案實際的路徑,如果設定錯誤的話則會出現錯誤。網路上的例子都是叫你改成 /var/www$fastcgi_script_name 一類的實際路徑,但其實有 $document_root 變數可以用,改成 $document_root$fastcgi_script_name 準沒錯。

最後追加的一行 location 則是跟 try_files 搭配,若是要求的檔案不存在,則回傳 404 錯誤頁面。原本沒有這一行記得加上去。(Source: 官方論壇 FastCGI returning "No Input File" instead of 404)

經過一番修改並儲存之後,接著是 PHP FastCGI的設定。

新增 PHP FastCGI 啟動設定檔

我們還要手動新增 PHP FastCGI 的啟動設定才行。因為 php-fcgi 這個套件對 lighttpd 比較好,已經幫它設定好了所以不必手動設定,使用其他伺服器的就只能手動設定了。本次的目的是設定 PHP FastCGI 讓它在開機時常駐。因此我們需要手動建立 /opt/etc/init.d/S80php-fcgi 檔案:
(vi或nano) /opt/etc/init.d/S80php-fcgi

檔案內容直接按照以下內容複製貼上:
#!/bin/sh

phpfcgid_children="1"
phpfcgid_requests="1000"
prefix="/opt" 
PATH=${prefix}/bin:${prefix}/sbin:/sbin:/bin:/usr/sbin:/usr/bin 
NAME=php-fcgi
DAEMON=${prefix}/bin/spawn-fcgi
DAEMON_OPTS="-s /tmp/php-fcgi.sock -C ${phpfcgid_children} -u nobody -P /var/run/php-fcgi.pid -- ${prefix}/bin/${NAME}"

test -x $DAEMON || exit 0

if [ -z "$1" ] ; then
    case `echo "$0" | sed 's:^.*/\(.*\):\1:g'` in
        S??*) rc="start" ;;
        K??*) rc="stop" ;;
        *) rc="usage" ;;
    esac
else
    rc="$1"
fi

case "$rc" in
    start)
        export PHP_FCGI_MAX_REQUESTS=$phpfcgid_requests
        echo "Starting web server: $NAME"
        $DAEMON $DAEMON_OPTS
        ;;
    stop)
        if [ -n "`pidof $NAME`" ]; then
            echo "Stopping web server: $NAME"
            killall $NAME 2> /dev/null
        fi
        ;;
    restart)
        "$0" stop
        sleep 1
        "$0" start
        ;;
    *)  
        echo "Usage: $0 (start|stop|restart|usage)"
        ;;
esac

exit 0

這個啟動檔主要是透過 spawn-fcgi 來建立 nobody 使用者的 php-fcgi 程序,並利用 Unix domain socket 方式建立 /tmp/php-fcgi.sock 檔案供 nginx 溝通。儲存檔案後記得還要給啟動檔執行權限:
chmod +x /opt/etc/init.d/S80php-fcgi

重新啟動 nginx 和 PHP FastCGI

這時候重新啟動 AP 就可以生效了,但也可以不必重新啟動便能發生效果,手動執行以下兩行指令便可以重新啟動:
/opt/etc/init.d/S80nginx restart
/opt/etc/init.d/S80php-fcgi restart

如果沒有任何錯誤訊息產生,恭喜伺服器已經運作正常。

附錄: 使用記憶體量

Mem: 13092K used, 660K free, 0K shrd, 744K buff, 6060K cached
CPU:   0% usr  38% sys   0% nic  61% idle   0% io   0% irq   0% sirq
Load average: 0.00 0.02 0.04 2/30 1572
  PID  PPID USER     STAT   VSZ %MEM %CPU COMMAND
 1377  1376 nobody   S    22176 161%   0% /opt/bin/php-fcgi
 1376     1 nobody   S    21876 159%   0% /opt/bin/php-fcgi
  853   852 nobody   S     4100  30%   0% nginx: worker process
  852     1 admin    S     4072  30%   0% nginx: master process /opt/sbin/nginx

對外: 防火牆的設定

如果你只有對內的需求 (即透過 AP LAN IP 存取服務),就不必管防火牆。如果你有對外的需求,就必須先設定防火牆才可以運作正常。

防火牆的設定檔案在 /usr/local/sbin/post-firewall,位於 flashfs 內。Oleg 的設計是要先將 Web 管理介面的 Internet Firewall 給關閉,系統才會去執行 post-firewall 檔案,因此請先將其關閉。就我直接觀察 iptables -L 的規則設定發現啟動或關閉此功能似乎沒有不同。(就算關閉了預設也還有一串規則)



關閉後重新啟動 AP,登入 Telnet/SSH 新增 /usr/local/sbin/post-firewall:
(vi 或 nano) /usr/local/sbin/post-firewall
這次的目標是要開啟 8080 Port (視伺服器設定),並將外部 80 Port 的要求轉到內部的 8080。之所以這樣做的原因是因為 AP 的 80 Port 早就被 Web 管理介面給佔據了,於是另架的伺服器就只能選擇其他如 8080。但瀏覽器輸入 http://(外部IP):8080 瀏覽也不太方便,所以利用 Prerouting 的方式將一般外部 80 轉到內部的 8080。範例: http://www.example.com/ -> 192.168.1.1:8080

#!/bin/sh

iptables -D INPUT -j DROP

# lighttpd / thttpd / nginx
iptables -A INPUT -p tcp --dport 8080 -j ACCEPT
iptables -t nat -A PREROUTING -i $1 -p tcp --dport 80 -j REDIRECT --to-port 8080

iptables -A INPUT -j DROP
$1 是代表網路介面,通常也就是 eth0。第一行 iptables -D INPUT -j DROP 代表刪除 INPUT 的最後一個設定 (預設最後一個設定是封鎖全部),必須先拿掉我們才能打開Port,最後再補回來,中間則是設定開放 8080 和 Prerouting 80 to 8080,最後一行則是設定 INPUT 剩下的 Port 全部封鎖 (補回來)。由於 iptables 是逐行執行,最後一行的位置千萬不要放到自訂規則前了。

然後記得給予執行權限,和寫回 flashfs:
chmod +x /usr/local/sbin/post-firewall
flashfs save && flashfs commit && flashfs enable

最後,重開 AP 讓整個設定能夠被執行,這樣防火牆設定和伺服器的架設就完成了。
reboot

測試成果



先在網頁根目錄新增一檔案:
echo "<?php phpinfo();?>" > /opt/share/www/phpinfo.php
然後用瀏覽器瀏覽檔案 (http://內部IP:8080/phpinfo.php 或 http://外部IP/phpinfo.php),如果像圖一樣有 PHP 的資訊那就大成功啦。

另外瀏覽外部IP的電腦必須不是用 AP 的 NAT 上網方式來看,這樣只會看到 Web 管理介面。利用 PPPoE-relay 撥號取得實體 IP 再來看才看的到正確的結果,或使用其他與 AP 無任何連線的電腦上網看。

沒有留言:

張貼留言