CentOS 7手动部署LNMP与WordPress:从零搭建可控的Web站点
1. 为什么这套组合至今仍是自建站点的稳妥选择把时间拨回到几年前如果你问一个刚接触独立建站的人该用什么方案十有八九会听到“LNMP”这个词。Linux Nginx MySQL PHP这四个字母的组合几乎撑起了早期互联网上相当一部分动态站点的运行底座。而WordPress作为内容管理系统的代表恰好把这四者串联成了一个完整的故事。今天各种容器化、面板化工具层出不穷但我依然认为手动在CentOS 7上把MySQL、Nginx、PHP 7和WordPress一步步跑通是理解Web服务运行逻辑最扎实的一条路径。这套方案的核心价值在于“可控”。用面板工具确实快点几下鼠标站点就起来了但一旦遇到性能瓶颈、权限报错、版本冲突你往往不知道从哪里下手。而手动部署的过程本质上是在亲手搭建一条从用户请求到页面渲染的完整链路Nginx负责接客和分发PHP负责动态处理MySQL负责数据存取WordPress则是在这套地基上盖起来的房子。你清楚每一块砖的位置出了问题就能顺着链路往回找。这篇文章适合谁看如果你手里有一台CentOS 7的服务器想从零搭一个属于自己的WordPress站点并且希望搞明白每一步背后的道理那这篇内容就是为你准备的。我会把安装顺序、参数配置、权限设置、常见报错都拆开讲清楚尽量做到你照着做就能跑通同时知道为什么要这么做。需要说明的是文中涉及的具体版本号和路径是基于常见实践给出的参考实际部署时你可以根据官方源的最新情况做微调。在正式开始之前先明确一个部署顺序上的原则先装数据库再装PHP然后装Nginx最后部署WordPress。这个顺序不是随便定的。MySQL作为数据层独立于Web服务先装好可以避免后续PHP扩展编译时找不到数据库头文件PHP作为中间层需要和Nginx通过FastCGI通信先装PHP再配置Nginx能让Nginx的配置文件一次写对WordPress作为应用层依赖前面三者全部就绪放最后最合理。很多人一上来就急着下载WordPress结果环境没配好页面报个数据库连接错误反而不知道从哪查起。2. 系统准备与软件源的那些门道2.1 最小化安装之后必须先做的几件事拿到一台刚装好CentOS 7的服务器不管是云主机还是本地虚拟机第一件事不是急着装软件而是把系统的基础状态调整好。最小化安装的系统往往缺少很多常用工具网络配置也可能不是最优状态。我习惯先做这么几件事更新系统、安装常用工具、配置时区、检查防火墙状态。更新系统用yum update -y这一步会拉取所有已安装软件包的最新版本。有人觉得这一步浪费时间但实测下来很多莫名其妙的依赖冲突都是因为系统包太旧导致的。更新完之后装几个必备工具yum install -y wget vim net-tools lsof。wget用来下载软件包vim用来编辑配置文件net-tools提供netstat命令方便查端口lsof能看文件被哪个进程占用这几个在后面排查问题时都会用到。时区设置经常被忽略但WordPress的发布时间、日志时间都依赖系统时区。用timedatectl set-timezone Asia/Shanghai把时区设成东八区然后timedatectl确认一下。防火墙方面CentOS 7默认用的是firewalld我建议先把它关掉或者放行必要端口。如果是内网测试环境直接systemctl stop firewalld systemctl disable firewalld最省事如果是公网环境那就老老实实放行80和443端口别图省事把防火墙全关了。提示关闭防火墙和SELinux只建议在测试环境或内网环境操作。公网服务器请务必保留防火墙并通过firewall-cmd按需放行端口。SELinux是CentOS上另一个容易让人抓狂的东西。它经常导致Nginx读不了文件、PHP写不了目录。我一般会把它设成permissive模式也就是只记录不拦截这样既保留了审计日志又不会影响部署。命令是setenforce 0临时生效要永久生效就编辑/etc/selinux/config把SELINUXenforcing改成SELINUXpermissive重启后生效。2.2 软件源的选择为什么我不推荐直接用默认源CentOS 7自带的yum源里PHP版本停留在5.4MySQL的替代品MariaDB版本也比较旧。WordPress现在对PHP版本的要求越来越高5.4早就不能满足需求了。所以我们需要引入额外的软件源来获取PHP 7和较新的MySQL版本。PHP 7我推荐用Remi源这是社区里维护得比较活跃的第三方源版本更新及时扩展也齐全。MySQL官方源则提供了MySQL 5.7和8.0的社区版。这里有个细节要注意CentOS 7的默认源里其实有MariaDB而MariaDB和MySQL在安装时会有包冲突。所以正确的做法是先用yum remove把系统里可能存在的MariaDB相关包清理干净再安装MySQL官方源。具体操作上先安装EPEL源作为基础因为Remi源依赖它yum install -y epel-release yum install -y https://rpms.remirepo.net/enterprise/remi-release-7.rpm yum install -y yum-utils然后启用Remi的PHP 7.4仓库这是目前兼容性和稳定性都比较好的版本yum-config-manager --enable remi-php74MySQL这边去官方下载社区版的repo包安装yum install -y https://dev.mysql.com/get/mysql80-community-release-el7-3.noarch.rpm装完之后可以用yum repolist看看源是否都生效了。这里有个经验如果你发现yum install的时候提示某个包在多个源里都有可以用--enablerepo参数明确指定从哪个源装避免装错版本。2.3 安装顺序背后的依赖逻辑前面说了先数据库、再PHP、后Nginx的顺序这里展开讲讲为什么。MySQL安装的时候会创建mysql用户和用户组并且初始化数据目录。如果你先装了PHPPHP的mysqlnd扩展在编译时可能会去探测系统里的MySQL客户端库如果这时候MySQL还没装探测就会失败导致PHP连数据库的扩展缺失。虽然可以后面补装但多一道手续。Nginx放后面装是因为Nginx的配置文件里要指定PHP-FPM的监听地址。如果PHP还没装你写Nginx配置的时候就得凭记忆写装完PHP再回来改容易漏。先把PHP装好确认php-fpm服务能起来、监听在哪个端口或socket上再去写Nginx的配置一次就能写对。WordPress放最后是因为它需要前面三个都正常。你可以先用一个简单的phpinfo.php文件测试PHP和Nginx是否连通再用一个测试脚本验证PHP能不能连上MySQL全部通过之后再部署WordPress这样出问题的时候排查范围就小很多。3. MySQL的安装与安全初始化3.1 安装MySQL 8.0并启动服务源配置好之后安装MySQL就一条命令yum install -y mysql-community-server这个包会同时装上mysql-community-client、mysql-community-common等依赖。安装完成后启动服务并设置开机自启systemctl start mysqld systemctl enable mysqldMySQL 8.0和5.7有一个很大的不同首次启动时会自动生成一个临时root密码并且密码策略默认要求比较高。临时密码可以用这条命令找出来grep temporary password /var/log/mysqld.log拿到临时密码后用mysql -uroot -p登录然后立刻修改密码。这里要注意MySQL 8.0默认的密码验证插件是caching_sha2_password而PHP 7.4的mysqlnd扩展是支持这个插件的所以不用像以前那样改成mysql_native_password。但如果你用的是更老的PHP版本可能就需要改插件了。修改密码的命令是ALTER USER rootlocalhost IDENTIFIED BY 你的强密码;密码里最好包含大小写字母、数字和特殊符号长度12位以上。这不是形式主义公网服务器上弱密码被暴力破解是分分钟的事。3.2 安全初始化脚本到底改了什么MySQL自带一个mysql_secure_installation脚本很多人跑一遍就完事但不知道它具体做了什么。我建议至少跑一遍并且理解每一步的含义。这个脚本会依次问你几个问题是否设置密码验证插件、是否修改root密码、是否删除匿名用户、是否禁止root远程登录、是否删除test数据库、是否重新加载权限表。删除匿名用户这一条特别重要。MySQL安装后默认会创建一个空用户名的匿名账户允许任何人从本地登录。虽然它只能访问test库但留着就是个隐患。禁止root远程登录也是同理root只允许从本机登录需要远程管理的话应该另建一个权限受限的账户。test数据库默认任何用户都能访问删掉它避免误用。最后重新加载权限表让所有修改立即生效。这几步做完MySQL的基础安全水位就上来了。3.3 为WordPress单独建库建用户的必要性直接用root账户跑WordPress是很多教程里的做法但这其实是个坏习惯。WordPress的代码是公开的万一某个插件有漏洞导致数据库配置泄露root权限就等于把整个数据库服务器交出去了。正确的做法是创建一个只对WordPress库有权限的专用账户。操作如下CREATE DATABASE wordpress DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; CREATE USER wpuserlocalhost IDENTIFIED BY 另一个强密码; GRANT ALL PRIVILEGES ON wordpress.* TO wpuserlocalhost; FLUSH PRIVILEGES;字符集选utf8mb4而不是utf8是因为utf8mb4才是真正的完整UTF-8实现能存储emoji等四字节字符。WordPress从4.2版本开始就推荐用utf8mb4了。排序规则用utf8mb4_unicode_ci它对多语言的支持更好。这里有个细节wpuser的主机部分写的是localhost意味着这个账户只能从本机连接。如果WordPress和MySQL不在同一台机器上才需要改成具体的IP或%。同机部署的情况下localhost是最安全的。4. PHP 7.4的安装与FPM配置调优4.1 装哪些扩展为什么一个都不能少PHP本体装起来简单yum install -y php php-fpm就完事了。但WordPress运行需要一系列扩展缺一个就可能出现白屏、上传失败、图片处理异常等问题。我列一个必备清单扩展名作用缺失后的典型症状php-mysqlnd连接MySQL数据库安装时提示无法连接数据库php-gd图像处理上传图片无法生成缩略图php-mbstring多字节字符串处理中文乱码、截断异常php-xmlXML解析部分主题和插件报错php-jsonJSON数据处理REST API不可用php-zip压缩包处理无法在线安装插件主题php-curlHTTP请求无法调用外部接口php-opcache字节码缓存性能下降但功能正常安装命令可以一次性写全yum install -y php php-fpm php-mysqlnd php-gd php-mbstring php-xml php-json php-zip php-curl php-opcache装完之后用php -m看看扩展列表确认上面这些都在。如果某个扩展没出现检查是不是源没启用对或者包名拼错了。4.2 php-fpm的进程管理参数怎么调php-fpm的配置文件在/etc/php-fpm.d/www.conf里面有几个参数直接决定了站点的并发能力。默认配置是给低配机器用的稍微有点流量就会不够。pm参数决定进程管理模式有三个可选值static、dynamic、ondemand。static是固定数量进程适合内存充足且流量稳定的场景dynamic是动态增减适合大多数场景ondemand是来了请求才创建进程省内存但响应慢。我一般用dynamic。pm.max_children是最关键的参数它决定同时能处理多少个PHP请求。这个值不能拍脑袋定要根据服务器内存算。每个PHP进程大概占用30到50MB内存假设服务器有2GB内存留给PHP 1GB那max_children设20到30比较稳妥。设太大了内存不够会触发OOM设太小了并发请求排队。pm.start_servers、pm.min_spare_servers、pm.max_spare_servers这三个是dynamic模式下的配套参数分别控制启动时的进程数、最少空闲进程数、最多空闲进程数。一般设成max_children的25%、25%、75%左右。还有一个pm.max_requests表示每个进程处理多少个请求后自动重启。设成500左右可以避免PHP进程长时间运行导致的内存泄漏累积。这个参数对稳定性帮助很大建议不要设成0不限制。4.3 让php-fpm和Nginx顺畅通信的两种方式php-fpm和Nginx之间通过FastCGI协议通信有两种连接方式TCP端口和Unix socket。默认配置用的是TCP监听在127.0.0.1:9000。Unix socket则是用一个文件来通信比如/run/php-fpm/www.sock。两种方式各有优劣。TCP方式配置简单跨机器也能用但走网络协议栈有一点点开销。Unix socket只在本机通信性能略好而且可以通过文件权限控制访问安全性更高。同机部署的情况下我推荐用Unix socket。改法是在www.conf里把listen 127.0.0.1:9000改成listen /run/php-fpm/www.sock然后确保listen.owner、listen.group、listen.mode设置正确。通常设成nginx用户和组权限660。改完重启php-fpm用ls -l /run/php-fpm/www.sock确认文件生成了属主属组对了再去配Nginx。注意如果SELinux处于enforcing模式Unix socket的创建和访问可能会被拦截。这也是我前面建议把SELinux设成permissive的原因之一。5. Nginx的配置与WordPress伪静态规则5.1 安装Nginx并理解它的配置文件结构Nginx的安装同样简单yum install -y nginx systemctl start nginx systemctl enable nginx装完之后访问服务器IP应该能看到Nginx的默认欢迎页。如果看不到先检查防火墙和SELinux再检查Nginx是否真的在监听80端口。Nginx的配置文件结构是分层的。主配置文件/etc/nginx/nginx.conf里通过include指令引入了/etc/nginx/conf.d/*.conf和/etc/nginx/default.d/*.conf。我习惯在conf.d目录下为每个站点建一个独立的配置文件比如wordpress.conf这样管理起来清晰不会把主配置文件改得乱七八糟。主配置文件里有一个server块是默认的监听80端口根目录在/usr/share/nginx/html。我们要做的是新建一个server块把根目录指向WordPress的安装目录并且配置PHP处理规则。5.2 server块里每一行配置的用意一个能跑WordPress的server块大概长这样server { listen 80; server_name your-domain.com www.your-domain.com; root /var/www/wordpress; index index.php index.html; location / { try_files $uri $uri/ /index.php?$args; } location ~ \.php$ { fastcgi_pass unix:/run/php-fpm/www.sock; fastcgi_index index.php; fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; include fastcgi_params; } location ~ /\.ht { deny all; } }逐行解释一下。listen 80是监听端口如果配了SSL证书就再加一个listen 443 ssl。server_name写域名没有域名就写IP或者下划线_表示匹配所有。root是站点根目录后面部署WordPress的时候要把文件放这里。index指定默认首页文件。location /里的try_files是WordPress伪静态的关键。它的逻辑是先找有没有对应的真实文件没有就找目录还没有就把请求交给index.php处理。这样/2024/01/hello-world/这样的固定链接才能正确路由到WordPress的入口文件而不是返回404。location ~ \.php$匹配所有以.php结尾的请求交给php-fpm处理。fastcgi_pass指定通信地址这里用的是Unix socket和前面php-fpm的配置对应。SCRIPT_FILENAME告诉php-fpm要执行哪个文件$document_root就是上面的root路径。location ~ /\.ht是禁止访问.htaccess等隐藏文件。虽然Nginx不读.htaccess但WordPress目录里可能有这类文件禁止访问能避免配置信息泄露。5.3 伪静态规则配错会导致什么现象伪静态规则是WordPress部署里最容易出问题的地方。如果try_files没配或者配错了典型现象是首页能打开但点进任何文章、页面都返回404。这是因为WordPress的固定链接依赖URL重写Nginx不认识这些路径就当成不存在的文件处理了。还有一种情况是后台能进前台文章打不开。这通常是因为try_files里漏了?$args导致查询参数丢失。WordPress有些功能依赖URL里的查询字符串丢了就会异常。排查的时候可以先在WordPress后台把固定链接改成“朴素”模式也就是带?p123这种如果朴素模式能访问说明PHP和数据库都没问题问题就出在伪静态规则上。改回“文章名”模式然后检查Nginx配置里的try_files那一行。另外改完Nginx配置一定要用nginx -t测试语法通过了再systemctl reload nginx。直接restart会中断现有连接reload则是平滑加载。如果nginx -t报错根据提示的行号去改别硬重启否则Nginx起不来整个站点都挂了。6. WordPress部署与权限设置的坑6.1 下载解压与目录归属WordPress的下载很简单cd /var/www wget https://wordpress.org/latest.tar.gz tar -xzf latest.tar.gz mv wordpress /var/www/wordpress解压出来的目录默认属主是root。Nginx和php-fpm运行的用户通常是nginx或者apache如果目录属主是root且权限是755php-fpm就没法往wp-content/uploads里写文件上传图片会失败。正确的做法是把整个WordPress目录的属主改成php-fpm的运行用户。先确认php-fpm跑在哪个用户下看www.conf里的user和group配置通常是nginx或者apache。然后chown -R nginx:nginx /var/www/wordpress find /var/www/wordpress -type d -exec chmod 755 {} \; find /var/www/wordpress -type f -exec chmod 644 {} \;目录755、文件644是Web服务的标准权限。目录需要执行权限才能进入文件不需要执行权限。这样设置既保证了php-fpm能读写又不会给其他用户过多权限。6.2 wp-config.php里那几个关键常量WordPress的配置文件需要手动创建。把wp-config-sample.php复制成wp-config.php然后填入数据库信息define(DB_NAME, wordpress); define(DB_USER, wpuser); define(DB_PASSWORD, 你设置的密码); define(DB_HOST, localhost); define(DB_CHARSET, utf8mb4); define(DB_COLLATE, );DB_HOST写localhost的时候PHP会优先用Unix socket连接MySQL比TCP快一点。如果写127.0.0.1就是走TCP。两者都可以但要注意MySQL用户的主机部分要和这里对应。还有一组安全密钥需要填去WordPress的密钥生成页面拿或者用curl命令生成curl -s https://api.wordpress.org/secret-key/1.1/salt/把输出的八行define语句粘贴到配置文件里。这组密钥用于加密登录cookie每个站点都应该不一样泄露了会导致登录状态被伪造。$table_prefix默认是wp_可以改成别的但没必要改了对安全性的提升微乎其微反而增加记忆负担。6.3 上传目录权限与SELinux的纠缠即使目录属主和权限都设对了如果SELinux是enforcing模式php-fpm往wp-content/uploads写文件还是会被拦截。日志里会看到avc: denied的记录。这就是为什么我前面建议把SELinux设成permissive。如果你坚持要用enforcing模式那就需要给WordPress目录打上正确的SELinux上下文标签chcon -R -t httpd_sys_rw_content_t /var/www/wordpress/wp-contenthttpd_sys_rw_content_t表示允许Web服务读写这个目录。但要注意chcon是临时的系统重启或者文件重新标记后会失效。永久生效需要用semanage fcontext配合restorecon操作起来更复杂。对于大多数个人站点permissive模式已经足够安全没必要在这上面耗太多时间。7. 部署完成后的验证与常见故障排查7.1 从浏览器到数据库的完整验证链路部署完成后不要急着庆祝按顺序验证一遍。第一步浏览器访问服务器IP或域名应该看到WordPress的安装向导页面。如果看到的是Nginx默认页说明server_name或者root配错了。如果看到502 Bad Gateway说明Nginx连不上php-fpm检查socket路径和权限。第二步填写站点信息创建管理员账户。如果这一步报“无法连接数据库”检查wp-config.php里的数据库信息以及MySQL用户的主机限制。可以在服务器上用mysql -uwpuser -p wordpress测试一下能不能登录。第三步登录后台发一篇文章上传一张图片。文章能发布说明数据库写入正常图片能上传说明目录权限正常。这三步都过了基本部署就成功了。7.2 502、404、403三类报错的快速定位502 Bad Gateway是最常见的。原因通常是php-fpm没启动、socket路径不对、或者socket权限不对。排查顺序systemctl status php-fpm看服务状态ls -l /run/php-fpm/www.sock看socket是否存在且权限正确tail -f /var/log/nginx/error.log看具体报错。404 Not Found分两种。一种是访问任何页面都404那可能是root路径配错了Nginx找不到文件。另一种是首页正常但文章404那就是伪静态规则的问题检查try_files。403 Forbidden通常是权限问题。Nginx没有权限读取站点目录或者目录下没有index文件且不允许列目录。检查目录权限和SELinux状态。7.3 日志文件才是排查问题的第一现场很多人遇到问题喜欢在网上搜但最快的办法其实是看日志。Nginx的错误日志在/var/log/nginx/error.logphp-fpm的日志在/var/log/php-fpm/www-error.logMySQL的日志在/var/log/mysqld.log。这三个日志覆盖了从请求接入到数据存取的全链路。看日志有个技巧用tail -f实时跟踪然后在浏览器里复现问题日志里立刻就会打出对应的错误信息。比如502的时候Nginx日志会写connect() to unix:/run/php-fpm/www.sock failed这就直接指向了socket问题不用瞎猜。WordPress自己的调试日志也很有用。在wp-config.php里把WP_DEBUG设成trueWP_DEBUG_LOG也设成true错误会写到wp-content/debug.log。插件冲突、主题报错这类问题看这个日志最直接。不过调试完记得关掉否则日志文件会越来越大而且错误信息暴露在前台有安全风险。8. 上线前值得再花十分钟做的几件事站点能跑起来只是第一步上线前还有几项配置能显著提升安全性和稳定性。第一件是配置HTTPS。现在有免费证书可以用申请下来后在Nginx里加一个443的server块把证书路径填进去然后把80端口的请求301重定向到443。WordPress后台的站点地址也要从http改成https否则会出混合内容警告。第二件是限制WordPress后台的访问来源。如果只是自己用可以在Nginx里给/wp-admin和/wp-login.php加一个IP白名单只允许自己的IP访问。这样即使密码泄露攻击者也进不了后台。配置方式是在对应的location块里加allow 你的IP; deny all;。第三件是设置数据库自动备份。WordPress的内容全在数据库里丢了就全没了。可以用mysqldump配合crontab每天凌晨备份一次备份文件存到另一个目录或者远程存储。命令大概是mysqldump -uwpuser -p密码 wordpress /backup/wordpress-$(date %F).sql然后写个脚本清理七天前的旧备份。第四件是调整PHP和Nginx的上传大小限制。默认的PHP上传限制是2MB现在随便一张图片都可能超过。改php.ini里的upload_max_filesize和post_max_size比如都设成64MB然后Nginx的client_max_body_size也要相应调整否则大文件会被Nginx直接拦掉。这几件事做完一个基本可用的WordPress站点就算正式上线了。整个过程走下来你会发现最耗时间的往往不是安装本身而是各种权限、路径、配置的细节调试。但正是这些细节构成了你对Web服务运行机制的真实理解。下次再遇到类似的环境搭建你就能凭经验快速定位问题而不是从头开始搜教程。