昨天,項目的 Elasticsearch 服務掛了,我說的掛可不是進程沒了,因為有 Supervisor 保護,而是服務不可用了。以前曾經出現過一次因為 ES_HEAP_SIZE 設定不當導致的服務不可用故障,於是我慣性的判斷應該還是 ES_HEAP_SIZE 的問題,不過登入伺服器後發現日誌裡顯示大量的「Too many open files」錯誤資訊。
那麼 Elasticsearch 設定的最大檔案數到底是多少呢?可以通過 proc 確認:
shell> cat /proc/<PID>/limits
結果是「4096」,我們還可以進一步看看 Elasticsearch 開啟的都是什麼東西:
shell> ls /proc/<PID>/fd
問題看上去非常簡單,只要加大相應的配置項應該就可以了。此配置在 Elasticsearch 裡叫做 MAX_OPEN_FILES,可惜配置後發現無效。
按我的經驗,通常此類問題多半是由於作業系統限制所致,可是檢查結果一切正常:
shell> cat /etc/security/limits.conf
* soft nofile 65535
* hard nofile 65535
問題進入了死胡同,於是我開始嘗試找一些奇技淫巧看看能不能先儘快緩解一下,我搜尋到 @-神仙- 的一篇文章:動態修改運行中進程的 rlimit,裡面介紹了如何動態修改閾值的方法,雖然我測試時都顯示成功了,可惜 Elasticsearch 還是不能正常工作:
shell> echo -n 'Max open files=65535:65535' > /proc/<PID>/limits
此外,我還檢查了系統核心參數 fs.file-nr 及 fs.file-max,總之一切和檔案有關的參數都查了,甚至在啟動指令碼裡寫入程式碼「ulimit -n 65535」,但一切努力都顯得毫無意義。
正當山窮水盡疑無路的時候,同事 @軒脈刃 一語道破玄機:關閉 Supervisor 的進程管理機制,改用手動方式啟動 Elasticsearch 進程試試看。結果一切恢複正常。
為什麼會這樣呢?因為使用 Supervisor 的進程管理機制,它會作為父進程 FORK 出子進程,也就是 Elasticsearch 進程,鑒於父子關係,子進程允許開啟的最大檔案數不能超過父進程的閾值限制,但是 Supervisor 中 minfds 指令預設設定的允許開啟的最大檔案數過小,進而導致 Elasticsearch 進程出現故障。
此故障原因本來非常簡單,但我卻陷入了經驗主義的固定思維,值得反思。
補充一個永久性解決方案:
1. 修改 /etc/security/limits.conf
增加如下
Shell
$user hard nofile 131072
$user是用來啟動WLS的使用者。2048是建議的數值,若遇到同樣問題可能需要再次增加。
*表示所有使用者:
Shell
* soft nofile 131072
* hard nofile 131072
參考 Oracle Enterprise Linux 的推薦設定:
Shell
oracle hard nofile 131072
oracle soft nofile 131072
oracle hard nproc 131072
oracle soft nproc 131072
oracle soft core unlimited
oracle hard core unlimited
oracle soft memlock 3500000
oracle hard memlock 3500000
# Recommended stack hard limit 32MB for oracle installations
# oracle hard stack 32768
2. 其他來自 Debian GNU/Linux 官方文檔和 Oracle Technology Network 的解決方案,直接修改核心參數,無須重啟系統。
Shell
sysctl -w fs.file-max 65536
# apply on-the-fly to proc
echo "65536" > /proc/sys/fs/file-max
# OR
echo 65536 | sudo tee /proc/sys/fs/file-max
兩者作用是相同的,前者改核心參數,後者直接作用於核心參數在虛擬檔案系統(procfs, psuedo file system)上對應的檔案而已。
可以用下面的命令查看新的限制
Shell
sysctl -a | grep fs.file-max
# or use proc
cat /proc/sys/fs/file-max
修改核心參數
/etc/sysctl.conf Shell
echo "fs.file-max=65536" >> /etc/sysctl.conf
sysctl -p
查看當前file handles使用方式:
Shell
sysctl -a | grep fs.file-nr
# OR
cat /proc/sys/fs/file-nr
825 0 65536
輸出格式: The number of allocated file handles, the number of free file handles, and the maximum number of file handles.
另外一個命令:
Shell
lsof | wc -l
有點讓我困惑的是,以上兩個命令獲得的結果總是不相同的;-( 原因如下:
簡單來說 file-nr 給出的是 File Descriptors (檔案描述符,資料結構,程式用來開啟檔案所需要的 handle),而 lsof 列出的是 Open Files (檔案),包括不是用檔案描述符的。例如:目前的目錄,映射到記憶體中的 library 檔案和可執行檔文字檔(指令碼?)。通常 lsof 輸出要比 file-nr 大。
舉個簡單的例子:當前系統中Firefox開啟的檔案數:
Shell
lsof -p pid | wc -l
# or
lsof | grep pid | wc -l
再看一下這個進程 PID 所佔用的檔案描述符數
Shell
ls /proc/pid/fd | wc -l
對比一下就明白了,註:載入到記憶體的 library 檔案詳情可以看 /proc/pid/maps
此外,用 sysctl 來修改核心參數 fs.file-max 和用 ulimit 的區別,花了不少時間研究,討教了 Linux/FreeBSD/Solaris/OpenSolaris 老鳥Jockey同學,得到點撥之後終於基本弄清楚其概念和區別了。
優先順序(Open File Descriptors):
soft limit < hard limit < kernel (NR_OPEN => /proc/sys/fs/nr_open) < 實現最大file descriptor數採用的資料結構所導致的限制
The Linux kernel provides the getrlimit and setrlimit system calls to get and set resource limits per process. Each resource has an associated soft and hard limit. The soft limit is the value that the kernel enforces for the corresponding resource. The hard limit acts as a ceiling for the soft limit: an unprivileged process may only set its soft limit to a value in the range from 0 up to the hard limit, and (irreversibly) lower its hard limit. A privileged process (one with the CAP_SYS_RESOURCE capability) may make arbitrary changes to either limit value.
作為測試環境,尤其是用 VMWare guest OS 的形式,安裝 OpenSSH Server, webmin, phpsysinfo 等工具可以提高效率。
針對 Oracle Enterprise Linux 和 Red Hat Enterprise Linux 的快捷解決方案:
另外 OEL 5 和 RHEL 5 可以直接安裝 oracle-validated 包來解決安裝 Oracle 資料庫和中介軟體所需要的包依賴和系統配置問題,推薦!
yum install oracle-validated
或者下載後手動安裝