
很多朋友手里还跑着Ubuntu 18.04的存量服务器尤其是那批LTS时代部署的老业务迁移成本高、依赖锁定压根不是说换就换的。在这个版本上装Apache看似是apt install apache2一句话的事但实际落下去会碰到软件源、systemd服务管理、防火墙放行、站点目录权限等一系列连锁问题。这篇文章是Linux之Apache安装系列的第五篇前面几篇我们把Ubuntu 18的基础环境、网络配置、SSH加固都过了一遍现在正好轮到Web服务这一层。我会完整走一遍从更新软件源到虚拟主机可用的全过程重点讲清楚每一步背后的机制和我在实际部署中遇到过的坑保证你照着做能避开大多数新手会踩的雷。1. 为什么到了Ubuntu 18还会重提Apache安装——版本差异与选型逻辑1.1 Ubuntu 18.04的Apache版本其实是老而稳的典型Ubuntu 18.04自带的Apache版本是2.4.29这个版本放在今天看绝对算不上新但它恰恰是LTS时代的经典组合。2.4系列从2.4.6开始就引入了事件型MPMmulti-processing module的成熟实现2.4.29已经是event模式非常稳定的状态。很多老项目的.htaccess规则、模块依赖都是围绕2.4系列写的你换成新买的VPS默认带的Apache 2.4.5x反而可能出现配置语法兼容问题比如某些老模块在新版本里被拆分或弃用。我接手过某公司的内部系统跑的就是Ubuntu 18 Apache 2.4.29上面挂着五六套老旧的PHP应用。运维那边不是不想升级而是每升一次就要重新验证几十个RewriteRule和认证模块的兼容性代价远大于收益。所以对相当一部分生产环境来说Ubuntu 18重装Apache依然是一个非常现实的需求——可能是容器化迁移前的过渡也可能是新扩容的机器要加入已有的负载均衡集群要求版本保持完全一致。1.2 选Apache而不是Nginx这里面的逻辑要说清楚经常有人问我新部署为什么不直接用Nginx我的回答是取决于你的应用长什么样。Apache和Nginx的核心差异不在性能而在于请求处理模型和配置理念。Apache的prefork模式每个请求一个进程内存开销大但兼容性好特别是配合mod_php的老式PHP环境几乎零适配成本。而event模式虽然性能和Nginx接近但配置复杂度高一些。Nginx的优势是高并发静态资源处理和反向代理缺点是动态内容的FastCGI配置链路长模块体系不像Apache的DSODynamic Shared Object那样可以装完即刻生效。如果你的项目有.htaccess文件的强依赖——比如某些老旧的CMS系统、WordPress插件、ThinkPHP的URL重写——Apache就是最省事的选择。Nginx虽然也能模拟.htaccess规则但你得手动把规则翻译到server块里这是个纯体力活还容易出错。在实际运维中没必要为了用新不用旧强行迁到Nginx那是给自己找事。2. 开始动手三个命令把Apache装上以及每一步的真实用意2.1 第一步是刷新软件源索引不是走过场在干净的Ubuntu 18上装Apache第一件事永远是sudo apt update这个命令很多人会习惯性执行但未必清楚它到底做了什么。apt update并不会升级任何软件它的作用是读取/etc/apt/sources.list和/etc/apt/sources.list.d/下的软件源配置然后从源服务器拉取软件包索引Packages.gz等文件到本地缓存。如果你新装的系统几个月没刷新过索引直接apt install很可能得到Unable to locate package apache2或者装到一个非常旧的版本。这里有个Ubuntu 18特有的细节需要注意18.04已经不在标准源里提供所有更新了很多机器配置了ESMExtended Security Maintenance源或者其他镜像源。如果apt update输出里出现Timeout或者404 Not Found优先检查/etc/apt/sources.list里是否有security.ubuntu.com或archive.ubuntu.com的旧地址。我多次遇到的问题是源列表里还写着EOL版本的组件仓库导致apt update到一半报错。稳妥做法是把官方源切换为可用的镜像源再用apt update验证直到不报错为止。2.2 安装apache2包Ubuntu裁剪版的默认行为有哪些索引刷新完成后安装命令非常简单sudo apt install -y apache2但装完之后发生的事才是重点。Ubuntu的apache2包和Apache官网的源码包有一个重要区别它没有单独提供httpd这个启动脚本而是完全走systemd服务模型服务名叫apache2.service。安装完成后不需要你手动注册服务包管理器已经帮你把服务文件和符号链接都放好了systemctl直接就能接管。同时Ubuntu包装版默认启用的MPM是mpm_event不是许多人印象中的prefork。如果你后续要装libapache2-mod-phpDPKG会自动帮你切换到mpm_prefork但你如果先手动改了MPM配置再装PHP模块就可能在重启服务时报模块冲突。这是我在给某项目部署LAMP环境时真踩过的手动把MPM改成prefork装PHP后服务直接起不来最后只能通过清除配置重装才解决。2.3 验证服务状态从systemd的角度看Apache安装完成后立刻检查三件事服务运行状态、开机自启状态、端口监听情况。systemctl status apache2 systemctl is-enabled apache2 ss -tlnp | grep :80systemctl status不仅能告诉你服务是否active还能看到主进程PID、内存占用、最近日志。is-enabled确认是否已经设置开机自启正常情况下返回enabled如果返回disabled需要执行sudo systemctl enable apache2。ss -tlnp是用来替代老命令netstat的现代工具。看到0.0.0.0:80或者[::]:80说明Apache已经监听所有IPv4和IPv6地址的80端口。注意如果你只想让Apache监听特定IP可以修改/etc/apache2/ports.conf里的Listen指令。比如只监听内网IPListen 192.168.1.10:80这样可以避免Web服务意外暴露到公网。这一步我在安全意识较强的项目里经常做能少很多不必要的扫描攻击。接着用curl验证默认页面是否正常响应curl -I http://127.0.0.1/正常情况下会返回HTTP/1.1 200 OK以及Server: Apache/2.4.29 (Ubuntu)响应头。如果看到的是curl: (7) Failed to connect那就需要从头排查服务状态和端口监听了。3. 装完不等于能用Ubuntu 18上必须处理的自启动与防火墙策略3.1 设置开机自启与确认监听地址两步打包做很多教程在安装完Apache之后就戛然而止我建议你多花一分钟确认开机自启。虽然不是每次都需要手动启用——Ubuntu的DEB包安装流程通常已经把服务启用写入了postinst脚本——但有些特殊的违规操作系统镜像或Docker容器里服务注册并不完整。检查命令sudo systemctl enable apache2 sudo systemctl restart apache2enable操作本质是在/etc/systemd/system/multi-user.target.wants/目录下创建一个指向/usr/lib/systemd/system/apache2.service的符号链接。知道这个原理之后你就能理解为什么有些禁用服务教程会直接让你删符号链接而不是用systemctl disable——两者效果一样但前者容易留下残留状态。确认监听地址时我建议同时看IPv4和IPv6因为Ubuntu 18默认开启了IPv6Apache的默认Listen 80和Listen 443会同时绑定*所有地址。如果机器商的防火墙对IPv6处理不完善可能出现IPv4能访问、IPv6打不开的诡异现象。你可以在浏览器或者用curl -6 http://[::1]/测试本机IPv6连通性然后在云控制台和本地UFW两侧都把相应协议放行。3.2 UFW防火墙放行HTTP/HTTPSIPv4和IPv6别漏Ubuntu 18默认没有启用UFW但生产环境我建议打开否则应用端口裸露在公网太危险。放行Apache相关的命令sudo ufw allow Apache Full sudo ufw enable sudo ufw status numberedApache Full是UFW自带的应用配置包含80和443两个端口。如果你不想开放HTTPS用sudo ufw allow Apache即可那个只放行80。需要注意几点ufw enable会立即激活防火墙规则如果你当前是通过SSH连接的请先确认SSH端口已经被放行否则会把自己关在外面。标准的做法是sudo ufw allow OpenSSH或者sudo ufw allow 22/tcp顺序上是先放行22端口再开防火墙。UFW规则会自动生成IPv6的对应条目前提是内核启用了IPv6且/etc/default/ufw里的IPV6yes。很多被精简过的系统镜像是IPV6no这会导致只有IPv4规则生效IPv6流量全部被挡。云服务商比如各种云主机的控制台安全组策略和主机内部的UFW是两层独立的东西。哪怕UFW里放行了80端口控制台安全组没放行的话外网依然访问不了。这一条我几乎每次做服务器巡检都要提醒太容易忽略了。4. 安装后的目录结构默认页面、站点配置和模块机制的来龙去脉4.1 /var/www/html、sites-available与sites-enabled的关系装完Apache后很多教程只说默认页面在/var/www/html但对配置目录的结构讲得很浅。Ubuntu的Apache目录结构其实是一个典型的可用/启用分离设计搞清楚它才能优雅地管理多站点。关键目录如下路径作用说明/etc/apache2/apache2.conf主配置文件一般不直接改内容通过Include引入其他配置/etc/apache2/ports.conf端口配置定义80、443等监听端口/etc/apache2/sites-available/站点可用配置存放所有站点的VirtualHost定义/etc/apache2/sites-enabled/站点启用配置通过符号链接指向available里的文件/etc/apache2/mods-available/模块可用配置需要加载的模块的.load和.conf文件/etc/apache2/mods-enabled/模块启用配置通过符号链接指向available里的文件/etc/apache2/conf-available/全局可用配置片段如安全策略、缓存参数等/var/www/html/默认站点的DocumentRootUbuntu默认首页所在位置sites-enabled和sites-available的关系是整个设计精髓。默认情况下你在sites-available里看到的是000-default.conf而sites-enabled里同样有一个000-default.conf的符号链接。Apache启动时会用IncludeOptional sites-enabled/*.conf把这层符号链接指向的真实文件全部读进来。这种机制的好处是你可以提前准备多个站点配置但不会被Apache加载直到你显式地用a2ensite把它们链接到enabled目录。4.2 模块启用的本质符号链接和a2enmod的等价操作同样的机制也适用于模块目录。很多人用一个a2enmod rewrite就完事了不理解它到底改了什么。查看mods-enabled目录就能看到端倪ls -la /etc/apache2/mods-enabled/里面满是rewrite.load - ../mods-available/rewrite.load这种符号链接。a2enmod rewrite做的事情无非就是创建这个符号链接然后在服务重载时让Apache加载rewrite.load里指定的共享对象文件。手动操作的话ln -s一条命令也能达到同样效果但很容易忘记同时启用相关的.conf文件有些模块有配置片段所以我还是建议一律用a2enmod和a2dismod管理。模块配置在Ubuntu两个文件里被加载.load负责加载模块二进制.conf负责模块的默认参数。例如a2enmod php7.2或a2enmod ssl前者会加载libphp7.2.so后者会启用mod_ssl并顺带加载SSL相关配置。理解这个机制之后你就能举一反三当Apache无法重启时报Module X already loaded错误多半是你在apache2.conf里手动LoadModule同时又用a2enmod启用了同一个模块导致重复加载。遇到这种问题不要慌检查mods-enabled里是否有对应符号链接有就去掉apache2.conf里手动加的那行。5. 逼近实战虚拟主机配置、日志位置和权限问题的常见坑5.1 用sites-available新建站点而不是直接改默认配置默认的000-default.conf内容很简单但新手最常见的错误就是直接在里面堆一堆配置然后发现以后想换站点结构时无处下手。更好的做法是每个站点一个独立配置文件放在sites-available里。假设我要为一个新业务建站点域名为example.local文档目录为/var/www/example.local/public_htmlsudo mkdir -p /var/www/example.local/public_html sudo tee /etc/apache2/sites-available/example.local.conf EOF VirtualHost *:80 ServerName example.local ServerAdmin webmasterexample.local DocumentRoot /var/www/example.local/public_html ErrorLog ${APACHE_LOG_DIR}/example.local_error.log CustomLog ${APACHE_LOG_DIR}/example.local_access.log combined Directory /var/www/example.local/public_html Options FollowSymLinks AllowOverride All Require all granted /Directory /VirtualHost EOF然后执行启用和重载sudo a2dissite 000-default.conf sudo a2ensite example.local.conf sudo systemctl reload apache2a2dissite同理本质是移除sites-enabled里的符号链接。systemctl reload不是restart在读配置后重新加载不会中断现有连接生产环境推荐。如果你修改了端口或MPM这种需要重新初始化进程的参数才需要restart。5.2 日志与权限改DocumentRoot之后最常见的403错误虚拟主机配置好之后最常出问题的不是语法而是权限。Apache2.4开始默认对目录访问控制更严格了常见的403错误有好几个来源文档根目录本身没有x权限给其他用户比如/var/www/example.local权限是700但Apache的worker进程以www-data用户运行无法进入目录。目录下没有索引文件且目录浏览未开启。虽然403在缺少index.html时常见但你如果确实没有DirectoryIndex匹配的文件Apache会尝试目录列表只有Options Indexes开启时才允许展示否则返回403。配置里的Require all granted缺失或写错。Apache2.4生效的是Require语法传统的Allow from all在2.4上默认不生效。排查时的最快方法是看错误日志sudo tail -f /var/log/apache2/error.log日志里会有明确提示比如[client 192.168.1.1] AH00035: access to / denied (filesystem path /var/www/example.local/public_html) because search permissions are missing on a component of the path。这句话的意思是路径链条中某一层目录缺少x执行/进入权限。解决方案通常是sudo chown -R www-data:www-data /var/www/example.local/public_html sudo chmod -R 755 /var/www/example.local顺便说一下/var/log/apache2/下默认会有access.log和error.log如果你的虚拟主机没有单独指定日志文件所有站点的请求都会进默认access.log。多站点生产环境建议一定要在每个站点里单独指定日志文件否则排查问题时根本分不清流量进的是哪个站。另外提醒一个Ubuntu特别容易遇到的坑如果你把DocumentRoot指向了/home/某用户/www这种位置Apache的www-data用户要跨目录访问用户主目录就必须确保从根目录到目标目录之间的每一层目录都有至少ox权限。但为了安全/home/用户本身通常权限是755或更严格这就是频繁出现403的原因。不要为了解决问题直接把整个home目录权限放开那等于把shell账号的可读范围暴露给了Web进程。6. 这套安装流程在系列教程里的承上启下下一步还能做什么6.1 装完Apache之后LAMP链路还差PHP和数据库这个系列既然叫Apache安装而非Web服务器配置那重点就是先让Apache跑起来跑得稳。但实际部署项目几乎没有人只装一个Apache。接下来顺理成章的步骤是装PHP和数据库Ubuntu 18上要注意PHP版本的默认源问题。Ubuntu 18.04默认PHP版本是7.2如果你需要PHP 7.4或更高通常要添加第三方源。而安装PHP时Apache的模块配置又会自动切换MPM我之前在第2.2节提到过这里再强调一次安装libapache2-mod-php7.2时包管理器会禁用php相关模块以外的mpm_event并启用mpm_prefork这是兼容性设计不是错误。看到systemctl status apache2里有mpm_prefork别吃惊。数据库方面推荐选择MariaDB或者MySQLUbuntu 18源里默认提供的是MySQL 5.7若添加了官方源才换和MariaDB 10.1。装完MySQL后Apache和PHP通过mysqli或PDO扩展连接数据库这时候要注意连接地址别写错成localhost导致socket连接失败有时需要明确写127.0.0.1:3306使用TCP连接。6.2 如何用一次重建实践来验证今天这套安装步骤最有效的验证方式不是打开浏览器看到It works页面而是故意破坏一次再修复。我建议你按下面的顺序做一轮破坏性练习把/etc/apache2/sites-available/000-default.conf里的DocumentRoot改成一个不存在的路径执行systemctl reload apache2看看浏览器返回什么错误。执行a2dismod rewrite然后访问一个依赖重写规则的应用观察500或404表现。把/var/www/html权限改成700观察403的出现和消失。这三步做完你对Apache的配置加载机制和权限模型的理解会比干看十篇教程都深。我在培训某项目组新人的时候就经常用这套练习效果比反复抄命令好太多。每做完一步记得用journalctl -u apache2 -e看日志输出日志会告诉你Apache眼中的世界是什么样的。6.3 本系列后续篇目的扩展方向SSL与性能调优既然Apache已经能稳定运行下一篇很自然的主题就是TLS/SSL证书配置。Ubuntu 18上加证书有两种路径一是直接用a2enmod ssl和a2ensite default-ssl启用默认SSL站点用自己的自签名证书先测通二是通过Lets Encrypt的客户端工具自动签发证书后者需要保证域名解析和80端口可访问性没问题。性能调优方面我会重点关注这几项MPM调优确认当前MPM模式apache2ctl -M可以列出现加载模块apache2ctl -V可以看编译时的MPM默认值。event模式下MaxRequestWorkers要结合机器内存来算理论上每个请求线程大约占用数MB到几十MB内存。启用的模块精简默认a2enmod并没有把所有模块都启用但很多历史配置会残留用不到的模块比如status、info这类信息泄漏风险模块能禁用就禁用。HTTP/2Apache 2.4.29已经支持mod_http2Ubuntu 18也提供了这个模块。启用后配合SSL站点在HTTPS下能看到明显的性能提升。这些内容如果展开又是一个万字长文正好留作这个系列的下一期。你现在不需要急着把所有优化做完先把Apache的安装、起停、站点建立、日志定位这四个动作做到条件反射的程度后面一切都会顺很多。