- 前言
- 为什么要写这本书
- 读者对象
- 如何阅读本书
- 勘误和支持
- 致谢
- 第一部分 安全运维篇
- 第 1 章 Linux 服务器安全运维
- 第 2 章 Linux 网络安全运维
- 第 3 章 数据安全工具 DRBD、extundelete
- 第二部分 运维故障排查篇
- 第 4 章 Linux 系统运维故障排查思路
- 第 5 章 Linux 故障排查案例实战
- 第三部分 自动化运维篇
- 第 6 章 轻量级运维利器 pssh、pdsh 和 mussh
- 第 7 章 分布式监控系统 Ganglia
- 第 8 章 基于 nagios 的分布式监控报警平台 Centreon
- 第 9 章 通过 Ganglia 与 Centreon 构建智能化监控报警平台
- 第四部分 集群架构篇
- 第 10 章 高性能 Web 服务器 Nginx
- 第 11 章 高性能集群软件 Keepalived
- 第 12 章 千万级高并发负载均衡软件 HAProxy
- 第 13 章 构建高性能的 MySQL 集群系统
- 第 14 章 高性能负载均衡集群软件 HAProxy
5.1 常见系统故障案例
本章主要介绍 Linux 系统运维过程中常见的一些故障和案例,这些案例都来自实际的生产环境,每个案例都会讲述一个小问题,这些问题非常简单,相信很多读者也都遇到过。但是需要说明的是,解决问题不是本章讲述的重点,系统运维过程中的错误也千差万别,不可能把每个问题都讲述一遍,这里重点讲述的是解决问题的思路。对每个问题的讲述,我们都从错误现象开始介绍,然后是解决问题的思路。接着是问题排查过程,最后给出解决问题的方法。通过这样抽丝剥茧的介绍,最终使读者掌握解决问题的统一方法和思路,有了这样的思路,相信所有问题都能迎刃而解。
5.1.1 su 切换用户带来的疑惑
这是一个客户的案例,客户的一台 Oracle 数据库服务器突然宕机了,由于在线业务的需要,客户没有考虑太多就直接重启了服务器,系统重新启动后没有出现问题,可是接下来,当客户准备切换到 oracle 用户下启动数据库时,怎么都无法进行 su 切换,于是问题出现了。
1.案例现象
在 root 用户下,su 切换到一个普通用户 oracle 却发生了错误,如图 5-1 所示。

图 5-1 su 切换发生 Permission denied 错误
于是,尝试直接通过 oracle 用户登录系统,发现此时的 oracle 用户也无法登录了,出现与上面同样的错误。
2.解决思路
从上面错误提示可知是权限出现了问题,那么可以从权限入手进行排查,基本思路如下:
- 用户目录/home/oracle 权限问题。
- su 程序执行权限问题。
- 程序依赖的共享库权限问题。
- SELinux 问题导致。
- 系统根空间问题。
3.排查问题
根据上面的思路,逐一检查,考虑到 su 在切换到 oracle 用户时会读取 oracle 目录下的环境变量配置文件,因此,首先检查/home/oracle 目录的权限是否存在问题:
[root@localhost home]# ls -al /home|grep oracle drwx------ 4 oracle oinstall 4096 01-31 10:45 oracle
从输出可知,/home/oracle 目录的属主是 oracle 用户,而 oracle 用户对这个目录具有“rwx”权限,因此,oracle 用户目录的权限设置是正确的,可以排除这个问题。
接着检查 su 执行权限问题:
[root@localhost home]# ll /bin/su -rwsr-xr-x 1 root root 24120 2007-11-30 /bin/su
可见 su 命令执行权限也没有问题,这个问题也排除了。
继续检查 su 依赖的共享库权限,使用 ldd 命令检查 su 命令依赖的共享库文件,如图 5-2 所示。
根据上面的操作,依次检查 su 命令依赖的每个库文件的权限,发现也都是正常的,都具有可执行权限,因此,共享库的问题也排除了。
根据上面的思路,继续检查 SELinux 的设置,执行命令如图 5-3 所示。
由输出可知,SELinux 处于关闭状态,这个原因也排除了。

图 5-2 通过 ldd 命令检查 su 命令依赖的共享库文件

