March 11Mar 11 Debian 13 系統資源限制調校 Part 2 全域核心優化 之前Debian 13配置時,已做了Part 1 Debian 13 系統資源限制調校 Part 1 (VPS 2C/2GB RAM) https://phpforumer.com/topic/1290/ 現在 LNMP 配置完後,接續修改Part 2 ---------------------------------------- 全域核心 sysctl 優化 ---------------------------------------- 強烈建議:不要直接修改 /etc/sysctl.conf,以避免系統更新時覆蓋設定 Debian支援在 /etc/sysctl.d/ 目錄下放置獨立的設定檔,便於版本管理與追蹤 建立專用設定檔 vi /etc/sysctl.d/99-lnmp-optimization.conf 寫入以下內容 # ===== 系統級別檔案處理 ===== # 整個系統可以開啟的最大檔案描述符總數 # 必須大於所有服務的 LimitNOFILE 設定值之總和 fs.file-max = 2097152 # 單一行程可分配的最大檔案描述符數量上限 # 此值必須 >= 任何服務的 LimitNOFILE 設定值 fs.nr_open = 2097152 # ===== 網路效能調校 ===== # BBR + 網路優化 已寫在 /etc/sysctl.d/99-network-optimization.conf # ===== 虛擬記憶體調校 ===== # 降低 Swap 使用傾向(0=盡量不用 Swap,100=積極使用) # 2GB VPS 建議設為 10,優先保留記憶體給應用程式 # vm.swappiness = 10 # 降低 dirty page 寫回比例,避免 I/O 突刺 vm.dirty_ratio = 15 vm.dirty_background_ratio = 5 儲存檔案並離開vi編輯器 按 Esc,輸入 :wq,按 Enter 立即套用核心參數 (無需重開機) sysctl -p /etc/sysctl.d/99-lnmp-optimization.conf ------------------------- 驗證是否已套用 ------------------------- sysctl fs.file-max # 降低Swap使用傾向,沒修改,則顯示預設 = 60 sysctl vm.swappiness ------------------------------------------ 服務級別限制調校 systemd Override ------------------------------------------ 這是整個調校的核心。使用 systemd override 的方式,可避免服務套件更新時覆蓋自訂設定 重要:/etc/security/limits.conf 對 systemd 管理的服務無效 MariaDB、Nginx、PHP-FPM 均須透過各自的 override.conf 設定資源限制 以下設定以 2 vCPU / 2GB RAM VPS 為基準,請依實際監控數據微調 ---------------------- MariaDB 資料庫 ---------------------- mkdir -p /etc/systemd/system/mariadb.service.d/ 編輯 vi /etc/systemd/system/mariadb.service.d/override.conf 寫入以下內容 [Service] # ===== 檔案描述符限制 ===== # 資料庫需要處理大量連線與資料檔案,此值應設定足夠高 LimitNOFILE=65535 LimitNPROC=65535 # ===== 記憶體限制 ===== # MemoryMax:絕對硬上限,超過即被 OOM Killer 終止 # MemoryHigh:軟性高水位,超過時系統會積極嘗試回收記憶體 MemoryMax=1536M MemoryHigh=1280M # ===== CPU 限制 ===== # 限制 CPU 使用率,避免資料庫查詢佔用所有 CPU 資源 # 2 vCPU 環境:CPUQuota=80% 表示最多使用 0.8 顆核心 # 若要充分利用兩顆核心,可調整為 160% CPUQuota=160% # ===== I/O 優先級 ===== # 提高資料庫的磁碟 I/O 優先級 # IOSchedulingClass=2 為 Best Effort(cfq/bfq 排程器) # IOSchedulingPriority 範圍 0(最高)~ 7(最低),4 為中等偏高 IOSchedulingClass=2 IOSchedulingPriority=4 # ===== 穩定性策略 ===== # 服務崩潰後自動重啟,RestartSec 為等待秒數 Restart=always RestartSec=10s # ===== 其他系統限制 ===== # 允許記憶體鎖定(mlock),避免關鍵資料被 Swap 出去 # 對 InnoDB Buffer Pool 等效能敏感操作有幫助 LimitMEMLOCK=infinity # 堆疊大小(單位:位元組,此處為 10MB) LimitSTACK=10485760 儲存檔案並離開vi編輯器 按 Esc,輸入 :wq,按 Enter ----------------------- Nginx 網頁伺服器 ----------------------- mkdir -p /etc/systemd/system/nginx.service.d/ 編輯 vi /etc/systemd/system/nginx.service.d/override.conf 寫入以下內容 [Service] # ===== 檔案描述符限制 ===== # 每個 HTTP 連線、靜態檔案、upstream 連線、Log 檔案都需要計入 LimitNOFILE=65535 LimitNPROC=65535 # ===== 記憶體限制 ===== # Nginx 以事件驅動架構著稱,記憶體用量很低 # 但仍需設定上限防止記憶體洩漏時無限成長 MemoryMax=512M MemoryHigh=384M # ===== CPU 限制 ===== # 靜態檔案服務 CPU 消耗較低,動態代理時消耗稍高 CPUQuota=50% # ===== 穩定性策略 ===== Restart=always RestartSec=5s 儲存檔案並離開vi編輯器 按 Esc,輸入 :wq,按 Enter ------------------------ PHP-FPM ------------------------ mkdir -p /etc/systemd/system/php8.4-fpm.service.d 編輯 vi /etc/systemd/system/php8.4-fpm.service.d/override.conf 寫入以下內容 [Service] # ===== 檔案描述符限制 ===== # PHP-FPM 可能同時連接資料庫、Redis、Session 儲存等多個外部資源 LimitNOFILE=65535 LimitNPROC=65535 # ===== 記憶體限制 ===== # PHP 動態內容處理是記憶體消耗主要來源之一 # 此限制為 PHP-FPM master + 所有 worker 行程的記憶體總和 MemoryMax=768M MemoryHigh=512M # ===== CPU 限制 ===== # PHP 腳本執行 (模板渲染、資料庫查詢組裝) 對CPU消耗較高 CPUQuota=70% # ===== 穩定性策略 ===== Restart=always RestartSec=10s 儲存檔案並離開vi編輯器 按 Esc,輸入 :wq,按 Enter ------------------------ 套用、驗證與監控 ------------------------ 設定完成後,必須依序執行以下步驟使設定生效 套用設定並重啟服務 重新載入 systemd 配置 (讓 systemd 讀取新增的 override 檔案) systemctl daemon-reload 依序重啟所有服務 systemctl restart mariadb systemctl restart nginx systemctl restart php8.4-fpm 確認服務狀態 systemctl status mariadb systemctl status nginx systemctl status php8.4-fpm 每個服務都應顯示 active (running) ---------------------- 驗證限制是否生效 ---------------------- 重要:查看 /proc/<PID>/limits 才是確認設定是否真正生效的唯一標準,而非查看設定檔本身 驗證各服務的檔案描述符限制 Max open files 應為 65535 MariaDB cat /proc/$(pgrep -o mariadbd)/limits | grep "Max open files" 驗證檔案描述符限制 Max open files 應為 65535 Nginx (取 master process PID) cat /proc/$(pgrep -o nginx)/limits | grep "Max open files" 驗證檔案描述符限制 Max open files 應為 65535 PHP-FPM (取 master process PID) cat /proc/$(pgrep -o php-fpm)/limits | grep "Max open files" 驗證檔案描述符限制 Max open files 應為 65535 使用 systemd 指令查看詳細屬性 systemctl show mariadb | grep -E "(LimitNOFILE|MemoryMax|CPUQuota)" systemctl show nginx | grep -E "(LimitNOFILE|MemoryMax|CPUQuota)" systemctl show php8.4-fpm | grep -E "(LimitNOFILE|MemoryMax|CPUQuota)" 查看核心檔案描述符使用狀況 查看 cat /proc/sys/fs/file-nr 輸出:已使用 未使用 最大值 ------------------ 安裝監控工具 ------------------ 安裝監控工具 apt update && apt install -y htop iotop iftop nethogs lsof 即時查看 CPU / 記憶體 / 行程使用量 htop 監控磁碟 I/O 使用狀況 iotop 即時網路流量監控 iftop 依行程分類顯示網路流量 nethogs 計算特定行程目前開啟的檔案數 lsof -p <PID> \| wc -l ---------------------- 設定日誌輪替 ---------------------- 避免日誌檔案無限增長而佔滿磁碟空間,這是生產環境容易忽略的維運重點 建立 vi /etc/logrotate.d/lnmp 貼上內容 /var/log/mysql/*.log /var/log/nginx/*.log /var/log/php8.4-fpm.log { daily # 每天輪替 rotate 30 # 保留 30 天份日誌 missingok # 日誌檔遺失時不報錯 compress # 壓縮舊日誌(使用 gzip) delaycompress # 延遲一次壓縮,確保服務釋放舊檔案後再壓縮 notifempty # 日誌為空則不執行輪替 create 0640 www-data adm # 建立新日誌檔的權限與擁有者 sharedscripts # 所有日誌輪替完成後,只執行一次 postrotate postrotate # 通知服務重新開啟日誌檔(發送 USR1 訊號) [ -f /var/run/mysqld/mysqld.pid ] && kill -USR1 $(cat /var/run/mysqld/mysqld.pid) 2>/dev/null || true [ -f /var/run/nginx.pid ] && kill -USR1 $(cat /var/run/nginx.pid) 2>/dev/null || true endscript } 儲存檔案並離開vi編輯器 按 Esc,輸入 :wq,按 Enter -------------------------------- 測試 logrotate 設定是否正確 -------------------------------- 測試 logrotate 設定是否正確 (加上 --debug 不實際執行) logrotate --debug /etc/logrotate.d/lnmp ---------------------------------------------- 2GB RAM VPS 記憶體配置建議 ---------------------------------------------- MariaDB 768MB ~ 1024MB 記憶體消耗大戶,InnoDB Buffer Pool 需足夠大 PHP-FPM 512MB 依 worker 數量與每個 worker 的 memory_limit 決定 Nginx 256MB 極低,事件驅動架構 Redis (選用) 128MB 用於 Session 或頁面快取 系統及其他 384MB OS 核心、SSH、Cron 等 合計 約 2GB 重要提醒:systemd 的 MemoryMax 設定值總和可以略高於實體記憶體,但若所有服務同時達到尖峰,仍可能觸發 OOM Killer 強制終止行程 請務必根據實際監控數據進行微調,切勿照單全收 ------------------------- 論壇部署最終檢查清單 ------------------------- 完成所有設定後,請逐項確認: 系統基礎 /etc/security/limits.conf 已針對所有使用者設定完成 Swap 空間已建立並設定開機自動掛載 /etc/sysctl.d/99-lnmp-optimization.conf 已建立,核心參數已套用 sysctl -p 防火牆 已啟用,僅開放 22、80、443 連接埠 服務設定 MariaDB、Nginx、PHP-FPM 的 systemd override.conf 均已正確建立 執行 systemctl daemon-reload 並已重啟所有服務 三個服務的 systemctl status 均顯示 active (running) 使用 cat /proc/<PID>/limits 驗證每個服務的 Max open files 均為 65535 安全性 已變更 MariaDB 的 root 密碼,並移除匿名帳號與測試資料庫 mysql_secure_installation Nginx 已關閉版本號顯示 server_tokens off PHP 已設定 expose_php = Off、display_errors = Off 已確認 AppArmor 或 SELinux 狀態不會阻擋服務運作 HTTPS TLS/SSL 已設定 維運 日誌輪替 logrotate 設定已完成並通過 --debug 測試 資料庫、網站檔案、設定檔的自動化備份策略已建立 (如 cron + rsync 或 borgbackup) 基礎監控告警已設定 (如 netdata、prometheus + node_exporter、或被動式監控服務) 快速狀態檢查腳本 /root/check-lnmp-limits.sh 已建立並可正常執行 --------------- 常見問題 --------------- Q1: 修改了 /etc/security/limits.conf,為什麼重啟 MariaDB 後,查看 /proc/pid/limits 還是沒變? 因為 MariaDB 是由 systemd 管理的服務,它不會讀取 limits.conf 你必須在對應的 systemd override.conf 中設定 LimitNOFILE Q2: 如何快速確認目前整個系統或特定服務開啟了多少檔案? 系統整體 cat /proc/sys/fs/file-nr 輸出: 已分配數 未使用數 總上限 查看特定服務 (以 nginx master process 為例) 開啟了多少檔案 lsof -p $(pgrep -o nginx) 2>/dev/null | wc -l Q3: 修改了 systemd override 並 daemon-reload 重啟後,服務卻無法啟動了,怎麼辦? 查看錯誤日誌 journalctl -u mariadb -xe --no-pager 檢查服務狀態 systemctl status mariadb 驗證 systemd 讀取到的限制值 systemctl show mariadb | grep -i "limitnofile\|memory" 常見原因: override.conf 語法錯誤 如多餘空格、缺少區段標頭 [Service] 設定了不支援的參數名稱 MemoryMax 值設得太小,服務一啟動就因超限而被終止 服務名稱與實際安裝的版本不符 (如 PHP 版本號) Q4:CPUQuota 設定的百分比是相對於什麼? CPUQuota=80% 表示允許使用最多 80% 的單顆CPU核心時間 若VPS有2顆 vCPU,則 CPUQuota=160% 才等於充分利用兩顆核心 請依VPS實際核心數調整 Q5:MemoryMax 和 MemoryHigh 有什麼差別? MemoryHigh 軟性高水位。超過後,系統會積極嘗試回收該服務的記憶體,但不會強制終止服務 MemoryMax 絕對硬上限。超過後,服務內的行程會被 OOM Killer 立即終止 建議將 MemoryHigh 設為 MemoryMax 的 80~85%,讓系統有空間提前介入,避免觸及硬上限 Q6:MariaDB 無法啟動 可能原因與解決方法: 查看錯誤日誌 journalctl -u mariadb -n 50 驗證設定檔語法 mariadbd --validate-config 若調整 innodb_log_file_size 後發生錯誤,刪除舊的 redo log 讓 MariaDB 重建 systemctl stop mariadb rm /var/lib/mysql/ib_logfile* systemctl start mariadb 檢查資料目錄權限 ls -la /var/lib/mysql chown -R mariadb:mariadb /var/lib/mysql Q7:Too many connections 錯誤 暫時調高連線數(立即生效) sudo mariadb -e "SET GLOBAL max_connections = 150;" 查看閒置連線 sudo mariadb -e "SHOW PROCESSLIST;" 終止閒置過久的連線 sudo mariadb -e "SHOW PROCESSLIST;" | grep Sleep | awk '{print "KILL "$1";"}' | mysql 永久調整請修改 50-server.cnf 中的 max_connections 後重啟服務 提示:應用端建議實作連線池 (如 PHP PDO persistent connection),可大幅降低連線開銷 Q8:磁碟空間持續增加 檢查磁碟使用 df -h du -sh /var/lib/mysql/ 檢查 Binary Log 使用狀況 sudo mariadb -e "SHOW BINARY LOGS;" 手動清除 7 天前的 Binary Log sudo mariadb -e "PURGE BINARY LOGS BEFORE DATE_SUB(NOW(), INTERVAL 7 DAY);" 檢查日誌大小 ls -lh /var/log/mysql/ 若完全不需要主從複製,可將 50-server.cnf 中 log-bin 相關行全部註解掉 Q9:innodb_buffer_pool_size 設定後發生 OOM 檢查記憶體使用 free -h top -o %MEM 2 GB 總記憶體建議 innodb_buffer_pool_size = 768M,並為系統 + 其他服務保留至少 512 MB 若系統頻繁使用 Swap,應考慮升級記憶體或降低 buffer pool Q10:設定檔改了但未生效 確認目前生效的設定 mariadbd --print-defaults 找出所有載入的設定檔路徑 mariadbd --help --verbose | grep -A 1 "Default options" 確認修改的檔案是否在載入列表中,再重新啟動 systemctl daemon-reload systemctl restart mariadb 確認特定參數 sudo mariadb -e "SHOW VARIABLES LIKE '參數名稱';" -----------------------快速健康檢查腳本-----------------------建立vi /root/mysql_healthcheck.sh貼上以下內容mysql_healthcheck.sh儲存檔案並離開vi編輯器按 Esc,輸入 :wq,按 Enter ------------------ 賦予執行權限 ------------------ 賦予執行權限 chmod 700 /root/mysql_healthcheck.sh chown root:root /root/mysql_healthcheck.sh 執行腳本 /root/mysql_healthcheck.sh -------------------------- crontab 定時任務 -------------------------- 設定每小時定期執行 crontab -e 加入以下行 0 * * * * /root/mysql_healthcheck.sh >> /var/log/mysql_health.log 2>&1 儲存檔案並離開vi編輯器 按 Esc,輸入 :wq,按 Enter -----------------快速檢查腳本-----------------將以下腳本儲存為 /root/check-lnmp-limits.sh,方便日後快速檢查系統狀態編輯vi /root/check-lnmp-limits.sh貼上以下內容check-lnmp-limits.sh儲存檔案並離開vi編輯器按 Esc,輸入 :wq,按 Enter賦予執行權限 chmod 700 /root/check-lnmp-limits.sh chown root:root /root/check-lnmp-limits.sh 執行腳本 /root/check-lnmp-limits.sh Edited March 27Mar 27 by Jack
Create an account or sign in to comment