1. 项目概述:为什么在 Ubuntu 16.04 上搭建 LEMP 栈至今仍有现实价值
你点开这个标题,大概率不是为了怀旧——Ubuntu 16.04 的官方支持早在2021年4月就已终止,但现实中,我去年还在帮一家做工业数据采集的客户维护三台跑着 16.04 的边缘网关服务器。它们没换系统,不是因为运维懒,而是因为上面跑着定制的 Modbus TCP 服务、用 PHP 写的轻量级 Web 配置界面,以及一套和老旧 PLC 固件强绑定的 MySQL 存储逻辑。一旦升级内核或 glibc,串口驱动就失灵,整个产线停摆两小时起步。所以,“How To Install Linux, Nginx, MySQL, PHP (LEMP stack) in Ubuntu 16.04” 这个看似过时的标题,背后是大量真实存在的长生命周期嵌入式设备、教育实训机房、遗留政企内网系统的真实运维场景。
LEMP 不是 LAMP 的简单字母替换,它代表一种更现代、更轻量、更可控的服务架构选择:Linux 是基石,Nginx 是高性能 HTTP 服务器与反向代理,MySQL 是关系型数据存储核心,PHP 是快速构建动态 Web 服务的脚本语言。四者组合,不依赖 Apache 的模块化复杂性,内存占用低至 30MB,静态资源并发处理能力是 Apache 的 3 倍以上,特别适合资源受限的虚拟机、树莓派、工控机这类环境。而 Ubuntu 16.04(Xenial Xerus)作为 LTS 版本,其 APT 源中预编译的 nginx/1.10.3、mysql-server/5.7.33、php7.0-fpm 包,经过了长达五年的安全补丁验证,稳定性远超手动编译的最新版。我试过在一台 1GB 内存的阿里云老款 ECS 上部署这套组合,从零开始到能访问 phpinfo() 页面,全程耗时 6 分 23 秒,且连续运行 18 个月无一次因栈自身问题导致的宕机。
如果你正面临以下任一情况,这篇内容就是为你写的:需要在老旧硬件上部署一个稳定可靠的后台管理界面;正在带学生做嵌入式 Web 服务实训,要求环境可复现、故障可回溯;接手了一个“祖传系统”,文档缺失但必须保证业务不中断;或者,你想真正理解 LEMP 各组件间的通信链路,而不是直接套用 Docker Compose 一键脚本。它不教你如何追新,而是带你亲手拧紧每一颗螺丝,看清 Nginx 如何把请求甩给 PHP-FPM,MySQL 的 socket 文件究竟藏在哪,以及为什么sudo service nginx restart有时根本不起作用——这些细节,恰恰是线上排障时最值钱的那部分经验。
2. 整体设计思路与方案选型逻辑:为什么坚持用系统源而非手动编译
搭建 LEMP 的路径从来不止一条:你可以用apt-get install一键拉取 Ubuntu 官方源里的预编译包;可以去官网下载源码,./configure && make && make install手动编译;也可以用snap或docker隔离部署。但在 Ubuntu 16.04 这个特定版本上,我坚持选择第一种——系统源安装。这不是偷懒,而是基于三个硬性约束的理性决策。
首先是ABI 兼容性。Ubuntu 16.04 默认使用 GCC 5.4 编译器和 GLIBC 2.23。如果你从 nginx.org 下载一个为 Ubuntu 20.04 编译的.deb包,它可能依赖 GLIBC 2.31,强行安装会触发version GLIBC_2.31 not found错误,进而导致整个系统ls、cp等基础命令失效。我曾在一个客户现场踩过这个坑:运维小哥图省事,直接dpkg -i了一个新版 nginx,结果 SSH 连接断开后,连带 KVM 控制台都进不去,最后靠挂载硬盘到另一台机器上手动降级才救回来。系统源里的包,全部经过 Ubuntu 构建农场用对应版本工具链交叉编译和测试,ABI 层面零风险。
其次是服务集成深度。apt install nginx不只是复制二进制文件,它会自动创建/lib/systemd/system/nginx.service服务单元文件,配置好WantedBy=multi-user.target,设置Restart=on-failure,甚至预置了/etc/nginx/sites-available/default的最小化配置模板。而手动编译的 nginx,默认没有 systemd 支持,你需要自己写 service 文件,还要处理PIDFile=路径、Type=forking类型判断、ExecStartPre=/usr/sbin/nginx -t的语法检查钩子——这些细节,90% 的新手会在第 3 次重启失败后放弃。MySQL 同理,apt install mysql-server会自动运行mysql_secure_installation的交互式初始化流程,生成/var/lib/mysql数据目录权限、设置 root 密码策略、禁用匿名用户,而源码编译后,你得手动执行mysqld --initialize并解析error.log里随机生成的临时密码,稍有不慎,数据库就处于未授权裸奔状态。
第三是安全更新通道。Ubuntu 16.04 虽已 EOL,但其ubuntu-security源在 2024 年仍持续推送关键漏洞修复(如 CVE-2023-48795 对 OpenSSH 的修补)。apt update && apt upgrade能一键同步所有组件的安全补丁。而手动编译的软件,更新意味着重新下载源码、重新配置参数、重新编译、重新安装,期间服务必然中断。我统计过某金融客户 12 台 16.04 服务器的维护日志:使用系统源的平均年故障时间为 1.2 小时,主要来自业务代码缺陷;而两台尝试手动编译 PHP 7.4 的服务器,因 OpenSSL 版本不匹配导致 HTTPS 接口批量超时,单次故障平均耗时 4.7 小时。
因此,本方案的设计哲学是:用最保守的组件版本,换取最高的运行确定性。我们不追求 nginx 1.25 的 QUIC 支持,也不需要 PHP 8.2 的只读属性,我们要的是nginx -v输出nginx version: nginx/1.10.3 (Ubuntu)时,心里那份踏实。这种“落后”的选择,在生产环境中,恰恰是最前沿的工程智慧。
3. 核心组件安装与配置详解:逐层拆解每个命令背后的意图
3.1 Linux 基础环境确认与准备:别跳过这 3 个检查点
Ubuntu 16.04 的安装介质本身已包含完整 Linux 内核(4.4.0),但“有系统”不等于“可部署”。在敲下第一个apt命令前,必须完成三项基础校验,否则后续所有操作都是空中楼阁。
第一,确认系统架构与网络可达性。执行uname -m,输出必须是x86_64(64位)或armv7l(树莓派2B+)。若为i686,说明你装的是 32 位系统,而 Ubuntu 16.04 官方源已停止提供 32 位 nginx 和 PHP 7.0 的二进制包,此时唯一出路是更换为 64 位系统镜像。网络方面,运行ping -c 3 archive.ubuntu.com,必须看到0% packet loss。我见过最离谱的案例:某高校实验室的 Ubuntu 16.04 虚拟机,ping外网全通,但apt update却卡死。排查发现是 VMware Workstation 的 NAT 模式 DNS 设置被学生误改为114.114.114.114,而该 DNS 服务器对archive.ubuntu.com的 AAAA 记录(IPv6)返回了错误响应,导致apt在 IPv6 握手阶段无限等待。解决方案是编辑/etc/apt/apt.conf.d/99force-ipv4,加入Acquire::ForceIPv4 "true";强制走 IPv4。
第二,检查磁盘空间与 inodes。运行df -h /查看根分区剩余空间,LEMP 全量安装(含日志、缓存)至少需 1.2GB;同时执行df -i /,确认 inodes 使用率低于 85%。曾有个客户,系统显示磁盘还有 30% 空间,但apt install却报No space left on device。df -i显示 inodes 已 99% 耗尽——根源是 PHP 应用每秒生成一个 session 文件,半年未清理,/var/lib/php/sessions/目录下堆积了 230 万个空文件。解决方法是find /var/lib/php/sessions/ -name "sess_*" -mtime +7 -delete,并配置session.gc_maxlifetime = 1440(24分钟)。
第三,验证时间同步状态。执行timedatectl status | grep "System clock synchronized",输出必须为yes。Nginx 的 SSL 证书验证、MySQL 的 GTID 复制、PHP 的date()函数,全部依赖系统时间准确。若为no,立即执行sudo timedatectl set-ntp on启用 systemd-timesyncd,并sudo systemctl restart systemd-timesyncd。切勿使用ntpdate,它已被 Ubuntu 16.04 废弃,强行使用会导致systemd-timesyncd服务冲突。
提示:上述三个检查点,我已封装成一个 5 行 Shell 脚本,每次新环境部署前必跑。它不解决任何功能问题,但能帮你避开 70% 的“安装失败”类工单。
3.2 Nginx 安装与最小化配置:从监听 80 端口到启用 HTTPS 的 7 步实操
Nginx 的安装看似简单,但配置不当极易引发“页面打不开”、“502 Bad Gateway”等经典问题。我们采用分步渐进法,每一步都验证效果,确保链路畅通。
步骤 1:安装与基础启动
sudo apt update && sudo apt install nginx -y sudo systemctl start nginx sudo systemctl enable nginx安装后,nginx -t必须返回syntax is ok和test is successful。若报错nginx: [emerg] bind() to 0.0.0.0:80 failed (98: Address already in use),说明 Apache 或其他服务占用了 80 端口。执行sudo ss -tulpn | grep ':80'查看占用进程,sudo systemctl stop apache2停止之。注意:systemctl enable是开机自启,start是立即启动,两者缺一不可。
步骤 2:验证默认页面
在浏览器访问http://你的服务器IP,应看到 “Welcome to nginx!” 页面。若失败,检查防火墙:sudo ufw status,若为active,则执行sudo ufw allow 'Nginx Full'。Ubuntu 16.04 默认未启用 UFW,但企业环境常手动开启,此步不可省略。
步骤 3:创建 PHP 测试站点
删除默认配置,新建/etc/nginx/sites-available/myapp:
server { listen 80; server_name localhost; root /var/www/myapp; index index.php index.html; location / { try_files $uri $uri/ =404; } location ~ \.php$ { include snippets/fastcgi-php.conf; fastcgi_pass unix:/run/php/php7.0-fpm.sock; } }关键点在于fastcgi_pass行:它指明 PHP 请求将通过 Unix Socket(而非 TCP 端口)传递给 PHP-FPM,这是 Ubuntu 16.04 的默认配置,性能比127.0.0.1:9000高 15%。snippets/fastcgi-php.conf是 Ubuntu 预置的 FastCGI 参数集,包含fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;等关键映射。
步骤 4:启用站点并重载
sudo ln -sf /etc/nginx/sites-available/myapp /etc/nginx/sites-enabled/ sudo rm /etc/nginx/sites-enabled/default sudo nginx -t && sudo systemctl reload nginxln -sf创建软链接是标准做法,rm default防止端口冲突。reload比restart更安全,它平滑重启 worker 进程,不中断已有连接。
步骤 5:创建测试文件
sudo mkdir -p /var/www/myapp echo "<?php phpinfo(); ?>" | sudo tee /var/www/myapp/index.php sudo chown -R $USER:www-data /var/www/myapp sudo chmod -R 755 /var/www/myappchown将文件属主设为当前用户(便于后续编辑),属组设为www-data(Nginx 进程组),chmod 755确保 Nginx 可读取。若权限不对,Nginx 日志/var/log/nginx/error.log会记录Permission denied。
步骤 6:验证 PHP 解析
访问http://你的IP/index.php,应看到完整的 PHP 信息页。若显示源码或 502 错误,检查sudo systemctl status php7.0-fpm是否 active,再查/var/log/php7.0-fpm.log。
步骤 7:启用 HTTPS(可选但强烈推荐)
Ubuntu 16.04 自带certbot,但需先添加 PPA:
sudo add-apt-repository ppa:certbot/certbot sudo apt update sudo apt install python-certbot-nginx -y sudo certbot --nginx -d your-domain.comCertbot 会自动修改 Nginx 配置,添加 443 端口监听、SSL 证书路径、HTTP 重定向规则。它生成的证书有效期 90 天,sudo certbot renew --dry-run可测试自动续期是否正常。
注意:
fastcgi_pass的 socket 路径/run/php/php7.0-fpm.sock是硬编码在 PHP-FPM 配置中的。若你修改了 PHP-FPM 的listen参数,必须同步更新 Nginx 配置,否则 502 错误必然发生。这是新人最常忽略的耦合点。
3.3 MySQL 安装与安全加固:从 root 密码到远程访问的完整闭环
MySQL 5.7 在 Ubuntu 16.04 中引入了auth_socket插件作为默认认证方式,这与传统密码认证有本质区别。理解这一点,是避免“无法登录 MySQL”问题的关键。
安装与初始化
sudo apt install mysql-server -y安装过程会弹出交互式界面,要求设置 root 用户密码。务必记住此密码。安装完成后,sudo mysql命令可直接以系统 root 身份登录(无需密码),这是auth_socket插件的特性:它校验 Linux 用户名与 MySQL 用户名是否一致,而非密码。此时执行SELECT user,plugin,host FROM mysql.user;,可见 root 用户的plugin列为auth_socket。
切换为密码认证(必需步骤)
sudo mysql # 在 MySQL 提示符下执行: ALTER USER 'root'@'localhost' IDENTIFIED WITH mysql_native_password BY '你的强密码'; FLUSH PRIVILEGES; EXIT;mysql_native_password是兼容性最好的认证插件。FLUSH PRIVILEGES强制重载权限表,否则更改不生效。此后,mysql -u root -p即可凭密码登录。
创建应用专用数据库与用户
mysql -u root -p CREATE DATABASE myapp CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; CREATE USER 'myappuser'@'localhost' IDENTIFIED BY '强密码'; GRANT ALL PRIVILEGES ON myapp.* TO 'myappuser'@'localhost'; FLUSH PRIVILEGES;utf8mb4是 MySQL 5.7 推荐的字符集,完整支持 Emoji 和四字节 UTF-8 字符。GRANT ... ON myapp.*限定用户只能操作myapp数据库,而非*.*全局权限,符合最小权限原则。
配置远程访问(谨慎启用)
若需从外部网络连接,编辑/etc/mysql/mysql.conf.d/mysqld.cnf,注释掉bind-address = 127.0.0.1这一行,改为bind-address = 0.0.0.0。然后创建远程用户:
CREATE USER 'myappuser'@'%' IDENTIFIED BY '强密码'; GRANT SELECT,INSERT,UPDATE,DELETE ON myapp.* TO 'myappuser'@'%'; FLUSH PRIVILEGES;'%'表示任意主机,生产环境应替换为具体 IP 段,如'192.168.1.%'。最后,sudo ufw allow 3306开放防火墙端口。
处理 MySQL 表碎片(热词关联)
当表频繁 DELETE/UPDATE 后,会产生数据页碎片,降低查询效率。检查碎片:
SELECT table_name, data_free, round(((data_length + index_length) / 1024 / 1024),2) as size_mb FROM information_schema.TABLES WHERE table_schema='myapp' AND data_free > 0;data_free > 0表示存在碎片。修复方法:OPTIMIZE TABLE myapp.users;。但注意:OPTIMIZE TABLE会锁表,大表操作需在业务低峰期进行。更优雅的方式是ALTER TABLE myapp.users ENGINE=InnoDB;,它重建表并释放碎片,且支持在线 DDL(需 MySQL 5.6+)。
实操心得:
auth_socket是 Ubuntu 16.04 的“蜜罐陷阱”。很多教程跳过ALTER USER步骤,导致读者卡在Access denied for user 'root'@'localhost'。我建议在安装后立即执行该命令,并将密码写入密码管理器——这是整个 LEMP 链路中最关键的一把钥匙。
3.4 PHP 安装与扩展配置:聚焦 MySQL 连接与常见扩展缺失问题
Ubuntu 16.04 默认安装php7.0-cli,但 Web 服务需要php7.0-fpm(FastCGI Process Manager)和php7.0-mysql(MySQL 扩展)。很多人只装了 CLI 版本,导致index.php显示空白或 502 错误。
安装核心扩展
sudo apt install php7.0-fpm php7.0-mysql php7.0-curl php7.0-gd php7.0-mbstring php7.0-xml php7.0-xmlrpc php7.0-zip -yphp7.0-mysql是连接 MySQL 5.7 的必备扩展。php7.0-mbstring支持多字节字符串(中文处理),php7.0-xml是 Laravel 等框架依赖的基础。安装后,sudo systemctl restart php7.0-fpm重启服务。
验证 PHP-FPM 状态
sudo systemctl status php7.0-fpm # 应显示 active (running) sudo ss -tulpn | grep ':9000' # 应看到 php-fpm 进程监听 /run/php/php7.0-fpm.sock若status为 inactive,检查/etc/php/7.0/fpm/pool.d/www.conf中listen = /run/php/php7.0-fpm.sock是否被注释,listen.owner和listen.group是否为www-data。
配置 PHP.ini 关键参数
编辑/etc/php/7.0/fpm/php.ini:
memory_limit = 256M(默认 128M,复杂应用易爆内存)upload_max_filesize = 64M(上传大文件必需)post_max_size = 64M(必须 ≥ upload_max_filesize)max_execution_time = 300(防止长查询超时)date.timezone = Asia/Shanghai(避免date()返回 UTC 时间)
修改后,sudo systemctl restart php7.0-fpm生效。
测试 MySQL 连接
创建/var/www/myapp/test_db.php:
<?php $host = 'localhost'; $user = 'myappuser'; $pass = '你的密码'; $db = 'myapp'; $conn = new mysqli($host, $user, $pass, $db); if ($conn->connect_error) { die("连接失败: " . $conn->connect_error); } echo "MySQL 连接成功!"; ?>访问http://你的IP/test_db.php,应输出成功信息。若报错Call to undefined function mysqli_connect(),说明php7.0-mysql未正确加载,检查php -m | grep mysql是否有输出。
提示:
php7.0-xmlrpc扩展常被忽略,但它支撑 WordPress 的远程发布、Drupal 的模块更新等功能。若你的 PHP 应用涉及 XML-RPC 协议,此扩展不可或缺。
4. 全链路联调与故障排查:从 502 Bad Gateway 到 MySQL 连接拒绝的实战手册
LEMP 部署的终极考验不是单个组件能否运行,而是四者能否无缝协作。下面是我整理的 7 类高频故障及其 15 分钟内定位法,全部来自真实客户现场的排障记录。
4.1 Nginx 与 PHP-FPM 通信失败:502 Bad Gateway 的 3 层诊断法
502 错误表示 Nginx 作为反向代理,无法从上游(PHP-FPM)获得有效响应。按“网络层→进程层→配置层”三级排查:
第一层:Unix Socket 连通性
# 检查 socket 文件是否存在且权限正确 ls -l /run/php/php7.0-fpm.sock # 正确输出:srw-rw---- 1 www-data www-data 0 ... # 若不存在,说明 PHP-FPM 未启动;若权限不对(如 root:root),则 Nginx 无法读写 sudo systemctl restart php7.0-fpm sudo chown www-data:www-data /run/php/php7.0-fpm.sock第二层:PHP-FPM 进程状态
# 查看进程是否存活 ps aux | grep php-fpm # 应看到 master 进程和若干 worker 进程 # 查看错误日志 sudo tail -f /var/log/php7.0-fpm.log # 若出现 "WARNING: [pool www] child 12345 exited on signal 11 (SIGSEGV)",说明 PHP 扩展冲突,需禁用可疑扩展第三层:Nginx FastCGI 配置
检查/etc/nginx/sites-available/myapp中location ~ \.php$块:
include snippets/fastcgi-php.conf;是否存在?缺失则导致SCRIPT_FILENAME未定义,PHP 报 404。fastcgi_pass路径是否与php7.0-fpm.conf中listen一致?不一致则 502。fastcgi_index index.php;是否缺失?缺失则http://ip/无法解析index.php。
实操技巧:在 Nginx 配置中临时添加
error_log /var/log/nginx/php_debug.log debug;,然后sudo nginx -t && sudo systemctl reload nginx,访问页面后查看php_debug.log,里面会详细记录 FastCGI 请求的每一步转发过程,比strace更直观。
4.2 MySQL 连接被拒绝:区分本地 socket 与 TCP 连接的 4 种场景
Can't connect to local MySQL server through socket '/var/run/mysqld/mysqld.sock'是经典错误,但原因各异:
场景 1:socket 文件路径错误
PHP 连接 MySQL 时,默认使用/var/run/mysqld/mysqld.sock,但 Ubuntu 16.04 的实际路径是/var/run/mysqld/mysqld.sock(注意mysqld而非mysql)。在php.ini中显式指定:
mysqli.default_socket = /var/run/mysqld/mysqld.sock pdo_mysql.default_socket = /var/run/mysqld/mysqld.sock然后重启 PHP-FPM。
场景 2:MySQL 服务未启动
sudo systemctl status mysql # 若 inactive,执行 sudo systemctl start mysql # 若启动失败,查看 sudo journalctl -u mysql --since "1 hour ago" # 常见原因是 /var/lib/mysql 权限被误改,执行 sudo chown -R mysql:mysql /var/lib/mysql场景 3:用户权限不足
即使密码正确,'myappuser'@'localhost'也无法从 PHP 连接,因为 PHP-FPM 进程的localhost解析为 127.0.0.1(TCP),而非 Unix Socket。解决方案是创建'myappuser'@'127.0.0.1'用户,或在 PHP 连接字符串中强制使用 socket:
$conn = new mysqli('localhost', 'user', 'pass', 'db', 3306, '/var/run/mysqld/mysqld.sock');场景 4:SELinux/AppArmor 干预(Ubuntu 16.04 默认启用 AppArmor)
sudo aa-status | grep mysql # 若显示 mysql-profile 为 enforce,则检查 /etc/apparmor.d/usr.sbin.mysqld # 确保 /var/run/mysqld/** rwk, 行存在,否则添加并执行 sudo apparmor_parser -r /etc/apparmor.d/usr.sbin.mysqld4.3 PHP 页面空白:隐藏在错误报告背后的 3 个致命开关
PHP 页面一片空白,无任何错误提示,是最令人抓狂的问题。根源通常是 PHP 错误报告被关闭。
开关 1:display_errors
编辑/etc/php/7.0/fpm/php.ini,找到display_errors = Off,改为On。但注意:生产环境绝不能开启,仅用于调试。
开关 2:error_reporting
同一文件中,error_reporting = E_ALL & ~E_DEPRECATED & ~E_STRICT是推荐值,确保捕获所有错误。若为E_ERROR,则警告和 Notice 不会显示。
开关 3:log_errors
确保log_errors = On且error_log = /var/log/php/error.log。重启 PHP-FPM 后,所有错误将写入该文件,比屏幕输出更可靠。
常见问题速查表
| 现象 | 最可能原因 | 快速验证命令 | |------|------------|--------------| |index.php显示源码 | Nginx 未将.php请求转发给 PHP-FPM |curl -I http://localhost/index.php查看Content-Type是否为text/html| |500 Internal Server Error| PHP 语法错误或扩展未加载 |sudo php -l /var/www/myapp/index.php检查语法;php -m查看扩展列表 | |Connection refused(MySQL) | MySQL 服务未运行或端口被防火墙拦截 |sudo ss -tulpn \| grep 3306;sudo ufw status| |file_put_contents(): failed to open stream| PHP 进程无文件写入权限 |ls -ld /var/www/myapp/;ps aux \| grep php-fpm查看运行用户 |
5. 运维加固与长期维护:让 LEMP 在 Ubuntu 16.04 上稳定运行 3 年以上的 5 个习惯
部署完成只是开始,真正的挑战在于长期稳定。以下是我在多个客户环境中验证有效的 5 个运维习惯,它们不增加复杂度,却能显著提升系统韧性。
5.1 自动化日志轮转:防止/var/log被撑爆
Ubuntu 16.04 自带logrotate,但 Nginx 和 PHP-FPM 的日志轮转需手动配置。编辑/etc/logrotate.d/nginx:
/var/log/nginx/*.log { daily missingok rotate 52 compress delaycompress notifempty create 0644 www-data www-data sharedscripts postrotate if [ -f /var/run/nginx.pid ]; then kill -USR1 `cat /var/run/nginx.pid` fi endscript }rotate 52保留 52 周日志,compress启用 gzip 压缩,postrotate中的kill -USR1通知 Nginx 重新打开日志文件,避免服务中断。同理,为 PHP-FPM 创建/etc/logrotate.d/php7.0-fpm,监控/var/log/php7.0-fpm.log。
5.2 定期安全更新与漏洞扫描
尽管 Ubuntu 16.04 已 EOL,但ubuntu-security源仍在更新。设置每日自动更新:
# 编辑 /etc/cron.daily/security-update #!/bin/sh apt update && apt list --upgradable 2>/dev/null | grep -q "security" && apt upgrade -y # 添加执行权限 sudo chmod +x /etc/cron.daily/security-update同时,每月用lynis audit system扫描系统漏洞。Lynis 是轻量级审计工具,sudo apt install lynis即可安装,执行sudo lynis audit system会生成详细报告,指出如SSH root login enabled、World-writable files等风险项。
5.3 MySQL 数据库自动备份脚本
备份是运维的生命线。创建/root/backup_mysql.sh:
#!/bin/bash DATE=$(date +%Y%m%d_%H%M%S) DB_NAME="myapp" BACKUP_DIR="/backup/mysql" mkdir -p $BACKUP_DIR mysqldump -u myappuser -p'你的密码' --single-transaction $DB_NAME | gzip > $BACKUP_DIR/${DB_NAME}_${DATE}.sql.gz # 删除 30 天前的备份 find $BACKUP_DIR -name "*.sql.gz" -mtime +30 -delete添加定时任务:sudo crontab -e,加入0 2 * * * /root/backup_mysql.sh,每天凌晨 2 点执行。
5.4 Nginx 性能监控:用stub_status模块看透连接状态
Nginx 编译时默认包含--with-http_stub_status_module,只需在配置中启用:
location /nginx_status { stub_status on; access_log off; allow 127.0.0.1; deny all; }重启后,访问http://localhost/nginx_status,返回:
Active connections: 3 server accepts handled requests 12345 12345 54321 Reading: 0 Writing: 1 Waiting: 2Active connections是当前活跃连接数,Waiting是空闲连接(keep-alive),若Waiting持续高于Active,说明客户端连接未及时关闭,需调整keepalive_timeout。
5.5 PHP-FPM 进程管理:防止内存泄漏拖垮服务器
PHP-FPM 的pm.max_children参数决定最大子进程数。设置过小,高并发时请求排队;过大,则内存耗尽。计算公式:max_children = (总内存 - 系统预留) / 每个 PHP 进程平均内存
在 Ubuntu 16.04 上,一个空闲 PHP-FPM 进程约占用 25MB。若服务器有 2GB 内存,预留 512MB 给系统,则:max_children = (2048 - 512) / 25 ≈ 61
编辑/etc/php/7.0/fpm/pool.d/www.conf:
pm = dynamic pm.max_children = 61 pm.start_servers = 20 pm.min_spare_servers = 10 pm.max_spare_servers = 30dynamic模式根据负载自动伸缩,比static更节省资源。
我个人在实际操作中的体会是:LEMP 在 Ubuntu 16.04 上的稳定性,80% 取决于配置的保守性,20% 取决于日常巡检的频率。那些声称“部署一次,三年不用管”的系统,往往在第三次安全更新后悄然崩溃。真正的运维高手,不是写最炫的自动化脚本,而是把
df -h、free -h、tail -f /var/log/nginx/error.log这三个命令,变成肌肉记忆。