图 5-3 检查 SELinux 的设置
到目前为止,问题变得扑朔迷离,到底是哪里出现问题了呢?作为 Linux 运维人员,例行检查系统根分区状态是非常必要的,那么首先检查一下根分区的磁盘空间大小,发现剩余空间还有很多,排除空间问题。既然报的错误是权限有问题,那么只要以权限为线索,不偏离这个核心就没错,于是继续尝试检查/home 目录下各个用户的权限,执行命令如图 5-4 所示。

图 5-4 检查/home 目录下各个用户的权限
从输出看每个用户的目录权限,都是“rwx------”,即“700”,完全没有问题。仔细检查思路,发现当前的目光一直停留在用户对应的目录上,而忽略了其他输出信息,而问题就藏在之前没有关注的信息中。在这个命令输出的前两行中,第一行权限对应的目录是“.”,代表当前目录,也就是/home 目录,权限为“rwxr-xr-x”,即“755”,第二行权限对应的目录是“..”,也就是根目录,权限却为“rw-rw-rw-”,即“666”,此时,问题终于查找到了,原来是根目录权限问题。
将根目录权限设置为“rw-rw-rw-”,显然是不正常的。在正常情况下根目录的权限应该是“755”,为何设置成了这样,很大的可能是误操作。通过 ls 命令查看根目录的权限时展示不是很清楚,也容易被很多运维人员忽略,其实我们可以通过另一个命令 stat 来详细查看每个目录的权限,如图 5-5 所示。

图 5-5 通过 stat 命令查看根目录权限
通过这个命令,可以很清晰地看到,根目录的权限是“0666”,这才是导致 su 执行失败的根本原因。
4.解决问题
知道了问题产生的原因,解决问题就非常简单,执行如下命令:
[root@localhost ~]# chmod 755 /
然后就可顺利执行 su 切换命令。
最后,做个简单的总结,这个问题主要是由于根目录没有可执行权限,而 Linux 下所有的操作都是在根目录下进行的,进而导致/home/oracle 目录没有执行权限。其实根目录权限的丢失对于系统中运行的每个用户存在同样的影响。因此,在权限出现问题时,一定要注意根目录的权限。
5.1.2 “Read-only file system”错误与解决方法
这个问题经常发生在有大量磁盘读写操作且磁盘分区很大的环境中,下面简单描述下此案例的应用环境。这是一个 Web 服务器故障案例,客户利用两台服务器加一个磁盘阵列做了一个双机热备的 Web 系统,所有网站数据都存储在磁盘阵列中,两台服务器共享一个磁盘阵列分区,在正常情况下主机挂载磁盘阵列,分区提供网站服务,主机故障时备机接管磁盘阵列分区继续提供网站服务。
1.案例现象
接到客户电话说他们的网站无法添加数据了,不过网站还可以正常访问,服务器和磁盘阵列也没有任何告警信息。
2.解决思路
根据这个简单信息,基本排查思路如下:
- 网站程序可能出现问题了。
- 服务器磁盘故障。
3.排查问题
首先通知研发人员对网站程序问题进行排查。经过检查,并没有发现程序有问题,而在程序日志中发现了一条信息:
java.lang.RuntimeException: Cannot make directory: file:/www/data/html/2013-03-30
根据这个输出可知,程序不能创建目录,那么尝试手动创建一个目录,登录 Web 服务器,在/www/data/html 目录下创建一个目录 test,操作如下:
[root@localhost html]# mkdir test mkdir: cannot create directory `test': Read-only file system
从这个输出信息可知,/www/data/html 目录所在的磁盘分区出现了问题,通过检查发现,/www/data/html 目录正是挂载的磁盘阵列分区,于是问题原因找到了。
4.解决问题
磁盘出现“Read-only file system”的原因有很多种,可能是文件系统数据块出现不一致导致的,也有可能是磁盘故障造成的。主流的 ext3、ext4 文件系统都有很强的自我修复机制,对于简单的错误,文件系统一般可自行修复,当遇到致命错误无法修复时,文件系统为了保证数据一致性和安全,会暂时屏蔽文件系统的写操作,将文件系统变为只读,进而出现了上面的“Read-only file system”现象。
手工修复文件系统错误的命令是 fsck,在修复文件系统前,最好卸载文件系统所在的磁盘分区:
[root@localhost ~]# umount /www/data umount: /www/data: device is busy
提示无法卸载,可能这个磁盘中还有文件对应的进程在运行,检查如下:
[root@localhost ~]# fuser -m /dev/sdb1 /dev/sdb1: 8800
接着检查一下 8800 端口对应的是什么进程,如图 5-6 所示。

