|
|
第 1 帖 | |
|
|
标题: [LFS实战]DIY一个实用的mini-LAPP服务器(Apache PHP PostgreSQL OpenSSH Iptables) 由于论坛排版效果不理想,而且我也只在我的个人空间对文章进行后继更新,所以建议直接到我的空间查看:
DIY一个实用的 miniLAPP 服务器 [x86版] http://lamp.linux.gov.cn/miniLAPP/LAPP-x86.html ------------------------------------------------------------------------------- 版权声明 本文作者是一位自由软件爱好者,所以本文虽然不是软件,但是本着 GPL 的精神发布。任何人都可以自由使用、转载、复制和再分发,但必须保留作者署名,亦不得对声明中的任何条款作任何形式的修改,也不得附加任何其它条件。您可以自由链接、下载、传播此文档,但前提是必须保证全文完整转载,包括完整的版权信息和作译者声明。 其他作品 本文作者十分愿意与他人共享劳动成果,如果你对我的其他翻译作品或者技术文章有兴趣,可以在如下位置查看现有作品的列表: 金步国作品列表 BUG报告,切磋与探讨 由于作者水平有限,因此不能保证作品内容准确无误,请在阅读中自行鉴别。如果你发现了作品中的错误,请您来信指出,哪怕是错别字也好,任何提高作品质量的建议我都将虚心接纳。如果你愿意就作品中的相关内容与我进行进一步切磋与探讨,也欢迎你与我联系。联系方式:Email: csfrank@citiz.net ; QQ: 70171448 ; MSN: csfrank122@hotmail.com ============================================== 先决条件 本文的读者应当至少手动安装过一次 LFS ,如果你没有做过 LFS ,这篇文章很可能不适合你,请先按照《Linux彻底定制指南》做一遍 LFS 。另外,如果你对编译优化也很感兴趣,那么《GCC编译优化指南》也很值得一读。 目标 youbest兄的大作《5M大小的Apache服务器》和其续篇《600K的Apache服务器》将LFS的精神发挥到了极致,令人叹为观止!然而许多实用主义者(包括我在内)也只能叹为观止而已,因为这样的服务器由于过分追求小巧而变得不太实用,基本上不能在实践中用于生产目的。 鉴于上述原因,本文打算制作一个既实用又小巧的 Linux + Apache + PHP + PostgreSQL + OpenSSH + Iptables 服务器,并且实用优先于小巧。尽管实用优先于小巧,但是与基于普通发行版搭建的LAPP服务器相比仍然相当Mini(本文制作出来的最终系统核心部分大约 15MB[其中体积较大的文件有:bzImage约1MB、postgres约3.5MB、libphp5.so约4.6MB、libc.so约 1.3MB、libcrypto.so约1.1MB],总体积大约150MB),因此我把它称为"miniLAPP"服务器。当然,除了小巧,灵活和高效也重要,这在本文中主要体现在:㈠对软件包进行最大限度的自定义配置,㈡在保证稳定的前提下进行编译优化。 为了避免纠缠于各种复杂的硬件环境(它不是重点所在),本文将在 VMware Workstation 5.5.5 上建立一个虚拟机。具体如下:Intel 440BX 主板,athlon-xp处理器(如果你的CPU与我不同,那么只要将本文中所有的"athlon-xp"都替换成你的CPU型号即可,具体可以参考Gentoo Safe Cflags),512M内存(最低384MB),100M网卡(PCnet32),4G硬盘(BusLogic SCSI 0:0),CDROM(IDE 1:0 lfslivecd-x86-6.3-r2160-min.iso 仅供安装)。 网络环境:ip:192.168.10.33/24 ,broadcast:192.168.10.255 ,gateway:192.168.10.250 ,没有DNS 。如果你的环境与此不同,请自行调整相应的ip命令。 思路 基本上,小巧和实用是有冲突的,因为越要求实用就越需要各种功能,也就越无法保证小巧。为了解决这个问题,本文采用了"核心+扩展"的思路。所谓" 核心"是指保证服务器正常运行必须使用的资源,比如:libc, init, httpd, postgres, libphp, sshd ... 以及各种设备文件、配置文件等等。所谓"扩展"是指非运行时必须的资源,比如:top, cat, gcc ... 等等,主要用于服务器维护。 解决方案是将"核心"部分安装在"/"目录下,使其在服务器一起动的时候就能够使用,而将"扩展"部分安装在"/usr"目录下,并且"/usr"位于独立的分区上,仅在需要使用的时候才手动挂载,使用完毕以后再手动卸载。 事实上,对于绝大多数软件包而言,所需要的只是其中的一小部分而已,因此,绝大部分软件包的主体都位于"/usr"目录下,仅将必须的某些部分安装到"/"下。 另一个问题是使用静态连接还是使用动态共享库?从小巧的目标来看,似乎应当使用静态连接,但是考虑到: 1. 追求小巧并非第一重要,此处"Mini"的含义并非偏重于磁盘空间,而是尽可能减少不必要的程序和组件。 2. 动态连接的程序可以在内存**享库文件,而静态连接的程序则无法实现。考虑到此服务器可能扩展为提供 DNS, FTP, Mail, Proxy 等其它服务,静态连接将导致运行时占用更多的内存。 因此本文决定采用常规的动态连接。更多关于静态连接的害处,可以参考"Static Linking Considered Harmful"一文。 制作工具链前的准备工作 使用 LiveCD 开机,直接按回车键启动,所有设置均采用默认。 如果希望使用ssh进行远程安装,请执行如下步骤: 代码:
代码:
代码:
[提示]如果在工具链的制作过程中中途关机,那么只需在重新开机后重新执行下面的命令即可恢复工作状态。不过,关机前一定要运行"sync"命令并 umount所有挂载点,否则可能出现意外。 [说明]为了binutils-pass2测试成功,这里使用了一个rm废除LiveCD的g++和cpp,否则ld测试可能会失败。不过,没有C++编译器将会导致Glibc的 bug-atexit3-lib.os tst-cancel24.o c++-types-check 测试失败,并且无法运行dejagnu的测试程序。 代码:
编译过程是一个漫长的冒险,特别是在使用了一大堆configure选项和优化参数的情况下,出现错误是再正常不过的事情了。本文的所有代码都在我的CPU上测试通过,因为我没有更多的CPU,所以无法进行更多的测试,但是一个基本原则就是,一旦遇到编译或测试错误,首先想到的应当就是降低优化级别,看看能不能通过。理论上,将本文所有的"athlon-xp"都替换成"i686",并且设置 CFLAGS="-O2 -fomit-frame-pointer -pipe -march=i686" LDFLAGS="-s" kLDFLAGS="",应当可以在目前实际使用的所有 x86 CPU 上顺利完成。如果降低优化级别仍然不能解决问题,那么可以继续调整configure选项。 下面这些优化选项可能是地雷,尽量不要使用,如果你喜欢冒险,并且不小心踩到了,别怪我没提醒过你..... CFLAGS "-D_FILE_OFFSET_BITS=64 -fvisibility=hidden; -malign-double; -mregparm=3; -msseregparm; -ftracer; --param max-gcse-memory=100M; --param max-gcse-passes=3; -Wa,-R; -Wa,-march=athlon"至少会导致Glibc配置或编译失败。"-m128bit-long-double"会导致GCC的 gcc.dg/pr19402-2.c测试失败,Glibc的math/test-misc.out测试失败,autoconf的AC_PROG_SED 测试失败,tar的31/33/35号测试失败。"-freg-struct-return"虽然能让Glibc通过测试但却会间接导致GCC的 gcc.dg/struct-ret-libc.c测试失败,这是二进制兼容性所致,一般可以忽略它。"-ftree-loop-linear"会导致 PostgreSQL无法运行测试程序(无法初始化数据库),还会导致JPEG测试失败。 LDFLAGS "--as-needed"将导致Glibc的stdlib,nptl,elf测试出现多处错误,"-znow"将导致 Glibc的elf测试出现多处错误。"--sort-common"会导致Automake的ccnoco.test测试失败(直接和间接原因都有??)。 kLDFLAGS "-s"会导致内核编译失败。 附件为本文所有需要使用的补丁和配置文件
__________________
@@ 此帖于 08-03-03 11:14 被 csfrank 编辑. |
|
|
|
|
|
|
|
第 2 帖 | |
|
|
确实很长,赞一个!
![]()
__________________
青橄榄在入口的时候是苦的,过了一会你就可以长久的品味那淡淡而又清爽的甜味。 青橄榄 http://youbest.cublog.cn 青橄榄计划的第一步总算迈出来了,等待它的是万里长征. 做技术要有做技术的原则,决不拿技术做侵权的事情,更不能用来做违法的事情。工作丢了可以再找一个,原则丢了就找不回来了。 |
|
|
|
|
|
|
|
第 3 帖 | |
|
|
楼主无私奉献,感激。支持
__________________
为了那些爱你的人和你爱的人 无论你多努力都不为过 徐牛 |
|
|
|
|
|
|
|
第 4 帖 | |
|
|
制作工具链
此处工具链的制作基本上与 LFS 第五章相同,只是增删几个包、改动了一些配置选项、简化了几个包的安装动作、将几个补丁的功能用 sed 进行了替代、以及其它一些小变化等等,因此下面只列出命令而不进行任何说明。 代码:
|
|
|
|
|
|
|
|
第 5 帖 | |
|
|
重启工具链
与 LFS 的标准做法不同,本文为了尽可能摆脱宿主系统的影响,做完工具链之后不是chroot进入虚根环境,而是做一些必要的准备工作,然后重新启动计算机,进入一个完全与宿主系统无关的工具链环境,再继续完成目标系统的构建。 创建基础目录结构、必需的符号连接与文件、存储随机数种子、用户和组([注意]pgsql属于www组,root的密码是"123"): [提示]如果现在就将 mtab 指向 /proc/mounts 的话,Coreutils 测试程序会将 /dev/root 挂载到 tests/rm/one-file-system.tmp/ 下的某个目录中,从而导致无法删除Coreutils 的编译目录。 代码:
由于本文不打算使用 Udev ,因此这里手动创建所有设备。 代码:
代码:
代码:
代码:
代码:
分支㈠[关机] 关机前的准备(如果没有启动ssh就省略相应的命令): 代码:
代码:
分支㈡[chroot] 为了方便期望在编译最终系统的过程中也能使用ssh的读者,这里也介绍一下传统的chroot方法。需要说明的是,由于此分支的方法不能完全摆脱宿主系统的影响,所以本文不推荐使用。仅供那些贪图copy/paste便利的玩家参考和测试。 代码:
|
|
|
|
|
|
|
|
第 6 帖 | |
|
|
依赖关系分析
在正式开始编译最终系统之前,我们需要静下心来认真分析一下这个最终系统究竟需要哪些东西。 所谓"依赖性"是多方面的。一般来说,可以分为"运行时依赖"、"编译安装依赖"、"测试依赖"三个层面。为了构建一个严谨的自依赖系统,显然这三种依赖性都必须满足。运行时依赖比较简单,一般就是库的依赖;而后两种依赖则比较复杂(运行时依赖实际上取决于编译安装依赖)。比如,如果你不需要安装文档,那么 Textinfo 就不是必须的;如果你不需要国际化支持,那么 GetText 也不是必需的,等等。庆幸的是 LFS-Book 的附录部分给出了宝贵的依赖关系资料,可以提供参考,这样可以省去很多麻烦。 首先,我们来看看"核心"部分需要哪些东西。很显然,下面这些是必须的:GRUB, Kernel, Glibc, Sysklogd, Dcron, Bash, IPRoute2, Apache, PHP, PostgreSQL, OpenSSH, Iptables 。再深入思考一下,就会发现如下组件也是"核心"部分必须的:E2fsprogs(被mount和xfs_repair依赖),XFS(用于开机时的磁盘检查),Ncurses(被Readline/Bash依赖),Readline(被Bash/PostgreSQL依赖),Zlib(被 Apache/PostgreSQL/OpenSSH依赖),JPEG+PNG(被PHP依赖),PCRE(被Apache/PHP依赖), OpenSSL(被Apache/OpenSSH/PostgreSQL依赖),Util-linux 与 Coreutils 中的部分程序。核心部分只需要满足运行时依赖即可。 然后,再看看"扩展"部分需要哪些东西。这部分的选择因人而异,这里就大致选择了几个系统管理相关的组件和几个实用工具:Coreutils, Procps, Psmisc, Util-linux, Bzip2, Findutils, Grep, Sed, Tar, XFS 。选择这部分组件时,暂时无需考虑依赖关系。 最后,考虑到可扩展性以及将来的组件升级与维护等,就必须构建一个自依赖的系统。也就是说,所有组件加在一起必须能构成一个完整的依赖环(3种依赖关系全部满足)。本文安装的、并且未在前面提到的软件包都属于这个用途。它们都属于"扩展"。 需要说明的是,这里的"核心"与"扩展"的划分不是绝对严格的:扩展部分的组件严格属于"扩展",而核心部分的组件通常只有某一部分属于"核心" (其他部分归入"扩展")。比如 Glibc 的基本库部分就属于"核心",而扩展库、实用程序、文档、头文件等则属于"扩展"。 包管理 其实这么简单的系统根本不需要包管理,所以本文并不使用任何一种包管理技术,只是使用了一种非常简单的 DESTDIR 方法来将每个包都复制一份到/root目录下,以方便管理员查看每个软件包究竟安装了哪些东西。当然,并不是所有软件包都遵循这个约定俗成的规则,对于这些软件包使用的命令也有所不同。 编译最终系统 配置选项 要做到最大限度的定制每一个软件包,获取完整的配置选项是必须的。当然,要想更加详细、全面的了解如何自定义安装,还需要查看 README INSTALL FAQ 之类的文档,甚至是软件包的官方手册。需要注意的是,有不少软件包的配置选项分布在多个 configure 脚本中,还有少数并不是通过 configure 脚本进行配置的,查看完整的配置信息就变成一件很吃力的事情了。因此唯一的建议就是:读文档、读文档、再读文档。当然,鸟语是免不了的... 关机与状态回复 由于编译过程漫长,下面的编译步骤被设计为"易于恢复状态的"(仅对"分支㈠"有效),意思是,你可以在编译完任意一个软件包之后关机,并且重新开机后就已经自动的恢复了工作状态。要达到这个目的,你可以使用任何你喜欢的方式关机(比如直接拔掉电源),但是你必须确保在关机前运行了"sync"命令。 对于分支㈡,恢复步骤如下: ①使用 LiveCD 开机,直接按回车键启动,所有设置均采用默认。 ②如果希望使用ssh进行远程安装,步骤同前。 ③挂载文件系统后进入虚根环境,即可完成状态恢复: 代码:
代码:
此帖于 08-03-03 10:15 被 csfrank 编辑. |
|
|
|
|
|
|
|
第 7 帖 | |
|
|
内核头文件 根据 Glibc 的 FAQ ,编译 Glibc 时使用的内核头文件版本可以比实际运行 Glibc 的内核版本高。比如用于编译 Glibc 的内核头文件版本为 2.6.24 ,但是实际运行 Glibc 的可以是 2.6.16 版本的内核(编译 Glibc 时必须使用 --enable-kernel=2.6.16 而不能使用 --enable-kernel=2.6.24 )。允许这样做的好处是即使将来把内核升级到 2.6.24 也不需要重新编译 Glibc 了。另一方面,如果实际运行的内核版本比头文件版本高,那么新内核的新特性(主要是系统调用)将无法被Glibc使用。[注意]不要直接 INSTALL_HDR_PATH=/usr ,这样可能使得Glibc变得不稳定。[小提示]如果将来把内核升级为更高版本,头文件是不是也需要跟着一起升级?答案是:NO!! 关于内核头文件变迁的历史,这里有一篇《[八卦故事]内核头文件传奇》,可以当作课外读物 ![]() 代码:
Glibc 的安装指南中说测试套件中的某些测试项目是假定以非 root 身份运行的,因此建议使用普通用户身份进行编译与安装。不过经过实践,以 root 用户进行编译和安装也没问题。这里仅安装了 zh_CN.UTF-8 的 locale 支持,如果你想支持更多的 locale 请自己添加适当的 localedef 命令。出于安全最大化的考虑还禁用了 DNS 。将时区设为UTC(相当于取消时区的概念,如果你有使用时区的需求可以设为"PRC")。更多关于安装 Glibc 的信息,请查看源码树下的 configure INSTALL FAQ 三个文件。[提示]由于没有安装C++编译器的缘故,bug-atexit3-lib.os tst-cancel24.o c++-types-check 测试将会失败,你可以安全的忽略它。touch用于阻止可能发生的Autoconf调用(当Makefile检测到一个configure文件的时间戳比它对应的configure.in文件旧的时候),这种调用有时候会导致编译失败。将"/dev/log"修改为"/dev/shm/log"是因为将来的根文件系统是只读挂载的,而syslogd会在启动的时候删除并重建此socket。Glibc GCC Binutils 三者是整个工具链的核心,因此如何对其进行定制就显得很重要。这里有一篇文章《Glibc 安装指南》,可以在漫长的编译过程中作为参考资料读一读。 代码:
代码:
[提示]使用 --with(out)-tk 将导致 expect 配置失败,使用了 --with-tcl 之后就不需要再详细指定 --with-tclconfig --with-tcllib --with-tcllibdir 了。使用 --with(out)-docbook 或 --with(out)-oskith 将导致 dejagnu 配置失败。sed 一定要先修改 configure.in 再修改 configure ,否则会导致 Autoconf 的调用。dejagnu 的安装指南说测试套件需要以非 root 身份运行,可是经过实践,无论 root 与否,都不能在第一次安装后立即通过测试,一般需要安装两次甚至三次才能测试成功(无论是否root都是这样),不知何故。再加上dejagnu的测试程序依赖于这里并不存在的C++编译器,因此这里跳过dejagnu测试。 代码:
由于"-finline-functions"会导致ld测试出现多处错误,因此这里需要专门禁用它。对Makefile.in的修改用于移除对 makeinfo的依赖,否则编译将会失败(因为工具链中并未安装Texinfo)。[提示]由于 binutils-2.17 的测试套件将路径 /bin/stty 进行了硬编码,且本文作者尚未找到简便的迂回方法,因此如果你使用的是 2.17 ,那么要先建立一个符号链接来满足测试套件的需求,安装完毕后再删除它。对于如何安装 Binutils 和 GCC 可以参考一下《Binutils与GCC配置选项简介》。 代码:
因为GCC的Makefile.in并不传递LDFLAGS,所以需要sed一下。为了得到更加高效的编译器,这里使用"make profiledbootstrap"代替"make"。对"LINK_SPEC"的修改是为了将"--hash-style=gnu"选项添加到默认的连接器选项中去,如果你希望这样的话,就去掉相应的注释。[提示]如果Glibc的CFLAGS中使用了-freg-struct-return的话, gcc.dg/struct-ret-libc.c测试将会失败,不过这是二进制兼容性所致,可以安全的忽略它。 代码:
Sed 代码:
[提示]"--disable-htree"和"--disable-debugfs"选项都会导致许多测试错误,"--disable-swapfs" 会导致lib/ext2fs测试由于找不到"tst_types"而失败,此外由于lib/ext2fs测试总是由于"Failed to allocate scratch memory!"失败,原因不明,所以这里使用sed跳过这个测试。CFLAGS和LDFLAGS必须在configure选项上设置。[依赖提示] UUID被XFS依赖,blkid,uuid 被mount/umount依赖。 代码:
运行 make check 的非特权用户要求至少位于两个不同的组中,因此这里将 pgsql 用户临时添加到 dummy 组内。"RUN_VERY_EXPENSIVE_TESTS=yes"用于运行更多的测试项目。[说明]因为test-getaddrinfo测试总是由于缺乏DNS支持而失败,需要跳过它。[说明]由于Findutils包内的 xargs 程序将 echo 程序硬编码为"/bin/echo",且不能在源代码中将其修改为"echo"(会导致测试失败,原因不明),另一方面echo也是bash的内置命令,所以就将它也安装在 /bin 目录下。 代码:
[依赖提示]至少被Bison/Autoconf/Automake/Flex依赖 代码:
[依赖提示]至少被Bash依赖 代码:
[依赖提示]至少XFS的安装依赖于它。 代码:
这个包的配置选项简直多如牛毛,这里没有(也没必要)涵盖全部选项。更多安装信息请查看源码树目录下的 INSTALL 文件(所有选项皆有描述,不需要查看 configure 的内容)。Ncurses 的测试套件与大多数包不同,不能用简单的非交互式"make check"来测试,因为 Ncurses 牵涉到视觉效果,所以必须是交互式的测试,更多有关如何测试的细节,请查看源码树下的 test/README 文件。更多官方补丁请查看:ftp://invisible-island.net/ncurses/5.6/ 。[提示]--enable/disable-database都将导致编译失败,不能使用。--disable-ext-funcs --disable-tparm-varargs 将导致 Procps 无法编译。如果你不想安装terminfo数据库可以使用"--disable-database --without-terminfo-dirs",如果你不想安装实用程序可以使用"--without-progs"。可以使用"make install.libs"仅安装共享库。 代码:
Procps 的测试套件与 Ncurses 类似,由于牵涉到视觉效果,因此也是交互式测试,具体细节请查看 README 文件。 代码:
Perl的测试很奇怪,有时候第一次运行失败,再运行一次却能成功。 代码:
由于只需要共享库,因此只编译和安装共享库(包括头文件)。"bash_cv_func_ctype_nonascii=yes"用于修正当en_US.ISO8859-1这个locale不存在时会出现的问题 代码:
由于只需要共享库,因此只编译和安装共享库(包括头文件)。[提示]configure脚本只传递了CFLAGS而未传递LDFLAGS,因此需要在配置完毕后(不能提前修改Makefile.in,configure会覆盖掉你的修改)再修改Makefile中的LDSHARED(作用于共享库)和 LDFLAGS(作用于二进制文件)。 代码:
| |