很多朋友手里还跑着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开始就引入了事件型MPM(multi-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的DSO(Dynamic 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已经不在标准源里提供所有更新了,很多机器配置了ESM(Extended 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-php,DPKG会自动帮你切换到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指令。比如只监听内网IP:Listen 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默认开启了IPv6,Apache的默认Listen 80和Listen 443会同时绑定*(所有地址)。如果机器商的防火墙对IPv6处理不完善,可能出现"IPv4能访问、IPv6打不开"的诡异现象。你可以在浏览器或者用curl -6 http://[::1]/测试本机IPv6连通性,然后在云控制台和本地UFW两侧都把相应协议放行。
3.2 UFW防火墙放行HTTP/HTTPS,IPv4和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里的IPV6=yes。很多被精简过的系统镜像是IPV6=no,这会导致只有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/ | 默认站点的DocumentRoot | Ubuntu默认首页所在位置 |
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_html:
sudo mkdir -p /var/www/example.local/public_html sudo tee /etc/apache2/sites-available/example.local.conf <<'EOF' <VirtualHost *:80> ServerName example.local ServerAdmin webmaster@example.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用户要跨目录访问用户主目录,就必须确保从根目录到目标目录之间的每一层目录都有至少o+x权限。但为了安全,/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或者MySQL,Ubuntu 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站点,用自己的自签名证书先测通;二是通过Let's Encrypt的客户端工具自动签发证书,后者需要保证域名解析和80端口可访问性没问题。
性能调优方面,我会重点关注这几项:
- MPM调优:确认当前MPM模式,
apache2ctl -M可以列出现加载模块,apache2ctl -V可以看编译时的MPM默认值。event模式下MaxRequestWorkers要结合机器内存来算,理论上每个请求线程大约占用数MB到几十MB内存。 - 启用的模块精简:默认
a2enmod并没有把所有模块都启用,但很多历史配置会残留用不到的模块,比如status、info这类信息泄漏风险模块,能禁用就禁用。 - HTTP/2:Apache 2.4.29已经支持
mod_http2,Ubuntu 18也提供了这个模块。启用后配合SSL站点,在HTTPS下能看到明显的性能提升。
这些内容如果展开又是一个万字长文,正好留作这个系列的下一期。你现在不需要急着把所有优化做完,先把Apache的安装、起停、站点建立、日志定位这四个动作做到条件反射的程度,后面一切都会顺很多。