图 5-6 查询 8800 端口对应的进程信息
原来是系统的 apache 进程还没有停止,停止 apache,成功卸载磁盘,操作如下:
[root@localhost ~]# /usr/local/apache2/bin/apachectl stop [root@localhost ~]# umount /www/data
最后,执行修复操作,如图 5-7 所示。

图 5-7 用 fsck 命令修复磁盘分区
修复过程比较简单,上面省略了很多输出信息。修复的时间根据磁盘大小和文件系统损坏程度而定。如果有些数据无法修复,会提示是否删除,此时可根据情况选择。修复完成后,被删除的文件会保留在对应磁盘分区挂载点的 lost+found 目录中。
修复完成后,执行挂载操作:
[root@localhost ~]# mount /dev/sdb1 /www/data
最后,在/www/data 目录下验证是否可以成功创建文件,至此,问题圆满解决。
5.1.3 “Argument list too long”错误与解决方法
作为一名运维人员,对这个错误并不陌生,在执行 rm、cp、mv 等命令时,如果要操作的文件数很多,可能会使用通配符批量处理大量文件,这时就可能会出现“Argument list too long”这个问题。
1.错误现象
这是一台 MySQL 数据库服务器,在系统中运行了很多定时任务,某天通过如下 crontab 命令又添加了一个计划任务,退出时系统报错。
#crontab -e
编辑完成后,保存退出,就出现如图 5-8 所示的错误。

图 5-8 crontab 命令编辑完成后发生的错误
2.解决思路
根据上面的报错信息,基本判定是磁盘空间满了,那么首先从检查服务器的磁盘空间开始,先检查/tmp 磁盘空间,然后检查根分区的磁盘空间,最后检查系统其他分区的磁盘空间。
3.问题排查
通过 df 命令查看了这个服务器上所有磁盘分区的情况,/tmp 分区空间还有很多,根分区也还有很大剩余空间,都不存在问题,最后发现是/var 磁盘分区空间已经达到 100%了。至此已经定位了问题,是/var 磁盘空间爆满导致的,因为 crontab 会在保存时将文件信息写到/var 目录下,然而这个磁盘没有空间了,所以报错也是理所当然了。
4.解决问题
接着通过“du -sh”命令检查/var 目录下所有文件或目录的大小,发现/var/spool/clientmqueue 目录占用了/var 整个分区大小的 90%,那么/var/spool/clientmqueue 目录下的文件都是怎么产生的呢,能否删除?下面简单介绍下/var/spool/clientmqueue 目录的文件是怎么生成的。
打开/var/spool/clientmqueue 目录下的一些文件,发现都是一些邮件信息,邮件内容大多是关于 Cron Daemon 的,其实/var/spool/clientmqueue 就是一个邮件暂存的目录。Linux 服务器在默认情况下会发一些邮件,比如当 cron 执行的程序有输出内容时,就会发送邮件信息到执行 cron 进程的用户。在发送邮件时,系统首先会把邮件复制到/var/spool/clientmqueue 目录下,然后等待 MTA(Mail Transfer Agent)程序来处理。而 MTA 主要的功能是把这个目录中的邮件转移到/var/spool/mqueue 目录下,然后再通过 sendmail 服务发送到真正的目的地。检查这个服务器的 sendmail 服务,发现其没有开启,这样/var/spool/clientmqueue 目录非常大的原因就找到了:没有发送邮件的客户端服务,所有邮件就都堆积在这个目录下了。
在确认完这些邮件内容都没用后,切换到/var/spool/clientmqueue 目录下,执行 rm 命令删除所有的文件时,出现如下错误:
[root@localhost clientmqueue]# rm * /bin/rm: argument list too long
此时出现了本节开头谈到的问题。
当在 Linux 系统中试图传递太多参数给一个系统命令时,就会出现“Argument list too long”错误。这是 Linux 系统一直以来都有的限制。查看这个限制可以通过命令“getconf ARG_MAX”来实现,如图 5-9 所示。
这是 CentOS 6.x 版本的一个最大值,而在 CentOS 5.x 中,这个值相对较小,如图 5-10 所示。
所以这个问题更多的是发生在 Linux 低版本中。

