迷之 crontab 異常:不運行、不報錯、無日誌?原來是這些原因


迷之 crontab 異常:不運行、不報錯、無日誌?原來是這些原因

1、背景

前幾天新同學入職,一不小心將跳板機上的 crontab 清空了,導致凌晨一大批任務異常,同事問了運維同學也沒有備份,這一百多個任務要是恢復起來可不是件容易的事兒。還好我去年某天開始做了定時備份,每分鐘一次 backup 到本地磁盤,最後很容易的將 crontab 給恢復了。

這件事情過後我也在想,一臺跳板機整個部門都共用一個賬號, Linux 水平和安全意識又參差不齊,其實很難避免以後還會誤操作,比如一下子將 home 目錄全乾掉。所以我想 backup 最好不要保存在本地,於是想一條命令將其備份到 hadoop 集群上去。

2、問題

迷之 crontab 異常:不運行、不報錯、無日誌?原來是這些原因

當時覺得這個問題很簡單,於是隨手寫了一條類似這樣的命令:

<code>*/1 * * * *  /bin/cat > /root/a.log 2>&1/<code>

本地測試了沒問題,但是 crontab 怎麼都不成功,也看不到錯誤日誌,a.log 一直是空的。

這個我就比較好奇了,按理說 a.log 應該是能拿到所有的標準輸出和標準錯誤的,究竟什麼原因導致 crontab 既不執行又不報錯呢?

迷之 crontab 異常:不運行、不報錯、無日誌?原來是這些原因

3、分析

debug 終極大法還是得看日誌,本 case 最讓人疑惑的在於沒有日誌,如果能找到日誌所有的迷霧應該都能煙消雲散。

於是,我嘗試看看 /var/log 下有沒有 crontab 的執行日誌,看了下服務器居然沒開啟 cron.log,由於非管理員沒權限修改任何配置或設置,於是我在本地 WSL 裡用 Ubuntu 把問題復現了下。

3.1 開啟 cron.log

<code>sudo vim /etc/rsyslog.d/50-default.conf
cron.*  /var/log/cron.log #將cron前面的註釋符去掉
#重啟rsyslog
#sudo /etc/init.d/rsyslog restart
sudo service rsyslog restart
sudo service cron restart/<code>

雖然能看到 crontab 執行日誌了,但全都是一些沒意義的日誌或 info 提示:

<code>Mar 31 20:58:20 Surface-Pro5 crontab[223]: (root) BEGIN EDIT (root)
Mar 31 20:58:53 Surface-Pro5 crontab[223]: (root) REPLACE (root)
Mar 31 20:58:53 Surface-Pro5 crontab[223]: (root) END EDIT (root)
...
Mar 31 21:13:01 Surface-Pro5 CRON[451]: (CRON) info (No MTA installed, discarding output)
Mar 31 21:14:01 Surface-Pro5 CRON[471]: (CRON) info (No MTA installed, discarding output)
.../<code>

仔細觀察日誌發現貌似在提示我們 MTA 沒裝,crontab 輸出被丟棄了。同時查看 sudo tail -f /var/mail/ 發現爆出大量 warning: unable to look up public/pickup: No such file or directory! 的警告。

3.2 安裝 postfix


迷之 crontab 異常:不運行、不報錯、無日誌?原來是這些原因

由於 crontab 通知機制是將錯誤會以郵件形式發給所屬登錄賬號或者系統管理員,如果沒有安裝郵件管理服務,那麼這部分信息會被系統丟棄。那咱們安裝 postfix 即可:

<code>sudo apt-get install postfix
sudo service postfix start/<code>

再次查看日誌發現了報錯日誌:

<code>  1 From [email protected]  Sat Mar 31 21:33:38 2018
  2 Return-Path: 
  3 X-Original-To: root
  4 Delivered-To: [email protected]
  5 Received: by Surface-Pro5.localdomain (Postfix, from userid 0)
  6     id CCE42300000000E229; Sat, 31 Mar 2018 21:25:02 +0800 (DST)
  7 From: [email protected] (Cron Daemon)
  8 To: [email protected]
  9 Subject: Cron  /bin/ls > /root/a.log 2>&1
 10 MIME-Version: 1.0
 11 Content-Type: text/plain; charset=UTF-8
 12 Content-Transfer-Encoding: 8bit
 13 X-Cron-Env: 
 14 X-Cron-Env: 
 15 X-Cron-Env: 
 16 X-Cron-Env: 
 17 Message-Id: <20180331133337.CCE42300000000E229>
 18 Date: Sat, 31 Mar 2018 21:25:02 +0800 (DST)
 19 
 20 /bin/sh: 1: Syntax error: "(" unexpected/<code>

3.3 如何修復

看到郵件裡的錯誤提示咱們立馬就能明白 crontab 之所以無法執行,是因為 crontab 環境變量默認加載的是 sh,而非 bash,不支持進程代換這種語法,咱們有兩種辦法避免:

3.3.1 crontab 開頭指定 shell 類型

完整的 crontab 格式如下

<code>SHELL=/bin/bash
PATH=/sbin:/bin:/usr/sbin:/usr/bin
MAILTO=root
HOME=/
# .---------------- minute (0 - 59) 
# |  .------------- hour (0 - 23)
# |  |  .---------- day of month (1 - 31)
# |  |  |  .------- month (1 - 12) OR jan,feb,mar,apr ... 
# |  |  |  |  .---- day of week (0 - 6) (Sunday=0 or 7)  OR sun,mon,tue,wed,thu,fri,sat 
# |  |  |  |  |
# *  *  *  *  *  command to be executed/<code>

也就是說,咱們可以在 crontab 文件的開頭指定 shell 類型這樣就不會有問題了。

3.3.2 封裝成腳本

迷之 crontab 異常:不運行、不報錯、無日誌?原來是這些原因

其實不建議在 crontab 裡執行復雜邏輯,最好封裝成腳本,這樣好控制,比如:

<code>*/1 * * * *  bash a.sh >> /root/a.log 2>&1/<code>

3.4 重定向無法獲取錯誤的原因

雖然咱們根據錯誤日誌知道怎樣修改讓命令正常執行,但是我們並未回答文章開頭的疑問:究竟為何 2>&1 無法重定向拿到所有的標準輸出和標準錯誤?有點違反常理了。這個還和 shell 解釋器類型無關,比如下面這條命令,在 bash 下也是隻能拿到標準輸出,無法拿到標準錯誤:

<code>ls  debuglog/a.log 2>&1/<code>

這個問題的深層次原因得追溯到 shell 的一個概念:子進程

其實上圖中的命令這樣改也行:

<code>ls > debuglog/b.log 2>&1) >> debuglog/a.log 2>&1/<code>

因為 debuglog/a.log 2>&1 只能拿到當前進程的標準輸出與標準錯誤。

另外需要注意的是通過()或管道fork出來的子進程,繼承了父進程的所有環境變量,和平時bash xxx.sh或者./xxx.sh起的不同的, 而$是一起繼承的,但$BASHPID繼承後重新賦值了,這和新開個bash的方式是不同的。

除了上面的寫法,如果要深究茴字還有幾種寫法,那麼還有如下兩種寫法:

<code>bash a.sh > debuglog/a.log 2>&1
bash -c "ls  debuglog/a.log 2>&1/<code>

至此,從文章開頭的問題,咱們從如何讓日誌輸出以及代碼如何改寫,到最後的 root cause 都分析了一遍,希望能對大家有所啟發和參考。

結尾:

小編近幾年在學習Python!對於想學習Python的朋友們,我想說:很多人學了一個星期就放棄了,為什麼呢?其實沒有好的學習資料給你去學習,你們是很難堅持的,這是小編收集的Python入門學習資料。關注,轉發,後臺(我主頁上方)如下圖操作,即可免費靈取!希望對你們有幫助!

迷之 crontab 異常:不運行、不報錯、無日誌?原來是這些原因


分享到:


相關文章: