摘要
我們項目框架是 ThinkPHP5.0.24
,系統環境爲 Linux(Red Hat 4.8.5-11)
。
因爲一些原因,每次到月初,項目應用總會報錯:
error_log(/******/runtime/log/201903/20_sql.log): failed to open stream: Permission denied
記錄一下,解決的過程和方法。
心路歷程
round 1
根據報錯信息可以知道,有可能是文件夾的權限問題。
解決方法是: 通過命令行或者圖形化工具授予該日誌文件夾 777 的權限 。
如果問題就這麼解決了,就不會有這篇文章了。
round 2
這個月處理完之後,下個月同樣會出現這個問題。
這就引起重視了呀。不然,難道每個月都要調鬧鐘、手動授權新增的文件夾寫入的權限嗎?
這不行。
欸!新增文件夾?
爲什麼新增的文件夾會權限不足呢?
查看各級日誌文件夾信息:
> ll runtime/log/201903/
total 588
-rw-r--r-- 1 root root 370701 Mar 20 23:24 20.log
-rw-r--r-- 1 root root 217029 Mar 20 21:52 20_sql.log
-rw-r--r-- 1 root root 1192016 Mar 21 15:03 21_cli.log
此時文件夾內只能生成所屬 root
的文件。
> ll runtime/log/
total 4
drwxr-xr-x 2 root root 4096 Mar 20 17:55 201903 // 此時報錯,error 日誌無權限寫入
// 改成 drwxrwxrwx 2 root root 4096 Mar 20 17:55 201903 可解決,但是下月問題重現
// 刪除此文件夾,主動請求接口生成:drwxr-xr-x 2 www www 4096 Mar 20 17:55 201903 可順利寫入
經過刪除文件夾「runtime/log/201903/」,跑接口測試重新生成文件夾發現,此時生成的文件夾所屬組和所屬用戶是 www
。
那麼說明,出問題的這個文件夾(所屬 root
)不是跑框架的接口記錄日誌時生成的。
那麼這個 root
所屬的文件夾是誰生成的呢?
觀察成功生成的日誌文件可發現有以 _cli.log
結尾的文件。這不是定時任務的日誌文件嗎?只有它能正常運行?
原因是:以 root
權限設置的 crontab
裏每分鐘執行的計劃任務,在每個月初第一時間執行並在框架下創建了 _cli.log
類型的日誌。所以該月的文件夾所屬就爲 root
,而用戶 www
在此文件夾內默認沒有寫入的權限,從而導致的報錯。
round 3
既然知道了是 crontab
的定時任務的問題,那就重點盤它吧。
在進一步瞭解 crontab
的知識1後得知,在 root
下可以設定指定用戶(比如我的 www
用戶)的時程表。原文如下:
-u user 是指設定指定 user 的時程表,這個前提是你必須要有其權限(比如說是 root)才能夠指定他人的時程表。如果不使用 -u user 的話,就是表示設定自己的時程表。
通過以下操作:
> crontab -e -u www
把原來每分鐘執行框架腳本的定時任務全部搬到了這個只針對 www
用戶的時程表裏。這樣第一時間生成的文件夾「runtime/log/201903/」的所屬就是 www
了。
搬到以 www
用戶運行的時程表後,發現:
- 生成的日誌文件夾「runtime/log/201903/」的所屬用戶(組)確實變爲了
www
,也沒有發生錯誤日誌生成權限不足的問題(也就是上文提及的問題成功解決了,證明這個方向是可取的); - 沒有生成
_cli.log
等定時任務運行產生的日誌文件,也就是說新的定時任務沒有執行成功。
round 4
排查爲何定時任務沒有執行成功。
先排查系統日誌
> tail -f /var/log/cron
Mar 21 10:18:01 localhost CROND[22349]: (www) CMD (「時程表中的 command」)
... (www) MAIL (mailed 58 bytes of output but got status 0x004b#012)
注意到上面的 MAIL
的信息,查了一下:
這個其實是 postfix 的配置問題,本次的問題不在這,是計劃任務在執行完之後會通過 postfix 發郵件給執行完的用戶郵箱,現在發不出去導致的,修改下配置就行了。
修改配置:
> vim /etc/postfix/main.cf
inet_interfaces = all
inet_protocols = all
確保這兩個都是all 就可以了,然後重啓 postfix。
重啓後確實沒有了那條關於 MAIL
的信息,但是會有如下提醒:
You have new mail in /var/spool/mail/root
通過命令查看:
> tail -f /var/spool/mail/root
X-Cron-Env: <SHELL=/bin/bash>
X-Cron-Env: <HOME=/usr/share/httpd>
X-Cron-Env: <PATH=/usr/bin:/bin>
X-Cron-Env: <LOGNAME=www>
X-Cron-Env: <USER=www>
Message-Id: <***@***.localdomain>
Date: Thu, 21 Mar 2019 10:17:01 +0800 (CST)
/bin/bash: ***.log: Permission denied
好了,Permission denied,也就是說時程表裏的任務後面的輸出日誌權限不足咯。
因爲時程表改成了指定用戶的了,所以權限不足也正常。
找到原因了,那麼解決方案有兩個:
- 找到輸出日誌的默認文件夾,授權給
www
用戶寫入的權限; - 刪除輸出日誌。
基於種種原因想偷懶,比如時程表裏跑的是框架內的 php
文件,會在框架內的日誌文件夾生成 _cli.log
或 _error_cli.log
的日誌,所以就選擇了直接刪掉了時程表中的輸出日誌部分。
刪除了輸出日誌,但是如果報錯,依舊會發送 mail
到 /var/spool/mail/root
,此時可以在命令後面加上 >/dev/null 2>&1
,就不會發送 mail
了。
至此,定時任務恢復正常。
也就是 KO!
其它問題
1. 日誌文件生成的所有者爲 root
通過命令:
> crontab -e
默認設定當前用戶的時程表。
如果以 root
用戶登陸(果然要儘量避免啊摔),設定的時程表就屬於 root
的了。
2. 關於 TP5 生成的錯誤日誌是否報錯的問題
測試如下:
try {
Log::write('test', 'error');
//Log::error('test:');
} catch (\Exception $e) {
echo '拋出異常啦!';
}
同樣是無寫入 error
文件的權限,Log::write
會拋出異常,中斷程序;而 Log::error
不會拋出異常,也不生成文件。
總結
雖然解決了困惑已久的問題很有成就感,但是歸根結底是因爲使用了 root
登陸管理服務器導致的問題。
流下了沒有技術的淚水啊。希望同道中人能避免踩坑吧。