图 5-9 查看 CentOS 6.3 版本的 Linux 系统传递参数限制

图 5-10 查看 CentOS 5.8 版本的 Linux 系统传递参数限制
知道了产生问题的原因,解决方法就很多了,这里提供四种解决此问题的方法,下面分别进行介绍。
(1)手动把命令行参数分成较小的部分
例如:
rm [a-n]* -rf rm [o-z]* -rf
这种方法最简单,但是相对较低级,因为必须知道怎么平均分割文件,同时对于文件数目极多的情况,需要输入很多次命令。
(2)使用 find 命令删除
基本原理是通过 find 命令筛选文件列表,把符合要求的文件传递给一系列命令。这种方法是最简洁的,也是最有效的。
例如:
find /var/spool/clientmqueue -type f -print -exec rm -f {} \;
但是这种方法也有缺点,需要遍历所有文件,因而在文件数量极多时比较耗时。
(3)通过 shell 脚本
这种方法是通过编写一个 shell 脚本,然后通过循序语句实现,与 find 方法类似。
例如,可以编写如下脚本:
#!/bin/bash # 设定需要删除的文件夹 RM_DIR='/var/spool/clientmqueue' cd $RM_DIR for I in `ls` do rm -f $I done
(4)重新编译 Linux 内核
这种方法需要手动增加内核中分配给命令行参数的页数,打开 kernel source 下面的 include/linux/binfmts.h 文件,找到如下行:
# define MAX_ARG_PAGES 32
将“32”改为更大的值,例如 64 或 128,然后重新编译内核。
此种方法永久有效,可以彻底解决问题,但是比较复杂,推荐给高级用户使用,没有 Linux 经验的用户不建议用这种方法。
5.1.4 inode 耗尽导致应用故障
1.错误现象
客户的一台 Oracle 数据库服务器在关机重启后,Oracle 监听无法启动,提示错误如图 5-11 所示。

图 5-11 inode 耗尽故障现象
从输出信息判断,应该是磁盘空间耗尽导致 Oracle 监听无法启动,因为 Oracle 在启动监听时需要创建监听日志文件,而上面三个 TNS 错误产生的原因都是由最后一行错误导致的,于是首先检查系统磁盘空间,如图 5-12 所示。

图 5-12 查看故障服务器磁盘空间信息
从磁盘输出信息可知,所有分区磁盘空间都还有不少剩余,而 Oracle 监听写日志的路径在/var 分区下,虽然/var 分区仅剩下 3.2GB 可用磁盘空间,但是这对于写一个监听日志文件来说足够了,为什么还提示空间不足呢?
2.解决思路
既然错误提示与磁盘空间有关,那就深入研究下关于磁盘空间的问题,在 Linux 系统中对磁盘空间的占用分为三个部分:第一个是物理磁盘空间,第二个是 inode 节点所占用的磁盘空间,第三个是 Linux 用来存放信号量的空间,而平时接触较多的是物理磁盘空间,对第二个和第三个空间的问题接触较少。既然不是物理磁盘空间的问题,接着检查是否是 inode 节点耗尽的问题,通过执行“df -i”查看系统可用的 inode 节点,如图 5-13 所示。

