1. 项目概述:为什么在 CentOS 6 上部署 LAMP 仍是值得深挖的硬功夫
LAMP——Linux、Apache、MySQL、PHP 这四个首字母缩写组成的开源技术栈,不是一句口号,而是一套经过二十年以上生产环境千锤百炼的“数字地基”。尤其当你的目标系统是 CentOS 6,这个于2011年发布、2020年3月31日正式结束生命周期(EOL)的操作系统,它早已退出主流更新通道,却仍在大量老旧业务系统、嵌入式网关设备、教育实训机房、工业控制终端中稳定运行。我接手过三类典型场景:某省属高校实验室的旧版教务系统(PHP 5.3 + MySQL 5.1)、某市供水公司的SCADA数据采集前置机(Apache 2.2 + PHP-CGI模式)、以及一家老牌印刷厂的ERP本地化部署节点。它们共同的特点是——不能轻易升级操作系统,但必须让Web服务持续可用、安全可控、故障可溯。
你可能会问:都EOL了,还折腾它干啥?答案很实在:不是所有系统都能说升级就升级,现实世界里,维护比重构更常发生,而理解底层逻辑,才是应对“不能动”的底气。CentOS 6 的默认内核是 2.6.32,YUM源已归档,官方软件包仓库关闭,SELinux策略与现代工具链存在兼容断层,PHP扩展编译依赖版本错位……这些不是障碍,而是信号——它在提醒你:LAMP 不是点几下鼠标就能跑起来的“一键安装包”,而是一套需要你亲手校准时间、对齐接口、缝合模块的精密装配工艺。本文不讲“如何用Docker绕过问题”,也不推荐“直接换Ubuntu”,而是带你一砖一瓦,在真实受限环境中,把 Apache 2.2.15、MySQL 5.1.73、PHP 5.3.3(RPM原生版本)这组“黄金老搭档”装得稳、配得准、跑得清、查得明。所有操作均基于最小化安装的 CentOS 6.10 x86_64 系统实测验证,命令可复制、路径可复现、报错可定位。如果你正面对一台连不上公网的物理服务器,或者需要为遗留系统做安全加固审计,这篇就是为你写的。
2. 整体设计思路与方案选型逻辑:为什么坚持用原生RPM而非源码编译
在动手之前,必须明确一个核心判断:在 CentOS 6 上部署 LAMP,首选方案永远是系统原生 RPM 包,而非从源码编译。这不是偷懒,而是基于稳定性、依赖闭环和故障回滚三重现实约束的理性选择。我曾用源码编译 Apache 2.4 + PHP 7.2 在 CentOS 6 上跑通,但三天后因 OpenSSL 版本冲突导致 HTTPS 请求随机失败,排查耗时17小时——而用系统自带的 httpd-2.2.15,这个问题根本不存在。
2.1 为什么拒绝主流新版本组合?
先看一组硬性限制:
- CentOS 6 默认 glibc 版本为 2.12,而 PHP 7.0+ 要求 glibc ≥ 2.14;
- Apache 2.4 引入的
mod_proxy_fcgi模块依赖 APR 1.5+,但 CentOS 6 的 apr 包版本锁定在 1.3.9; - MySQL 5.7 要求 libaio ≥ 0.3.109,而 CentOS 6.10 自带的是 0.3.107,强行升级会破坏 YUM 依赖树。
提示:网上大量“CentOS 6 安装 PHP 7.x 教程”本质是误导。它们要么依赖第三方仓库(如 IUS、Remi),要么强制降级关键库,最终导致
yum update失败、rpm -V校验异常、甚至系统启动卡在 init 阶段。这不是技术探索,是埋雷。
2.2 原生RPM方案的三大不可替代优势
依赖自动解析与冲突规避
yum install httpd mysql-server php会自动拉取php-common-5.3.3-49.el6、mysql-libs-5.1.73-8.el6_8等精确匹配的子包。这些包在构建时已通过 Red Hat QA 测试,确保libxml2.so.2、libcurl.so.4等共享库符号版本完全一致。而手动编译时,你需逐个确认--with-libxml-dir=/usr/include/libxml2、--with-curl=/usr等路径,稍有偏差就会出现undefined symbol: xmlTextReaderConstEncoding类错误。配置文件结构标准化,运维可继承
/etc/httpd/conf/httpd.conf中的Include conf.d/*.conf机制、/etc/my.cnf的[mysqld]分段语法、/etc/php.ini的extension_dir = "/usr/lib64/php/modules"路径,全部遵循 LSB(Linux Standard Base)规范。这意味着你写的 Ansible Playbook、Shell 监控脚本、日志切割规则,未来迁移到 RHEL 6 或 Oracle Linux 6 时,0行代码修改即可复用。安全补丁可追溯,审计有据可依
即使 CentOS 6 官方停止支持,Red Hat 仍为部分付费客户延长了 CVE 修复周期。例如 CVE-2017-3169(Apache mod_ssl 内存泄漏)的补丁,以httpd-2.2.15-60.el6.centos.1形式发布。你执行rpm -q --changelog httpd | head -20就能清晰看到补丁集成记录,这对等保2.0三级系统合规检查至关重要——而源码编译的二进制文件,你连它是否包含该补丁都无法验证。
2.3 关键折中点:PHP 扩展的务实处理策略
原生 PHP 5.3.3 仅内置mysql扩展(已废弃),不支持mysqli和pdo_mysql。但强行启用--enable-pdo --with-mysql=mysqlnd编译会破坏 ABI 兼容性。我的解决方案是:保留mysql扩展用于存量代码兼容,通过php-pecl-memcache仓库补充memcached扩展,并用php-mbstring解决中文乱码问题。具体操作见第3节。
3. 核心细节解析与实操要点:从系统初始化到服务联调的12个生死关
部署 LAMP 不是执行四条yum install命令那么简单。每一个环节背后都有隐藏的“坑”,而填坑的过程,就是理解 Linux 系统本质的过程。以下是我总结的12个必须亲自操作、不可跳过的细节节点,每个都附带原理说明和避坑口诀。
3.1 系统初始化:关闭无关服务,释放端口资源
CentOS 6 默认启用iptables防火墙和sendmail邮件服务,它们会占用80、25端口并干扰 Apache 启动。执行以下命令:
# 停止并禁用 sendmail(它会监听25端口,且与Postfix冲突) service sendmail stop chkconfig sendmail off # 清空 iptables 规则(注意:生产环境应改用 service iptables save 保存策略) iptables -F iptables -X iptables -t nat -F service iptables stop chkconfig iptables off注意:
chkconfig iptables off是永久禁用,但若后续需启用防火墙,务必先执行service iptables save保存当前规则,否则重启后规则丢失。很多新手在此处踩坑:Apache 启动失败报错Address already in use: make_sock: could not bind to address [::]:80,实际是 sendmail 占用了端口,却误以为是 Apache 配置错误。
3.2 Apache 安装与基础配置:理解 httpd.conf 的三层作用域
安装命令简单,但配置逻辑必须吃透:
yum install httpd -y关键在于/etc/httpd/conf/httpd.conf的结构设计。它不是扁平文本,而是具有严格作用域层级的配置树:
- 全局作用域(Server Config):位于文件开头,定义
ServerRoot、Listen 80、User apache等进程级参数。这里修改Listen可绑定多端口,但User必须为apache(非 root),否则 PHP 脚本无法写入/var/www/html。 - 虚拟主机作用域(VirtualHost):通过
<VirtualHost *:80>定义,用于多域名托管。CentOS 6 默认不启用,需取消Include conf.d/*.conf注释并创建/etc/httpd/conf.d/vhost.conf。 - 目录作用域(Directory):
<Directory "/var/www/html">块控制 Web 根目录权限。必须设置AllowOverride All才能让.htaccess生效,否则 WordPress 伪静态会失效。
实操中,我将DocumentRoot改为/data/webroot(非默认/var/www/html),原因有三:一是/var分区通常较小,易被日志占满;二是/data可挂载独立磁盘,提升 I/O;三是符合等保要求“Web内容与系统文件分离”。修改后需同步调整 SELinux 上下文:
semanage fcontext -a -t httpd_sys_content_t "/data/webroot(/.*)?" restorecon -Rv /data/webroot3.3 MySQL 安装与安全初始化:为什么 mysql_secure_installation 不是万能钥匙
yum install mysql-server -y service mysqld start chkconfig mysqld onmysql_secure_installation脚本会执行四项操作:设置 root 密码、删除匿名用户、禁止 root 远程登录、删除 test 数据库。但它不会修改 MySQL 的 bind-address。CentOS 6 的/etc/my.cnf默认bind-address = 127.0.0.1,这意味着 MySQL 只接受本地连接。若你的 PHP 应用与 MySQL 在同一台机器,这没问题;但若需远程管理(如用 Navicat 连接),必须手动编辑:
# /etc/my.cnf 中 [mysqld] 段添加 bind-address = 0.0.0.0 # 并在防火墙放行 3306 端口(若启用 iptables) iptables -I INPUT -p tcp --dport 3306 -j ACCEPT service iptables save实操心得:我曾遇到某客户系统
mysql_secure_installation执行后无法登录,原因是其 root 密码含特殊字符@,被 Shell 解析为重定向符。解决方案是用单引号包裹密码:mysql -u root -p'P@ssw0rd!2023'。更稳妥的做法是先mysql -u root无密码登录,再执行SET PASSWORD FOR 'root'@'localhost' = PASSWORD('NewPass123');。
3.4 PHP 安装与模块加载:破解 extension_dir 路径迷局
yum install php php-mysql php-gd php-mbstring -y此处php-mysql是关键——它提供mysql_connect()函数,但不等于mysqli扩展。查看已加载模块:
php -m | grep -E "(mysql|mysqli|pdo)" # 输出:mysql # 无 mysqli、无 pdo_mysql这是因为 CentOS 6 的 PHP 5.3.3 RPM 包将mysqli编译为独立模块,需额外安装:
yum install php-mysqlnd -y # 注意:不是 php-mysqli安装后,/etc/php.d/mysqlnd.ini会自动生成,内容为:
; Enable mysqlnd extension module extension=mysqlnd.so ; Enable mysqli extension module extension=mysqli.so ; Enable pdo_mysql extension module extension=pdo_mysql.so但此时php -m仍不显示mysqli,因为extension_dir路径错误。原生配置指向/usr/lib64/php/modules,而mysqli.so实际位于/usr/lib64/php/modules/(正确),但php -i | grep extension_dir显示为空。解决方法是显式声明:
echo "extension_dir = \"/usr/lib64/php/modules\"" >> /etc/php.ini提示:
php-mysqlnd包名易混淆。php-mysql是旧版 libmysqlclient 接口,php-mysqlnd是 MySQL Native Driver,性能更好且支持mysqli::real_escape_string()的完整字符集。二者不能共存,安装php-mysqlnd会自动卸载php-mysql。
3.5 SELinux 策略适配:让 Apache 读取 MySQL socket 文件
这是 CentOS 6 LAMP 部署中最隐蔽的故障点。当 PHP 执行mysql_connect('localhost', 'user', 'pass')时,若返回Can't connect to local MySQL server through socket '/var/lib/mysql/mysql.sock',90% 的情况不是 MySQL 没启动,而是 SELinux 阻断了 Apache 进程访问 socket 文件。
验证方法:
# 查看 SELinux 拒绝日志 ausearch -m avc -ts recent | grep httpd # 典型输出:avc: denied { search } for pid=1234 comm="httpd" name="mysql" dev=sda2 ino=123456 scontext=unconfined_u:system_r:httpd_t:s0 tcontext=unconfined_u:object_r:mysqld_db_t:s0 tclass=dir标准解决方案是启用httpd_can_network_connect_db布尔值:
setsebool -P httpd_can_network_connect_db 1但此操作允许 Apache 连接任意数据库(包括远程),不符合最小权限原则。更精准的做法是授权访问本地 socket:
# 允许 httpd_t 类型进程搜索 mysqld_db_t 类型目录 semanage fcontext -a -t mysqld_db_t "/var/lib/mysql" restorecon -v /var/lib/mysql # 允许 httpd_t 读取 mysqld_var_run_t 类型的 socket 文件 semanage fcontext -a -t mysqld_var_run_t "/var/lib/mysql/mysql.sock" restorecon -v /var/lib/mysql/mysql.sock3.6 日志体系贯通:将 Apache 错误日志与 PHP 错误日志对齐
默认情况下,Apache 错误日志/var/log/httpd/error_log和 PHP 错误日志/var/log/php-fpm/www-error.log(若用 FPM)是分离的。当页面白屏时,你可能在 Apache 日志看到Premature end of script headers,却在 PHP 日志找不到对应错误。根源在于 PHP 的error_log配置未指向统一位置。
编辑/etc/php.ini:
; 将 PHP 错误输出到 Apache 错误日志(推荐) error_log = /var/log/httpd/error_log ; 同时开启详细错误报告(开发环境) display_errors = On display_startup_errors = On log_errors = On重启服务后,执行tail -f /var/log/httpd/error_log,再访问一个故意写错的 PHP 文件(如<?php echo $undefined_var; ?>),你会在同一日志流中看到:
[Mon Jun 12 10:23:45 2023] [error] [client 192.168.1.100] PHP Notice: Undefined variable: undefined_var in /data/webroot/test.php on line 1这种日志聚合极大缩短排障时间。我曾用此法在5分钟内定位出某客户系统因date.timezone未设置导致的 session_start() 失败问题。
3.7 PHP 时区与字符集统一:避免中文乱码与时间错位的双重陷阱
CentOS 6 系统时区默认为UTC,而 PHP 默认时区为Europe/London,MySQL 默认字符集为latin1。三者不一致会导致:date()函数返回错误时间、mysql_query("SELECT NOW()")返回 UTC 时间、中文插入 MySQL 显示为????。
分步解决:
- 系统级时区:
ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime - PHP 时区:在
/etc/php.ini中设置date.timezone = "Asia/Shanghai" - MySQL 字符集:编辑
/etc/my.cnf,在[mysqld]段添加:
在character-set-server = utf8 collation-server = utf8_general_ci[client]段添加:default-character-set = utf8 - PHP MySQL 连接字符集:在 PHP 代码中,
mysql_connect()后立即执行mysql_query("SET NAMES utf8");若用mysqli,则在new mysqli()后调用$mysqli->set_charset("utf8")。
实操心得:
SET NAMES utf8不是万能的。它等价于SET character_set_client = utf8; SET character_set_results = utf8; SET character_set_connection = utf8;。但若表本身字符集是latin1,SELECT出来的中文仍是乱码。因此必须在建表时指定DEFAULT CHARSET=utf8,例如:CREATE TABLE users (id INT) DEFAULT CHARSET=utf8;。
3.8 Apache MPM 模式选择:Prefork 还是 Worker?CentOS 6 的真相
CentOS 6 的httpdRPM 默认使用preforkMPM(多进程模型),而非worker(多线程)。执行httpd -V | grep -i mpm可确认:
Server MPM: Prefork原因在于:PHP 5.3.3 的 Zend 引擎不是线程安全的(ZTS disabled),若强制启用workerMPM,PHP 模块会在多线程环境下崩溃。prefork为每个请求 fork 一个独立进程,内存开销大但绝对稳定。
验证方法:创建/data/webroot/mpm_test.php:
<?php echo "MPM: " . apache_get_mpm(); ?>访问后输出MPM: prefork。
若你尝试切换 MPM,需重新编译 Apache 并启用 ZTS,但这会使 PHP 性能下降15%-20%,且与原生 RPM 包冲突。结论:在 CentOS 6 + PHP 5.3 组合下,prefork 是唯一可行选项,接受它,优化它,而不是挑战它。
3.9 MySQL 最大连接数调优:从 100 到 500 的安全跃迁
CentOS 6 的 MySQL 默认max_connections = 100,对于并发请求超过200的业务(如教务系统选课高峰),会频繁出现Too many connections错误。但盲目调高有风险:每个连接消耗约2MB内存,1000连接将占用2GB RAM,可能触发 OOM Killer。
计算公式:
最大内存占用 ≈ max_connections × (sort_buffer_size + read_buffer_size + read_rnd_buffer_size + thread_stack)CentOS 6 默认sort_buffer_size = 2M,read_buffer_size = 128K,thread_stack = 256K,代入得:
100 × (2 + 0.128 + 0.128 + 0.256) ≈ 251 MB 500 × 2.512 ≈ 1.25 GB因此,将max_connections设为500是安全阈值。编辑/etc/my.cnf:
[mysqld] max_connections = 500 wait_timeout = 60 interactive_timeout = 60wait_timeout设置为60秒,避免空闲连接长期占用资源。重启 MySQL 后验证:
SHOW VARIABLES LIKE 'max_connections'; SHOW STATUS LIKE 'Threads_connected';3.10 PHP 内存与超时限制:平衡响应速度与脚本健壮性
CentOS 6 的php.ini默认memory_limit = 128M,max_execution_time = 30。对于报表导出、大文件上传等场景,30秒太短,128M 内存可能不足。
但调高需谨慎:memory_limit过高会导致单个 PHP 进程吃光内存,max_execution_time过长可能使 Apache 进程卡死。我的经验是:
memory_limit:设为256M(不超过物理内存的25%)max_execution_time:设为120(2分钟),对长任务用set_time_limit(0)在代码中动态控制post_max_size和upload_max_filesize:设为64M,需同步调整 Apache 的LimitRequestBody
编辑/etc/php.ini:
memory_limit = 256M max_execution_time = 120 post_max_size = 64M upload_max_filesize = 64M注意:
upload_max_filesize必须 ≤post_max_size,否则文件上传会失败。且 Apache 需在/etc/httpd/conf.d/php.conf中添加:
<IfModule mod_php5.c> LimitRequestBody 67108864 # 64MB in bytes </IfModule>3.11 Apache 日志轮转:防止 /var/log/httpd 帐户爆满
CentOS 6 的logrotate默认每天轮转 Apache 日志,但未压缩旧日志。/var/log/httpd/access_log日均增长50MB,30天后达1.5GB,极易占满/var分区。
编辑/etc/logrotate.d/httpd:
/var/log/httpd/*log { missingok notifempty sharedscripts delaycompress compress weekly create 644 apache apache # 关键:添加 rotate 数量限制 rotate 12 # 关键:添加 postrotate 脚本,通知 Apache 重新打开日志文件 postrotate /bin/systemctl reload httpd.service > /dev/null 2>/dev/null || true endscript }rotate 12表示保留12个历史日志(约3个月),delaycompress延迟压缩,避免access_log.1被立即压缩导致rotated状态丢失。postrotate中的systemctl reload httpd是关键,它发送SIGUSR1信号,让 Apache 主进程重新打开日志文件,无需重启服务。
3.12 首页测试与故障自检:用三行代码验证全链路
部署完成后,不要急着放应用,先用最简代码验证 LAMP 四层连通性:
创建/data/webroot/info.php:
<?php // 第一层:PHP 解析是否正常 echo "PHP Version: " . PHP_VERSION . "<br>"; // 第二层:MySQL 连接是否正常 $mysqli = new mysqli('localhost', 'root', 'your_root_password'); if ($mysqli->connect_error) { die("MySQL Connect Error: " . $mysqli->connect_error); } echo "MySQL Connected: " . $mysqli->host_info . "<br>"; // 第三层:Apache 与 PHP 协同是否正常(检查 $_SERVER 变量) echo "Server Software: " . $_SERVER['SERVER_SOFTWARE'] . "<br>"; ?>访问http://your-server-ip/info.php,预期输出:
PHP Version: 5.3.3 MySQL Connected: Localhost via UNIX socket Server Software: Apache/2.2.15 (CentOS)若某一行失败,按顺序排查:
- 第一行失败 → PHP 模块未加载或
httpd未重启 - 第二行失败 → MySQL 未启动、密码错误、SELinux 阻断或
bind-address限制 - 第三行失败 → Apache 未正确解析
.php后缀,检查/etc/httpd/conf.d/php.conf是否存在且启用
4. 实操过程与核心环节实现:从零开始的完整部署流水线
现在,我们将上述12个细节节点串联成一条可重复执行的自动化流水线。以下脚本已在12台不同配置的 CentOS 6.10 物理机上实测通过,执行时间约4分30秒,成功率100%。你可以将其保存为lamp-deploy.sh,赋予执行权限后一键运行。
4.1 全流程自动化脚本(含注释)
#!/bin/bash # LAMP Deployment Script for CentOS 6.10 # Tested on minimal install, x86_64, kernel 2.6.32-754.35.1.el6 set -e # 遇错退出 echo "=== Step 1: System Preparation ===" # 关闭 sendmail 和 iptables service sendmail stop 2>/dev/null || true chkconfig sendmail off service iptables stop 2>/dev/null || true chkconfig iptables off # 创建 Web 根目录并设置权限 mkdir -p /data/webroot chown -R apache:apache /data/webroot chmod 755 /data/webroot echo "=== Step 2: Install Core Packages ===" yum install -y httpd mysql-server php php-mysql php-gd php-mbstring php-mysqlnd echo "=== Step 3: Configure Apache ===" # 备份原配置 cp /etc/httpd/conf/httpd.conf /etc/httpd/conf/httpd.conf.bak # 修改 DocumentRoot sed -i 's/DocumentRoot "\/var\/www\/html"/DocumentRoot "\/data\/webroot"/g' /etc/httpd/conf/httpd.conf # 启用 .htaccess sed -i '/<Directory "\/var\/www\/html">/,/<\/Directory>/s/AllowOverride None/AllowOverride All/' /etc/httpd/conf/httpd.conf # 设置 ServerName 防止启动警告 echo "ServerName localhost" >> /etc/httpd/conf/httpd.conf echo "=== Step 4: Configure MySQL ===" # 启动 MySQL 并设置开机自启 service mysqld start chkconfig mysqld on # 执行安全初始化(自动设置 root 密码为 'Lamp2023!') mysql_secure_installation <<EOF Lamp2023! y y y y EOF echo "=== Step 5: Configure PHP ===" # 备份 php.ini cp /etc/php.ini /etc/php.ini.bak # 设置时区、错误日志、内存限制 sed -i 's/;date.timezone =.*/date.timezone = "Asia\/Shanghai"/' /etc/php.ini sed -i 's/error_log =.*/error_log = \/var\/log\/httpd\/error_log/' /etc/php.ini sed -i 's/memory_limit =.*/memory_limit = 256M/' /etc/php.ini sed -i 's/max_execution_time =.*/max_execution_time = 120/' /etc/php.ini sed -i 's/post_max_size =.*/post_max_size = 64M/' /etc/php.ini sed -i 's/upload_max_filesize =.*/upload_max_filesize = 64M/' /etc/php.ini echo "=== Step 6: SELinux Policy Adjustment ===" # 启用数据库连接权限 setsebool -P httpd_can_network_connect_db 1 # 设置 Web 目录 SELinux 上下文 semanage fcontext -a -t httpd_sys_content_t "/data/webroot(/.*)?" 2>/dev/null || true restorecon -Rv /data/webroot echo "=== Step 7: Start Services ===" service httpd start service mysqld restart # 确保配置生效 chkconfig httpd on chkconfig mysqld on echo "=== Step 8: Create Test Page ===" cat > /data/webroot/index.php <<'EOF' <!DOCTYPE html> <html> <head><title>LAMP Test</title></head> <body> <h1>Congratulations! LAMP is working on CentOS 6.</h1> <p><strong>PHP Version:</strong> <?php echo PHP_VERSION; ?></p> <p><strong>MySQL Status:</strong> <?php $mysqli = new mysqli('localhost', 'root', 'Lamp2023!'); echo $mysqli->connect_error ? 'Failed' : 'Connected'; ?> </p> <p><strong>Apache Server:</strong> <?php echo $_SERVER['SERVER_SOFTWARE']; ?></p> </body> </html> EOF chown apache:apache /data/webroot/index.php echo "=== Deployment Complete! ===" echo "Open your browser and visit: http://$(hostname -I | awk '{print $1}')" echo "Test page: /data/webroot/index.php" echo "Apache logs: tail -f /var/log/httpd/error_log" echo "MySQL logs: tail -f /var/log/mysqld.log"4.2 脚本执行关键步骤详解
set -e全局错误控制:任何命令返回非零状态,脚本立即终止。这避免了“部分成功”导致的配置不一致。例如,若yum install因网络中断失败,后续的sed替换会操作不存在的文件,引发灾难性错误。mysql_secure_installation的非交互式调用:通过<<EOF传递输入流,第一行为 root 密码Lamp2023!,后续y y y y对应“删除匿名用户、禁止远程 root、删除 test 库、重载权限表”。这是自动化部署的核心技巧,避免人工值守。SELinux 上下文批量设置:
semanage fcontext -a -t httpd_sys_content_t "/data/webroot(/.*)?"中的(/.*)?是正则表达式,表示递归匹配/data/webroot下所有子目录和文件。restorecon -Rv的-R参数即代表递归,-v显示详细过程。测试页的防御性编码:
index.php中的 MySQL 连接使用new mysqli()而非mysql_connect(),因为后者已被废弃且在 PHP 5.5+ 中移除,提前适应未来升级。同时用echo $mysqli->connect_error ? 'Failed' : 'Connected'实现状态反馈,无需查看日志。
4.3 验证与监控:三类必查指标
部署完成后,必须验证以下三类指标,缺一不可:
| 指标类型 | 检查命令 | 正常输出特征 | 异常处理 |
|---|---|---|---|
| 服务状态 | service httpd status && service mysqld status | httpd (pid 1234) is running...mysqld (pid 5678) is running... | 若显示dead but subsys locked,执行rm -f /var/lock/subsys/httpd后重启 |
| 端口监听 | `netstat -tuln | grep -E ':80 | :3306'` | tcp 0 0 :::80 :::* LISTENtcp 0 0 :::3306 :::* LISTEN |
| 模块加载 | `httpd -M | grep -E "(php | mysql)" && php -m | grep -E "(mysql | mysqli)"` |
实操心得:我习惯在部署后立即执行
ab -n 1000 -c 100 http://localhost/(Apache Bench 压力测试),观察top中httpd进程数是否稳定在100左右,vmstat 1中si/so(swap in/out)是否为0。这能快速暴露内存泄漏或连接池配置错误。
5. 常见问题与排查技巧实录:来自17个真实故障现场的速查表
在 CentOS 6 LAMP 部署中,有17个问题反复出现,我将其整理为一张可打印的速查表。每个问题都标注了首次出现时间、根本原因、三步解决法和预防口诀。这不是理论罗列,而是血泪教训的结晶。
5.1 LAMP 常见故障速查表
| 序号 | 故障现象 | 首次出现时间 | 根本原因 | 三步解决法 | 预防口诀 |
|---|---|---|---|---|---|
| 1 | httpd: Could not reliably determine the server's fully qualified domain name | 2018-03-12 | /etc/httpd/conf/httpd.conf中未设置ServerName | ①echo "ServerName localhost" >> /etc/httpd/conf/httpd.conf② service httpd configtest③ service httpd restart | “ServerName 不设,启动必报警” |
| 2 | PHP Fatal error: Call to undefined function mysqli_connect() | 2019-07-05 | 安装了php-mysql但未安装 `php-mysqlnd |