一、先说清楚:为什么我不建议长期用面板
这篇文章不是要论证面板一无是处。宝塔这类面板在国内站长圈覆盖面极广,几百块一年的服务器、半小时装好 LNMP、点几下就能开站,对刚入门的人是实打实的效率工具。但它有一个结构性特点,你必须提前知道:面板自己也是一层会被改的配置。
它改 nginx 的主配置、改 PHP 的 ini、改 crontab、改防火墙、改 MySQL 的 my.cnf,还会写自己的定时任务、自己的日志轮转、自己的进程守护。这些改动不写在你的文档里,也不在你的版本管理里。所以当面板自身出错(升级、磁盘满、面板进程挂了)时,你的站点会跟着一起进入一种「看不懂的状态」——现象是页面 502,但你去 nginx -t 看又没报错,因为配置文件根本没问题,问题在面板没把你的站点配置 include 进去。
我这几年把几个小站从面板迁到了纯命令行,也帮朋友做过几次迁移。总结下来,迁移的难点不在「装环境」,而在于三件事:第一,把面板做过的事一件不漏地挖出来;第二,在不停站的前提下把域名和证书切过去;第三,迁完之后让下一次故障能被自己看懂。下面按这三件事展开。
二、清点:面板到底替你做了哪些事
迁之前先做一次完整清点,五个方向,一个都不能漏。清点不是「看一眼面板首页有什么」,而是要落到文件系统里找证据。
第一个方向是站点配置。宝塔的站点配置在 /www/server/panel/vhost/nginx/,每个站点一个 .conf,主配置 /www/server/nginx/conf/nginx.conf 里有一行 include /www/server/panel/vhost/nginx/*.conf;。你要看的不只是站点本身,还包括:伪静态规则(面板的「伪静态」模板会被写成 include /www/server/panel/vhost/rewrite/域名.conf)、SSL 配置(在 vhost/nginx/ 下同一个文件里)、反向代理配置(在 vhost/nginx/proxy/ 目录)。这三处经常是分散的,只拷主 conf 会丢东西。
第二个方向是定时任务。面板的「计划任务」实际写进的是系统的 crontab,可以先 crontab -l,再看 /var/spool/cron/ 下的文件。面板自己还会加一条每小时的任务用于统计和清理,名字里带 panel 或 BT,这条迁移后不需要带走,但要确认它不会在你删掉面板之后仍然空转报错。
第三个方向是防火墙与安全。宝塔有自己的防火墙(会写 iptables/nftables 规则),还有「SSH 端口修改」功能。如果你曾经在面板里改过 SSH 端口,那这个改动写在 /etc/ssh/sshd_config 里,迁移时必须知道——不然新机器上你会以为自己连不上。
第四个方向是数据库。除了数据本身,还有 my.cnf 里的参数(面板的「性能调整」会改一批 innodb_buffer_pool_size、max_connections),以及 MySQL 的用户与授权(面板会给每个数据库建独立用户,权限只限本库)。迁的时候如果不带上用户与授权,应用会连不上,报 Access denied。
第五个方向是被忽略的目录:/www/wwwlogs(访问日志,里面有历史数据,值得带走)、/www/backup(面板自己的备份,可以最后带走当保险)、/www/server/cron(面板计划任务的脚本本体)、以及 /etc/letsencrypt 或面板自己的证书目录 /www/server/panel/vhost/cert/。
# 清点脚本:一次把关键路径列出来
du -sh /www/server/panel/vhost/nginx /www/server/panel/vhost/rewrite \
/www/server/panel/vhost/cert /www/wwwlogs /www/server/cron 2>/dev/null
ls /www/server/panel/vhost/nginx/ | head
crontab -l | grep -vE '^\s*#' | head -20
grep -rniE 'listen|server_name|root ' /www/server/panel/vhost/nginx/*.conf | head -40这套清点建议做两遍,隔一天再做一次。很多时候第一天你只注意到了主站的配置,第二天想起来还有一个子域名的反向代理、一个用于测试的二级目录——这些在面板里点得很随意,迁的时候最容易漏。
三、目标环境的选型:别装面板重来一遍
如果迁移的目标是「换一台服务器继续用面板」,那本文大部分内容不适用,直接面板的迁移工具更省事。本文假设你要脱离面板,用发行版自带的包管理装 Nginx、PHP、MySQL。
选型上只有两个真实的选择:编译安装还是包管理安装。面板用的是编译安装,文件都在 /www/server/ 下,与系统隔离;包管理安装的文件分散在 /etc/nginx、/etc/php、/usr/share/nginx。后者的好处是升级、安全补丁、日志轮转(/etc/logrotate.d/ 里已经有现成配置)全部由系统负责,你不需要维护一套自己的升级流程;坏处是 PHP 扩展要额外装包,版本可能比编译安装旧一些。
对小站我给的建议一律是包管理安装,并保持「同版本迁移」:先在旧机器上记录版本号 nginx -v、php -v、mysql --version,新机器装同一大版本。跨大版本迁移(比如 PHP 5.6 到 8.1)当然值得做,但要把它当成独立的项目——先迁版本相同的环境把站跑起来,再单独安排升级,两次变更分开做,出问题时才知道是哪一步的锅。
目录结构也要提前定。我一般用:代码在 /srv/www/站点名/,日志在 /var/log/nginx/,证书在 /etc/letsencrypt/live/,站点配置在 /etc/nginx/sites-available/ 加软链到 sites-enabled/。这套结构的好处是「一眼能看出哪个配置是启用的」——软链在 sites-enabled 里就是启用,摘掉就是关闭,比面板里点开关更可追溯,也天然适合放进 Git。
四、迁移顺序:先数据、再代码、最后切流量
顺序错了会给自己制造麻烦。正确的顺序是:新机器先完整起站,用 hosts 绑定测试,测试通过了再动 DNS。整个过程旧站不停。
第一步,装环境并确认版本。装完之后不要急着拷配置,先确认服务能起来、php -m 里的扩展清单和旧机一致。扩展不一致是迁完之后最常见的问题,表现是某个页面白屏或者 fatal error,日志里写「Call to undefined function」。
第二步,拷代码。这里要注意权限与属主。面板上的站点属主通常是 www:www,而包管理安装的 Debian/Ubuntu 上是 www-data:www-data。拷完之后用 rsync -a 保留权限,再统一 chown -R www-data:www-data,最后确认几个必须可写的目录(上传目录、缓存目录、session 目录)权限是 755 或 775,文件是 644。不要在迁移时顺手设成 777——那是把「排查权限问题」的时间省了,换成了「被人写进 webshell」的风险。
rsync -av --exclude='.git' --exclude='node_modules' \
/www/wwwroot/site/ user@newhost:/srv/www/site/
ssh newhost 'chown -R www-data:www-data /srv/www/site && find /srv/www/site -type d -exec chmod 755 {} +'第三步,导数据库。用 mysqldump --single-transaction --default-character-set=utf8mb4 导出,在新机器上建好同名库与专用用户,导入后用 SELECT COUNT(*) 对几张核心表核对行数。字符集这件事在迁移里是最容易被忽略的,导出与导入两侧的字符集必须都显式指定,否则中文和 emoji 会在这一跳丢一次,而且丢得悄无声息。
第四步,写配置。把清点出来的三处(站点、伪静态、反向代理)合到一个文件里,写成包管理风格:/etc/nginx/sites-available/site.conf,软链进 sites-enabled。原来面板里 include rewrite/xxx.conf 的伪静态规则,直接展开成 location / 块里的 try_files 或 rewrite。这一步是迁移里最需要细心的地方——面板的伪静态模板往往带了面板专用的路径(/www/wwwroot/...),不展开就会 404。
第五步,hosts 绑定测试。在本机 /etc/hosts 里把域名指向新机器 IP,用浏览器和 curl 走一遍核心页面,登录、发帖、上传、后台各来一次。这时旧站还在对外服务,你测的是新机器,出问题完全不影响线上。
五、切流量:证书、DNS 与回滚窗口
测试通过之后才切流量,切换本身要控制在「可以快速回滚」的范围内。顺序是证书 → DNS → 观察。
证书先申请,用 certbot 的 webroot 模式,把验证文件写到站点的 .well-known/acme-challenge/ 目录,这样不需要停 Nginx。申请失败最常见的原因是 location ~ \.well-known 被其他规则抢先匹配(尤其是把所有请求都 rewrite 到 index.php 的伪静态规则),解决办法是在配置里把 location ^~ /.well-known/acme-challenge/ 放到最前面,用 ^~ 前缀匹配压过正则。
DNS 切换用 TTL 控制。切换前一天把域名 TTL 从 600 改成 300 甚至 60,等原 TTL 时长过去之后再切 A 记录,这样传播最快。切完之后用 dig @8.8.8.8 www.example.com +short 和本地解析各查一次,确认新 IP 已经生效。这里有个必须接受的事实:DNS 传播期间会有一部分用户仍然访问旧机器,所以旧机器上的数据不能立刻删——至少要保留 24 到 48 小时,并且在这段时间里,如果新站有用户上传或发帖,旧机器上的数据不会自动同步过去,迁完需要手工合并。对交互多的站点,务实的做法是切换期间把旧站设置成只读,或者干脆先关掉写入口。
回滚方案在心里必须清楚:把 DNS 改回旧 IP 是最快的路,5 分钟 TTL 的话十分钟内就能回;如果新站只是某个功能坏了,也可以先在新机器上 nginx -t 检查、看 /var/log/nginx/error.log,能当场修的当场修,不能修的才回滚。回滚不是失败,硬扛着修才是。
六、迁完之后:把「面板替你做的事」补上
站跑起来了,活儿只干了一半。面板替你做的那些日常事务,现在要自己接管,最少有四件。
日志轮转。检查 /etc/logrotate.d/nginx 是否存在,默认配置通常是每天切割、保留 14 天、切割后发 USR1 信号让 Nginx 重开日志文件。这个配置对大多数站够用,但要确认 postrotate 里的 reload 命令指向对的服务名。可以在迁完后手动跑一次 logrotate -d /etc/logrotate.d/nginx 看干跑结果,确认没报错。
备份。面板的备份是把整站打包,你可以换成更轻的方案:数据库每天 mysqldump、代码用 Git、上传目录用 rsync 增量。关键是「备份要能被验证」——每周抽一次把最新的库导到本地跑一遍 mysql < backup.sql,能导进去才算备份有效,文件存在不算。
监控与告警。面板首页的 CPU/内存曲线没有了,你需要替代品。最轻的做法是一个每五分钟跑的脚本,检查 systemctl is-active nginx php8.1-fpm mysql、磁盘使用率、以及首页的 HTTP 状态码,任一异常就发一封邮件或一条 webhook。不要等到用户反馈才知道站点挂了。
安全更新。包管理安装的好处就是这一条能用:apt list --upgradable 每周看一次,安全更新及时打。这是编译安装做不到的便利,也是我认为脱离面板后最值得享受的一点。同时把 SSH 改成密钥登录、关掉密码认证、防火墙只开 22(或自定义端口)、80、443,这三件事做完,安全性就比面板默认状态好很多。
最后说一个迁移中容易被忽略的收尾动作:清理旧机器之前,把清点阶段收集的东西归档——配置文件的最终版、迁移当天的库导出、/www/wwwlogs 里的历史访问日志。这些东西在半年后排查「某段时间数据为什么不对」时会成为唯一的证据。归档之后,旧机器再多留一周,确认没有任何流量、没有任何定时任务还在跑,再关机。
整个迁移如果规划得当,一个中等规模的内容站可以用一个下午完成核心部分,再用两天做观察与收尾。真正花时间的不是敲命令,是第二节那次清点——面板做过什么,你必须比它自己更清楚,否则迁完之后总会有某个角落的表现和原来不一样。