图 5-13 查看磁盘分区的 inode 使用信息
由输出可知,果然是 inode 节点耗尽导致无法写日志文件。由于 inode 被全部用完了,虽然还有可用磁盘空间,但是文件系统已经无法再记录这些空余空间了,因此也就不能再创建新文件或文件夹了。由于涉及了 inode 知识,接下来就简单介绍下 Linux 中 inode 的概念。
在 Linux 系统中,文件由数据块和元数据组成,数据块就是多个连续性的扇区,是文件存取的最小单位。块(block)的大小,最常见的是 4KB,即连续 8 个 sector 组成一个 block。而元数据用来记录文件的创建者、文件的创建日期、文件的大小等,这种存储文件元数据信息的区域就叫做 inode,或者称为“索引节点”。
由于 inode 也是用来存储文件相关属性信息的,因此 inode 也会消耗硬盘空间,在硬盘格式化的时候,操作系统会自动将硬盘分成两个区域。一个是数据区,存放文件数据;另一个是 inode 区(inode table),存放 inode 所包含的信息。
每个 inode 节点的大小一般是 128B 或 256B。inode 节点的总数在格式化文件系统的时候,就已经确定,可以通过如下命令查看某个磁盘分区 inode 的总数:
[root@localhost ~]# dumpe2fs -h /dev/sda3|grep 'Inode count' dumpe2fs 1.39 (29-May-2006) Inode count: 5244736
举个形象的例子,如果将文件系统比作一本书,那么,inode 就是这本书的目录。在格式化文件系统的时候,这本书的最大目录数已经确定了。在写书(保存文件到磁盘)的过程中,可能发生这样的情况:纸用完了(磁盘空间不足),此时肯定无法保存新的文件;但是还存在另外一种情况,就是目录写完了(inode 节点全部分配完了),在这种情况下,虽然还有纸(磁盘空间),但是目录(inode)已经没有了,在文件系统上当然不能新建文件了,因为没有了目录,就无法通过索引找到文件。
另外,每个 inode 都有一个号码,操作系统用 inode 号码来区分不同的文件。通过“ls -i”命令,可以查看文件名对应的 inode 号,例如:
[root@localhost ~]# ls -i install.log 325762 install.log
如果要查看这个文件更详细的 inode 信息,可以通过 stat 命令实现,如图 5-14 所示。

图 5-14 查看某文件 inode 的详细信息
3.解决问题
知道这个故障是由 inode 导致的后,接下来就要查看/var 目录下为何耗尽了 inode,通过检查发现/var/spool/clientmqueue/这个目录里面的文件仅 500 多万个,至于产生的原因,分析后确定应该是系统的 crontab 导致的,因为系统开了多个 crontab 任务,而如果 crontab 任务没有重定向,默认就会在这个目录下创建一个文件,日积月累,此目录下的小文件就会超级多。解决的方法很简单,删除这些没用的文件即可。删除的方法是直接使用 rm 命令,这时肯定会提示“Argument list too long”的错误,而解决这个的方法前面章节已经有过详细介绍,通过如下命令即可完成:
[root@localhost ~]# find /var/spool/clientmqueue/ -name "*" -exec rm -rf {} \;
删除日志文件后,再次启动 Oracle 监听,可以顺利实现启动,查看发现新的监听日志文件已经生成,至此,问题得到圆满解决。
5.1.5 文件已删除但空间不释放的原因
1.错误现象
运维的监控系统发来通知,报告一台服务器空间满了,登录服务器查看,根分区确实没有空间了,如图 5-15 所示。

图 5-15 查看服务器磁盘空间
这里首先说明一下服务器的一些删除策略,由于 Linux 没有回收站功能,所以线上服务器上所有要删除的文件都会先移动到系统/tmp 目录下,然后定期清除/tmp 目录下的数据。这个策略本身没有问题,但是通过检查发现这台服务器的系统分区中并没有单独划分/tmp 分区,这样/tmp 下的数据其实占用了根分区的空间。既然找到了问题,那么删除/tmp 目录下一些占空间较大的数据文件即可,检查/tmp 下最大的三个数据文件,如图 5-16 所示。

图 5-16 查看/tmp 下最大的前三个数据文件
通过命令输出发现在/tmp 目录下有个 66GB 大小的文件 access_log,这个文件应该是 Apache 产生的访问日志文件,从日志大小来看,应该是很久没有清理 Apache 日志文件了,基本判定是这个文件导致的根空间爆满,在确认此文件可以删除后,执行如下删除操作:
[root@localhost ~]# rm /tmp/access_log
接着查看系统根分区空间是否释放,如图 5-17 所示。

