顯示具有 Nginx 標籤的文章。 顯示所有文章
顯示具有 Nginx 標籤的文章。 顯示所有文章

2018/01/18

Nginx 最大接收檔案大小

因為最近網路攻擊蠻多的,索性用 reverse proxy 把 NAS 藏在 Nginx 後面,順便讓 Nginx 處理掉 SSL 那段工作:
   location / {
      proxy_pass http://192.168.0.xxx:80;
      proxy_set_header Host $host;       
      proxy_set_header X-Real-IP $remote_addr;
      proxy_buffers 32 32k;
      proxy_buffer_size 32k;
      error_page 404  404.html;
      error_page 500 502 503 504  500.html;
   }

不過後來友人說上傳檔案一直失敗,在某個大小以上的檔案通通出錯。

觀察一下網路流量才發現 Nginx 並不是直接把流量即時 proxy 到 NAS 端,而是整個 HTTP request 完成以後才一次送到 NAS 那邊去,所以上傳檔案時自然會遇到 upload size 問題。

解決方法就是把 Nginx 的 client_max_body_size 調大:
server {
    ....

    client_max_body_size 1g;

    ....
}


Ref:

2017/08/02

Apache / Nginx 與 PHP 各種搭配方式的效能測試

以 PHP 為主的 HTTP server 有很多種架設方式,最傳統的大概就是 Apache + mod_php 來執行 PHP scripts。隨著技術一直在翻新,後來也有了 Nginx + php-fpm 的搭配模式出現,甚至連 PHP 在 5.4.0 都推出了 built-in server

因公司需要,就順手測試了以下幾種搭配:

在開始看壓力測試結果之前,先來說一下測試環境:
  • 所有的 HTTP server 除了以上搭配方式之外,其他都使用預設值 (像是不開啟 gzip 壓縮等)
  • 另外壓力測試的 PHP script 內容也很簡單,僅有「phpinfo();」一行
  • 1 core CPU
  • 4 GB RAM
  • 使用 ab (Apache Bench) 做測試
  • ab 中每個 concurrency 固定會發出 1000 個 HTTP request,如 concurrency = 1 時僅有 1000 個 request,concurrency = 100 時,則送出 1000 x 100 個 request。
  • 每次 ab 跑完一次壓力測試,均重新啟動 HTTP server 與 PHP server,以防止使用到 cache。

先簡單做個結論,詳細的壓力測試結果再請大家自行過目:
  • concurrency = 1 時,沒有壓力測試會出現的情況,不過可以把 response time 和伺服器的 CPU、記憶體使用量當作一個基準值作為參考。
  • Apache mpm-prefork 搭配 mod_php 的記憶體使用量最大,畢竟要 fork 一個 Apache instance 出來就要把 Apache 的 module 也 initialize 出來。小型伺服器不推薦這個方案。
  • php-fpm 即使設定「pm.max_children = 50」,也可以輕鬆負荷超過 50 個 concurrency。比較微妙的是從 htop 看不出 php-fpm 有使用 thread 來同時處理 request。
  • php-fpm listen on Unix socket 的效率比 listen TCP (127.0.0.1:9000) 的效率還要高一些,但是在高負荷的情況下會變得很不穩定,猜測和 OS 的 socket 實作、設定有關連。
  • PHP built-in server 遇到在高負載的情況下表現的不錯,唯獨 log 給的資訊太少,不方便做分析。但也可考慮透過 Nginx proxy 給 PHP built-in server,讓 Nginx 來處理 log。

最後做個廣告,Hackmd 真不錯用,筆記寫完最後還以轉換成 HTML 下載,有夠方便的啦!

2017/07/14

Nginx URL rewrite for REST API in subfolder

一般來說,要讓 Nginx 遇到 PHP 程式時,只要按照以下寫法就可以將 request 轉接給 php-fpm 處理:
server {
    location / {
        try_files $uri $uri/index.html =404;
    }

    location ~ \.php* {
        root /home/www/data;

        include        fastcgi_params;
        fastcgi_pass   127.0.0.1:9000;
        fastcgi_index  index.php;
        fastcgi_param  SCRIPT_FILENAME  $document_root$fastcgi_script_name;
    }
}

