Jump to content
View in the app

A better way to browse. Learn more.

PHP论坛人

A full-screen app on your home screen with push notifications, badges and more.

To install this app on iOS and iPadOS
  1. Tap the Share icon in Safari
  2. Scroll the menu and tap Add to Home Screen.
  3. Tap Add in the top-right corner.
To install this app on Android
  1. Tap the 3-dot menu (⋮) in the top-right corner of the browser.
  2. Tap Add to Home screen or Install app.
  3. Confirm by tapping Install.

Debian 13 系統資源限制調校 Part 2 全域核心優化

Featured Replies

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 by Jack

Create an account or sign in to comment

Account

Navigation

Search

Configure browser push notifications

Chrome (Android)
  1. Tap the lock icon next to the address bar.
  2. Tap Permissions → Notifications.
  3. Adjust your preference.
Chrome (Desktop)
  1. Click the padlock icon in the address bar.
  2. Select Site settings.
  3. Find Notifications and adjust your preference.