图 5-17 查看磁盘空间是否释放
从输出可以看到,根分区空间仍然没有释放,这是怎么回事?
2.解决思路
一般来说不会出现删除文件后空间不释放的情况,但是也存在例外,比如文件被进程锁定,或者有进程一直在向这个文件写数据等,要理解这个问题,就需要知道 Linux 下文件的存储机制和存储结构。
一个文件在文件系统中的存放分为两个部分:数据部分和指针部分,指针位于文件系统的 meta-data 中,在将数据删除后,这个指针就从 meta-data 中清除了,而数据部分存储在磁盘中。在将数据对应的指针从 meta-data 中清除后,文件数据部分占用的空间就可以被覆盖并写入新的内容,之所以在出现删除 access_log 文件后,空间还没释放,就是因为 httpd 进程还在一直向这个文件写入内容,导致虽然删除了 access_log 文件,但是由于进程锁定,文件对应的指针部分并未从 meta-data 中清除,而由于指针并未删除,系统内核就认为文件并未删除,因此通过 df 命令查询空间并未释放也就不足为奇了。
3.问题排查
既然有了解决问题的思路,那么接下来看看是否有进程一直在向 access_log 文件中写数据,这里需要用到 Linux 下的 lsof 命令,通过这个命令可以获取一个仍然被应用程序占用的已删除文件列表,命令执行如图 5-18 所示。

图 5-18 查看被应用程序锁定的已删除文件列表
从输出结果可以看到,/tmp/access_log 文件被进程 httpd 锁定,而 httpd 进程还一直向这个文件写入日志数据。从第 7 列可知,这个日志文件大小约 70GB,而系统根分区总大小才 100GB,由此可知,这个文件就是导致系统根分区空间耗尽的罪魁祸首。最后一列的“deleted”状态说明这个日志文件已经被删除,但由于进程还在一直向此文件写入数据,因此空间并未释放。
4.解决问题
到这里问题就基本排查清楚了,解决这一类问题的方法有很多种,最简单的方法是关闭或重启 httpd 进程,当然也可以重启操作系统,不过这些并不是最好的方法。对待这种进程不停对文件写日志的操作,要释放文件占用的磁盘空间,最好的方法是在线清空这个文件,具体可以通过如下命令完成:
[root@localhost ~]# echo " " >/tmp/access_log
通过这种方法,磁盘空间不但可以马上释放,也可保障进程继续向文件写入日志,这种方法经常用于在线清理 Apache、Tomcat、Nginx 等 Web 服务产生的日志文件。
5.1.6 “Too many open files”错误与解决方法
1.问题现象
这是一个基于 Java 的 Web 应用系统,在后台添加数据时提示无法添加,于是登录服务器查看 Tomcat 日志,发现如下异常信息:
java.io.IOException: Too many open files
通过这个报错信息,基本判断是系统可用的文件描述符不够了,由于 Tomcat 服务是系统 www 用户启动的,于是以 www 用户登录系统,通过“ulimit -n”命令查看系统可以打开最大文件描述符的数量,输出如下:
[www@tomcatserver ~]$ ulimit -n 65535
可以看到这台服务器设置的最大可打开的文件描述符已经是 65535 了,这么大的值应该够用了,但是为什么提示这样的错误呢?
2.解决思路
这个案例涉及 Linux 下 ulimit 命令的使用,这里简单介绍下 ulimit 的作用和使用技巧。ulimit 主要用来限制进程对资源的使用情况,它支持各种类型的限制,常用的有:
- 内核文件的大小限制。
- 进程数据块的大小限制。
- shell 进程创建文件大小限制。
- 可加锁内存大小限制。
- 常驻内存集的大小限制。
- 打开文件句柄数限制。
- 分配堆栈的最大大小限制。
- CPU 占用时间限制用户最大可用的进程数限制。
- shell 进程所能使用的最大虚拟内存限制。
ulimit 使用的基本格式为:
ulimit [options] [limit]
具体的 ulimit 参数(即 options)含义如表 5-1 所示。
表 5-1 ulimit 参数的含义

