源码编译安装Apache:定制模块与路径的实战指南
1. 为什么源码编译安装依然值得折腾源码编译安装 Apache 这件事放在今天这个包管理器满天飞的时代看起来确实有点“复古”。apt install apache2或者yum install httpd一条命令就能搞定的事为什么要花半小时甚至更久去编译我刚开始接触 Linux 运维的时候也有这个疑问直到在实际工作中踩了几次坑才明白包管理器给你的是“通用答案”而源码编译给你的是“定制答案”。最直接的场景就是你需要特定的模块组合。比如你要在一台内网机器上跑一个只服务于内部系统的 HTTP 服务不需要 LDAP 认证、不需要 Lua 脚本支持、不需要 mod_security但你需要把 mod_http2 和 mod_proxy 编进去同时把 prefork MPM 换成 event MPM。包管理器安装的版本往往把一堆你可能永远用不到的模块都编译进去了而源码编译可以让你精确控制每一个--enable和--disable开关。另一个常见场景是版本锁定——某些老旧的业务系统对 Apache 版本有严格要求比如必须用 2.4.52 而不能用 2.4.58因为某个模块的行为在新版本里发生了变化。这时候源码编译就是唯一的选择。还有一类需求是路径定制。包管理器安装的 Apache 默认把配置文件放在/etc/httpd或/etc/apache2二进制文件放在/usr/sbin日志放在/var/log。但有些公司的运维规范要求所有自建服务统一放在/opt下面配置文件、日志、二进制全部自包含方便迁移和备份。源码编译配合--prefix参数可以完美满足这个需求整个 Apache 就是一个目录打包带走就能在另一台机器上跑起来。当然源码编译也有它的代价。你需要手动处理依赖关系需要自己写 systemd 服务文件或者 init 脚本升级的时候要重新编译。所以我的建议是如果你只是想要一个能跑的 Web 服务器包管理器足够了但如果你需要精确控制编译选项、需要特定版本、或者需要把服务完全自包含那源码编译才是正解。这篇文章就是写给后一种人的。2. 编译之前的准备工作2.1 依赖包的安装与确认编译 Apache 之前有几个依赖是必须提前装好的。这些东西不装configure阶段就会报错而且报错信息有时候不太直观新手容易卡在这里。首先是APRApache Portable Runtime和APR-util。这两个是 Apache 的底层运行时库负责跨平台的文件操作、网络通信、内存管理等等。虽然 Apache 源码包里自带了一份 APR 的副本但我强烈建议你单独编译安装 APR 和 APR-util然后用--with-apr和--with-apr-util指定路径。原因很简单自带的副本版本可能比较老而且多个 Apache 实例共享同一份 APR 也更省资源。其次是PCREPerl Compatible Regular Expressions。Apache 的mod_rewrite模块依赖它来做正则匹配。如果你的系统里没有 PCRE 开发包configure会直接报错说找不到 pcre-config。注意这里要装的是pcre-devel而不是pcre前者包含头文件和静态库后者只有运行时库。然后是OpenSSL开发包。如果你要启用mod_ssl基本上都要就需要 OpenSSL 的头文件和库。同样注意装openssl-devel而不是openssl。最后是编译器工具链gcc、make、autoconf、libtool。这些在大多数发行版的最小化安装里都不带需要手动装。在基于 RPM 的系统上一条命令搞定yum install -y gcc gcc-c make autoconf libtool \ pcre-devel openssl-devel expat-devel \ libxml2-devel zlib-devel在基于 Debian 的系统上apt-get install -y build-essential autoconf libtool \ libpcre3-dev libssl-dev libexpat1-dev \ libxml2-dev zlib1g-dev注意expat-devel和libxml2-devel是为了支持mod_dav和mod_xml2enc等模块。如果你确定不需要这些模块可以不装但装了也没坏处编译时不用就是了。2.2 源码包的下载与校验Apache 的源码包可以从官方镜像站下载。我一般习惯把源码统一放在/usr/local/src下面方便管理。下载的时候注意两个文件一个是源码压缩包httpd-2.4.xx.tar.gz另一个是对应的签名文件.asc。校验签名这一步很多人会跳过但在生产环境里我建议还是做一下确保下载的文件没有被篡改或者下载不完整。cd /usr/local/src wget https://archive.apache.org/dist/httpd/httpd-2.4.58.tar.gz wget https://archive.apache.org/dist/httpd/httpd-2.4.58.tar.gz.asc gpg --verify httpd-2.4.58.tar.gz.asc httpd-2.4.58.tar.gz如果提示找不到公钥需要先导入 Apache 的发布签名密钥。这一步的具体命令我就不展开了官方文档里有详细说明。校验通过之后解压源码包tar -xzf httpd-2.4.58.tar.gz cd httpd-2.4.582.3 目录规划与用户创建在编译之前先想清楚你的目录结构。我通常采用这样的布局目录用途/opt/apacheApache 安装根目录/opt/apache/conf配置文件/opt/apache/logs日志文件/opt/apache/htdocs默认网站根目录/opt/apache/modules动态加载的模块/opt/apache/bin可执行文件这种布局的好处是所有东西都在一个目录下备份的时候直接打包/opt/apache就行迁移到另一台机器上只要路径一致就能直接跑。另外Apache 默认以daemon用户运行但这个用户在很多系统上已经存在且可能有其他用途。我建议创建一个专用的用户和组groupadd -r apache useradd -r -g apache -s /sbin/nologin -d /opt/apache apache-r表示创建系统用户-s /sbin/nologin表示这个用户不能登录 shell-d /opt/apache指定家目录。这样 Apache 进程就以apache用户的身份运行权限最小化安全性更好。3. 编译配置的核心参数拆解3.1 configure 参数逐项解析configure是整个编译过程中最关键的一步它决定了你的 Apache 最终长什么样。参数写错了后面编译出来的东西可能完全不是你想要的。下面我逐项拆解常用的参数。--prefix/opt/apache指定安装根目录。这个不用多说所有文件都会安装到这个目录下面。--with-apr/usr/local/apr和--with-apr-util/usr/local/apr-util指定 APR 和 APR-util 的安装路径。前提是你已经单独编译安装了这两个库。如果你不想单独装也可以不指定这两个参数Apache 会用自带的副本但我不推荐这么做。--with-pcre/usr/bin/pcre-config指定 PCRE 的配置脚本路径。大多数情况下configure能自动找到但如果你的 PCRE 装在非标准路径下就需要手动指定。--with-ssl/usr/include/openssl指定 OpenSSL 的头文件路径。同样标准路径下不需要指定非标准路径才需要。--enable-so是最重要的参数之一。它允许 Apache 在运行时动态加载模块DSODynamic Shared Object。没有这个参数所有模块都必须静态编译进主程序以后想加个模块就得重新编译整个 Apache。有了它你可以用LoadModule指令在配置文件里灵活加载.so文件。--enable-rewrite启用mod_rewrite模块用于 URL 重写。这个模块几乎是必备的WordPress、Discuz 之类的应用都依赖它来做伪静态。--enable-ssl启用mod_ssl模块支持 HTTPS。这个也不用多说现在没有 HTTPS 的网站基本没法看。--enable-headers启用mod_headers允许你修改 HTTP 响应头。做跨域配置、缓存控制的时候经常用到。--enable-expires启用mod_expires控制静态资源的过期时间对性能优化很有帮助。--enable-deflate启用mod_deflate对响应内容进行 gzip 压缩能显著减少传输数据量。--enable-proxy和--enable-proxy-http启用代理模块做反向代理的时候需要。--enable-http2启用 HTTP/2 支持。注意这个模块依赖nghttp2库需要提前安装libnghttp2-devel。--with-mpmevent指定使用 event MPM。Apache 2.4 支持三种 MPMprefork、worker、event。prefork 是每个请求一个进程稳定但资源消耗大worker 是每个进程多个线程资源利用率高但一个线程崩溃可能影响同进程的其他线程event 在 worker 的基础上进一步优化用异步方式处理 keep-alive 连接是高并发场景下的首选。--disable-status之类的参数用来禁用不需要的模块。比如你不需要查看服务器状态页面就可以禁掉mod_status减少攻击面。一个完整的configure命令大概长这样./configure \ --prefix/opt/apache \ --with-apr/usr/local/apr \ --with-apr-util/usr/local/apr-util \ --with-pcre/usr/bin/pcre-config \ --with-ssl/usr/include/openssl \ --enable-so \ --enable-rewrite \ --enable-ssl \ --enable-headers \ --enable-expires \ --enable-deflate \ --enable-proxy \ --enable-proxy-http \ --enable-http2 \ --with-mpmevent \ --disable-status \ --disable-autoindex \ --disable-userdir3.2 模块选择背后的取舍逻辑上面那些--enable和--disable不是随便写的每一个背后都有考量。我拿几个典型的来说说。为什么禁用 autoindexmod_autoindex的作用是当目录下没有index.html时自动生成一个文件列表页面。这个功能在开发环境里挺方便但在生产环境里是个安全隐患——攻击者可以通过它浏览你的目录结构找到本不该暴露的文件。所以生产环境我一般直接禁掉。为什么禁用 userdirmod_userdir允许通过/~username的方式访问用户家目录下的网站。这个功能在共享主机环境里有用但在自己的服务器上完全没必要而且容易造成权限混乱。为什么启用 deflate 而不是用 PHP 的 gzip如果压缩工作在 Apache 层面完成PHP 就不需要再处理一遍减少了 PHP 进程的负担。而且mod_deflate可以对静态文件HTML、CSS、JS也进行压缩这是 PHP 做不到的。为什么用 event MPM 而不是 prefork前面说了event 在高并发下表现更好。但有一个例外如果你需要用mod_php把 PHP 作为 Apache 模块加载那就只能用 prefork因为mod_php不是线程安全的。不过现在主流做法都是用 PHP-FPM 配合mod_proxy_fcgi所以 event MPM 是更好的选择。实操心得第一次编译的时候建议把所有--enable和--disable都记在一个文本文件里后面升级版本的时候直接复制粘贴不用重新想一遍。我见过太多人升级 Apache 的时候忘了当初编译了哪些模块结果新版本跑起来发现某个功能没了。3.3 编译与安装的实操记录configure跑完之后如果最后输出没有报错就可以开始编译了。编译用make安装用make install。这两个命令看起来简单但有几个细节值得注意。首先是make的并行编译。默认情况下make是单线程的编译 Apache 大概需要几分钟。如果你的机器有多核 CPU可以用-j参数加速make -j$(nproc)$(nproc)会自动获取 CPU 核心数。比如 8 核的机器就用make -j8编译时间能缩短到原来的几分之一。但注意不要设置得太大一般不超过核心数的两倍否则可能因为内存不足导致编译失败。编译过程中如果报错最常见的两类问题一是找不到某个头文件说明对应的-devel包没装二是某个函数未定义通常是库的版本不匹配。遇到报错不要慌仔细看错误信息里提到的文件名和函数名基本就能定位到问题。编译完成后安装make install安装过程很快就是把编译好的文件复制到--prefix指定的目录下。安装完成后检查一下目录结构ls -la /opt/apache/你应该能看到bin、conf、htdocs、logs、modules等目录。如果这些都有了说明安装成功。4. 安装后的配置与调优4.1 主配置文件的精简与调整安装完成后/opt/apache/conf/httpd.conf是主配置文件。默认的配置文件有一千多行里面大部分是注释和示例实际生效的配置并不多。我一般会先备份一份原始配置然后按照自己的需求精简。首先修改ServerRoot和DocumentRootServerRoot /opt/apache DocumentRoot /opt/apache/htdocs然后确认Listen指令。默认是监听 80 端口Listen 80如果你需要 HTTPS还要加上Listen 443接下来是用户和组的配置。找到User和Group指令改成前面创建的apache用户User apache Group apache然后是模块加载部分。默认配置里有一大堆LoadModule指令你需要根据实际编译的模块来调整。比如你编译时启用了--enable-rewrite那就要确保这一行没有被注释掉LoadModule rewrite_module modules/mod_rewrite.so一个常见的坑是编译时启用了某个模块但配置文件里忘了加载结果功能不生效。排查的时候可以用httpd -M命令列出所有已加载的模块对比一下你期望的模块列表。4.2 日志目录与权限设置Apache 需要向日志目录写入文件所以logs目录的权限必须正确。默认情况下make install会把logs目录的所有者设为root但 Apache 进程是以apache用户运行的没有写入权限。需要手动修改chown -R apache:apache /opt/apache/logs chmod 755 /opt/apache/logs同样htdocs目录也需要确保apache用户有读取权限。如果是静态网站755就够了如果需要上传文件可能要给775或者把所有者改成apache。注意不要图省事把整个/opt/apache目录都改成apache用户所有。配置文件和二进制文件应该保持root所有只有日志和需要写入的目录才给apache用户权限。这是最小权限原则的基本要求。4.3 服务管理脚本的编写源码编译安装的 Apache 没有自动生成 systemd 服务文件需要手动创建。在/etc/systemd/system/下新建一个apache.service[Unit] DescriptionApache HTTP Server Afternetwork.target [Service] Typeforking ExecStart/opt/apache/bin/apachectl start ExecStop/opt/apache/bin/apachectl stop ExecReload/opt/apache/bin/apachectl graceful PIDFile/opt/apache/logs/httpd.pid PrivateTmptrue [Install] WantedBymulti-user.target然后重新加载 systemd 配置并启动服务systemctl daemon-reload systemctl enable apache systemctl start apache检查服务状态systemctl status apache如果看到active (running)说明服务启动成功。再用curl测试一下curl -I http://127.0.0.1应该返回HTTP/1.1 200 OK。5. 常见问题与排查技巧实录5.1 编译阶段的高频报错编译 Apache 时遇到的报错八成集中在依赖缺失上。我整理了一个速查表报错信息原因解决方法configure: error: APR not found没装 APR 或路径不对安装 APR 并用--with-apr指定路径configure: error: APR-util not found同上安装 APR-util 并指定路径configure: error: pcre-config for libpcre not found没装 PCRE 开发包安装pcre-devel或libpcre3-devconfigure: error: OpenSSL version is too oldOpenSSL 版本太低升级 OpenSSL 或指定新版本路径make: *** No targets specified and no makefile foundconfigure没成功执行检查configure的输出解决报错后重新执行undefined reference to SSL_CTX_new链接时找不到 OpenSSL 库确认--with-ssl路径正确或加LDFLAGS还有一个比较隐蔽的问题configure成功执行了但make到一半报错说某个.h文件找不到。这种情况通常是头文件路径没有正确传递给编译器。可以尝试在configure时加上CPPFLAGS和LDFLAGSCPPFLAGS-I/usr/local/apr/include/apr-1 \ LDFLAGS-L/usr/local/apr/lib \ ./configure ...5.2 启动阶段的典型故障Apache 编译安装成功但启动时报错这类问题也很常见。最典型的是端口被占用(98)Address already in use: AH00072: make_sock: could not bind to address [::]:80这说明 80 端口已经被其他程序占用了。用ss -tlnp | grep :80或者netstat -tlnp | grep :80查看是哪个进程占用的。如果是系统自带的 HTTP 服务先停掉它systemctl stop httpd systemctl disable httpd另一个常见问题是配置文件语法错误AH00526: Syntax error on line 52 of /opt/apache/conf/httpd.conf这种报错会明确告诉你行号直接去看那一行就行了。常见的语法错误包括指令拼写错误、参数类型不对、模块没加载但使用了该模块的指令。用httpd -t可以只检查配置文件语法而不启动服务/opt/apache/bin/httpd -t如果输出Syntax OK说明配置文件没问题。还有一个坑是 SELinux。在某些发行版上SELinux 默认是 enforcing 模式会阻止 Apache 绑定非标准端口或者访问非标准目录。如果确认是 SELinux 的问题可以临时设为 permissive 模式来验证setenforce 0如果设为 permissive 后问题消失那就需要为 Apache 配置合适的 SELinux 策略而不是直接关掉 SELinux。具体策略配置这里不展开但记住一点生产环境不要长期关闭 SELinux。5.3 运行阶段的性能与安全调优Apache 跑起来之后还需要根据实际负载做一些调优。event MPM 的核心参数在httpd-mpm.conf里或者直接写在httpd.conf里IfModule mpm_event_module StartServers 3 MinSpareThreads 75 MaxSpareThreads 250 ThreadsPerChild 25 MaxRequestWorkers 400 MaxConnectionsPerChild 10000 /IfModule这几个参数的含义StartServers是启动时的进程数MinSpareThreads和MaxSpareThreads控制空闲线程的数量范围ThreadsPerChild是每个进程的线程数MaxRequestWorkers是最大并发请求数等于进程数乘以每进程线程数MaxConnectionsPerChild是每个进程处理多少个请求后重启设为 0 表示不重启但建议设一个非零值来避免内存泄漏累积。调优的时候不要凭感觉要看实际数据。mod_status可以提供一个实时状态页面显示当前有多少请求在处理、多少线程空闲、总共处理了多少请求。虽然前面说生产环境可以禁用mod_status但在调优阶段临时启用一下是很有帮助的。安全方面有几个配置是必须做的。首先是隐藏 Apache 版本信息ServerTokens Prod ServerSignature Off这样 HTTP 响应头里只会显示Server: Apache不会暴露具体的版本号和操作系统信息。其次是禁用 TRACE 方法TraceEnable OffTRACE 方法在某些情况下可能被用于跨站追踪攻击直接禁掉最省心。实操心得每次修改配置文件后不要直接restart而是先用httpd -t检查语法然后用apachectl graceful平滑重启。graceful会等待当前正在处理的请求完成后再重启不会中断用户连接。只有graceful失败的时候才用restart。6. 版本升级与模块扩展的实操建议6.1 小版本升级的正确姿势Apache 的小版本升级比如从 2.4.57 升到 2.4.58通常只是修 bug 和安全补丁编译参数和配置文件基本不用改。升级流程可以总结为“编译新版本、替换二进制、保留配置”。具体操作先下载新版本源码用和之前完全相同的configure参数编译安装到一个临时目录比如/opt/apache-new。然后停掉旧服务把旧目录改名备份把新目录改名为/opt/apache再把备份目录里的conf和logs复制回来。最后启动服务用httpd -v确认版本号。systemctl stop apache mv /opt/apache /opt/apache-old mv /opt/apache-new /opt/apache cp -a /opt/apache-old/conf/* /opt/apache/conf/ cp -a /opt/apache-old/logs/* /opt/apache/logs/ systemctl start apache /opt/apache/bin/httpd -v这种方式的优点是回滚极其简单——如果新版本有问题停掉服务把目录名换回来就行。6.2 动态添加新模块前面说了--enable-so的好处是支持动态加载模块。如果你后来发现需要某个编译时没启用的模块比如mod_ldap不需要重新编译整个 Apache只需要单独编译这个模块然后加载即可。Apache 源码包里有一个apxs工具在/opt/apache/bin/apxs可以用它来编译第三方模块或者源码包里自带的模块。以mod_ldap为例cd /usr/local/src/httpd-2.4.58/modules/ldap /opt/apache/bin/apxs -i -a -c mod_ldap.c-i表示安装模块-a表示自动在httpd.conf里添加LoadModule指令-c表示编译。执行完之后mod_ldap.so会被复制到/opt/apache/modules/目录并且httpd.conf里会自动加上对应的LoadModule行。但要注意apxs编译模块时需要对应的开发库。比如mod_ldap需要openldap-develmod_session需要libcurl-devel。如果编译时报错说找不到头文件先确认对应的开发包是否安装。6.3 编译参数变更后的处理有时候你不得不改变编译参数比如原来没启用 HTTP/2现在需要了。这种情况下不能简单地替换二进制文件因为新编译的 Apache 可能依赖新的库或者模块的 ABI 发生了变化。正确的做法是用新的configure参数完整编译安装到一个新目录然后对比新旧目录的差异。重点检查modules目录下多了哪些.so文件conf目录下的httpd.conf有没有新增的配置项。把新增的LoadModule指令合并到你的配置文件里然后切换目录。如果新旧版本的模块 ABI 不兼容比如从 2.4.5x 升到 2.4.6x 时某些模块的接口变了那就不能混用新旧模块必须全部用新编译的版本。这也是为什么我建议每次升级都完整编译而不是只替换主程序。最后分享一个小技巧在编译之前用./configure --help看一下所有可用的参数。Apache 的configure脚本支持很多细粒度的控制比如--enable-mods-sharedmost可以一次性把所有常用模块编译成动态模块省去逐个--enable的麻烦。但这样编译出来的模块比较多需要你在配置文件里手动决定加载哪些。对于新手来说我建议还是逐个--enable这样你对每个模块的作用和依赖关系会更清楚。