CentOS 需注意 /var/lib/nginx/ 的 Permission

休耕結束,決定要讓長草許久的 Blog 再次開張,雖然不見得有足夠時間撰寫長篇或深入論述的文章,但期許自己恢復文字紀錄,將工作上的點滴筆記保留下來。這篇是紀錄在 CentOS 上使用 Nginx 遇到的一個小狀況。

(在 Google 搜尋 500 internal server error 的結果。) (本文同步發表於 Medium

TL;DR

在 CentOS 安裝 Nginx 時,預設會新增的 Linux User 是 nginx,同時在預設的 Config 檔案 /etc/nginx/nginx.conf裡面也會看見參數 user nginx;。如果你習慣修改 nginx.conf 的參數 user,例如改成 www-data,那麼要注意 /var/lib/nginx/ 也同時要開放權限給 www-data。不然當 Nginx 在處理較大的 Request 而需要寫入一些暫存資料時,就會因為權限不足而回傳 500 internal server error,同時在 Error log 中會出現類似 /var/lib/nginx/tmp/client_body/0000000001" failed (13: Permission denied) 的紀錄。

過程紀錄


某個專案開發完畢了,但將專案部署上線至 Production 環境後,卻出現了不同於測試環境的異常狀況。

異常的狀況是:

  • 乍看之下網站功能都正常,前台似乎都能正常的讀取資料。
  • 出現異常的功能都是 POST Request。
  • 但大部分的 POST Request 也都正常,唯獨要上傳圖片時,瀏覽器會顯示 500 的 HTTP Status Code。

由於 Production 環境處於一個特別的私有網路之內,據說在 Production 主機之外有多了一層非我方設置的 Load balancer(或其他網路設備)。因此當下的第一個念頭是,這 500 到底是誰吐出來的?

首先從瀏覽器的開發者工具查閱 Request 與 Response,發現確實是由 Nginx 回傳 500 internal server error

於是接著查看為應用程式所設置的 Nginx Error log,按照 Nginx site config 中的設置,從 server { } 之內找到該應用程式的 Error log 存放位置。

error_log       /var/log/nginx/application_error.log;

結果打開 /var/log/nginx/application_error.log 一看,居然是一片空白!?怪哉,明明瀏覽器都顯示 500 了,Nginx 的 Error log 卻沒有留下任何資訊?沒關係,也許先看看應用程式或 PHP 的 Log 可以找到其他資訊。但結果依然是一片空白,沒有 Log,這下是要我先學會通靈了嗎?

由於除錯狀況貌似卡關,決定重新來過,轉換一下除錯流程,也許能有不一樣的啟發。首先再次驗證何種狀況才會產生此 500 錯誤?

目前收到的錯誤情境是上傳圖片時,會收到 500,那麼如果不是上傳圖片,而是在表單內填寫大量的資料呢?是不是當 Request 超過一定大小,就能得到同樣的錯誤?

實驗結果證實上面的推論是正確的。與之同時在實驗過程還真讓我發現了一個新的線索——原來不論 POST Request 是成功或失敗(500),為應用程式設置的 Nginx Access log 都有紀錄該次 POST Request 的資訊;即是 Nginx 在收到該 Request 時,一開始是有正常處理 Request,但在處理到一半時出現錯誤了,所以才會出現這種為應用程式分配的 Nginx Error log 沒有任何記錄的狀況。

然後,研究到這裡就發現自己非常天才,在前面的除錯過程中,居然忘了同步查看全域的 Nginx Error log。

# 預設會存放在此位置
error_log /var/log/nginx/error.log;

終於不用通靈,可以看著 Error log 除錯,得到的關鍵錯誤資訊如下。

[crit] 15559#0: *5 open() "/var/lib/nginx/tmp/client_body/0000000001" failed (13: Permission denied), request: "POST /forum HTTP/1.1"

Nginx 由於 Request 較大,需要在 /var/lib/nginx/tmp/client_body/ 寫入暫存資料,但卻遇到 Permission denied 權限不足的狀況。所以才會回傳 500 internal server error,因為確實是已成功接受 Request,但處理到一半出現異常,所以說 HTTP Status Code 並沒有騙人啊。

但問題就怪在怎麼會出現 Permission denied 呢?

先確認目前運行中的 Nginx,其 Worker process 是運行在 www-data

# ps aux | grep nginx
www-data  6252  0.0  0.0  56216  6264 ?        S     2020   0:00 nginx: worker process
www-data  6253  0.0  0.0  56480  6292 ?        S     2020   0:01 nginx: worker process
www-data  6254  0.0  0.0  56216  6300 ?        S     2020   0:01 nginx: worker process
www-data  6255  0.0  0.0  56480  6500 ?        S     2020   0:03 nginx: worker process
root     15636  0.0  0.0  55008  3840 ?        Ss    2020   0:00 nginx: master process /usr/sbin/nginx

/var/lib/nginx/tmp/ 資料夾的權限卻是 nginx

這時才想起,由於該專案 Production 環境的特殊性,所以 Production 環境與慣用的測試環境是不同的 Linux OS,然而在佈建 Production 環境時,依然是使用了慣用環境的 Config。

由於在 CentOS 安裝 Nginx 時,/var/lib/nginx/ 的 Owner 預設會是 nginx,但在慣用環境的 nginx.conf,素來都習慣設定讓 Nginx worker process 配合 php-fpm 一律以 user www-data 來運行。由於 user 與 owner 的不同,才導致了這次 Permission denied 的異常狀況。

結語


如果是遇到同樣 Error 資訊的朋友,那麼你想要知道的答案已經寫在文章標題及最前面 TL;DR,記得去查看 nginx.conf 的設定與 /var/lib/nginx/ 的權限。

結語剩下的空間,想要談一談與這次事件有關的其他議題。這次的異常狀況,最終看起來只是一個 Config 與資料夾權限設定錯誤的問題,但就更高的角度來看,這其實是組態配置及環境管理的議題。

  • 為何測試環境與 Production 環境,兩者是如此大不相同?

以現今大家都已經普遍認識 DevOps、CI/CD、Automation、Infrastructure as Code(IaC)的觀念,相信各位都會提出上面的疑問。

由於兩個環境的差異過大,導致問題無法被及早發現,同時也意味著,並沒有讓前述之各項優良實踐方法發揮它們能為團隊帶來的好處。

  • 環境不同,無法在測試環境再現 Production 環境的使用者操作情境。
  • 環境不同,無法在測試環境驗證 Production 環境的組態配置,甚至是其他的自動化腳本。
  • 環境不同,即便有規劃 CI/CD,依然無法正確模擬 Production 環境,讓 CI/CD 實現及早驗證的目標。
  • 環境不同,代表有可能需要多維護一套組態配置及自動化腳本,耗費更多人力成本。
  • 環境不同⋯⋯

原來環境不同可能會產生這麼多的不良影響,除了上述幾點之外,你覺得還有哪些不良影響呢?同時你覺得該如何避免這些狀況呢?相信大家一定有著各種相關的經驗談可以分享吧!

你的企業與團隊目前 DevOps、CI/CD、IaC 的實踐狀況如何呢?歡迎與我分享、或是來到 DevOps Taiwan Community 公開分享你的經驗喔。

更多文章