假設 project 在 document root (如上面的就是 /home/www/data),設定方式其實不難,網路到處都可以找的到。像是 Laravel 可以參考 pretty URL 的設定方式,把 index.php 隱藏起來:
location / {
    try_files $uri $uri/ /index.php?$query_string;
}

上面這個設定,可以讓 request 從「http://my.site/user/1」,被改寫為「http://my.site/index.php/user/1」,這個時候 index.php 就可以從 $_SERVER 的參數來判斷到底要走哪個 route。

但假設寫的專案沒有一個 domain name、沒辦法做 virtual host,可以讓專案根目錄當作 document root,且新的 PHP framework 都會把 index.php 放在 /public 目錄下來避免安全問題,這樣的 URL rewrite 就會出問題。

例如今天的 request 是「http://my.site/project/user/1」,在經過上面的 route 以後會被轉成「http://my.site/index.php/project/user/1」,而實際上我們需要的是「http://my.site/project/index.php/user/1」才能讓專案正確運作。

這時需要另外建立 route 規則。先把 http://my.site/project 對應到的正確檔案路徑設定好:
location ^~ /project {
    # define script real path
    alias /home/www/data/project/public;

    try_files $uri $uri/ /project/public/index.php$uri;
}

到這邊算是設定完成一半,暫停來看一下目前 routing 的情況。請求「http://my.site/project/user/1」會被轉換為「http://my.site/project/public/index.php/project/user/1」。index.php 後半部的參數差了一點點,把「project/」片段拿掉就完成了。

這時我們再新增一條規則,使用 REGEX 來處理後面這段參數:
location @project-rule {
    # 若 URI 起始為 /project/
    # 把後面的參數抓出來,放在 /project/public/index.php/ 後方 
    rewrite ^/project/(.*)$ /project/public/index.php/$1 last;
}

調整後,完整的 Nginx URL rewrite 規則會長這樣:
location ^~ /project {
    alias /home/www/data/project/public;

    # 一般規則無法正確找到路徑
    # 就使用 @project-rule 規則來做查詢
    try_files $uri $uri/ @project-rule;
}

location @project-rule {
    rewrite ^/project/(.*)$ /project/public/index.php/$1 last;
}


Nginx 的 rewrite rule 實在很難 debug,這段是自己花了數小時嘗試錯誤並觀察 $_SERVER 參數變化才找到規律的,希望多少對大家有一點幫助。

2017/02/24

Nginx + PHP + MySQL 的 windows 懶人包

以前都是 WAMP 懶人包,現在終於看到已 Nginx + php-fpm + MySQL 為主的懶人包了:WPN-XM


WPN-XM 看起來已經有不少人在維護,新的 RC 版本也開始支援 PHP 7.x 了,看起來可以關注一下。

2017/02/02

phpBB 3.2 的 nginx 的 location 設定

先說一下 phpBB 安裝工具吧,安裝程式的路徑不是在跟目錄而是在「/install」,entry point 是 app.php,而 app.php 又有自己的 route

如果只是這樣設定的話,route 會失敗:
location / {
    try_files  $uri /index.php?$uri$args;
}

所以另外對「/install」增加了一個 rule 才能正常執行安裝工具:
location /install {
    try_files  $uri /app.php?$uri$args;
}



安裝完成後,論壇系統的有二個 entry point,所以不能只單設定 index.php,還有 app.php,因此 nginx 的設定要改成下面這樣:
location / {
    try_files  $uri /app.php?$uri$args /index.php?$uri$args;  # 注意順序
}

當然,安裝完成以後就可以把「/install」的設定砍掉了。

2014/08/22

Nginx upstream 的容錯設定方法

自己的 Redmine server 是使用 Thin 執行,在透過 Nginx 將 request 轉送給 Thin。

Nginx 中設定 Thin server pool:
upstream redmine_thin_servers {
  server unix:/home/redmine/run/thin.0.socket;
  server unix:/home/redmine/run/thin.1.socket;
  server unix:/home/redmine/run/thin.2.socket;
  server unix:/home/redmine/run/thin.3.socket;
}

Nginx server:
  location / {
    try_files $uri/index.html $uri/index.htm @redmine_thin_servers;
  }

  location @redmine_thin_servers {
    proxy_pass http://redmine_thin_servers;
  }

這樣設定,Nginx 會將 request 平均轉送給 4 個 Thin instance。

