SSH免密登录与LNMP环境一键部署实战指南
作为一个常年折腾服务器和网站部署的老手我几乎每周都要经历一次从本地开发到线上环境的发布流程。刚入行那会儿每次部署都是输入密码、等上传、再输入密码、再等上传碰上密码复杂点或者网络波动心态直接爆炸。后来我认认真真把“本地免密链路”这件事彻底搞明白了才发现部署这件事完全可以做到一键完成、全程无感。这篇博客就把我的整套实战经验分享出来从免密原理到网站上线一条龙讲透保证你看完就能直接照着操作。1. 部署方案整体设计1.1 先想清楚再动手网站类型决定部署方式很多新手拿到一台服务器第一反应是“赶紧装个宝塔面板”或者“直接把我电脑上的文件拖上去”。这其实是个误区——不同的网站类型部署方式天差地别。如果是纯静态网站HTML/CSS/JS那最简单Nginx配个根目录文件丢上去就能跑。如果是PHP项目需要装PHP-FPM和MySQL再把代码放到站点目录。如果是Java或者Node.js项目则要多一层进程守护和反向代理复杂度直接上升一个档次。我自己最常用的是LNMP架构Linux Nginx MySQL PHP偶尔也部署Node.js应用。对于个人博客、企业官网、API服务这套组合已经能覆盖90%以上的需求。这篇攻略会围绕LNMP展开同时讲清楚免密链路怎么跟部署流程结合让你以后不管是手动更新还是写脚本自动发布都能一气呵成。选型上还有个小建议别一上来就追求花哨的容器化部署。Docker确实好但如果你的目标只是让一个网站快速上线并且后期容易维护传统方式反而更直接。容器化的学习成本是后置的等你对Linux运维有足够手感再上也不迟。1.2 服务器与域名准备的关键点部署的前提是有一台能用的服务器和一个已经解析好的域名。服务器我建议选择主流的云服务商阿里云、腾讯云或者搬瓦工这类海外厂商都行系统用Ubuntu 22.04 LTS 或者 Debian 12这两个版本稳定、资料多、包管理顺手。域名这块需要提前把A记录解析到服务器IP。这一步很多人会忽略等待时间其实一般几分钟内就能生效但有个快速验证方法本地打开终端输入ping 你的域名如果能解析到你的服务器IP就说明DNS已经ok了。服务器安全组规则也是经常踩坑的点。云服务商一般默认只开22端口如果你忘了在控制台放行80和443后面Nginx再配得正确网站也访问不了。我习惯在部署前先把要用的端口一次性放行80HTTP、443HTTPS、22SSH。提示千万不要在安全组里开0.0.0.0/0的3306端口MySQL默认端口。数据库只绑定内网或者仅允许你自己的IP访问这是高危操作我会在第5章问题排查里细讲。2. 本地到服务器的免密链路搭建2.1 SSH免密登录的原理并不复杂免密登录的核心不是“没有密码”而是用密钥替代密码。整个机制可以这样理解你本地有一把“钥匙”私钥服务器上有一把“锁”公钥。当你发起SSH连接时服务器会拿着一把“挑战之锁”问你你能用私钥解开就证明你是钥匙的主人于是直接放行。这套体系基于非对称加密算法常用的有RSA和Ed25519。RSA兼容性最好基本所有老系统都认Ed25519更安全、密钥更短、性能更好但有个别老旧系统不支持。我推荐直接用Ed25519除非你要连接的是那种十年没更新的老服务器。具体流程是本地机器生成一对密钥公钥私钥把公钥内容追加到服务器~/.ssh/authorized_keys文件里服务器在收到连接请求时会检查你有没有对应的私钥这个过程中私钥永远只应该存在于你自己的电脑上绝不要传到网上或者发给任何人。公钥随便公开都没问题。2.2 生成密钥对与公钥部署在实际操作之前先确认本地环境。Windows用户建议直接用自带的PowerShell或者安装Git BashmacOS和Linux用户直接打开终端即可。第一步在本地生成密钥对。命令很简单ssh-keygen -t ed25519 -C your_emailexample.com执行之后系统会问你保存位置和passphrase口令。保存位置直接用默认的~/.ssh/id_ed25519就行passphrase可以留空但如果你对安全性有要求建议设置一个。这个passphrase不是服务器的密码而是保护你私钥文件的本地密码就算私钥泄露了没有passphrase也解不开。第二步查看并复制你的公钥cat ~/.ssh/id_ed25519.pub这会输出一串以ssh-ed25519开头的内容把它完整复制。第三步登录服务器把公钥放到指定位置。有两种办法办法一推荐一条命令搞定ssh-copy-id -i ~/.ssh/id_ed25519.pub root你的服务器IP这个命令会自动创建~/.ssh目录、写入authorized_keys文件并且设置好权限。它会要求你输入一次服务器密码这是整个链路搭建过程中唯一一次手动输密码。办法二手动操作适合ssh-copy-id不可用的场景先登录服务器然后mkdir -p ~/.ssh echo 粘贴你刚复制的公钥内容 ~/.ssh/authorized_keys chmod 700 ~/.ssh chmod 600 ~/.ssh/authorized_keys这里有个关键点authorized_keys 文件权限必须是 600目录权限必须是 700。如果权限过于宽松比如文件是 644部分SSH服务端会直接拒绝读取该文件导致免密不生效。2.3 免密链路的验证与安全加固公钥部署完成后先别急着关掉密码登录。我们需要在本地重新开一个终端测试免密是否生效ssh root你的服务器IP如果直接进入服务器不提示输入密码恭喜免密链路已经打通了。如果还是提示输入密码优先检查权限设置和公钥内容是否有换行或空格错误。链路打通之后我强烈建议做两件事第一修改SSH配置禁用密码登录。编辑服务器的/etc/ssh/sshd_config找到PasswordAuthentication改成no然后重启SSH服务sudo systemctl restart sshd注意这步操作前一定要确认免密登录已经100%稳定否则你会把自己锁在服务器外面。不放心的话可以先保持终端窗口开着测试新连接正常再重启。第二推荐换一个非默认SSH端口。虽然22端口是默认标准但也是暴力破解的重灾区。把Port改成2222或22022这类高位端口能在很大程度上减少扫描攻击。改完同样要重启SSH并且在云服务商安全组里放行新端口。3. Web服务环境配置要点3.1 安装配置Nginx反向代理与静态资源服务免密链路搭好之后就可以在服务器上装 Web 环境了。Nginx 是我最常用的Web服务器它既可以直接托管静态文件也能作为反向代理把请求转发给后端的 PHP-FPM 或 Node 服务。安装Nginx异常简单在Ubuntu/Debian上sudo apt update sudo apt install nginx -y装完之后Nginx会自动启动直接访问服务器IP应该能看到欢迎页。Nginx的配置目录在/etc/nginx/核心配置文件是nginx.conf而站点配置一般在sites-available/和sites-enabled/两个目录下。新手容易搞混这两个目录sites-available存放所有的可用配置sites-enabled里放的是已经启用的配置软链接。这种设计方便在多个站点间切换启用站点只要做个软链接sudo ln -s /etc/nginx/sites-available/myblog /etc/nginx/sites-enabled/myblog一个标准的PHP站点配置长这样server { listen 80; server_name yourdomain.com; root /var/www/myblog; index index.php index.html; location / { try_files $uri $uri/ /index.php?$query_string; } location ~ \.php$ { include snippets/fastcgi-php.conf; fastcgi_pass unix:/run/php/php8.1-fpm.sock; } location ~ /\.ht { deny all; } }配置的关键点在于root目录要存在index要包含index.php而且PHP请求要正确转发给 PHP-FPM 的 socket。如果漏了fastcgi_pass这一行浏览器访问PHP文件时会直接变成“白屏”或者干脆把PHP源码下载下来。写完配置后测试并重载sudo nginx -t sudo systemctl reload nginxnginx -t会检查语法是否正确我的习惯是改完配置先跑一遍确认syntax is ok再 reload避免把线上的服务弄挂。3.2 数据库与PHP-FPM准备LNMP里还有两个角色MySQL或MariaDB和PHP-FPM。MySQL负责数据存储PHP-FPM负责解析PHP代码。安装sudo apt install mysql-server php php-fpm php-mysql -y装完之后MySQL默认只有root用户而且root只能本机登录。我们可以用mysql_secure_installation跑一遍基础安全设置然后创建站点专用的数据库和用户CREATE DATABASE myblog CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; CREATE USER bloguserlocalhost IDENTIFIED BY 强密码; GRANT ALL PRIVILEGES ON myblog.* TO bloguserlocalhost; FLUSH PRIVILEGES;这里有个常见问题PHP版本不同PHP-FPM的socket路径也不一样。上面Nginx配置里写的是php8.1-fpm.sock如果你装的是PHP 8.2就要改成对应的文件名。查看方式ls /run/php/把Nginx配置里的socket路径替换成实际看到的文件名即可。PHP-FPM的调优也是个值得聊的话题。默认配置下pm.max_children是动态管理的我个人经验是1核2G的服务器pm.max_children设置5~10个就足够了设置过大反而会内存爆掉。详细参数在/etc/php/8.x/fpm/pool.d/www.conf。经验数据库和Web服务装在同一台服务器上时MySQL监听地址默认是127.0.0.1这个千万不能改成0.0.0.0。如果改成全网监听数据库就直接暴露在公网上了暴力破解风险极高。4. 完整部署流程实操4.1 首次部署从本地推送到服务器现在环境都准备好了可以开始正式部署网站。以前用FTP/SFTP拖文件的方式效率太低既然我们已经搭好了免密链路完全可以用rsync来做增量同步。rsync 是个非常强大的同步工具它只传输变化的部分第一次全量同步后后续每次更新都极快。用法如下rsync -avz --delete -e ssh ./dist/ root你的服务器IP:/var/www/myblog/这里拆解一下参数-a归档模式保留文件权限和时间戳-v显示同步过程-z传输时压缩--delete删除目标目录中本地已不存在的文件保持完全一致-e ssh指定使用SSH协议传输走的就是我们配置好的免密链路如果你是PHP项目把./dist/换成你本地项目根目录把目标路径换成服务器的站点目录。如果是Java或Node.js项目则要先构建mvn package或npm run build再把构建产物同步过去。rsync 走免密链路有个好处在自动化脚本里执行不需要任何人工干预。我后面写的自动部署脚本底层就是靠它完成的。4.2 写一个自动部署脚本解放双手既然免密链路已经打通rsync也能用了那就干脆写一个部署脚本让发布回归到“一条命令完成”。我习惯在本地项目根目录放一个deploy.sh内容如下#!/bin/bash # 网站部署脚本 - 使用rsync SSH免密 REMOTE_USERroot REMOTE_HOST你的服务器IP REMOTE_DIR/var/www/myblog LOCAL_DIR./ # 前端项目先构建 # npm run build # LOCAL_DIR./dist/ # 同步文件到服务器 rsync -avz --delete -e ssh $LOCAL_DIR ${REMOTE_USER}${REMOTE_HOST}:${REMOTE_DIR} # 远程执行命令修改权限、重启服务 ssh ${REMOTE_USER}${REMOTE_HOST} chown -R www-data:www-data ${REMOTE_DIR} systemctl reload php8.1-fpm echo 部署完成这个脚本做了什么它先用rsync把本地最新的文件同步到服务器再通过SSH远程执行两件事把文件属主改成www-dataNginx/PHP-FPM的运行用户避免权限问题然后重载PHP-FPM让配置生效。执行前记得给它加执行权限chmod x deploy.sh ./deploy.sh整个过程中不需要输入任何密码非常丝滑。这个脚本已经在我自己的多个项目里跑了两三年稳定可靠。你完全可以根据自己的项目类型把构建命令和远程操作改成对应内容。4.3 域名解析与HTTPS证书配置网站文件部署到位后还剩下域名解析和HTTPS证书这两步。域名解析在云服务商的DNS控制台操作添加一条A记录主机记录填www或者记录值填服务器IP即可。解析生效后把Nginx配置里的server_name填上你的域名reload一下就能用域名访问网站了。HTTPS证书这块我的经验是直接用Lets Encrypt配合Certbot工具自动申请和续期sudo apt install certbot python3-certbot-nginx -y sudo certbot --nginx -d yourdomain.com -d www.yourdomain.comCertbot会自动修改Nginx配置把HTTP跳转到HTTPS并且设置一个定时任务自动续期证书。整个过程只需要跟着提示操作基本不会出错。有一点要注意Lets Encrypt证书的有效期是90天如果服务器系统时间不准确续期可能会失败。所以部署时要确保服务器时间同步正确sudo apt install chrony -y # 或者直接用自带的systemd-timesyncd timedatectl set-ntp true5. 常见问题与排查技巧实录5.1 免密登录失效最经典的三种原因免密登录“昨天还好好的今天突然要密码了”这是最常见的故障。根据我的经验原因基本逃不出这三种第一权限不对。服务器上~/.ssh目录是 700authorized_keys文件是 600这是铁律。如果哪次不小心用chmod -R 777改了家目录权限免密就直接失效了。处理方法chmod 700 ~/.ssh chmod 600 ~/.ssh/authorized_keys第二本地用了多个密钥但没有指定。如果你本地有id_rsa、id_ed25519好几对密钥SSH客户端默认只尝试id_rsa或id_ed25519这一个默认文件而你要用的私钥可能命名不同。解决方法是手动指定ssh -i ~/.ssh/你的私钥文件名 root服务器IP或者编辑~/.ssh/config给不同服务器指定不同密钥。第三SELinux强制访问控制捣乱。这个问题主要出现在CentOS/RHEL系统上。服务器虽然允许用户建好文件但SELinux会阻止SSH读取authorized_keys。我没有用CentOS之后就很少遇到了但如果你是CentOS用户碰到免密失效可以试试restorecon -R -v /root/.ssh排查思路整理成速查表排查点检查方法解决方案本地密钥文件是否存在ls -la ~/.ssh/重新生成密钥对公钥是否在服务器上登录服务器查看cat ~/.ssh/authorized_keys重新追加公钥权限是否正常ls -la ~/.ssh重置为700/600SSH配置是否允许公钥认证查看/etc/ssh/sshd_config确认PubkeyAuthentication yes是否是端口问题检查能否免密连接其他服务器确认SSH端口是否正确5.2 部署过程中容易踩的五个坑部署过程中的坑就更多了我把踩过的教训都整理出来省得你再走弯路。坑一前端资源404。这多半是Nginx的root路径配错了或者try_files没有正确匹配路由。排查时先curl -I看响应头再用sudo tail -f /var/log/nginx/error.log实时看错误日志基本能快速定位。坑二PHP文件被下载而不是执行。出现“源码直接显示在浏览器里”这种情况99%是Nginx配置里少了fastcgi_pass语句或者PHP-FPM socket路径不对。检查一下location ~ \.php$这一段配置再确认/run/php/下的socket文件名与配置一致。坑三网站访问巨慢或超时。先排查是不是DNS解析问题然后看Nginx日志里的响应时间。排除了网络因素后很可能是PHP-FPM进程数太少、MySQL慢查询甚至服务器内存不足触发Swap。这时候用htop和mysqladmin status快速判断瓶颈。坑四权限问题导致上传/安装失败。很多PHP程序如WordPress、Typecho在安装插件时要写文件如果目录属主不是www-data就会提示“没有权限创建目录”。解决办法是修改站点目录属主sudo chown -R www-data:www-data /var/www/myblog但要注意如果你以后还要用rsync增量同步rsync同步文件时默认保留属主信息可能把属主又改回root——所以部署脚本里我特意加了一步远程chown就是为了处理这个冲突。坑五HTTPS证书刷不出来。这通常有两条路一是域名解析还没生效Certbot无法验证域名归属二是服务器的80端口在安全组里没放行导致Let‘s Encrypt的挑战请求无法到达。确认这两点后再重跑一次Certbot命令就能解决。5.3 一个真实案例从“免费虚机”到“快速部署”的迁移复盘两三年前我在一个免费虚拟主机上托管了个WordPress博客后来发现限制太多——不能装想要的插件、数据库动不动锁表、流量稍微一高就502。于是我把整站迁到了自己租的云服务器上。迁移流程其实很经典值得展开讲讲先在服务器上装好 LNMP 环境就是我前面讲的流程然后在本地用rsync把WordPress的wp-content/uploads静态资源推送到服务器再导出数据库mysqldump -u 用户名 -p 数据库名 backup.sql把backup.sql上传到服务器后导入mysql -u 用户名 -p 数据库名 backup.sql最后修改wp-config.php里的数据库配置更新域名用WP-CLI或直接改wp_options表。这套流程跑完之后博客访问速度比原来快了几倍而且我再也不需要依赖别人的限制。最关键的是整个迁移过程只花了一个小时左右如果没有免密链路和rsync的加持光上传文件就能折腾半天。6. 不Nginx换Caddy可以吗聊聊另一种部署思路到这里LNMPNginx的整套网站部署流程已经完整讲完了。最后我忍不住想提一个有意思的替代方案Caddy Web服务器。Caddy的一大卖点是“HTTPS自动配置”它不需要单独装Certbot只要在Caddyfile里声明域名它就会自动申请并续期Let’s Encrypt证书反向代理的配置也比Nginx简洁得多。一个Node.js站点的Caddyfile长这样yourdomain.com { reverse_proxy 127.0.0.1:3000 }这比起Nginx那十几行配置确实清爽很多。尤其是懒得折腾证书、又希望默认走HTTPS的场景Caddy能帮你省掉相当多的时间。如果你只是部署个小项目用Caddy反而更合适如果未来要面临复杂的负载均衡、缓存规则、限流策略那还是老老实实Nginx毕竟资料多、模块丰富、生产环境验证充分。我个人的建议是小项目用Caddy快速起步复杂项目还是Nginx更稳。当然免密链路这套东西和Web服务器选型无关不管你怎么选它都能帮你把重复的发布操作压缩成一条命令。踩过那么多坑之后我现在体会最深的一点是部署不是建好环境就万事大吉真正决定体验的是后面日复一日更新迭代的流程顺不顺畅。本地到服务器这条免密链路就像一座桥桥搭得越稳你后面走的每一步都会越轻松。照着这篇攻略把链路通起来在写上一键部署脚本你会明显感觉到——原来上线一个网站可以这么省心。
上一篇/下一篇内容由系统自动关联
返回资讯列表 →