Linux作为开源自由、最先进的操作系统,广泛应用超级计算机,数以亿计的服务器,以及各种嵌入式设备。虽然我们无时无刻不在使用它,但对它自身的运行知之甚少。毕竟大多数情况下,更多的系统管理员只是把系统安装好,调优参数,把更多软件安装到位,也能正常工作。
但如果能够通过深入浅出的解剖Linux的组成,学习Linux系统内部如何工作,学习如何编译内核来实现新特性,增强安全,提升性能;有了对Linux庖丁解牛的能力,我们还可以有针对性的裁剪,通过定制打造最小优化系统,内嵌了ssh/puppet/ansible模块,亦能传递自动化运维的心得和思想。
那为什么要定制系统呢? 什么时候才需要定制?
我们称GNU/Linux为通用操作系统,之所以为通用操作系统,从中可以看出它的目标是为了满足世界上各式各样的硬件而设计,大到超级计算机,小到嵌入式,或者眼下最火的M2M物联网,都可以使用。无论是什么样的Linux发行版本,如Redhat/Debian发行公司,它只要维护一个或二个版本(如服务器版本/桌面版本),就可以涵盖所有的适用场合,何乐而不为? 但是,既然通用就表示什么都可以用,就意味着要兼容太多的假设,如各种硬件驱动、应用场景、文件系统等,从性能第一的角度来说,通用系统不够专一,比不上我们为特定场景定制的系统和优化,精简系统体积小,无需安装过程,直接便携式插拔设备引导启动,运行速度又快又可靠。
特定应用场景的如WEB/代理/缓存服务器,在精简系统的同时,只保留特定业务所需的软件,避免了第三方软件引入的风险,如不需要安装高危的软件如Bind,也没有gcc/make/bison这样的编译相关命令及依赖库,也没有yum/apt-get,减少入侵风险;甚至你在定制的过程中,直接使用了更新的版本组件,如升级openssl底层库,从而可以避免引入不必要的隐患,让系统变得更加安全。
因为很多黑客入侵之前,先用嗅探工具踩点,如检测你的系统版本,端口服务,猜测你的系统,然后再从黑客库里寻找相应版本已经知晓的漏洞,如果你在定制过程中,也知晓相应的知识,做了一些举手之劳的升级或者障眼法,其实对黑客来说就是竹篮打水一场空。当然,软件版本的选择也有很多讲究和经验判断,漏洞并不能完全避免,只要是人写的,总有一些意想不到的问题,但我们可以不断降低入侵的风险。
大多嵌入式系统或智能硬件,更多考虑的是基于固定的硬件(规模化采购BOM可节省成本),专属的软件以及可靠性要求,对数量众多的硬件兼容性、各种花哨的新功能如各式各样的文件系统并不关心,所以,通过裁剪内核和周边的命令,减少无用的依赖,可以把Linux瘦身到10M左右,启动时间控制在1~10s内,不光小巧还大大提升性能,对最终用户来说,开机即用的体验是留下最深刻的第一印象。
举例说,VIA威盛在做嵌入式车载系统的时候,基于自己全套的主板芯片、CPU、显卡、网卡、应用场景也很固定,所以,我们只用了以下组件:
linux kernel 2.4内核(稳定可靠,功能够用就行) ext2文件系统(电子盘不需要日志修复功能) 必要的驱动(全部只有via的驱动程序) 必要的指令(没有python/perl/ruby,只要一些基本命令如ls,cmd,passed,wget,dd …)运行的系统可以精简到12M大小,烧录到DOM电子盘中,开机从DOM引导运行的时候,利用Ramdisk技术,把系统直接解压到内存里去运行,后面就一直跑在内存里,所以速度非常快和稳定。
比如以我们的CDN业务模型为例,我们只需要定制跟CDN相关的软件就可以,不需要安装很多无关的软件包,通过裁剪和选取更少的软件,内置了sshd和ansible模块,我们就可以定制出精简的、方便二次打包、更加安全的发行版本,安装和部署的效率更高。可能有些朋友会说,那我们也可以通过kickstart+pxeboot /cobber,做集中式的批量安装和部署,这种方式确实也是一种可行的方案,但是对于IDC机房分散在全国各地,上架节奏也不统一的的CDN提供商来说,未必是个好主意。
这种方式适用于
定制公司的Logo,如打包销售给客户的盒装CD; 光盘封面的个性化印刷; 系统安装界面的定制配置; 安装过程中自动化业务软件的安装和内置的各种优化措施,简化人工;
从现有的安装了Redhat系统的机器上裁剪出来,好处是:
继承了Redhat的优良血统 编译程序,运维方便 非破坏性的改造
裁剪(基础环境,必要组件) 优化(系统优化,网络优化,内核优化) 烧录(USB技巧,转存)
这种方式比较极客,适用于从头打造一个 Linux 发行版(LFS),它的好处是:
可以更好的了解 Linux 系统的内部工作机制 可以开发出一个灵活的适应您需求的系统 开发的系统(LFS)会更加紧凑,因为对该包含/不该包含什么拥有绝对的掌控 开发的系统(LFS)在安全性上会更好
Linux操作系统只要理解三个核心组件就行,分别是
Kernel内核 FHS文件目录 BootLoader引导器有很多人会好奇为什么没有ls,wc,bash,awk,sed等各种命令,或者怀疑为什么没有www,ftp,ssh服务呢? 其实,这些功能都可以统称为外围软件,是围绕上述三个核心组件而组合成一个更加具象的发行版本。用汽车打个比方,核心组件就像发动机,车身车架,驱动装置,点火装置。除此之外的真皮坐椅,导航系统,全景天窗等就是体现汽车差异性竞争的外围配件。
所以类似gnone/kde/lxde等图形管理器,FTP服务,WWW服务,Perl/Python语言环境,yum/apt/pacman包管理工具都只是不同的发行版本所附加的一些增值服务和个性服务,其实并不是必须的,但你可以根据应用场景做一些选配。
Kernel内核的作用,可以把它理解成汽车上联系各个组件的CAN BUS总线系统。它是和硬件紧密结合打交道的东西,对于我们使用者/程序员来说,所理解硬件的无外乎就是CPU、内存、硬盘三大件,还有其它的如显卡,网卡,usb, scsi等各种各样的硬件都需要kernel的驱动,如最新的硬件,有时候就需要新Kernel才能够识别它、驱动它。除了提供硬件抽象层外,它还有封装了一些对编程友好的接口,包括:
负责底层的硬件驱动,如电源,CPU,内存,硬盘,网卡,音视频硬件,定时器,各种输入输出设备; 系统调用和封装,包括如文件系统,进程管理,内存管理,调度器,加密API,KVM内核级虚拟化,IPC进程间通信和调度; 包括一些内核级应用程序的调用,如lvs,iptables,VLAN,aoe/iscsi/nfs/samba/rdb等内核级应用
Linux Kernel是开源界的杰出代表,也是公开的源代码量最大的项目,汇聚了最优秀的工程师的精华,涉及各种硬件的工作原理,各种算法实现如红黑树、hashtable、LRU表,有兴趣的话,kernel就是最好的代码大全。
又拍云经历使用的内核有:
3.10.xx 主要是使用ext4/ncq/ahci 3.18.xx 主要是使用aio/blkmq/overlayfs/Bcache 4.9.xx 主要是使用xfs/bbr
那如何编译一个有效可靠的内核呢?建议在编译Kernel的时候,尽量在原先旧内核的基础上,在保证可用性的前提下,慢慢做减法。
首先拿一份让你机器运行起来的文件,接着要能够明确哪些东西可以去掉,比如说我们的机器都是英特尔的芯片、主板、网卡,在编译内核的时候特征就很明显,我可以把非英特尔的芯片、网卡、或者其他的东西都去掉,再去掉无关紧要的蓝牙模块,红外线模块,无线电模块,小众的文件系统等,它的Kernel会变得很小。
文件系统模块是文件尺寸最大的,如果特定系统不需要其它特殊的文件系统,一般保留ext4/xfs差不式够用了,这是又拍云存储的配置,仅供参考。
编译内核要注意,内核不是越小越小;
文件系统层次结构标准(Filesystem Hierarchy Standard)其实就是一个目录树结构,在Linux发展之初做层级设备的时候,那时候硬盘都比较小,当空间不够用,就会挂载多个硬盘,如/usr,/home,/opt,早期的大牛们把这个分类出来,是为了方便在各个硬盘中存放,虽然现在硬盘都很大,但这个优秀的传统也被保留下来。
第二个原因,Linux是个百花齐发的现状,有着超过200个变种的操作系统,如果没有统一FHS标准,那整个Linux就像是一群乌合之众,每个版本的系统管理员的学习和迁移成本就非常高,所以FHS也是业界统一的目录层次标准,方便知识技能的互通,通过tar命令很容易把应用程序的备份、迁移拷贝、恢复。
接下来是POSIX和SysV,这是重点。里面会见到有一些库文件,还有头文件,这是C语言里特有的。当一个应用程序要跑起来,二进制文件是必不可少的。但是这个二进制文件有些是动态链结,有的是静态编译。头文件只存在于被别人依赖的时候,可以不需要。所以我们可以把Linux裁得很小,就是因为可以不要这些东西。还有配置软件,比如像login,passwd,sshd这些程序,是需要有配置文件来控制它,定制它。比如说WC,less,echo此类命令,它就是一个单词一个执行就可以了。有这些部分,再加上配制文件,变成一个程序。
BootLoader的杰出代表如Lilo,Grub,利用它把一段特定的引导程序写入MBR(磁盘的前512字节),才能实现当机器通电后,经过BIOS的基本硬件检测,就可以通过grub引导,加载Kernel/initrd.img,然后切换到真正的操作系统里,才能工作起来,所以BootLoader也是非常重要的一个环节。
其实操作系统是个更高级的BIOS,理论上操作系统本身就可以直接从服务器通电就直接进入系统。这想法没错,不过,由于服务器生产和编写操作系统的是两个不同领域的行业,所以,为了通用性,服务器厂家都会保留BIOS的存在,实现基本硬件检测,硬件功能的配置,如超频设置,温度控制,启动顺序等通用性设置。当然,也有像LinuxBIOS这样的项目,直接把Linux系统烧录到BIOS芯片,在某些领域里实现更加快速的启动。
比如说又拍云一天要完成200台机器的安装和部署,你要考虑各种条件的限制,如:
除了上面这些因素,我们还遇到过:
其实就算在又拍云这样的公司,3000平方也没有这么大的空闲区域同时堆得下这么多机器,也没有足够的电力能够保证200台机器同时在线接受广播安装的,所以,kickstart+pxeboot/cobber这种方式只能适合在集中式的机房里面被统一运输,统一上架,统一管理,而且安装之前还需要先调试好交换机,保证所有连接的网络通畅才能使用。
然后,你再想像一下,我们的运维工程师像搬运工人一样在那里拆箱,搬机器,押运机器,上架,连接网线……假设所有机器都可以被正常点亮,其实我们在这些繁琐的环节上已经浪费了大量的人力和精力,等你终于安装完了,觉得可以大舒一口气了,别急,我们的服务器是要发往全国各地的,再重新下架打包装箱吧。
然后上线运行后一段时间,我们的服务器要做维护,或者从CentOS5升级到CentOS6吧,在一个已经运行的系统做整体升级,这几乎就是运维的噩梦,先不说实施步骤险象环生,跟重装一遍一样劳心劳力,而且必须停机维护,这对于互联网7×24小时不间断服务的SLA保障就是一个巨坑。
那我们是如何解决这些问题的呢?
我们基于CentOS 6裁剪出一个大约150M的系统(自身系统80M+,Python运行环境约70M+,还有类似于cloud-init的读取网络配置文件的脚本,目前新增了Docker容器的支持),所以你随便找1GB/4GB/8GB的U盘,成本20~30元左右,基本上都可以满足你的要求。之所以没有像威盛那样做得这么极致,是因为我们还是基于通用x86架构服务器,用着c/python/nodejs/lua为代表的通用生产力语言,为互联网应用提供通用服务,所以,我们的系统只要保证运行起来,能够接受Ansible 自动化运维利器管理就行。
从淘宝上买几个10口usb hub,单人单次可以同时烧录10个udisk,200M的系统大概只要3分钟就写入完毕,所以,200个U盘系统如果是单人工作,大概1个小时左右,如果是3个人一起做,就只要20分钟就可以搞定系统了。然后,就可以把这些U盘发送给我们的供应商,直接在生产线上就插入我们的U盘系统,配置BIOS默认为Usb disk启动就可以了,我们的验收标准就是U盘引导启动进入系统后,运行一个脚本打印出必须的硬件指标如硬盘容量,截屏认可就算通过。
所以,我们不需要考虑电力问题,场地问题,初期也没有网络问题,能够进入系统截屏就说明机器正常点亮,脚本采集到硬件容器显示正常说明固件版本符合标准,运行正常。于是,借助于小系统和简单易用的Shell编程脚本,我们让供应商参与到我们的品质管理第一环节,建立了良好的开端,更重要的事,我们巧妙地借助了他人的力量来帮助我们减轻了很多的工作量。至于整体升级,我们有了U盘和系统分区,把两个不同的介质里做切换升级更加不是难事。
![]()