问题
几天前,一台内核为 2.6.32、物理内存为 128 GB 的 CentOS 机器遇到了麻烦。负责的系统管理员告诉我,由于交换,PHP-FPM 应用程序无法及时响应请求,而且他发现free
几乎没有剩余内存,因此选择重启机器。
我知道在 Linux 上,可用内存可能是一个令人困惑的概念,重启也许是错误的做法。然而,上述管理员将责任归咎于 PHP 应用程序(由我负责),并拒绝进一步调查。
我自己能发现的是:
- 重启之前,可用内存(包括缓冲区和缓存)只有几百 MB。
- 重启之前,
/proc/meminfo
报告 Slab 内存使用量约为 90 GB(是的,GB)。 - 重启后,可用内存为 119 GB,一小时内降至 100 GB 左右,因为 PHP-FPM 工作进程(约 600 个)正在恢复运行,每个进程在 top 的 RES 列中显示 30 到 40 MB(这种情况已经持续了几个月,考虑到 PHP 应用程序的性质,这是完全合理的)。进程列表中没有其他进程消耗异常或值得注意的 RAM 量。
- 重启后Slab内存在300MB左右
从那时起,我一直在监控系统,最值得注意的是,Slab 内存以每天约 5 GB 的速度直线增加。 报告的可用内存free
和/proc/meminfo
以相同的速率减少。 Slab 目前为 46 GB。 据slabtop
大多数记录,它用于以下条目dentry
:
可用内存:
free -m
total used free shared buffers cached
Mem: 129048 76435 52612 0 144 7675
-/+ buffers/cache: 68615 60432
Swap: 8191 0 8191
记忆信息:
cat /proc/meminfo
MemTotal: 132145324 kB
MemFree: 53620068 kB
Buffers: 147760 kB
Cached: 8239072 kB
SwapCached: 0 kB
Active: 20300940 kB
Inactive: 6512716 kB
Active(anon): 18408460 kB
Inactive(anon): 24736 kB
Active(file): 1892480 kB
Inactive(file): 6487980 kB
Unevictable: 8608 kB
Mlocked: 8608 kB
SwapTotal: 8388600 kB
SwapFree: 8388600 kB
Dirty: 11416 kB
Writeback: 0 kB
AnonPages: 18436224 kB
Mapped: 94536 kB
Shmem: 6364 kB
Slab: 46240380 kB
SReclaimable: 44561644 kB
SUnreclaim: 1678736 kB
KernelStack: 9336 kB
PageTables: 457516 kB
NFS_Unstable: 0 kB
Bounce: 0 kB
WritebackTmp: 0 kB
CommitLimit: 72364108 kB
Committed_AS: 22305444 kB
VmallocTotal: 34359738367 kB
VmallocUsed: 480164 kB
VmallocChunk: 34290830848 kB
HardwareCorrupted: 0 kB
AnonHugePages: 12216320 kB
HugePages_Total: 2048
HugePages_Free: 2048
HugePages_Rsvd: 0
HugePages_Surp: 0
Hugepagesize: 2048 kB
DirectMap4k: 5604 kB
DirectMap2M: 2078720 kB
DirectMap1G: 132120576 kB
板顶:
slabtop --once
Active / Total Objects (% used) : 225920064 / 226193412 (99.9%)
Active / Total Slabs (% used) : 11556364 / 11556415 (100.0%)
Active / Total Caches (% used) : 110 / 194 (56.7%)
Active / Total Size (% used) : 43278793.73K / 43315465.42K (99.9%)
Minimum / Average / Maximum Object : 0.02K / 0.19K / 4096.00K
OBJS ACTIVE USE OBJ SIZE SLABS OBJ/SLAB CACHE SIZE NAME
221416340 221416039 3% 0.19K 11070817 20 44283268K dentry
1123443 1122739 99% 0.41K 124827 9 499308K fuse_request
1122320 1122180 99% 0.75K 224464 5 897856K fuse_inode
761539 754272 99% 0.20K 40081 19 160324K vm_area_struct
437858 223259 50% 0.10K 11834 37 47336K buffer_head
353353 347519 98% 0.05K 4589 77 18356K anon_vma_chain
325090 324190 99% 0.06K 5510 59 22040K size-64
146272 145422 99% 0.03K 1306 112 5224K size-32
137625 137614 99% 1.02K 45875 3 183500K nfs_inode_cache
128800 118407 91% 0.04K 1400 92 5600K anon_vma
59101 46853 79% 0.55K 8443 7 33772K radix_tree_node
52620 52009 98% 0.12K 1754 30 7016K size-128
19359 19253 99% 0.14K 717 27 2868K sysfs_dir_cache
10240 7746 75% 0.19K 512 20 2048K filp
VFS 缓存压力:
cat /proc/sys/vm/vfs_cache_pressure
125
交换性:
cat /proc/sys/vm/swappiness
0
我知道未使用的内存是浪费的内存,所以这不一定是坏事(尤其是考虑到 44 GB 显示为 SReclaimable)。然而,显然这台机器还是遇到了问题,我担心几天后当 Slab 超过 90 GB 时,同样的事情会再次发生。
问题
我有以下问题:
- 我是否正确地认为 Slab 内存始终是物理 RAM,并且该数字已从 MemFree 值中减去?
- dentry 条目数量如此之多正常吗?PHP 应用程序可以访问大约 1.5 M 个文件,但其中大多数都是档案,常规网络流量根本不会访问它们。
- 怎么解释缓存的 inode 数量比缓存的 dentry 数量低得多这一事实呢?它们之间难道不应该以某种方式关联吗?
- 如果系统遇到内存问题,内核是否不应该自动释放一些目录项?没有发生这种情况的原因可能是什么?
- 有没有办法“查看” dentry 缓存以查看所有这些内存(即正在缓存的路径是什么)?也许这表明存在某种内存泄漏、符号链接循环,或者确实表明 PHP 应用程序做错了什么。
- PHP 应用程序代码以及所有资产文件都通过 GlusterFS 网络文件系统挂载,这与此有关系吗?
请记住,我无法以 root 身份进行调查,只能以普通用户身份进行调查,而且管理员拒绝提供帮助。他甚至不会运行典型echo 2 > /proc/sys/vm/drop_caches
测试来查看 Slab 内存是否确实可回收。
如果您能提供任何关于可能发生的情况以及我如何进一步调查的见解,我将不胜感激。
更新
一些进一步的诊断信息:
坐骑:
cat /proc/self/mounts
rootfs / rootfs rw 0 0
proc /proc proc rw,relatime 0 0
sysfs /sys sysfs rw,relatime 0 0
devtmpfs /dev devtmpfs rw,relatime,size=66063000k,nr_inodes=16515750,mode=755 0 0
devpts /dev/pts devpts rw,relatime,gid=5,mode=620,ptmxmode=000 0 0
tmpfs /dev/shm tmpfs rw,relatime 0 0
/dev/mapper/sysvg-lv_root / ext4 rw,relatime,barrier=1,data=ordered 0 0
/proc/bus/usb /proc/bus/usb usbfs rw,relatime 0 0
/dev/sda1 /boot ext4 rw,relatime,barrier=1,data=ordered 0 0
tmpfs /phptmp tmpfs rw,noatime,size=1048576k,nr_inodes=15728640,mode=777 0 0
tmpfs /wsdltmp tmpfs rw,noatime,size=1048576k,nr_inodes=15728640,mode=777 0 0
none /proc/sys/fs/binfmt_misc binfmt_misc rw,relatime 0 0
cgroup /cgroup/cpuset cgroup rw,relatime,cpuset 0 0
cgroup /cgroup/cpu cgroup rw,relatime,cpu 0 0
cgroup /cgroup/cpuacct cgroup rw,relatime,cpuacct 0 0
cgroup /cgroup/memory cgroup rw,relatime,memory 0 0
cgroup /cgroup/devices cgroup rw,relatime,devices 0 0
cgroup /cgroup/freezer cgroup rw,relatime,freezer 0 0
cgroup /cgroup/net_cls cgroup rw,relatime,net_cls 0 0
cgroup /cgroup/blkio cgroup rw,relatime,blkio 0 0
/etc/glusterfs/glusterfs-www.vol /var/www fuse.glusterfs rw,relatime,user_id=0,group_id=0,default_permissions,allow_other,max_read=131072 0 0
/etc/glusterfs/glusterfs-upload.vol /var/upload fuse.glusterfs rw,relatime,user_id=0,group_id=0,default_permissions,allow_other,max_read=131072 0 0
sunrpc /var/lib/nfs/rpc_pipefs rpc_pipefs rw,relatime 0 0
172.17.39.78:/www /data/www nfs rw,relatime,vers=3,rsize=65536,wsize=65536,namlen=255,hard,proto=tcp,port=38467,timeo=600,retrans=2,sec=sys,mountaddr=172.17.39.78,mountvers=3,mountport=38465,mountproto=tcp,local_lock=none,addr=172.17.39.78 0 0
安装信息:
cat /proc/self/mountinfo
16 21 0:3 / /proc rw,relatime - proc proc rw
17 21 0:0 / /sys rw,relatime - sysfs sysfs rw
18 21 0:5 / /dev rw,relatime - devtmpfs devtmpfs rw,size=66063000k,nr_inodes=16515750,mode=755
19 18 0:11 / /dev/pts rw,relatime - devpts devpts rw,gid=5,mode=620,ptmxmode=000
20 18 0:16 / /dev/shm rw,relatime - tmpfs tmpfs rw
21 1 253:1 / / rw,relatime - ext4 /dev/mapper/sysvg-lv_root rw,barrier=1,data=ordered
22 16 0:15 / /proc/bus/usb rw,relatime - usbfs /proc/bus/usb rw
23 21 8:1 / /boot rw,relatime - ext4 /dev/sda1 rw,barrier=1,data=ordered
24 21 0:17 / /phptmp rw,noatime - tmpfs tmpfs rw,size=1048576k,nr_inodes=15728640,mode=777
25 21 0:18 / /wsdltmp rw,noatime - tmpfs tmpfs rw,size=1048576k,nr_inodes=15728640,mode=777
26 16 0:19 / /proc/sys/fs/binfmt_misc rw,relatime - binfmt_misc none rw
27 21 0:20 / /cgroup/cpuset rw,relatime - cgroup cgroup rw,cpuset
28 21 0:21 / /cgroup/cpu rw,relatime - cgroup cgroup rw,cpu
29 21 0:22 / /cgroup/cpuacct rw,relatime - cgroup cgroup rw,cpuacct
30 21 0:23 / /cgroup/memory rw,relatime - cgroup cgroup rw,memory
31 21 0:24 / /cgroup/devices rw,relatime - cgroup cgroup rw,devices
32 21 0:25 / /cgroup/freezer rw,relatime - cgroup cgroup rw,freezer
33 21 0:26 / /cgroup/net_cls rw,relatime - cgroup cgroup rw,net_cls
34 21 0:27 / /cgroup/blkio rw,relatime - cgroup cgroup rw,blkio
35 21 0:28 / /var/www rw,relatime - fuse.glusterfs /etc/glusterfs/glusterfs-www.vol rw,user_id=0,group_id=0,default_permissions,allow_other,max_read=131072
36 21 0:29 / /var/upload rw,relatime - fuse.glusterfs /etc/glusterfs/glusterfs-upload.vol rw,user_id=0,group_id=0,default_permissions,allow_other,max_read=131072
37 21 0:30 / /var/lib/nfs/rpc_pipefs rw,relatime - rpc_pipefs sunrpc rw
39 21 0:31 / /data/www rw,relatime - nfs 172.17.39.78:/www rw,vers=3,rsize=65536,wsize=65536,namlen=255,hard,proto=tcp,port=38467,timeo=600,retrans=2,sec=sys,mountaddr=172.17.39.78,mountvers=3,mountport=38465,mountproto=tcp,local_lock=none,addr=172.17.39.78
GlusterFS 配置:
cat /etc/glusterfs/glusterfs-www.vol
volume remote1
type protocol/client
option transport-type tcp
option remote-host 172.17.39.71
option ping-timeout 10
option transport.socket.nodelay on # undocumented option for speed
# http://gluster.org/pipermail/gluster-users/2009-September/003158.html
option remote-subvolume /data/www
end-volume
volume remote2
type protocol/client
option transport-type tcp
option remote-host 172.17.39.72
option ping-timeout 10
option transport.socket.nodelay on # undocumented option for speed
# http://gluster.org/pipermail/gluster-users/2009-September/003158.html
option remote-subvolume /data/www
end-volume
volume remote3
type protocol/client
option transport-type tcp
option remote-host 172.17.39.73
option ping-timeout 10
option transport.socket.nodelay on # undocumented option for speed
# http://gluster.org/pipermail/gluster-users/2009-September/003158.html
option remote-subvolume /data/www
end-volume
volume remote4
type protocol/client
option transport-type tcp
option remote-host 172.17.39.74
option ping-timeout 10
option transport.socket.nodelay on # undocumented option for speed
# http://gluster.org/pipermail/gluster-users/2009-September/003158.html
option remote-subvolume /data/www
end-volume
volume replicate1
type cluster/replicate
option lookup-unhashed off # off will reduce cpu usage, and network
option local-volume-name 'hostname'
subvolumes remote1 remote2
end-volume
volume replicate2
type cluster/replicate
option lookup-unhashed off # off will reduce cpu usage, and network
option local-volume-name 'hostname'
subvolumes remote3 remote4
end-volume
volume distribute
type cluster/distribute
subvolumes replicate1 replicate2
end-volume
volume iocache
type performance/io-cache
option cache-size 8192MB # default is 32MB
subvolumes distribute
end-volume
volume writeback
type performance/write-behind
option cache-size 1024MB
option window-size 1MB
subvolumes iocache
end-volume
### Add io-threads for parallel requisitions
volume iothreads
type performance/io-threads
option thread-count 64 # default is 16
subvolumes writeback
end-volume
volume ra
type performance/read-ahead
option page-size 2MB
option page-count 16
option force-atime-update no
subvolumes iothreads
end-volume
答案1
我是否正确地认为 Slab 内存始终是物理 RAM,并且该数字已从 MemFree 值中减去?
是的。
dentry 条目数量如此之多正常吗?PHP 应用程序可以访问大约 1.5 M 个文件,但其中大多数都是档案,常规网络流量根本不会访问它们。
是的,如果系统没有内存压力的话。它必须使用内存来做某事,并且可能在您的特定使用模式中,这是使用该内存的最佳方式。
怎么解释缓存的 inode 数量比缓存的 dentry 数量低得多这一事实呢?它们之间难道不应该以某种方式关联吗?
大量的目录操作是最有可能的解释。
如果系统遇到内存问题,内核是否不应该自动释放一些目录项?没有发生这种情况的原因可能是什么?
应该可以,我想不出任何不可以的理由。我不确定这是否是真正出错的地方。我强烈建议升级内核或进一步增加 vfs_cache_pressure。
有没有办法“查看” dentry 缓存以查看所有这些内存(即正在缓存的路径是什么)?也许这表明存在某种内存泄漏、符号链接循环,或者确实表明 PHP 应用程序做错了什么。
我不相信有。我会寻找任何具有大量条目或被搜索或遍历的非常深的目录结构的目录。
PHP 应用程序代码以及所有资产文件都通过 GlusterFS 网络文件系统挂载,这与此有关系吗?
肯定是文件系统的问题。例如,文件系统错误导致 dentry 无法释放,这就是一种可能。
答案2
确认解决方案
对于任何可能遇到同样问题的人。数据中心人员今天终于找到了答案。罪魁祸首是与 Libcurl 捆绑在一起的 NSS(网络安全服务)库。升级到最新版本解决了这个问题。
描述详细信息的错误报告在这里:
https://bugzilla.redhat.com/show_bug.cgi?format=multiple&id=1044666
显然,为了确定某个路径是本地路径还是网络驱动器路径,NSS 会查找一个不存在的文件并测量文件系统报告所需的时间!如果您有足够多的 Curl 请求和足够的内存,这些请求都会被缓存并堆叠起来。
答案3
我遇到了这个确切的问题,虽然 Wolfgang 对原因的判断是正确的,但缺少一些重要的细节。
此问题会影响使用 curl 或 libcurl 或任何其他使用 mozilla NSS 进行安全连接的软件执行的 SSL 请求。非安全请求不会触发此问题。
该问题不需要并发 curl 请求。只要 curl 调用频率足够高,超过操作系统回收 RAM 的努力,就会发生 dentry 累积。
NSS 的新版本 3.16.0 确实包含针对此问题的修复。但是,您无法通过升级 NSS 免费获得修复,也不必升级所有 NSS。您至少只需升级 nss-softokn(它对 nss-utils 有必需的依赖性)。并且为了获得好处,您需要为使用 libcurl 的进程设置环境变量 NSS_SDB_USE_CACHE。该环境变量的存在使得可以跳过代价高昂的不存在文件检查。
FWIW,我写了一个博客条目提供更多背景/细节,以防有人需要。
答案4
这实际上并没有解释您的答案,但作为该系统的用户,您提供了以下信息:
cat /proc/meminfo
MemTotal: 132145324 kB
...
SReclaimable: 44561644 kB
SUnreclaim: 1678736 kB
足以告诉我这是不是你的问题系统管理员有责任提供充分的解释。
我不想在这里听起来很粗鲁,但是;
- 您缺少有关此主持人角色的具体信息。
- 主机如何对资源进行优先排序超出了您的范围。
- 您不熟悉或者没有参与过此主机上的存储的设计和部署。
- 由于您不是 root 用户,因此无法提供某些系统输出。
这是你的系统管理员的责任来证明或解决 slab 分配异常。要么您没有向我们完整展示导致您出现这种情况的整个过程(坦率地说,我对此不感兴趣),要么您的系统管理员在处理此问题时表现得不负责任和/或不称职。
你可以随意告诉他,互联网上一些陌生人认为他没有认真履行自己的职责。