先說結論
經過一整個星期的高壓工作,現在輕松好多
先前 我在找造成硬碟爆漲的原因,主因是 模擬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 命令末尾重定向輸出:例如
> /dev/null 2>&1
,作用和MAILTO=""
類似,但需要在每一條命令後重複設置。完全卸載 Postfix:這會移除
sendmail
命令本身,cron 調用失敗也會避免進程堆積,但可能會在其他真正需要發郵件的地方產生錯誤日志。您的方法(保留軟件但禁用其觸發條件)更乾淨。
總結
您的診斷完全正確:停服不禁用 -> 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 列出所有 python3.6 進程的詳細命令 ps aux | grep python3.6 # 方法二:使用 top 或 htop,然後按 c 鍵顯示完整命令列 top # 在 top 介面中,按 'c'
重點看每一行的最後部分,通常會顯示腳本名稱,例如
/path/to/your/script.py
或-m somemodule
。分析系統日誌(如果程式有輸出)
檢查系統的日誌檔(如/var/log/messages
或/var/log/syslog
),或者檢查您的應用程式(如 Apache、MySQL)的日誌,看看在 OOM 發生前後有沒有相關的錯誤資訊,這可能能幫你定位到是哪個應用先出的問題。使用審計工具(事後分析)
您可以安裝auditd
服務來記錄所有進程的執行事件,這樣下次再發生時就能有據可查。# 安裝 auditd sudo yum install auditd # 適用於 CentOS/RHEL sudo apt-get install auditd # 適用於 Ubuntu/Debian # 新增一條規則,記錄所有執行 python3.6 的動作 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 服務在運行一天後會佔用過多記憶體,可以設定在每天凌晨流量低峰期重啟它。
# 編輯 root 的定時任務 sudo crontab -e # 加入以下行,表示每天凌晨 3 點重啟您的服務 # 假設您的服務由 systemd 管理,名為 my-python-service 0 3 * * * systemctl restart my-python-service # 或者直接殺死程序(不優雅) # 0 3 * * * pkill -f "python3.6 /path/to/script.py"
注意:這只是暫時的應急方案。長期來看,仍然需要按照第二部分的建議,從根本上去分析和解決記憶體問題。
總結與行動步驟
立即檢查:使用
ps aux | grep python3.6
立刻查看是哪幾個 Python 腳本在運行。臨時緩解:
考慮新增 Swap 空間。
為關鍵資料庫進程(如 mysqld)設定 OOM 保護。
根本解決:
定位問題程式:找到那幾個被殺的 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
# 对于基于 RedHat/CentOS 的系统 sudo yum remove postfix # 对于基于 Debian/Ubuntu 的系统 sudo apt-get remove postfix
2. 禁用 cron 邮件通知
即使卸载了 Postfix,cron 仍然会尝试发送邮件,这可能会导致错误。您可以通过以下方式禁用 cron 邮件:
# 在 crontab 文件顶部添加(对所有用户生效) echo 'MAILTO=""' | sudo tee -a /etc/crontab # 或者为特定用户设置 crontab -e # 在文件顶部添加 MAILTO=""
3. 重定向 cron 输出到日志文件或 /dev/null
对于个别 cron 作业,您可以重定向输出:
# 将输出重定向到 /dev/null(完全丢弃) * * * * * /path/to/command > /dev/null 2>&1 # 或将输出重定向到日志文件 * * * * * /path/to/command >> /var/log/mycommand.log 2>&1
4. 杀死现有的 sendmail 进程
# 杀死所有 sendmail 进程 sudo pkill sendmail # 杀死所有 postdrop 进程 sudo pkill postdrop
5. 安装替代的轻量级邮件工具(可选)
如果您确实需要发送邮件功能,但不想运行完整的 Postfix,可以考虑安装更轻量的替代方案:
# 安装 ssmtp(简单邮件传输) sudo yum install ssmtp # CentOS/RHEL sudo apt-get install ssmtp # Ubuntu/Debian # 或安装 nullmailer sudo yum install nullmailer # CentOS/RHEL sudo apt-get install nullmailer # Ubuntu/Debian
6. 检查系统日志确认问题
# 查看系统日志中与邮件相关的错误 sudo grep -i mail /var/log/messages sudo grep -i postfix /var/log/messages sudo grep -i sendmail /var/log/messages
预防措施
为了防止这些进程再次出现,您可以:
定期检查进程:
# 定期检查是否有 sendmail 或 postdrop 进程 ps aux | grep -E '(sendmail|postdrop)'
设置监控警报:
# 如果检测到这些进程,发送警报 if ps aux | grep -q '[s]endmail'; then echo "警告: sendmail 进程仍在运行 - $(date)" | mail -s "进程警报" admin@example.com fi
审查所有 cron 作业:
# 查看所有用户的 cron 作业 sudo ls /var/spool/cron/ sudo cat /var/spool/cron/* # 查看系统 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 状态的含义:
睡眠状态的进程正在等待某些事件发生(如I/O操作完成、网络请求响应、定时器到期等)
它们不占用CPU资源,但可能占用内存
这是大多数后台服务和守护进程的正常状态
您的系统状态:
总进程数:583个
运行中的进程:仅1个(非常低)
睡眠进程:465个(这是正常的)
没有停止或僵尸进程(这是好现象)
如何查看 Sleeping 进程的详细信息
要查看哪些进程处于睡眠状态以及它们可能占用的资源,可以使用以下命令:
# 查看所有进程的状态和资源使用 ps aux # 只查看睡眠状态的进程 ps -eo pid,state,cmd,%mem,%cpu | grep -w S # 按内存使用排序查看进程 ps aux --sort=-%mem | head -20 # 按CPU使用排序查看进程 ps aux --sort=-%cpu | head -20 # 使用更直观的 htop 工具(如果已安装) htop
系统资源分析
CPU 使用情况:
用户空间程序占用:16.1% (us)
系统空间占用:4.0% (sy)
空闲:78.7% (id)
CPU 使用率总体较低,系统负载较轻
内存使用情况:
总内存:964MB
已使用:781MB (约81%)
可用内存:183MB
缓存:244MB(这部分内存会在需要时被释放)
交换空间使用:
总交换空间:262MB
已使用:251MB (约96%)
可用交换空间:仅10MB
这是最需要关注的问题:交换空间几乎耗尽
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. 识别真正消耗资源的进程
虽然大多数睡眠进程是正常的,但有些可能确实占用了过多资源。使用以下命令识别:
# 查看内存占用最高的前10个进程 ps aux --sort=-%mem | head -11 # 查看CPU占用最高的前10个进程 ps aux --sort=-%cpu | head -11 # 查看I/O等待高的进程(如果有iotop) sudo iotop
3. 检查是否有内存泄漏
某些睡眠进程可能有内存泄漏问题:
# 监控进程内存增长 watch -n 5 'ps aux --sort=-%mem | head -10' # 查看进程的详细内存映射 pmap -x <PID>
总结与建议
Sleeping 进程通常是正常的:465个睡眠进程在Linux系统中是常见的,它们大多是在等待事件的后台服务。
主要问题是交换空间不足:您的交换空间几乎耗尽,这是最需要立即解决的问题。
系统负载较轻:CPU使用率不高,只有一个进程在运行,系统负载平均值也较低。
建议操作:
立即增加交换空间
监控内存使用情况,确保不再出现OOM Killer
定期检查是否有异常进程消耗过多资源
考虑优化应用程序以减少内存使用
您的系统整体状态还算健康,主要是交换空间不足需要解决。增加交换空间后,应该能够显著改善系统稳定性。
沒有留言:
張貼留言