• 磁盘之上的Raid与LVM一探

    机械硬盘与SSD背景

    我们知道对于一台服务器来说,影响它性能的组建包括:CPU、主板总线IO、内存IO、硬盘IO、网卡IO等。随着计算机的发展,CPU的运算速度已经是非常快的了,同时我们的内存IO速度也已经达到了非常快的地步了(差不多应该有5G每秒),而硬盘上的速度却成了瓶颈。

    说到磁盘的性能,我们应该对它有一个深刻的了解,这在运维工作中是至关重要的,而衡量磁盘的性能,主要有两个指标:

    磁盘的 IOPS:也就是在一秒内,磁盘进行多少次 I/O 读写。

    磁盘的吞吐量:也就是每秒磁盘 I/O 的流量,即磁盘写入加上读出的数据的大小。

    两者之间的关系:每秒 I/O 吞吐量= IOPS* 平均 I/O SIZE。

    两个指标分别对应不同的业务场景,随机读写频繁的应用,如小文件存储(图片)、OLTP数据库、邮件服务器,关注随机读写性能,IOPS是关键衡量指标。顺序读写频繁的应用,传输大量连续数据,如电视台的视频编辑,视频点播VOD(Video On Demand),关注连续读写性能。数据吞吐量是关键衡量指标。
    比如:

    读取10000个1KB文件,用时10秒  Throught(吞吐量)=1MB/s ,IOPS=1000  追求IOPS
    读取1个10MB文件,用时0.2秒  Throught(吞吐量)=50MB/s, IOPS=5             追求吞吐量

    所以,对于同一个磁盘(或者 LUN),随着每次 I/O 读写数据的大小不通,IOPS 的数值也不是固定不变的。例如,每次 I/O 写入或者读出的都是连续的大数据块,此时 IOPS 相对会低一些;在不频繁换道的情况下,每次写入或者读出的数据块小,相对来讲 IOPS 就会高一些。也就是说,IOPS 也取决与I/O块的大小,采用不同I/O块的大小测出的IOPS值是不同的。 对一个具体的IOPS, 可以了解它当时测试的I/O块的尺寸。

    说到这里,突然想到了我心中疑虑已久的两个问题:

    我们在采购或衡量硬盘时,会可参考磁盘的转速,这个转数与所谓的 IOPS和 吞吐量有什么联系呢 ?

    常听人讲起SSD比普通的机械硬盘好,快成百上千倍,究竟快在哪里?为什么快?是不是真的快那么多?

    我们知道,传统的机械键盘由于它的构成结构,所以一次I/O请求所花费的时间,它由寻道时间、旋转延迟和数据传输时间三部分构成(下图摘自网上)。

    a

    如上,磁盘是一个扁平的圆盘(与电唱机的唱片类似)。盘面上有许多称为磁道的圆圈,数据就记录在这些磁道上。磁盘可以是单片的,也可以是由若干盘片组成的盘组,每一盘片上有两个面。如上图11.3中所示的6片盘组为例,除去最顶端和最底端的外侧面不存储数据之外,一共有10个面可以用来保存信息。

    活动头盘 (如上图)的磁头是可移动的。每一个盘面上只有一个磁头(磁头是双向的,因此正反盘面都能读写)。它可以从该面的一个磁道移动到另一个磁道。所有磁头都装在同一个动臂上,因此不同盘面上的所有磁头都是同时移动的(行动整齐划一)。当盘片绕主轴旋转的时候,磁头与旋转的盘片形成一个圆柱体。各个盘面上半径相同的磁道组成了一个圆柱面,我们称为柱面 。因此,柱面的个数也就是盘面上的磁道数。

    磁盘上数据必须用一个三维地址唯一标示:柱面号、盘面号、块号(磁道上的盘块)。 所以读/写磁盘上某一指定数据需要下面3个步骤:
    (1)  首先移动臂根据柱面号使磁头移动到所需要的柱面上,这一过程被称为定位或查找 。
    (2)  如上图11.3中所示的6盘组示意图中,所有磁头都定位到了10个盘面的10条磁道上(磁头都是双向的)。这时根据盘面号来确定指定盘面上的磁道。
    (3) 盘面确定以后,盘片开始旋转,将指定块号的磁道段移动至磁头下。

    所以访问某一具体信息,由3部分时间组成:

    • 查找寻道时间:即完成上述步骤(1)所需要的时间。是指将读写磁头移动至正确的磁道上所需要的时间。寻道时间越短,I/O操作越快,目前磁盘的平均寻道时间一般在3-15ms不等(一般情况下大概4ms就差不多了)。而寻道时间与进行的I/O类型有关,随机读和顺序读的差别也主要在于此点,如下,顺序读的话,磁臂不需要来回跳动,而随机读需要来回跳到对应的点,自然耗时。

    2d91e6a8-829a-317f-98d2-dbcf8c4ed801

    • 旋转延迟 : 是指盘片旋转将请求数据所在扇区移至读写磁头下方所需要的时间。旋转延迟取决于磁盘转速,通常使用磁盘旋转一周所需时间的1/2表示。我们看到所谓的转速其实是一次I/O请求(IOPS)的一部分,所以它直接关系到一次磁盘的性能问题。目前市场是常见得机械硬盘大概有7500 rpm、10000 rpm等。

    常见磁盘平均物理寻道时间为:

    • 7200转/分的STAT硬盘平均物理寻道时间是9ms
    • 10000转/分的STAT硬盘平均物理寻道时间是6ms
    • 15000转/分的SAS硬盘平均物理寻道时间是4ms

    常见硬盘的旋转延迟时间为

    • 7200   rpm的磁盘平均旋转延迟大约为60*1000/7200/2 = 4.17ms
    • 10000 rpm的磁盘平均旋转延迟大约为60*1000/10000/2 = 3ms
    • 15000 rpm的磁盘其平均旋转延迟约为60*1000/15000/2 = 2ms
    • 数据传输时间:是指完成传输所请求的数据所需要的时间,它取决于数据传输率,其值等于数据大小除以数据传输率。目前IDE/ATA能达到133MB/s,SATA II可达到300MB/s的接口数据传输率,数据传输时间通常远小于前两部分消耗时间。简单计算时可忽略。而数据传输时间,则与数据类型接口有很大的关系,我们来看看目前流行的硬盘接口类型及速度(数据可能不准确,不过基本差不多)。
      硬盘类型   速度
      SATA <150M/s
      SCSI <200M/s
      SAS 200M/s左右
      SSD固态硬盘 500M/s左右

    所以综上所述,我们发现机械硬盘的时间损耗主要集中于寻址和旋转两个步骤。

    到这里其实我们对机械硬盘已经有一个大概的了解了,接下来就说说这个SSD:

    SSD固态硬盘的全集成电路化、无任何机械运动部件的革命性设计,从根本上解决了在移动办公环境下,对于数据读写稳定性的需求。全集成电路化设计可以让固态硬盘做成任何形状。与传统硬盘相比,SSD固态电子盘具有以下优点:

    1. SSD不需要机械结构,完全的半导体化,不存在数据查找时间、延迟时间和磁盘寻道时间,数据存取速度快。
    2. SSD全部采用闪存芯片,经久耐用,防震抗摔,即使发生与硬物碰撞,数据丢失的可能性也能够降到最小。
    3. 得益于无机械部件及FLASH闪存芯片,SSD没有任何噪音,功耗低。
    4. 质量轻,比常规1.8英寸硬盘重量轻20-30克,使得便携设备搭载多块SSD成为可能。同时因其完全半导体化,无结构限制,可根据实际情况设计成各种不同接口、形状的特殊电子硬盘。

    哇塞,不存在数据查找时间、延迟时间和磁盘寻道时间,听起来就好吓人啊,这岂不是比机械硬盘快很多了 ?  那必须的啊,至于能快多少呢 ?

    这个其实我本身也没有什么经验,网上搜了一些,没有搜到太官方的一些对比和统计说法,只能在这里暂时先贴上一些大概的数据,心里有个数,后续有机会了再完善的更加详细一点(图片摘自网上):

    普通机械硬盘IOPS:

    20140217152204421

    SSD硬盘IOPS:

     

    20140217152230828

    看到现在,我想我们大概可以回答我之前的那两个疑虑了:

    1. 我们在采购或衡量机械硬盘时,会可参考磁盘的转速,这个转数与所谓的 IOPS和 吞吐量有什么联系呢 ?

    因为机械磁盘在进行I/O时,需要转动硬盘到合适的位置寻址,所谓转速,就是这个转动速度,转动越快,磁盘响应越快,IOPS和吞吐量自然会有提升。

    2. 常听人讲起SSD比普通的机械硬盘好,快成百上千倍,究竟快在哪里?为什么快?是不是真的快那么多?

    SSD比机械硬盘快在它没有数据查找时间、延迟时间和磁盘寻道时间。而至于快多少倍,则要分具体场景,以及比较的维度。打个比方,如果你比的是两者的IOPS,而类型的随机读写,那么可能SSD要比机械硬盘强很多。 但是,比如你要比的是顺序读写的IOPS,那么可能差距又没有那么大了。 而且也与机械硬盘的转速,接口类型等,都有关系。 所以,其实盲目的说SSD比机械硬盘快成百上千是不对的,要分很多场景和条件。

    比如,我们举个例子,以数据库的读写性能为例,这里附上大佬叶金荣的一篇地址:

    SAS vs SSD对比测试MySQL tpch性能


    磁盘之上的RAID

    根据上面我们对磁盘的了解,即使是使用SSD固态硬盘,相对来说还是会影响计算机的性能(与内存和CPU的宏观比较),而且如果一个硬盘发生了故障或者损坏,那么这块硬盘就已经不能再使用了,这如果是在对数据保存要求特别高的地方来说,其是不可想象的。也正因为如此,就诞生了一种新的技术 —— RAID

    RAID (Redundant Array of Independent Disks) 是廉价磁盘冗余阵列技术的英文缩写,它的原理就是可以将多个磁盘并行运行来提高整个计算机的I/O存储性能

    RAID的评判标准有如下三个:

    1. 速度:读写速度的提升。
    2. 磁盘使用率:多磁盘的空间使用率。
    3. 冗余性: 能够支持几块磁盘损坏而不丢失数据。

    这就是它的存在意义,基于以上三个评判标准,RAID分为很多种类,常用的RAID级别主要是以下四种:

    • RAID0:提高读写性能
    • RAID1:提高读写性能、冗余性
    • RAID5:提高读写性能、冗余性(允许1块硬盘发生故障)
    • RAID6:提高读写性能、冗余性(运行2块硬盘发生故障)

    下面我们就基于RAID的三个评判标准来看看常用的这四个RAID级别各自的特点:

    RAID0

    25214430-cdfe94115fea46f48c2d58fed06f65d2

     

    RAID0 至少需要两块硬盘,当使用RAID0时,我们在读写数据的时候是将数据分开读写到多块硬盘上,所以其读写速度是最快的,但是因为多块硬盘上保存了数据的一部分,所以当一块硬盘发生损坏时,其整个RAID的数据也就损坏了。

    • 空间利用率:所有硬盘空间之和
    • 性能:所有硬盘读写速度之和
    • 冗余能力:无

     

     

    注:正常情况下,一个分区是不能跨多个硬盘的,除非我们使用RAID或者LVM(下面会讲到)技术,这也是这些技术重要的存在意义之一。

    RAID1

    yy

    RAID1至少需要2块硬盘,在写数据的时候RAID 1的每一个磁盘都具有一个对应的镜像盘,任何时候数据都同步镜像,系统可以从一组 镜像盘中的任何一个磁盘读取数据。读的时候同时从多块硬盘上读取数据,以提高读的性能。

    • 空间利用率:磁盘所能使用的空间只有磁盘容量总和的一半,系统成本高。
    • 性能:读性能是所有硬盘之和,写性能有所减弱。
    • 冗余能力:只要系统中任何一对镜像盘中至少有一块磁盘可以使用,出现硬盘故障的RAID系统不再可靠,应当及时的更换损坏的硬盘,否则剩余的镜像盘也出现问题,那么整个系统就会崩溃。当读取数据时,系统先从RAID1的源盘读取数据,如果读取数据成功,则系统不去管备份盘上的数据;如果读取源盘数据失败,则系统自动转而读取备份盘 上的数据,不会造成用户工作任务的中断。

     

    RAID5

    yy

    RAID5至少需要3块硬盘,在写数据的时候RAID5会对数据进行奇偶校验运算,并将校验信息也保存在了硬盘上,所以即使我们其中一块硬盘发生了损坏,RAID5也能通过其他硬盘以及校验信息对数据进行恢复使用。但是如果2块或者2块以上的硬盘发生了损坏,整个数据也就损坏了。

    • 空间利用率:1 – 1/n
    • 性能:读性能接近RAID0,写性能相比RAID0要弱一些
    • 冗余能力:可以接受1块硬盘的损坏

     

    RAID1+0

    yy

     

     

    所谓RAID 1 0 ,其实就是结合了RAID0 和 RAID 1,用两者的优点来弥补两者的缺点。

    • 空间利用率: n/2
    • 性能:良好的RAID读写性能,高于RAID1,接近RAID0
    • 冗余能力:与RAID1相同。

     

     

    软件RAID与硬件RAID

    软件RAID

    通过系统功能或者RAID软件来实现RAID,没有独立的硬件和接口,需要占用一定的系统资源(CPU、硬盘接口速度),并且受到操作系统稳定性的影响。软件RAID的是通过 mdadm这个程序来实现的,这里就不深入探究了,网上的资料很多。

    硬件RAID

    通过独立的RAID硬件卡实现,有些主板集成了RAID硬件,有些需要购买独立的RAID硬件卡,硬件RAID实现不需要占用其他硬件资源,稳定性和速度都比软件RAID要强,所以对于服务器来说,最好是使用硬件RAID来提高计算机的性能。但是硬件RAID的价格稍微贵了点,不过也还在可接受范围值内,比如我们常用的R730上的硬件RAID卡在淘宝上可以买到:

     

    %e5%b1%8f%e5%b9%95%e5%bf%ab%e7%85%a7-2017-10-29-%e4%b8%8b%e5%8d%888-06-26

     

    LVM (Logical Volume Manager)逻辑卷管理器

    抛开RAID不说,想象一个情况,如果你当初规划主机的时候只为 /home 分配了50G的空间,而等到后面的用户越来越多导致这50G的空间不够用的时候,此时你怎么做 ? 大部分情况下,我们只能采取笨办法:再加一块磁盘,然后分区、格式化,将/home的数据完整的复制过来,然后将原本的分区卸除重新挂载新的分区。 是的,很笨的办法。

    LVM的重点就在于可以弹性调整文件系统的容量,这是他的主要存在意义。 LVM 可以将多个物理分区整合在一起,让这些分区看起来像是一个大磁盘一样。

    那么它是怎么做到的呢 ? 我们先了解几个概念:

    Physical Volume, PV, 实体卷轴

    我们实际的物理分区需要调整系统识别码 (system ID) 成为 8e (LVM 的识别码),然后再经过 pvcreate 的命令将他转成 LVM 最底层的实体卷轴 (PV) ,之后才能够将这些 PV 加以利用! 调整 system ID 的方是就是透过 fdisk 啦!

    Volume Group, VG, 卷轴群组

    所谓的 LVM 大磁盘就是将许多 PV 整合成这个 VG !所以 VG 就是 LVM 组合起来的大磁盘!这么想就好了。 那么这个大磁碟最大可以到多少容量呢?这与底下要说明的 PE 有关,因为每个 VG 最多仅能包含 65534 个 PE 而已。 如果使用 LVM 默认的参数,则一个 VG 最大可达 256GB 的容量啊!(参考底下的 PE 说明)

    Physical Extend, PE, 实体延伸区块

    LVM 默认使用 4MB 的 PE 区块,而 LVM 的 VG 最多仅能含有 65534 个 PE ,因此默认的 LVM VG 会有 4M*65534/(1024M/G)=256G。 这个 PE 很有趣喔!他是整个 LVM 最小的储存区块,也就是说,其实我们的文件数据都是借由写入 PE 来处理的。 简单的说,这个 PE 就有点像文件系统里面的 block 。 所以调整 PE 会影响到 VG 的最大容量!

    Logical Volume, LV, 逻辑卷轴

    最终的 VG 还会被切成 LV,这个 LV 就是最后可以被格式化使用的类似分割槽的了!LV的大小也不能随意指定,与在此 LV 内的 PE 总数有关。 为了方便使用者利用 LVM 来管理其系统,因此 LV 的装置通常指定为 /dev/vgname/lvname 的样式!

    结合上面的概念,我们可以将LVM的结构描绘成类似于下图的样子(摘自网上):

    yy

    首先是实际的物理磁盘及其划分的分区和其上的物理卷(PV)。一个或多个物理卷可以用来创建卷组(VG)。然后基于卷组可以创建逻辑卷(LV),再利用mkfs将LV格式化为可以利用的文件系统即可使用了。

    或许这里你会好奇,当我的数据写入LV时,到底它是怎么写入硬盘当中的 ? 其实,依据写入方式的不同,有两种方式:

    线性模式(linear):假如将 /dev/sdb1、/dev/sdb2 这两个分区加入到 VG 中,并且 VG 只有一个 LV。那么,线性模式就是,当 /dev/sdb1 的容量用完之后,/dev/sdb2 的硬盘才会使用。

    交错模式(triped):交错的意思是将一份数据拆成两部分,分别写入 /dev/sdb1 和 /dev/sdb2 。如此一来,一份数据写入两个磁盘。理论上,读写性能会较好。

    通常,LVM 最主要的用处就是产生一个大磁盘,而不是在建立性能好的磁盘。所以,我们使用的是 LVM 可以管理整个分区大小的灵活性,而不是着眼在性能上。因此,LVM 默认的读写模式是线性模式如果使用交错模式,要注意,当任何一个分区“归天”时,所有的数据都会“损坏”。所以,不是很合适使用这种模式。如果要强调性能与备份,那么就直接使用 RAID 即可,不需要使用 LVM。

    接下来,我们来实际操练一下:

    1. 准备磁盘分区

    通过使用fdisk,创建磁盘分区。我们需要创建3个1G分区,注意,并不要求分区的大小一致。同样,分区需要使用‘8e’类型来使他们可用于LVM。

    重复上面的操作来创建其他两个分区。分区创建完成后,我们应该有类似如下的输出:

    2. 准备物理卷(PV)

    刚创建的分区是用来储存物理卷的。LVM可以使用不同大小的物理卷。

    使用下列命令检查物理卷的创建情况。下面截取部分输出。”/dev/sdb2″是一个新的”1.01 GiB”物理卷。

    3. 准备卷组(VG)

    下列命令用来创建名为’volume-group1’的卷组,使用/dev/sdb1, /dev/sdb2 和 /dev/sdb3创建。

    使用下列命令可以来验证卷组。

    从输出中,我们可以看见卷组的使用量/总量。物理卷给卷组提供空间。只要在这个卷组中还有可用空间,我们就可以随意创建逻辑卷。

    使用下列命令删除卷组。

    4. 创建逻辑卷

    下列命令创建一个名为1v1、大小为100MB的逻辑卷。我们使用小分区减少执行时间。这个逻辑卷使用之前创建的卷组的空间。

    逻辑卷可使用lvdisplay命令查看。

    现在逻辑卷已经准备好了,我们可以格式化和挂载逻辑卷,就像其它ext2/3/4分区一样!

    一旦逻辑卷挂载,我们就可以到挂载点 /lvm-mount/ 上读写了。要创建和挂载其它的逻辑卷,我们重复这个过程。

    最后,使用lvremove我们可以删除逻辑卷。

    5. 扩展一个逻辑卷

    调整逻辑卷大小的功能是LVM最有用的功能。这个部分会讨论我们怎么样扩展一个存在的逻辑卷。下面,我们将会扩展先前创建的逻辑卷‘lv1’扩大到200MB。

    注意,调整逻辑卷大小之后,也需要对文件系统调整大小进行匹配。这个额外的步骤各不相同,取决于创建文件系统的类型。在本文中,我们使用’lv1’创建了ext4类型的文件系统,所以这里的操作是针对ext4文件系统的。(ext2/3文件系统也类同)。命令的执行顺序是很重要的。

    首先,我们卸载掉lv1卷:

    然后,设置卷的大小为200M:

    运行以下命令扩展文件系统以后,ext4信息就更新了。

    现在,这个逻辑卷应该已经扩展到200MB了。我们检查LV的状态来验证。

    6. 扩展一个卷组

    我们假设我们的卷组’volume-group1’已经满了,需要扩大。手上的硬盘(sdb)已经没有其他空闲分区,我们添加了另外一个硬盘(sdc)。我们将看到如何把sdc的分区添加到卷组以扩展。

    检测现在卷组状态

    首先,我们创建一个2GB分区sdc1,类型为LVM(8e),如教程前所述。

    然后,我们创建一个物理卷 /dev/sdc1

    现在,物理卷已经准备好了,我们可以简单地将它增加到已存在的卷组’volume-group1’上。

    使用vgdisplay来验证(可以看到卷组大小已经增大)。

    注意,尽管我们使用一个单独的磁盘做示范,其实只要是‘8e’类型的磁盘分区都可以用来扩展卷组。

     

     

    参考

    https://blog.csdn.net/v_JULY_v/article/details/6530142

    http://blog.csdn.net/hanchengxi/article/details/19089589

    http://elf8848.iteye.com/blog/1731274

    http://elf8848.iteye.com/blog/1731301

    http://imysql.cn/2012/12/25/sas-vs-ssd-mysql-tpch-olap-benchmark.html

    http://www.cnblogs.com/xiaoluo501395377/archive/2013/05/25/3099464.html

    https://linux.cn/article-3218-1.html#3_1109

    http://www.cnblogs.com/shawnloong/p/3722469.html

     

  • Core文件定位系统错误及问题

    背景

    收到线上反馈,一台机器的mysql命令无法正常使用了,登上去看了下:

    查看系统日志可以看到如下错误:

    从上面看到系统日志里面显示将core文件记录在了 /var/spool/abrt/ccpp-2017-10-27-16:54:07-185507 等目录下,但是我登录服务器后却发现没有。才突然意识到,我对core文件不是很了解,于是补了下基本知识:

    Core文件基本概念

    core 文件是大多数 UNIX 系统实现的一种特性,当进程崩溃时,操作系统会将进程当前的内存映像和一部分相关的调试信息写入 core 文件,方便人们后面对问题进行定位。

    哪些信号可能会产生core文件 ?

    在这里或许你会觉得好突然,这和信号有啥关系 ?

    我们都知道,在Linux系统中有很多信号,我们可以通过shell命令 kill -l 来查看。当操作系统收到信号时,内核会按照以下三种方式之一去对信号进行处理:

    • 忽略此信号。大多数的信号都可以用这种方式去处理,即内核收到此信号时,对进程不做任何处理,直接忽略。但是SIGKILL和SIGSTOP这两个信号不能被忽略,因为它们向超级用户提供了使进程终止或停止的可靠方法。
    • 捕捉信号。即我们向内核注册一个信号处理函数,当内核收到某个信号时,就去调用注册的信号处理函数对信号进行处理。比如我们经常使用的命令kill默认发的是SIGTERM终止信号。注意,不能捕捉SIGKILL和SIGSTOP信号。
    • 执行默认动作。每个系统都有一套自己默认的信号处理函数,即如果我们不显式的去捕捉信号,那内核收到信号时,要么忽略此信号,要么执行默认的操作。可以理解为操作系统有自己默认的信号处理函数。

    而有些信号就有这个core文件有关。 如果默认情况下我们没有定义下列信号的信号处理函数,那么内核收到这些信号,将终止进程,并产生该进程的core文件(该进程的内存映像以及一些调试信息便保存在该core文件中)。

    信号名字 说明 默认动作
    SIGABRT 异常终止(调用abort函数产生此信号) 终止+core
    SIGBUS 硬件故障,比如出现某些内存故障 终止+core
    SIGEMT 硬件故障 终止+core
    SIGFPE 算术异常,比如除以0,浮点溢出等 终止+core
    SIGILL 非法硬件指令 终止+core
    SIGIOT 硬件故障 终止+core
    SIGQUIT 终端退出符,比如Ctrl+C 终止+core
    SIGSEGV 无效内存引用 终止+core
    SIGSYS 无效系统调用 终止+core
    SIGXCPU 超过CPU限制(setrlimit) 终止+core/忽略
    SIGXFSZ 超过文件长度限制(setrlimit) 终止+core/忽略

    哦,懂了,那么为什么日志里面说是产生了core文件,但服务器上缺没有呢 ? 为什么? 为什么要欺骗我的感情 ? 因为类UNIX操作系统为我们提供了一个可以打开与关闭core文件的开关,因为并非所有场景我们都希望可以生成core文件。类UNIX操作系统为我们提供了一个工具ulimit可以用来设置和查看文件大小的限制,所以我们也可以用这个工具来查看和设置core大小与限制。

    使用 ulimit -a 可以查看系统上面所有的文件大小的限制,比如下面是我的系统的输出结果:

    我们看到第一行就是和core文件相关的,为0表示关闭core文件/不产生core文件,单位是blocks;我们可以使用

    去设置core文件的大小,注意core_size单位是字节。如果core_size为0,则表示不生成core文件,unlimited表示对core文件大小不做限制。我们可以将该命令现在某个用户的环境变量里面去对不同的用户进行设置,比如写在/home/kobe/.bashrc文件里面只对kobe用户有效;也可以写在系统的环境变量里面,对所有用户有效,比如/etc/bash_profile。

    如何自定义core文件名和目录

    我们可以通过修改下面这个文件的内容来控制产生的core文件的文件名中是否添加pid作为扩展:

    如果添加则文件内容为1,否则为0。如果添加了pid,生成的core文件一般为core.xxx(xxx为core dump的进程号)。默认为0,即不添加进程pid。

    我们可以设置格式化的core文件保存位置或文件名,比如我的系统的默认值为:

    我们可以修改此文件来达到修改路径和格式的目的,这里列举一下常用的参数:

    如何查看core文件

    那么重点来了,有了core文件之后我们应该如何利用他进行问题的定位和分析呢 ?

    我们可以使用如上的gdb来查看core文件(core文件必须是完好的,比如如果我们限制的core文件大小比较小,导致core文件被截断,则gdb查看时将出错)。gdb进去以后,可以使用bt或where来查看进程崩溃之前的堆栈信息。

    我们以上面那个例子为例:

    如上,它显示在 /usr/lib64/libstdc++.so.6中有代码错误抛出,因为出问题的这台机器是我们的web服务器之一,与其他几机器的环境都一样,所以我这边将这个文件备份后,从其他机器拷贝了一份这个文件过来,问题就解决了。  应该是某个开发不小心将此文件给改了。

    设置core文件需要注意的地方:

    1. 要保证存放Coredump的目录存在且进程对该目录有写权限。存放Coredump的目录即进程的当前目录,一般就是当初发出命令启动该进程时所在的目录。但如果是通过脚本启动,则脚本可能会修改当前目录,这时进程真正的当前目录就会与当初执行脚本所在目录不同。这时可以查看”/proc/<进程pid>/cwd“符号链接的目标来确定进程 真正的当前目录地址。通过系统服务启动的进程也可通过这一方法查看。

    2. 若程序调用了seteuid()/setegid()改变 了进程的有效用户或组,则在默认情况下系统不会为这些进程生成Coredump。很多服务程序都会调用seteuid(),如MySQL,不论你用什么用 户运行mysqld_safe启动MySQL,mysqld进行的有效用户始终是msyql用户。如果你当初是以用户A运行了某个程序,但在ps里看到的 这个程序的用户却是B的话,那么这些进程就是调用了seteuid了。为了能够让这些进程生成core dump,需要将/proc/sys/fs /suid_dumpable文件的内容改为1(一般默认是0)。

    3. 这个一般都知道,就是要设置足够大的Core文件大小限制 。程序崩溃时生成的Core文件大小即为程序运行时占用的内存大小。但程序崩溃时的行为不可按平常时的行为来估计,比如缓冲区溢出等错误可能导致堆栈被 破坏,因此经常会出现某个变量的值被修改成乱七八糟的,然后程序用这个大小去申请内存就可能导致程序比平常时多占用很多内存。因此无论程序正常运行时占用 的内存多么少,要保证生成Core文件还是将大小限制设为unlimited为好。

     

    参考

    Linux 的 core 文件

     

  • Linux故障处理案例之iptables的nf_conntrack表满

    背景

    接到开发反馈说数据库链接出现超时的情况,我在第一时间查看了一下MySQL的监控,主要包括以下几个粒度:

    • MySQL链接数

    %e5%9b%be%e7%89%87-1

    • MySQL DML数

    %e5%9b%be%e7%89%87-1

    • MySQL慢查询数

    %e5%9b%be%e7%89%87-1

    • MySQL 流量统计

    %e5%9b%be%e7%89%87-1

    经过上述查看,发现在那个时间点MySQL本身并没有什么明显的性能波动或者瓶颈。 于是就去对应的web机器手动直连了MySQL,发现问题没有复现。

    既然MySQL本身没有大的瓶颈,就可以看看Linux系统本身是否出现了什么问题呢 ?

    第一时间我查看了一下linux的系统日志,果然发现了对应的异常:

    这个错误网上搜了一下:

    什么是 nf_conntrack

    nf_conntrack(在kernel 2.6.15 Linux 内核前叫 ip_conntrack,只支持ipv4)是一个内核模块,用于跟踪一个连接的状态的。连接状态跟踪可以供其他模块使用,最常见的两个使用场景是 iptables 的 nat 的 state 模块。

    • iptables 的 nat 通过规则来修改目的/源地址,但光修改地址不行,我们还需要能让回来的包能路由到最初的来源主机。这就需要借助 nf_conntrack 来记录找到原来那个连接的才行。 
    • iptables 的state 模块则是直接使用 nf_conntrack 来里记录的连接的状态来匹配用户定义的相关规则。例如下面这条 INPUT 规则用于放行 1234 端口上的状态为 NEW 的连接上的包。

    nf_conntrack用1个哈希表记录已建立的连接,包括其他机器到本机、本机到其他机器、本机到本机(例如 ping 127.0.0.1 也会被跟踪)

    如果连接进来比释放的快,把哈希表塞满了,新连接的数据包会被丢掉,此时netfilter变成了一个黑洞,导致拒绝服务
    这发生在3层(网络层),应用程序毫无办法。 但是他会在linux系统日志里面打出上面我们看到的那样的日志。

     nf_conntrack 查看与调整

    既然这个问题这么恶心,那么我能不能不用它了呢 ? 能否禁用 nf_conntrack 要看服务器干啥了,如果没有使用iptables的 nat 和 state模块是可以禁用的。 但是 RHEL 默认的 iptables 规则里都是用到了state模块的:

    按理说打开端口的规则完全不需要使用 -m state --state NEW,只要匹配了目标端口直接放行就好。我理解这样做是为了优化性能。注意看第一条规则,只要连接状态是 ESTABLISHED 或者 RELATED 直接放行。这样做,只有连接初始化时的包需要经过下面的重重规则去一条条匹配,一旦建立上连接了(代表被放行了),后续的包直接通过第一条规则就放行了,省去了一条条匹配后续规则的麻烦(O(1)复杂度?)。

    而且在很多场景下,其实我们都需要通过iptables的state模块来配置防火墙规则。

    那么当遇到这个问题是该怎么解决呢 ? 我们先来看看有哪些系统参数可以控制它呢 ?

    当时出问题的时候,我们捕捉到这台机器的链接状态信息如下:

    我们看到有大量的 CLOSE_WAIT 状态,根据上面的消息来看,很有可能是我们的哈希表记录了那些处于 CLOSE_WAIT状态的状态信息,导致这个表被塞满了,所以我做出了如下调整:

    问题得到了解决。

    这是我的这个案例场景,那么一般情况下针对 nf_conntrack: table full, dropping packet报错可以采取如下措施:

    1. 关闭防火墙

    对不直接暴露在公网、没有用到NAT转发的服务器来说,关闭Linux防火墙是最简单也是最佳的办法

    2. 设置不跟踪连接的规则

    对需要防火墙/NAT的机器,可以在iptables设置NOTRACK规则,减少要跟踪的连接数。

    iptables 中的 raw 表跟包的跟踪有关,基本就是用来干一件事,通过 NOTRACK 给不需要被连接跟踪的包打标记,也就是说,如果一个连接遇到了 -j NOTRACK,conntrack 就不会跟踪该连接,raw 的优先级大于 mangle, nat, filter,包含 PREROUTING 和 OUTPUT 链。
    当执行 -t raw 时,系统会自动加载 iptable_raw 模块(需要该模块存在)。raw 在 2.4 以及 2.6 早期的内核中不存在,除非打了 patch,目前的系统应该都有支持:

    3. 修改netfilter相关参数

    调大nf_conntrack_maxnf_conntrack_buckets,调小超时时间,能暂时缓解问题,(关于 nf_conntrack_max 和 nf_conntrack_buckets 的值,网上所有出现公式的文章无一例外都指向08年的 Conntrack_tuning 这篇wiki,权威性相当可疑,但网上没有更详细的解释了,照样摘录,文章里有些东西明显过时了,即使确实出自当时的netfilter团队,剩下没法验证的部分也有很大几率同样过时,不能盲目照搬)

    超时设置跟具体业务有关,基本思路是先看/proc/net/nf_conntrack文件,哪种协议哪种状态的连接最多,改小对应的超时参数 ,在不影响业务的前提下让nf_conntrack_count降得快一点

    内核参数修改方法:

     

    参考

    https://testerhome.com/topics/7509

    http://jerrypeng.me/2014/12/08/dreadful-nf-conntrack-table-full-issue/

  • Linux 设置开机自启动

    背景

    首先,我们来回顾一下Linux系统的启动流程。

    1. 加载BIOS的硬件信息并进行自我测试,并依据设置得到第一个可启动的设备。

    2. 读取并执行第一个启动设备内MBR的boot Loader(即grub等)。

    3. 依据boot Loader的设置加载Kernel,Kernel会开始检测硬件和加载驱动程序。

    4. 在硬件驱动成功后,Kernel会调用init进程,而init进程会取得run-level信息。

    5. init执行/etc/rc.d/rc.sysinit文件来准备软件执行的操作环境(如网络,时区等)

    6. init执行run-level 的各个服务的启动(script方式)

    7. init执行/etc/rc.d/rc.local 文件。

    8. init执行终端机模拟程序mingetty来启动login进程,最后就等待用户登录。

    注: 可能大家会有一个小疑问,第七步中所说的那个耳熟能详的rc.local不是在/etc下吗?  怎么会在/etc/rc.d底下呢?  其实,/etc/rc.local是/etc/rc.d/rc.local 下面的软链接,不光是这个,/etc地下几乎是有/etc/rc.d/下面的几乎所有的文件和目录的软链接。  你可能会问这是为什么呢 ? 做这些软链接的目的是什么呢?

    查阅了一些资料后貌似之所以这样做是因为想要和unix保持兼容性。

    内容

    从背景知识里面我们基本可以知道linux系统启动的时候的大概步骤,那么我们就顺着上面的思路来说linux如何设置开启自启动,我们这里介绍两种Linux自启动的方法。

    1. 在/etc/rc.local文件中添加自启动命令。

    我们都知道在Linux中有7种运行级别(可在/etc/inittab文件设置),每种运行级别分别对应着/etc/rc.d/rc[0~6].d这7个目录:

    yy

    这7个目录中,每个目录分别存放着对应运行级别加载时需要关闭或启动的服务,由详细信息可以知道,其实每个脚本文件都对应着/etc/init.d/目录下具体的服务,以Kxx开头的 ,都以stop为参数来调用;凡是以Sxx开头的,都以start为参数来调用。调用的顺序按xx 从小到大来执行。例如,假设缺省的运行模式是3,/etc/rc.d/rc就会按上述方式调用
    /etc/rc.d/rc3.d/下的脚本。

    yy

    全部都是链接文件,连接到服务启动的/etc/init.d/去。 现在我们知道为什么我们经常用/etc/init.d/服务名 {start, stop}来启动和关闭服务了吧。其实想当于我们将linux系统启动时的方法那过来执行了而已。

    那么现在回到我们的正题,怎样设置开机自启动呢?   难道我们要创建一个脚本放在/etc/rc.d/rc3.d/目录下面,然后再软链接到/etc/init.d/下面吗?  这样做当然是可以的。 但是我们有更简单的方式,我们背景介绍中的第7步阐述了在系统启动过程中init会调用/etc/rc.local这个文件。 那我们把对应的启动命令加入到这个文件里面不就ok了吗?

    比如我们想把zabbix-aggent加入到开机自启动里面来的话就可以这样做:

    echo “/etc/init.d/zabbix-agent restart”   >> /etc/rc.local

    注意,一定要是 >>,不能是 > , 否则就悲剧了,你会将文件里面的其他内容删掉的。

    2. chkconfig 方式

    chkconfig命令主要用来更新(启动或停止)和查询系统服务的运行级信息。但chkconfig不会立即自动禁止或激活一个服务,它只是简单的改变了符号连接。

    首先,来回顾一下Linux的7中启动级别的含义:

    等级0表示:表示关机

    等级1表示:单用户模式

    等级2表示:无网络连接的多用户命令行模式

    等级3表示:有网络连接的多用户命令行模式

    等级4表示:不可用

    等级5表示:带图形界面的多用户模式

    等级6表示:重新启动

    命令详解:

    chkconfig –list [name]:显示所有运行级系统服务的运行状态信息(on或off)。如果指定了name,那么只显示指定的服务在不同运行级的状态。

    yy

     

    chkconfig –add name:增加一项新的服务。chkconfig确保每个运行级有一项启动(S)或者杀死(K)入口。如有缺少,则会从缺省的init脚本自动建立。

    chkconfig –del name:删除服务,并把相关符号连接从/etc/rc[0-6].d删除。

    chkconfig [–level levels] name:设置某一服务在指定的运行级是被启动,停止还是重置。

    使用范例:

    chkconfig –list        #列出所有的系统服务

    chkconfig –add httpd        #增加http服务

    chkconfig –del httpd        #删除http服务

    chkconfig –level httpd 2345 on        #设置httpd在运行级别为2、3、4、5的情况下都是on(开启)的状态

    chkconfig –list        #列出系统所有的服务启动情况

    chkconfig –list mysqld        #列出mysqld服务设置情况

    chkconfig –level 35 mysqld on        #设定mysqld在等级3和5为开机运行服务,–level 35表示操作只在等级3和5执行,on表示启动,off表示关闭

    chkconfig mysqld on        #设定mysqld在各等级为on,“各等级”包括2、3、4、5等级

    本人理解:chkconfig也只是把我们在上面讲的/etc/rc.d/目录下的格式启动项给封装起来方便管理了而已。操作系统自带的服务,如ssh,ftp等等,开机都是自动启动的,我们也可以通过chkconfig方式让自己开发的程序提高“身价”。

    那么如何升级一个服务的身价呢?

    1.将具有start和stop功能项的服务脚本必须存放在/etc/ini.d/目录下;并且用chmod +x 赋予执行权限。

    2.chkconfig –add servicename      在chkconfig工具服务列表中增加此服务,此时服务会被在/etc/rc.d/rcN.d中赋予K/S入口了;

    3.chkconfig –level 35 servicename on     修改服务的默认启动等级。

    4.chkconfig –list servicename    检测是否添加成功

    当我们将一个服务添加成功后就可以使用service  服务名  【start, stop, status】的方式来管理了。