但若其中一個 Thin instance 處理叫複雜或是較花時間的工作,如 checkout repository 等,會長時間沒有回應,可能沒辦法再同時處理另一個 request。此時若 Nginx 將另一個 request 轉送到已經 pending 的 Thin instance,使用者就會一直等到 Nginx 預設的 proxy timeout 後,出現 HTTP 500 Internal Sever Error 錯誤訊息。但實際上可以讓 Nginx 暫時略過 pending 的 server,將 request 優先轉送給其他 3 個正在 stand by 的 server。

利用 ngx_http_upstream_module 中的 parameter,將 timeout 的 server 標記先移出 pool,讓 request 不會送到 pending 的 server,過一段時間以後再拉進 pool 繼續使用。

以下設定若該 server 超過 10 秒沒有回應,則移出 pool,過 5 分鐘在拉回 pool:
upstream redmine_thin_servers {
  server unix:/home/redmine/run/thin.0.socket fail_timeout=10s slow_start=300s;
  ....
}

另外也可讓 Nginx 主動去檢查 server status,不要在使用者送 request failed 後才將 server 移出 pool。health_check 會讓 Nginx 主動發送間單的 request,透過 HTTP status 來判斷 server 狀態:
  location / {
    try_files $uri/index.html $uri/index.htm @redmine_thin_servers;
  }

  location @redmine_thin_servers {
    proxy_pass http://redmine_thin_servers;
    health_check interval=3s 
  }


測試機上,不曉得是不是 Nginx 1.6.0 還不支援這幾個參數,以上的設定在 configtest 會出現錯誤訊息:
invalid parameter "slow_start=300s"
unknown directive "health_check"


後來是繞路換一個方法。Nginx 的 ngx_http_proxy_module 中,可以設定當 server 多久沒有收到回應 (proxy_read_timeout) 時,直接當作失效並從 pool 中找下一台 server 送 request:
  location / {
    try_files $uri/index.html $uri/index.htm @redmine_thin_servers;
  }

  location @redmine_thin_servers {
    proxy_pass http://redmine_thin_servers;

    # 超過 3 秒沒有收到 response 則當作 timeout
    proxy_read_timeout 3s;

    # 若 response 是 HTTP 50x 也當作 server 失效
    proxy_next_upstream error timeout http_500 http_502 http_503 http_504;
  }

2013/10/08

Nginx 將 HTTP 轉向 HTTPS

看了「Switch to HTTPS Now, For Free」後,到 StartSSL 將手上幾個常用的網域都申請了 SSL 憑證。

SSL 憑證上線使用後,再來就讓任何 HTTP 連線都可以導到 HTTPS。

server {
   location / {
      # rewrite ^ https://my.url;
      rewrite ^ https://$server_name permanent;
   }
}


或者要將網址後半部的參數一起轉過去:
location {
   rewrite ^ https://$server_name$request_uri permanent;
}


note: 「permanent」會發送 HTTP 301

Ref:
Nginx HttpRewriteModule
http://wiki.nginx.org/HttpRewriteModule

2013/09/29

URL Rewrite for FuelPHP on Nginx

在 Nginx 設定檔預設值時,FuelPHP 的 URL 看起來是:
http://my.site/index.php/controller/param/

將 URL rewrite 成:
http://my.site/controller/param/

先改 Nginx 設定:
location / {
   try_files  $uri /index.php?$uri$args;
}

這樣能讓 Nginx 處理不含 index.php 的 URL,再來需要修改 FuelPHP 的設定,讓 Uri::create() 等產生的網址自動將 index.php 去掉。

修改 fuel/app/config/config.php:
return arary(
   //'index_file'  => 'index.php',
   'index_file'  => false,
);

2013/06/15

隱藏 HTTP Server 資訊

Apache 和 Nginx 預設頁尾都會加上伺服器資訊,像是:


有些環境或許很忌諱出輸出這類的資料,可以修改設定將他隱藏起來。

Apache conf 中調整 ServerTokens 參數,將頁尾伺服器資訊作不同的調整。設定檔路徑可能會不一樣,可以 grep ServerTokens,以下是 Ubuntu 的路徑。

/etc/apache/conf.d/security:
#ServerTokens OS
ServerTokens Prod


Nginx conf 裡面則是用 server_tokens 參數,將其設定為 off 則不會輸出伺服器版本號。
server {
   server_tokens off;

   ...
}