其實想把Apache換成Nginx已經很多年了,只是一直懶得弄,也可能是因為上了年紀的關係,有點得過且過的意思。當然更主要的原因是沒有壓力,目前我的所有應用在Apache下都跑得挺好,對Apache的配置也比較熟悉。但是Nginx 10倍的效能優勢始終在那裡,這是一個擋不住的誘惑。
上周與令狐和幫主小聚的時候順手在手機的Ubuntu裡裝了個Nginx,但是Ubuntu 9.04帶的那個版本實在太老了,也就沒有再弄。後來因為換手機把那個Ubuntu搞掉了,還沒重裝,這兩天就在工作機的Ubuntu 12.04上來裝了個配置一下。後來還正式部署到了一台Debian伺服器上。順便做點筆記。
安裝
在Ubuntu 12.04下是簡單。
apt-get install nginx php5-cgi php5-cli php5-fpm php-doc
不過在Debian 6下就麻煩一些,因為apt裡沒有php5-fpm,只能源碼安裝,或者使用這個源:
#在 sources.list 裡加入以下源sudo echo "deb http://php53.dotdeb.org stable all" >> /etc/apt/sources.list#或者:deb http://packages.dotdeb.org stable all#如有必要還可以再加上:deb-src http://packages.dotdeb.org stable all#加入keywget http://www.dotdeb.org/dotdeb.gpgcat dotdeb.gpg | sudo apt-key add -rm dotdeb.gpgsudo apt-get updatesudo apt-get install php5-fpm
初步配置
首先關閉Apapche的自啟動,可以用 sysv-rc-conf 來配置。
然後配置php-fpm,主要修改這幾個檔案:
/etc/php5/fpm/php.ini/etc/php5/fpm/php-fpm.conf/etc/php5/fpm/pool.d/www.conf
第一個為與php有關的配置,這裡要有這一句:
cgi.fix_pathinfo = 0;
原因見nginx預設設定檔中的注釋說明。
第二個為fpm有關的配置,通常沒什麼要改的。
最後一個為與web有關的配置,可以在這裡修改fpm的監聽連接埠號碼什麼的。
基本的nginx配置
主要的設定檔是這些:
/etc/nginx/nginx.conf/etc/nginx/conf.d/*.conf/etc/nginx/sites-enabled/*
基本上不需要修改nginx.conf,所有跟全站http相關的配置都可以放在conf.d/*.conf裡,各虛擬機器主機的配置則放在sites-enabled/*裡即可。
參考全站公用配置內容:
log_format main '$remote_addr - $remote_user [$time_local] "$request" ' '$status $body_bytes_sent "$http_referer" ' '"$http_user_agent" "$http_x_forwarded_for"';charset utf-8;
這裡有一點需要注意的是:charset設定並不能保證輸出一定是utf-8。對於靜態檔案來說一般沒問題,但是對於動態內容來說(比如來自PHP或Python WSGI),即使返回內容的確是用utf-8編碼,但是沒有在HTTP回應標頭裡指定編碼方式的話,nginx會預設為 ISO-8859-1 ,即使這裡指定了utf-8也沒用,結果就是導致在FireFox等瀏覽裡器顯示亂碼(部分瀏覽器會識別網頁中的meta,不一定按照HTTP回應標頭的指定編碼方式)。
解決方案有兩個:一是治標的辦法——在nginx配置裡加入一個ISO-8859-1到utf-8的charset_map(內容為空白即可,當然這樣的話碰到真正的ISO-8859-1內容時會亂碼)。另一個當然是治本的——在動態內容裡增加HTTP回應標頭內容,指定編碼方式。
參考虛擬機器主機配置:
server {listen 80; ## listen for ipv4; this line is default and impliedserver_name yoursite.com www.yoursite.com;root /home/username/www;index index.html index.htm index.php; error_log /var/log/nginx/yoursite.error.log warn; access_log /var/log/nginx/yoursite.access.log main; location = /favicon.ico { log_not_found off; access_log off; } location = /robots.txt { allow all; log_not_found off; access_log off; } location / { # This is cool because no php is touched for static content. # include the "?$args" part so non-default permalinks doesn't break when using query string try_files $uri $uri/ /index.php?$args; } location ~ \.php$ { #NOTE: You should have "cgi.fix_pathinfo = 0;" in php.ini fastcgi_pass 127.0.0.1:9000; fastcgi_index index.php; fastcgi_param SCRIPT_FILENAME /home/username/www$fastcgi_script_name; include fastcgi_params; fastcgi_intercept_errors on; } location ~* \.(js|css|png|jpg|jpeg|gif|ico)$ { expires max; log_not_found off; }location ~ /\.ht {deny all;}}
此配置根據nginx官方的wordpress配置修改而來。注意其中的SCRIPT_FILENAME一行,此行缺少會導致PHP頁面顯示空白,但HTTP響應為沒有錯誤的200,此行錯誤則會導致html顯示正常,但php顯示404找不到。
配置完成後啟動nginx和php5-fpm。
sudo /etc/init.d/php5-fpm startsudo /etc/init.d/nginx start
停止的方式類似:
sudo /etc/init.d/php5-fpm stopsudo /etc/init.d/nginx stop
SSL配置
這個配置倒很簡單,跟Apache基本一樣。認證產生的部分就不說了,都是用openssl搞的。不過nginx預設是用.crt/.key檔案,apache還可以用.pem檔案,實際上可以手工把.pem檔案拆成.crt/.key。
基本配置如下:
server { listen 443; ## listen for ipv4 server_name yoursite.com; ssl on; ssl_certificate /etc/nginx/ssl/nginx.crt; ssl_certificate_key /etc/nginx/ssl/nginx.key; ssl_session_timeout 5m; ssl_protocols SSLv3 TLSv1; ssl_ciphers ALL:!ADH:!EXPORT56:RC4+RSA:+HIGH:+MEDIUM:+LOW:+SSLv3:+EXP; ssl_prefer_server_ciphers on; error_log /var/log/nginx/yoursite.error.log warn; access_log /var/log/nginx/yoursite.access.log main;# 其它配置與 http 相同# 如 location /webalizer { root /home/username/www; index index.html autoindex off; }}
另外要注意的是:php5-fpm那邊是不知道nginx這邊是否使用https的,所以用 $_SERVER['HTTPS'] 將取得空串,需要這樣設定一下:
location ~ \.php$ { fastcgi_pass 127.0.0.1:9000; fastcgi_index index.php; fastcgi_param SCRIPT_FILENAME /home/username/www$fastcgi_script_name;# 注意這句 fastcgi_param HTTPS on; include fastcgi_params; fastcgi_intercept_errors on; }gevent-WSGI配置
以web.py為例。首先需要修改web.py的代碼以使用gevent-WSGI(雖然這隻是個參考實現,但效能還是很不錯的)。
if __name__ == "__main__": from gevent import socket from gevent import monkey monkey.patch_all() from gevent.wsgi import WSGIServer import pwd sys.stdout = sys.stderr SOCK = "/var/www/sockets/webpy.sock" pe = pwd.getpwnam('www-data') sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) try: os.remove(SOCK) except OSError: pass sock.bind(SOCK) os.chown(SOCK, pe.pw_uid, pe.pw_gid) os.chmod(SOCK, 0770) sock.listen(256) WSGIServer(sock, app.wsgifunc()).serve_forever()
改完即可啟動監聽相應的socket。
python /home/username/webpy/start-gevent.py 2>> /var/log/nginx/webpy.log &
其次是修改nginx,將相應的請求轉寄到web.py+gevent-WSGI的監聽socket。
參考配置:
location /webpy { try_files $uri @webpy; } location @webpy { proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header Host $http_host; proxy_pass http://unix:/var/www/sockets/webpy.sock; } location /webpy/static { root /var/www; autoindex off; }
搞定收工。