Debian unattended-upgrades 為何沒有作用?
Debian 預設未啟用 unattended-upgrades。正確設定 Origins-Pattern,檢查 apt-daily timers,並確認它確實能自行套用更新。
新安裝的 Debian 上,為什麼 unattended-upgrades 沒有作用
在 Debian 上,即使已安裝 unattended-upgrades,也可能一次更新都不會執行,因為安裝套件與啟用套件是兩個不同步驟。套件會在設定自身之前提出 debconf 問題,而 Debian installer 會將該問題的答案儲存為 false。Ubuntu 對同一個問題的回答則相反,因此相同的套件在 Ubuntu 上看似正常,在 Debian 上卻像是故障。
系統不會明確指出這個問題。開機時不會顯示錯誤,登入時不會顯示警告,也沒有可供查看的 log,因為負責寫入該 log 的程式碼從未被呼叫。啟用它只需執行一個命令。本指南其餘內容會說明啟用後仍可能阻止更新的 4 個因素:可從哪些 repositories 執行升級、systemd timers 實際何時觸發、如何得知執行失敗,以及是否允許機器自行重新開機。
變更任何內容前,先查看系統目前的判斷
dpkg -l unattended-upgrades | tail -n 1
sudo apt install -y debconf-utils
sudo debconf-show unattended-upgrades
cat /etc/apt/apt.conf.d/20auto-upgrades
apt-config dump APT::Periodicdebconf-show 會列印儲存於 unattended-upgrades/enable_auto_updates 的值。該行開頭的 * 表示這個值是由某項設定指定,而不是套件預設值。在 Debian installer 建立的機器上,這項設定通常來自 installer。
cat 可能會列印 cat: /etc/apt/apt.conf.d/20auto-upgrades: No such file or directory。這本身就能解釋為何沒有任何動作:沒有該檔案時,就沒有定期執行的設定,因此永遠不會排程任何工作。
apt-config dump 才是重要的命令。APT 會依檔名順序讀取 /etc/apt/apt.conf.d/ 中的每個檔案並合併內容,因此在 99local 設定的單純值,會覆寫 20auto-upgrades 中的相同值。讀取單一檔案只能得知該檔案的內容。apt-config dump 則會告訴你 APT 實際會執行的動作。
有兩個 key 會決定是否執行任何動作:
APT::Periodic::Update-Package-Lists會更新套件清單,這就是apt update手動執行的工作。APT::Periodic::Unattended-Upgrade會實際執行升級。
它們的值不是 true 或 false,而是以天數表示的間隔。"1" 表示「如果過去 1 天內尚未執行,就執行這項工作」;"7" 表示每週執行;"0" 表示永不執行。APT::Periodic::Unattended-Upgrade "0"; 是有效的設定,但會永久不執行任何工作,而且不會顯示錯誤。如果輸出在該 key 顯示 0,或完全沒有顯示該 key,原因就在這裡。
啟用方式:使用 dpkg-reconfigure,或自行寫入金鑰
sudo apt update
sudo apt install -y unattended-upgrades
sudo dpkg-reconfigure --priority=low unattended-upgrades此處 --priority=low 並非選用項目。該問題的優先級很低,因此在預設優先級下,dpkg-reconfigure 不會顯示任何內容、不會變更任何設定,並以 0 結束;這看起來完全像是命令已成功執行。請在對話方塊中回答 yes。接著,套件的 postinst 會根據你的回答寫入 /etc/apt/apt.conf.d/20auto-upgrades。
如果是透過 script 建置的機器,就沒有可供回答的對話方塊,因此請先設定答案:
echo 'unattended-upgrades unattended-upgrades/enable_auto_updates boolean true' \
| sudo debconf-set-selections
sudo dpkg-reconfigure -f noninteractive unattended-upgrades
apt-config dump APT::Periodic你也可以直接寫入這兩個金鑰:
printf 'APT::Periodic::Update-Package-Lists "1";\nAPT::Periodic::Unattended-Upgrade "1";\n' \
| sudo tee /etc/apt/apt.conf.d/20auto-upgrades
apt-config dump APT::Periodic這會立即生效,但也會留下陷阱。debconf 中的答案仍然是舊值,因此下一次執行 dpkg-reconfigure 或重新安裝套件時,系統會根據 debconf 重新寫入該檔案,完全不輸出任何訊息就撤銷你的修改。請同時設定這兩者,或設定 debconf,並讓 postinst 管理該檔案。
這是它與另一側同系列套件之間唯一真正的差異。另一側的安裝程式會替你啟用相同的套件,因此 Ubuntu unattended-upgrades 設定從一台已經自行套用修補程式的機器開始,主要處理調整工作。從這裡開始的所有內容都適用於兩者。
Debian 實際會為你安裝哪些更新?
啟用 timer 不等於同意安裝所有更新。每個套件來源都帶有版本中繼資料,包括 origin、label、suite、codename 和 site。unattended-upgrades 會查看每個可升級套件的 candidate version,讀取其來源的中繼資料,只有在該來源符合 Unattended-Upgrade::Origins-Pattern 中的項目時才會安裝。沒有符合項目就不升級;這是設計上的行為,不會顯示錯誤。
先列印你的模式,再列印與模式比對的中繼資料:
apt-config dump Unattended-Upgrade::Origins-Pattern
grep -H -E '^(Origin|Label|Suite|Codename|Components):' /var/lib/apt/lists/*Release
apt-cache policygrep -H 會在每一行保留檔名,讓你看出每一段中繼資料屬於哪個來源。這些欄位的值就是模式右側的內容。模式中的 ${distro_codename} 會在執行時替換為目前所在版本的 codename,因此同一個檔案可跨越版本升級持續使用。
請將自己的模式與中繼資料並列查看,因為「我只會取得安全性修正,還是也會取得 point release 更新」的答案只寫在這裡:
- 指定
label=Debian-Security的模式會符合 security archive。Debian 的安全性公告會發布到這裡。 - 指定
-updatessuite 的模式會符合 stable-updates。這裡提供 Debian 在 point release 之間發布的更新,例如時區資料。 - 指定未附加其他名稱的 release suite,會在 point release 變更發布時一併取得這些變更。這會帶來更多變動,也需要你進行更多測試。
- 你自行加入的 repository 在你為它撰寫模式前,完全不會符合任何項目。
最後一點常讓人意外。第三方 repository 有自己的 origin 和 label,因此 unattended-upgrades 會看到 candidate,也會看到其來源不符合任何項目,接著略過它。為第三方 repository 加入模式前應審慎評估,因為 vendor repository 可能在同一個 suite 中發布新的 major version;這代表你同意讓該軟體在深夜自動進行 major upgrade。
unattended-upgrades 也不會將 Debian 從一個 release 升級到另一個 release。它只會在目前所在的 release 內升級套件。從一個 stable release 升級到下一個版本,仍然需要由你自行安排並手動執行。
修改清單時,請記住 APT 的清單會採用附加方式處理。另一個檔案中的第二個 Origins-Pattern 區塊會新增到套件提供的清單,而不是取代它。如果你的目的是取代原清單,請先清除它:
#clear Unattended-Upgrade::Origins-Pattern;
Unattended-Upgrade::Origins-Pattern {
"origin=Debian,codename=${distro_codename},label=Debian-Security";
"origin=Debian,codename=${distro_codename}-security,label=Debian-Security";
};請將本機變更放在新檔案中,並讓檔名排序在套件提供的檔案之後,例如 /etc/apt/apt.conf.d/52unattended-upgrades-local。50unattended-upgrades 是 conffile,因此編輯它會讓套件未來每次升級時暫停,並詢問如何處理你的版本。使用獨立檔案則不會發生衝突。
另外還有兩個值得一併列印的設定鍵。Unattended-Upgrade::Allowed-Origins 是相同概念的舊格式,以 origin:archive 配對表示;系統仍會讀取它,因此從教學文章複製的設定常會同時包含兩種清單,卻無法清楚判斷究竟是哪一份清單符合條件。Unattended-Upgrade::Package-Blacklist 儲存套用於套件名稱的正規表示式;如果表達式過於寬鬆,封鎖的套件會遠超過你的原本意圖。以下的 dry run 會列印實際套用的結果。
為什麼沒有在預期時間執行?
這項工作由兩個 systemd timers 驅動,且兩者的職責不同。apt-daily.timer 會啟動 apt-daily.service,負責更新套件清單並下載套件。apt-daily-upgrade.timer 會啟動 apt-daily-upgrade.service,而這個單元才會呼叫 unattended-upgrade。兩者都會以不同參數執行 /usr/lib/apt/apt.systemd.daily。如果停用或 mask 第二個 timer,兩個週期性工作都可能讀取 1,但始終不會安裝任何內容。
systemctl list-timers 'apt-daily*' --all
systemctl is-enabled apt-daily.timer apt-daily-upgrade.timer
systemctl cat apt-daily-upgrade.timerlist-timers 會針對每個單元提供 NEXT、LEFT、LAST 和 PASSED。沒有 NEXT 的 timer 不會執行。若 is-enabled 顯示 masked,表示有人強制停用了該單元;你在 apt.conf.d 中撰寫的任何內容都不會改變這個狀態。
現在查看 systemctl cat 顯示的 [Timer] 區段。OnCalendar 是 timer 最早可能觸發的時間。RandomizedDelaySec 會在該時間之後加入隨機等待,避免一批 Debian 電腦在同一秒連線到相同的 mirror。因此,NEXT 欄顯示的時間可能與 OnCalendar 不一致,昨天的執行時間也可能落在不同分鐘。這是預期行為。Persistent=true 表示電腦在排定時間關機時,會在下次開機後不久執行工作,而不是略過當天。
若要調整時間範圍,請覆寫單元,不要直接編輯它:
sudo systemctl edit apt-daily-upgrade.timer[Timer]
OnCalendar=
OnCalendar=03:30
RandomizedDelaySec=30m空白的 OnCalendar= 列是必要的,因為值為清單的單元設定會累加:省略這一列會保留套件提供的排程,並再增加一個排程。systemctl edit 會替你重新載入 systemd,因此請使用 systemctl list-timers 'apt-daily*' 確認結果,並查看新的 NEXT。
你不必等待 timer 觸發即可測試這些設定:
sudo systemctl start apt-daily-upgrade.service
journalctl -u apt-daily-upgrade.service --since -1h有些人查看 /etc/cron.daily 時會對一點感到困惑:APT 仍會提供 /etc/cron.daily/apt-compat,供未使用 systemd 的系統使用。請使用 cat 讀取它。在使用 systemd 的系統上,它會提早結束,因此不會重複執行工作。
驗證是否正常運作:unattended-upgrade --dry-run --debug
sudo unattended-upgrade --dry-run --debug執行檔名稱是單數,套件名稱則是複數。在此輸入 unattended-upgrades 會得到 command not found,許多人會將此結果解讀為套件不存在的證明。
這個指令會回答幾乎所有「為什麼略過該套件」的問題,因為它會輸出自己的判斷依據。輸出開頭附近會列出根據模式計算出的來源:
Allowed origins are: ...接著每個候選套件各列出一行,包含預計安裝版本的來源記錄:
Checking: curl ([<Origin component:'main' archive:'stable' origin:'Debian' label:'Debian' site:'deb.debian.org' isTrusted:True>])最後列出預計處理的套件;如果系統目前沒有任何工作,則會顯示:
No packages found that can be upgraded unattended and no pending auto-removals將前兩項輸出放在一起,便能直接診斷問題。找出預期應升級之套件的 Checking: 行。逐欄比較其中的來源記錄與上方列出的允許來源。只要有一個欄位不相符,最常見的是 label 或 archive,就是該套件遭略過的完整原因。
--dry-run 只會在記憶體中標記套件,不會安裝任何內容,因此可以隨時執行。它仍會將結果附加至 /var/log/unattended-upgrades/unattended-upgrades.log。
如果來源相符,但套件仍被保留,請依序檢查以下項目:
apt-mark showhold會列出你或其他工具設定固定版本的套件。unattended-upgrades 不會處理已保留的套件。- 該升級可能需要移除或新增其他套件。除非相關金鑰允許,否則 unattended-upgrades 會避免這類變更。請與
sudo apt-get -s upgrade比較;它會在不套用這些安全規則的情況下顯示相同判斷。 - dpkg 在中斷的執行程序後處於部分設定狀態。使用
sudo dpkg --configure -a修正,然後再次檢查。 /var沒有可用空間,因此無法下載或解開任何內容。使用df -h /var檢查。/boot充滿舊的核心,導致下一次核心升級失敗。使用df -h /boot檢查,並輸出apt-config dump Unattended-Upgrade::Remove-Unused-Kernel-Packages,確認是否已啟用清理功能。
如果計時器正在執行時手動執行 apt,就會看到 Could not get lock /var/lib/dpkg/lock-frontend。這則訊息表示 unattended-upgrades 正在正常運作。請等待它完成。
如何得知執行失敗的時間?
先查看日誌,因為不論是否設定其他項目,日誌都會存在:
sudo tail -n 40 /var/log/unattended-upgrades/unattended-upgrades.log
sudo tail -n 40 /var/log/unattended-upgrades/unattended-upgrades-dpkg.log
journalctl -u apt-daily-upgrade.service --since -7d第一個檔案是決策日誌,記錄檢查了哪些項目、選擇了哪些項目,以及安裝了哪些項目。第二個檔案包含原始的 dpkg 輸出;如果套件的 postinst 指令碼執行失敗,通常可在這裡看到。若升級是在機器關機時執行,還會出現獨立的關機日誌。
郵件是最常見的報告管道:
Unattended-Upgrade::Mail "you@example.com";
Unattended-Upgrade::MailReport "on-change";MailReport 接受 always、on-change 和 only-on-error。only-on-error 聽起來是較嚴謹的選擇,但對無人登入的伺服器而言通常不適合,因為機器完全停止升級後也不會傳送錯誤。如此一來,正常運作的主機和已停止運作的主機都同樣保持沉默。on-change 則會在每次安裝項目時傳送郵件,因此郵件也能證明 timer 仍在運作。
只有機器能傳送郵件時,郵件才能離開機器。unattended-upgrades 會將訊息交給本機郵件系統,因此需要 postfix 等 MTA(mail transfer agent),或 msmtp 等相容於 sendmail 的 relay client。使用 command -v sendmail 和 command -v mail 進行檢查。如果兩者都不存在,報告就無法送達;升級仍會成功,而整個失敗狀況也不會被發現。此外,請注意新的 VPS IP 位址沒有傳送信譽,因此直接寄往公開信箱的郵件經常會被歸入垃圾郵件。透過你已在使用的郵件供應商進行 relay,比自行執行郵件伺服器更可靠。
如果不想執行郵件服務,可以監看現有監控系統所記錄的日誌時間戳記:
stat -c '%y %n' /var/log/unattended-upgrades/unattended-upgrades.log如果時間戳記在一週內沒有更新,表示 timer 已停止,不論設定檔中寫了什麼。這項檢查應與 例行 Linux 伺服器維護檢查 一起執行。
系統應自行重新開機嗎?
Unattended-Upgrade::Automatic-Reboot "false";
Unattended-Upgrade::Automatic-Reboot-WithUsers "true";
Unattended-Upgrade::Automatic-Reboot-Time "02:00";將 Automatic-Reboot 設為 true 後,unattended-upgrades 會在不提示確認的情況下重新開機,但前提是執行完成後檔案 /var/run/reboot-required 存在。這個標記檔不是由 unattended-upgrades 建立的,必須由其他套件建立;在 Debian 上,通常是 needrestart 建立。不要假設該檔案一定存在。下次升級 kernel 後,請檢查:
ls -l /var/run/reboot-required /var/run/reboot-required.pkgs
uname -r
dpkg -l 'linux-image-*' | grep '^ii'如果該檔案在你的機器上始終沒有出現,Automatic-Reboot "true" 就永遠不會觸發。你可能會以為自己已因 kernel 更新而重新開機數個月,實際上卻仍在執行舊版 kernel。執行 uname -r,對照最新安裝的 linux-image 套件,即可確認狀況。
Automatic-Reboot-Time 會將重新開機排程到指定時間,而不是立即執行。將 Automatic-Reboot-WithUsers 設為 false 後,只要有人登入就會略過重新開機;對於一直有工作階段開啟的伺服器而言,這代表它完全不會重新開機。
這項決策關注的是系統重新啟動後的狀態,而不是重新開機本身。有人手動啟動的服務不會自動恢復。開機時需要輸入密碼的加密磁碟區不會掛載。彼此相依的兩台機器可能會以錯誤順序恢復。對於可接受在 02:00 中斷 1 分鐘的無狀態 Web 伺服器,可以啟用自動重新開機。若需要人員處理,則應保持停用,改為監控標記檔並發出警示,由人員決定時機。介於兩者之間的是 needrestart。它會重新啟動仍使用已升級函式庫的服務,因此可涵蓋除 kernel 以外的所有情況。其預設模式會在重新啟動前詢問,因此在無人值守的執行環境中使用前,請先閱讀 /etc/needrestart/needrestart.conf。
Debian testing 與 unstable 的運作方式也相同嗎?
以上內容說明的是 Debian stable。安全修正會從具有獨立標籤的個別套件庫發布。這種架構才能讓「僅安裝安全更新」成為可設定的選項。其他套件組的建置方式不同,因此從 stable 伺服器複製的設定檔,實際效果通常不如原作者預期。在持續變動的套件組上啟用自動升級,也代表可能在無人介入的情況下進行主要版本變更。這是另一項需要明確同意的設定。如果你正在評估這個選擇,在伺服器上執行 Debian stable、testing 或 unstable 說明了各套件組的特性。在 Red Hat 系列中,相同工作使用不同工具,也採用不同術語;Rocky Linux 與 AlmaLinux 上的 dnf-automatic 則透過自己的 timer 與設定檔執行。
自動升級可縮短修正發布與安裝之間的時間差。但它不會告訴你哪些項目仍然暴露在風險中,因此應搭配檢查伺服器上已知的 CVE。CVE 是 common vulnerabilities and exposures 的縮寫,也是追蹤修正所使用的公開識別碼。
五分鐘檢查
apt-config dump APT::Periodic會列印兩個非零值的金鑰。systemctl list-timers 'apt-daily*'會列印兩個計時器的NEXT時間。sudo unattended-upgrade --dry-run --debug會列印允許的來源,其中包含適用於您代號的安全性套件庫。sudo systemctl start apt-daily-upgrade.service會完成,且/var/log/unattended-upgrades/unattended-upgrades.log的時間戳記會更新。- 一週後,同一份日誌會列出它安裝的套件。
前四項通過,表示機器已完成設定。第五項通過,表示設定確實運作。
FAQ
為什麼 Debian 會安裝 unattended-upgrades,卻維持停用狀態?
該套件會提出 debconf 問題 unattended-upgrades/enable_auto_updates,並根據答案寫入 /etc/apt/apt.conf.d/20auto-upgrades。Debian installer 會儲存該問題的 false,因此當套件隨 task 或作為相依套件安裝時,會被設定為不執行任何動作。執行 sudo debconf-show unattended-upgrades 查看儲存的答案,再執行 sudo dpkg-reconfigure --priority=low unattended-upgrades 進行變更。低優先級設定很重要,因為使用預設優先級時,該命令會直接結束,不會顯示問題。
如何在不等待 timer 的情況下測試 unattended-upgrades?
執行 sudo unattended-upgrade --dry-run --debug。該命令會列出它將接受的來源;對每個可升級套件輸出一行 Checking:,並附上該套件的來源記錄,同時列出它會安裝的套件,但不會實際安裝任何內容。若要測試實際執行流程,請執行 sudo systemctl start apt-daily-upgrade.service,然後搭配 /var/log/unattended-upgrades/unattended-upgrades.log 讀取 journalctl -u apt-daily-upgrade.service --since -1h。
unattended-upgrades 是否也會安裝一般更新,而不只是安全性修正?
只有在某個 pattern 符合時才會。當套件來源符合 Unattended-Upgrade::Origins-Pattern 中的項目時,才會安裝該套件的升級版本。security archive、stable-updates suite,以及您自行加入的任何 repository 都是分開的項目。在您自己的機器上輸出 apt-config dump Unattended-Upgrade::Origins-Pattern,再與 grep -H -E '^(Origin|Label|Suite|Codename):' /var/lib/apt/lists/*Release 比較。它也不會將系統移轉到其他 Debian release。
為什麼升級不會在 timer 設定的時間執行?
apt-daily-upgrade.timer 會在 OnCalendar 的基礎上設定 RandomizedDelaySec,因此 systemd 會在該時間範圍內隨機選擇執行時間,而不是在行事曆指定的時間觸發。這能將負載分散到所有指向相同 mirrors 的 Debian 機器。systemctl list-timers 'apt-daily*' 會顯示實際選定的時間。若要變更時間範圍,請執行 sudo systemctl edit apt-daily-upgrade.timer,並提供一行空白的 OnCalendar=,接著輸入您自己的值。
我是否應該為安全性更新啟用自動重新開機?
只有在非預期重新開機不會造成問題的環境中才應啟用。每次執行後,只要存在 /var/run/reboot-required,Unattended-Upgrade::Automatic-Reboot "true" 就會在不確認的情況下重新開機;該標記檔案通常由其他套件(通常是 needrestart)寫入,而不是由 unattended-upgrades 本身寫入。在信任這項設定前,請先確認 kernel upgrade 後該檔案會出現在您的機器上。若機器開機時需要輸入 passphrase,或執行有人手動啟動的服務,請維持 false,並針對標記檔案設定告警,讓人員決定重新開機的時機。