先說結論
經過一整個星期的高壓工作,現在輕松好多
先前 我在找造成硬碟爆漲的原因,主因是 模擬browser行為的程式中斷,造成一堆暫存檔,同時我發現有每天一堆系統通知信已累積十幾年,我從未讀,就刪掉,因為我也不會去讀,所以就去停用postfix,但之後硬碟雖未失控爆滿,但系統又會因記憶體不足而而崩潰, 假日半夜我都還在查找原因,朋友約吃飯我也只能先reboot, 換空檔時間,今早總算找到原因
我再向AI確認一下,是否我判斷正確
我檢討原因是,我停用了postfix, 雖然減少大量的寄不出信的空間,但 crontab 觸發sendmail, 卻一直累積大量的process, 最終爆掉, 現在用了 mailto="" , 總算排除,是這樣嗎
您完全正確!您的分析非常精準,這正是問題發生的根本原因和解決機制。讓我為您梳理一下這個完整的邏輯鏈:
問題根本原因分析 (Root Cause Analysis)
您停用了 Postfix:這停止了主要的郵件傳輸代理(MTA)服務。
但 Cron 的機制未變:Linux 系統中,cron
守護進程的默認行為是:任何 cron 作業只要有輸出(無論是標準輸出 stdout 還是標準錯誤 stderr),它就會嘗試调用系統預設的郵件發送程序(通常是 sendmail
或 postdrop
)將這些輸出以郵件形式發送給該作業所屬的用户。
進程堆積:由於 Postfix 已被停用,sendmail
/postdrop
进程無法成功發送郵件。但它們並不會立即失敗退出,而是會不斷重試或等待,這個過程會產生新的進程。舊的進程卡住,新的進程又不斷被 cron 觸發,導致這些“殭屍”郵件進程像雪球一樣越滾越多。
資源耗盡:每一個休眠的進程都會佔用一定的系統資源(主要是 PID 號、內存中的進程描述符等)。當這種進程數量積累到數百個時,最終會耗盡系統的進程表(PIDs)或內存,觸發 OOM Killer。
解決方案為什麼有效
您採用的 MAILTO=""
方案之所以有效,是因為它從源頭上切斷了問題鏈:
MAILTO=""
:這個設置明確地告訴 cron:「不要為這個作業發送任何郵件通知」。
當 cron 作業有輸出時,cron 看到這個設置,就會直接丟棄輸出,而不是去调用 sendmail
。
從根源上杜絕了無效的郵件發送嘗試,因此也就不會再產生堆積如山的 sendmail
和 postdrop
進程。
您的解決方案是最正確、最優雅的方法之一。其他替代方法還包括:
總結
您的診斷完全正確:停服不禁用 -> cron 持續觸發 -> 進程堆積 -> 資源耗盡。
您採用的 MAILTO=""
方案是對這個問題的標準且有效的解決方案,它直接修改了 cron 的行為模式,從觸發源頭解決了問題。系統狀態的前後對比(從 583 個進程降到 237 個,Swap 使用從 96% 降到 10%)完美地證明了這一點。
這是一個非常經典的 Linux 系統管理案例,您很好地掌握了它!
因為真正的問題,在大量的python程式,被密集排程
執行過程可能遇到些大小不同的狀況
在還需要運作前
就先停用 sendmail , postdrop, postfix
然後陸續找出python程式的問題,或移到別台主機
從您的系統日誌來看,伺服器正在頻繁地發生 Out of Memory (OOM) 的情況,導致核心(Kernel)的 OOM Killer 機制被迫殺死多個進程來釋放記憶體,其中包括 httpd
(Apache)、mysqld
(MySQL) 和多個 python3.6
進程。
這是一個非常嚴重的問題,需要從「找出元兇」和「根本預防」兩個方面來處理。
第一部分:如何找出是哪一支 Python 程式?
被殺死的進程名稱都是 python3.6
,這無法直接區分。您需要查看的是這些進程當時實際執行的命令和參數。
解決方法如下:
查看目前正在執行的 Python 程式
如果問題正在發生,最快的辦法是使用 ps
或 top
命令。被殺死的進程 PID 在日誌中都有記錄(例如 1156, 1203, 1137, 1143 等),雖然它們現在已經不存在,但同一個程式很可能會重新啟動。查看類似的新進程。
ps aux | grep python3.6
top
重點看每一行的最後部分,通常會顯示腳本名稱,例如 /path/to/your/script.py
或 -m somemodule
。
分析系統日誌(如果程式有輸出)
檢查系統的日誌檔(如 /var/log/messages
或 /var/log/syslog
),或者檢查您的應用程式(如 Apache、MySQL)的日誌,看看在 OOM 發生前後有沒有相關的錯誤資訊,這可能能幫你定位到是哪個應用先出的問題。
使用審計工具(事後分析)
您可以安裝 auditd
服務來記錄所有進程的執行事件,這樣下次再發生時就能有據可查。
sudo yum install auditd
sudo apt-get install auditd
sudo auditctl -a always,exit -F arch=b64 -S execve -F exe=/usr/bin/python3.6
sudo ausearch -sc execve -x python3.6
第二部分:如何預防?
單純重啟進程無法解決問題,必須找到記憶體消耗過大的根本原因。
1. 優先優化您的程式碼
這是最根本的解決方法。多個 Python 進程被殺死,極有可能是因為:
* 記憶體洩漏(Memory Leak):程式沒有正確釋放不再使用的物件,導致記憶體使用量隨時間無限增長。
* 載入了過大的資料集:例如一次性將一個幾GB的檔案讀入記憶體進行處理。
* 不合理的快取策略:快設了太多內容且沒有過期機制。
**如何優化?**
* 使用 `tracemalloc`、`objgraph`、`pympler` 等工具來分析 Python 程式的記憶體使用情況和洩漏點。
* 對於處理大資料,使用分塊讀取、生成器(Generators)等方式,避免一次性載入。
* 為快取設定大小上限或過期時間。
2. 調整 OOM Killer 的優先級
OOM Killer 在選擇殺死哪個進程時,會參考 oom_score_adj
(日誌裡有顯示)。值越低,進程越重要,越不容易被殺死。您可以保護關鍵進程(如 MySQL、主要的 Web Server)。
```bash
# 保護 mysqld 進程,使其非常不容易被殺死
echo '-1000' | sudo tee /proc/$(pgrep mysqld)/oom_score_adj
# 但注意,保護了某些進程,意味著其他進程(如你的 Python 程式)會更容易被殺死。
# 更永久的辦法是編輯 /etc/systemd/system.conf 或服務的 systemd unit file,加入 `OOMScoreAdjust=-1000`
```
3. 增加交換空間(Swap Space)
Swap 空間可以作為記憶體的緩衝地帶,雖然速度慢,但可以在物理記憶體不足時防止進程被直接殺死。如果您的伺服器完全沒有配置 Swap,強烈建議新增。
```bash
# 檢查當前 Swap 空間
free -h
# 新增一個 2GB 的 Swap 檔案
sudo fallocate -l 2G /swapfile
sudo chmod 600 /swapfile
sudo mkswap /swapfile
sudo swapon /swapfile
# 永久生效,寫入 /etc/fstab
echo '/swapfile none swap sw 0 0' | sudo tee -a /etc/fstab
```
4. 增加實體記憶體(RAM)
如果經過優化後,您的應用程式確實就需要那麼多記憶體才能正常運行,那麼升級伺服器規格,增加實體記憶體是最直接、最有效的方案。
5. 設定程序監控與重啟
使用 systemd
或 supervisord
來管理您的 Python 程式。它們可以在程式被 OOM Killer 殺掉後自動將其重啟,並記錄日誌,保證服務的可用性(但這並不能解決記憶體不足的根本問題)。
第三部分:是否能定時做什麼處理?
「定時重啟」是一種治標不治本的方法,但在某些無法立即優化程式碼的場景下,可以作為暫時的緩解措施。
使用 Crontab 定時重啟服務
例如,如果您發現某個 Python 服務在運行一天後會佔用過多記憶體,可以設定在每天凌晨流量低峰期重啟它。
sudo crontab -e
0 3 * * * systemctl restart my-python-service
注意:這只是暫時的應急方案。長期來看,仍然需要按照第二部分的建議,從根本上去分析和解決記憶體問題。
總結與行動步驟
立即檢查:使用 ps aux | grep python3.6
立刻查看是哪幾個 Python 腳本在運行。
臨時緩解:
根本解決:
定位問題程式:找到那幾個被殺的 Python 程式。
程式碼優化:使用記憶體分析工具對其進行分析和優化。
資源評估:如果優化後記憶體仍然不足,規劃升級伺服器記憶體。
監控:設定監控系統(如 Prometheus/Grafana),監控伺服器的記憶體使用情況,提前預警,而不是等 OOM 發生後再來處理。
21430 S /usr/sbin/sendmail -FCronDa 0.6 0.0
21431 S /usr/sbin/postdrop -r 0.6 0.0
21596 S CROND 0.2 0.0
21613 S /usr/sbin/sendmail -FCronDa 0.6 0.0
21614 S /usr/sbin/postdrop -r 0.6 0.0
21712 S CROND 0.2 0.0
21717 S /usr/sbin/sendmail -FCronDa 0.6 0.0
21718 S /usr/sbin/postdrop -r 0.6 0.0
21925 S CROND 0.2 0.0
我已停用 postfix 為何還有sendmail,postdrop ...
您观察到的这些 sendmail
和 postdrop
进程确实是 Postfix 邮件系统的组件,即使您已经停用了 Postfix 服务,这些进程仍然可能出现。以下是原因和解决方案:
为什么停用 Postfix 后仍有这些进程?
Cron 作业触发:这些进程大多与 CROND
进程相关联,表明是由 cron 作业触发的。当 cron 作业产生输出时,系统会尝试通过邮件发送这些输出给用户。
未完全卸载:您可能只是停止了 Postfix 服务,但没有完全卸载它,或者系统有其他组件仍然依赖邮件功能。
系统默认配置:许多 Linux 发行版默认配置 cron 将作业输出通过邮件发送。
解决方案
1. 完全卸载 Postfix
sudo yum remove postfix
sudo apt-get remove postfix
2. 禁用 cron 邮件通知
即使卸载了 Postfix,cron 仍然会尝试发送邮件,这可能会导致错误。您可以通过以下方式禁用 cron 邮件:
echo 'MAILTO=""' | sudo tee -a /etc/crontab
crontab -e
MAILTO=""
3. 重定向 cron 输出到日志文件或 /dev/null
对于个别 cron 作业,您可以重定向输出:
* * * * * /path/to/command > /dev/null 2>&1
* * * * * /path/to/command >> /var/log/mycommand.log 2>&1
4. 杀死现有的 sendmail 进程
sudo pkill sendmail
sudo pkill postdrop
5. 安装替代的轻量级邮件工具(可选)
如果您确实需要发送邮件功能,但不想运行完整的 Postfix,可以考虑安装更轻量的替代方案:
sudo yum install ssmtp
sudo apt-get install ssmtp
sudo yum install nullmailer
sudo apt-get install nullmailer
6. 检查系统日志确认问题
sudo grep -i mail /var/log/messages
sudo grep -i postfix /var/log/messages
sudo grep -i sendmail /var/log/messages
预防措施
为了防止这些进程再次出现,您可以:
定期检查进程:
ps aux | grep -E '(sendmail|postdrop)'
设置监控警报:
if ps aux | grep -q '[s]endmail'; then
echo "警告: sendmail 进程仍在运行 - $(date)" | mail -s "进程警报" admin@example.com
fi
审查所有 cron 作业:
sudo ls /var/spool/cron/
sudo cat /var/spool/cron/*
ls /etc/cron.d/
ls /etc/cron.hourly/
ls /etc/cron.daily/
ls /etc/cron.weekly/
ls /etc/cron.monthly/
通过上述方法,您应该能够彻底解决停用 Postfix 后仍然出现相关进程的问题。最有效的方法是完全卸载 Postfix 并配置 cron 不发送邮件通知。
关于 Sleeping 进程
Sleeping 进程通常是正常的,不一定是资源消耗者:
Sleeping 状态的含义:
您的系统状态:
总进程数:583个
运行中的进程:仅1个(非常低)
睡眠进程:465个(这是正常的)
没有停止或僵尸进程(这是好现象)
如何查看 Sleeping 进程的详细信息
要查看哪些进程处于睡眠状态以及它们可能占用的资源,可以使用以下命令:
ps aux
ps -eo pid,state,cmd,%mem,%cpu | grep -w S
ps aux --sort=-%mem | head -20
ps aux --sort=-%cpu | head -20
htop
系统资源分析
CPU 使用情况:
用户空间程序占用:16.1% (us)
系统空间占用:4.0% (sy)
空闲:78.7% (id)
CPU 使用率总体较低,系统负载较轻
内存使用情况:
总内存:964MB
已使用:781MB (约81%)
可用内存:183MB
缓存:244MB(这部分内存会在需要时被释放)
交换空间使用:
总交换空间:262MB
已使用:251MB (约96%)
可用交换空间:仅10MB
这是最需要关注的问题:交换空间几乎耗尽
潜在问题与解决方案
1. 交换空间不足问题
您的交换空间使用率高达96%,这会导致系统性能严重下降,因为系统被迫频繁在内存和磁盘之间交换数据。
解决方案:
sudo fallocate -l 2G /swapfile
sudo chmod 600 /swapfile
sudo mkswap /swapfile
sudo swapon /swapfile
echo '/swapfile none swap sw 0 0' | sudo tee -a /etc/fstab
2. 识别真正消耗资源的进程
虽然大多数睡眠进程是正常的,但有些可能确实占用了过多资源。使用以下命令识别:
ps aux --sort=-%mem | head -11
ps aux --sort=-%cpu | head -11
sudo iotop
3. 检查是否有内存泄漏
某些睡眠进程可能有内存泄漏问题:
watch -n 5 'ps aux --sort=-%mem | head -10'
pmap -x <PID>
总结与建议
Sleeping 进程通常是正常的:465个睡眠进程在Linux系统中是常见的,它们大多是在等待事件的后台服务。
主要问题是交换空间不足:您的交换空间几乎耗尽,这是最需要立即解决的问题。
系统负载较轻:CPU使用率不高,只有一个进程在运行,系统负载平均值也较低。
建议操作:
您的系统整体状态还算健康,主要是交换空间不足需要解决。增加交换空间后,应该能够显著改善系统稳定性。