CentOS 需注意 /var/lib/nginx/ 的 Permission
(2021/12/25 註記:左思右想,決定棄用 Medium,未來更多文章請參閱 https://chengweichen.com)
(本文的 Canonical URL —https://chengweichen.com/2021/01/centos-nginx-permission.html)
休耕結束,決定要讓長草許久的 Blog 再次開張,雖然不見得有足夠時間撰寫長篇或深入論述的文章,但期許自己恢復文字紀錄,將工作上的點滴筆記保留下來。這篇即是紀錄在 CentOS 上使用 Nginx 遇到的一個小狀況。
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 公開分享你的經驗喔。