在使用 ulimit 时,有以下几种使用方法。
(1)在用户环境变量中加入
如果用户使用的是 bash,那么可以在用户目录的环境变量文件.bashrc 或.bash_profile 中加入“ulimit -u 128”来限制用户最多可以使用 128 个进程。
(2)在应用程序的启动脚本中加入
如果应用程序是 Tomcat,那么可以在 Tomcat 的启动脚本 startup.sh 中加入“ulimit -n 65535”来限制用户最多可以使用 65535 个文件描述符。
(3)直接在 shell 命令终端执行 ulimit 命令
这种方法的资源限制仅仅在执行命令的终端生效,在退出或关闭终端后,设置失效,并且这个设置不影响其他 shell 终端。
有时候为了方便起见,也可以将用户资源的限制统一由一个文件来配置,这个文件就是/etc/security/limits.conf,该文件不但能对指定用户的资源进行限制,还能对指定组的资源进行限制。该文件的使用规则如下:
<domain> <type> <item> <value>
其中:
- domain 表示用户或用户组的名字,还可以使用“*”作为通配符,表示任何用户或用户组。
- type 表示限制的类型,可以有两个值:soft 和 hard,分别表示软、硬资源限制。
- item 表示需要限定的资源名称,常用的有 nofile、cpu、stack 等。分别表示最大打开句柄数、占用的 CPU 时间、最大的堆栈大小。
- value 表示限制各种资源的具体数值。
除了 limits.conf 文件之外,还有一个/etc/security/limits.d 目录,可以将资源限制创建一个文件放到这个目录中,系统默认会首先读取这个目录下的所有文件,然后才去读取 limits.conf 文件。在所有资源限制设置完成后,退出 shell 终端,再次登录 shell 终端后,ulimit 设置即可自动生效。
3.解决问题
在了解 ulimit 知识后,接着上面的案例,既然 ulimit 设置没问题,那么一定是设置没有生效导致的。接下来检查下启动 Tomcat 的 www 用户环境变量下是否添加了 ulimit 限制,检查后发现,www 用户下并无 ulimit 资源限制。于是继续检查 Tomcat 启动脚本 startup.sh 文件是否添加了 ulimit 限制,检查后发现也没有添加。最后考虑是否将限制加到了 limits.conf 文件中,于是检查 limits.conf 文件,操作如下:
[root@tomcatserver ~]# cat /etc/security/limits.conf|grep www www soft nofile 65535 www hard nofile 65535
从输出可知,ulimit 限制加在了 limits.conf 文件中,既然限制已经加了,配置也没有错,为何还会报错呢?经过长时间思考,判断只有一种可能,那就是 Tomcat 的启动时间早于 ulimit 资源限制的添加时间,于是首先查看下 Tomcat 的启动时间,操作如下:
[root@tomcatserver ~]# more /etc/issue CentOS release 6.3 (Final) Kernel \r on an \m [root@tomcatserver ~]# uptime 15:10:19 up 283 days, 5:37, 4 users, load average: 1.20, 1.41, 1.35 [root@tomcatserver ~]# pgrep –f tomcat 4667 [root@tomcatserver ~]# ps -eo pid,lstart,etime|grep 4667 4667 Sat Jul 6 09:33:39 2013 77-05:26:02
从输出可以看出,这台服务器已经有 283 天没有重启过了,而 Tomcat 是在 2013 年 7 月 6 日 9 点多启动的,启动了近 77 天零 5 个半小时了,接着继续看看 limits.conf 文件的修改时间,操作如图 5-19 所示。

图 5-19 查看 limits.conf 文件的最后修改时间
通过 stat 命令可以很清楚地看出,limits.conf 文件最后的修改时间是 2013-07-12,通过查问相关的 Linux 系统管理人员,基本确认就是在这个时候添加的 ulimit 资源限制,这样此案例的问题就很明确了。由于 ulimit 限制的添加时间晚于 Tomcat 最后一次的启动时间,而在此期间内,Tomcat 服务一直未重启过,操作系统也一直未重启过,那么 ulimit 资源限制对于 Tomcat 来说始终是不生效的,同时,由于此操作系统是 CentOS 6.3,系统默认的最大可用句柄数是 1024,java 进程采用的是 Linux 默认的这个值,因此出现“Too many open files”的错误也是合乎情理的。
清楚问题之后,解决问题的方法非常简单,重启 Tomcat 服务即可。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论