2.2 手动搭 LNMP:什么时候必须自己装

2.1 聊了应用镜像有多省心,这篇泼一盆冷水:有些场景,你还真得自己装。我自己的一个老项目就遇上了——一个跑了多年的站点要求 PHP 7.4(新版插件不兼容 PHP 8),还依赖一个 PHP 扩展,镜像里没有。那一刻我意识到,镜像解决的是 80% 的标准需求,剩下 20% 得靠手动。这篇就讲清楚什么情况值得动手,以及手动搭 LNMP 的完整清单。

四种必须手动装的情形

先给结论,别为了折腾而折腾。下面这四种我才建议你自己装:

情形 例子 为什么镜像搞不定
指定旧版或新版运行时 PHP 7.4 老站、特定 Node 大版本 应用镜像只提供主流版本
需要特殊扩展或编译参数 某扩展、自定义 PHP 编译项 镜像里没装,apt 仓库也没有
多站点、多版本并存 一台机器跑 PHP 7.4 和 PHP 8.2 两个站 单镜像只带一套环境
非 WordPress 的异构应用 自己写的 Go/Python 服务 应用镜像根本没有对应方案

一份判断表:这次到底要不要手动

每次有人问我“我这个需求是不是得手动装”,我都让他先看下面这张判断表,逐条对号入座:

你的情况 建议方案 理由
只想要一个 WordPress 应用镜像 镜像就是为这个场景做的
需要图形界面管服务器 宝塔/1Panel 镜像或后装 命令行对新手不友好
站点必须跑旧版 PHP 手动 LNMP 镜像版本你不一定能挑
缺冷门 PHP 扩展 手动 LNMP 需要自己编译或配源
有合规要求,组件必须自证来源 手动 LNMP 自装才能控制每一层版本和来源
一台机器要跑多个不同 PHP 版本 手动 LNMP 需要多版本 php-fpm 共存
自己写的 Go/Python 服务 手动或纯净镜像 应用镜像没有现成方案

判断原则一句话:凡是能被现成镜像满足的,都别手动;只有需求里出现版本、扩展、合规字样的,才值得动手。

反过来,如果你只是要一个 WordPress、要可视化操作、要省事——千万别手动,回头去看 2.1。朋友的官网我用的就是镜像,手动装那台是我自己的项目,主战场分得很清楚。

再补充一种容易被误判的情形:只是单纯觉得镜像“不放心”、想自己掌控一切。这种心态我理解,但通常会演变成花两天时间调出一个厂商镜像早就调好的东西,然后每隔几个月还要自己跟一次安全补丁。控制感不等于价值,把精力留给业务内容才是小站该有的优先级。

LNMP 手动安装步骤清单

LNMP 就是 Linux + Nginx + MySQL + PHP。以 Ubuntu 系统为例,整个流程我分成五步,全程在 SSH 里完成。

第一步,更新软件源、装基础组件:

sudo apt update
sudo apt install -y nginx mysql-server php-fpm php-mysql \
  php-xml php-mbstring php-curl php-gd php-imagick

一行命令把 Nginx、MySQL、PHP-FPM 和 WordPress 常用的 PHP 扩展都带上。装完先确认服务在跑:

sudo systemctl status nginx mysql php-fpm

第二步,建数据库和用户。 别用 root 跑网站,单独建一个库、一个用户,权限只给这个库:

sudo mysql <<EOF
CREATE DATABASE wpdb CHARACTER SET utf8mb4;
CREATE USER 'wpuser'@'localhost' IDENTIFIED BY '换成你自己的强密码';
GRANT ALL ON wpdb.* TO 'wpuser'@'localhost';
FLUSH PRIVILEGES;
EOF

第三步,下载并放置 WordPress 程序:

sudo mkdir -p /var/www/wordpress
cd /var/www/wordpress
sudo wget https://wordpress.org/latest.tar.gz
sudo tar -xzvf latest.tar.gz --strip-components=1
sudo chown -R www-data:www-data /var/www/wordpress

最后一条权限命令很关键,否则 WordPress 后台装插件、传图片时会找你要 FTP 凭据——这是手动装最常见的第一个坑。

第四步,配 Nginx 站点。 新建一个站点配置:

server {
    listen 80;
    server_name example.com;
    root /var/www/wordpress;
    index index.php index.html;

    location / {
        try_files $uri $uri/ /index.php?$args;
    }

    location ~ \.php$ {
        include fastcgi_params;
        fastcgi_pass unix:/run/php/php-fpm.sock;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
    }
}

写好后软链到启用目录并重载:

sudo ln -s /etc/nginx/sites-available/wp /etc/nginx/sites-enabled/
sudo nginx -t && sudo systemctl reload nginx

第五步,跑安装向导。 浏览器打开 http://服务器IP/,按页面填刚才建的库名、用户名、密码,数据库主机填 localhost,表前缀默认 wp_。填完等它建表,进后台就算成了。如果安装向导这步报数据库连接失败,回头检查第二步的库名和密码是否一致,以及 php-mysql 扩展有没有装上。

整个过程熟手二十分钟,新手照着清单走一个小时以内也能搞定。把每一步记成清单存好,因为半年后你一定忘记自己装了什么、装在哪个路径。更复杂的面板式管理可以看1Panel 面板运维手记,它比纯手工 LNMP 省心很多。

手动 LNMP 与应用镜像的对比

两种方式我都在用,差异很直观:

维度 应用镜像 手动 LNMP
上手时间 十分钟 半小时到一小时
环境定制 只能用厂商给的版本 任意版本、任意扩展
后续维护 厂商调好,少操心 升级、备份、排错全靠自己
迁移成本 打快照或整体迁移 配置文件可复用,反而灵活
适合谁 第一次建站、标准需求 有明确定制需求的开发者

我的原则是:能用镜像就别手动;一旦决定手动,就把每一步记成清单存好,因为半年后你一定忘记自己装了什么。网站做大之后,服务器加云数据库的组合更稳,那是腾讯云实战手记里的路线。

还有个折中方案:先选 LNMP 应用镜像(环境装好、站点程序自己放),既省掉装 Nginx/MySQL 的基础工作,又保留对 PHP 版本和扩展的部分控制权。比纯系统镜像省事,比 WordPress 镜像灵活,适合要装自己代码又怕折腾的人。不过它毕竟是镜像的预设版本,遇到真正冷门的扩展需求,依然得回到本篇的手动路线。

踩坑与解法

坑一:装完 WordPress 让填 FTP 信息。 就是上面说的目录权限问题,chown 给 www-data 即可。

坑二:文章页 404。 Nginx 少了伪静态那行 try_files,加上再 reload。

坑三:php-fpm 的 sock 路径对不上。 Ubuntu 不同版本的路径是 /run/php/php7.4-fpm.sock 这种带版本号的,别照着旧教程抄。用这条命令看真实路径:

ls /run/php/

坑四:MySQL 装完无法登录。 Ubuntu 下 root 默认走 auth_socket,想用密码登录得先 sudo mysql 进去改认证方式,或者干脆像第二步那样新建普通用户。我自己的做法是永远新建用户、root 只留本地 socket,权限最小化比什么都踏实。

坑五:扩展装完不生效。 改了 php.ini 或装了新扩展,只重启 Nginx 是没用的,得重启 php-fpm;如果机器上有多个 PHP 版本,还要确认重启的是站点实际运行的那个。验证当前版本的命令:

php -v
php -m | grep -i imagemagick

整个安装过程我反复跑过几遍,其中一次印象很深,值得单独记下来当排错样板。

一次排错记录:扩展装不上

那次在 Ubuntu 22.04 上装 php-imagick,apt 报了一串依赖错误,大意是缺 libmagickcore 的某个开发包,而且仓库里版本对不上。第一反应是换源、apt -f install 强修,都没用。冷静下来按三步走:

  1. 先看错误信息里到底缺哪个具体包,而不是笼统地重试。
  2. 到官方软件源搜这个包名,确认需要的版本与系统版本匹配。
  3. 手动补上缺的依赖再重装:
sudo apt install -y libmagickcore-6.q16-6-extra
sudo apt install -y php-imagick

虽然镜像里 php-imagick 通常是预装好的,但手动环境下这类“传递依赖缺失”的问题非常典型。排错的通用套路就一句话:apt 报什么缺就补什么,报哪个版本对不上就查哪个版本,别跳过报错信息去猜。另外留个心眼:每次解决问题后把命令记进清单,半年后你绝对会感谢现在的自己。

坑六:内存不够被 OOM。 1 GB 内存的小机器跑 MySQL + PHP-FPM,默认配置偶尔会被 OOM killer 光顾。把 php-fpm 的子进程数调小(比如 max_children 改成 5),用 dmesg | tail 可以查是不是被杀过进程。

延伸阅读