↓ 跳过正文
  1. 系列/
  2. VPS 实战系列/

任务 12:VPS内核优化、Tailscale组网与前端公网穿透

其雁过无痕
作者
其雁过无痕
目录

写在前面: 在服务器“毛坯房”装修进入深水区后,单纯的防火墙配置已经无法满足复杂网络环境下的安全与效率需求。本文记录了如何完成服务器环境的高阶配置,主要包括内核网络优化(BBR)与组建虚拟局域网 。核心目标是开启 Linux 内核自带的 BBR 拥塞控制算法以提升网络吞吐量,并使用 Tailscale 将本地电脑与云服务器组建成一个加密的局域网 。

阶段一:VPS 内核网络优化 (BBR 拥塞控制算法)
#

目的: 榨干服务器的网络吞吐量,降低高延迟环境下的丢包率。

操作过程:

  1. 检查默认状态:通过 sysctl net.ipv4.tcp_congestion_control 命令,发现系统默认使用的拥塞控制算法是相对保守的 cubic。

  2. 修改系统内核变量:在终端执行以下命令,将队列调度算法改为 fq,并将拥塞控制算法改为 BBR:

echo "net.core.default_qdisc=fq" | sudo tee -a /etc/sysctl.conf
echo "net.ipv4.tcp_congestion_control=bbr" | sudo tee -a /etc/sysctl.conf
  1. 应用并验证:使用 sudo sysctl -p 使配置生效,随后通过 lsmod | grep bbr 确认内核模块已成功加载。

小结:这是提升服务器网络物理性能的基础操作,完成后肉眼可见 SSH 连接的响应变得更加顺滑。

阶段二:组建零信任虚拟局域网 (Tailscale 双端部署)
#

背景痛点:

由于在本地开发时,常处于移动蜂窝网络(手机热点)或校园网的复杂 NAT 结构之后,没有固定的公网 IP。每次连接服务器或测试内网服务,都要添加新的防火墙白名单(当然,没有防火墙就没有这个困扰,但 Tailscale 的功能远不止这些)。引入基于 WireGuard 协议的 Tailscale 可以彻底解决动态 IP 带来的困扰。

操作过程:

  1. VPS 端安装与授权:

    • 在服务器终端通过一键脚本完成安装:curl -fsSL https://tailscale.com/install.sh | sh。
    • 执行 sudo tailscale up 启动服务。终端返回了一串包含授权参数的 URL。
    • 将 URL 复制到本地浏览器,使用个人谷歌账号完成登录授权,将服务器拉入专属的 Tailnet(虚拟局域网)。
  2. 本地 Windows 端部署:

    • 在本地电脑下载 Windows 版 Tailscale 客户端。
    • 关键步骤:使用与 VPS 端完全一致的谷歌账号登录。
    • 登录成功后,系统托盘出现 Tailscale 图标,本地电脑获得了一个 100.x.x.x 开头的内网 IP。

测试连通性:在本地 cmd 中直接 ping 服务器分配的 100 开头的 IP,收到稳定延迟的回复。至此,本地电脑与远端 VPS 已无视物理网络限制,宛如插在同一台路由器上。

阶段三:端口防护
#

注意:Docker 默认会绕过系统防火墙。

当在 Linux 系统里配置好 UFW 防火墙,并设置了“拒绝所有公网访问 3306 端口”,然后用 Docker 跑起了一个 MySQL 容器。这时,Docker 为了实现容器通信,会直接去修改更底层的 iptables 规则。而它的优先级高于常规的 UFW,等于在防火墙上开了一扇面向全公网的窗。

不出几个小时,全网的嗅探脚本就能顺着你的公网 IP 摸进来,开始疯狂爆破你的数据库密码。(AI说的,听起来挺可怕的,但其实在做这个防护前我已经拥有这个VPS一段时间了,我也不知道会出现什么问题)

解法:利用 Tailscale 进行网卡级绑定

既然防不住 Docker 开窗,那就从源头上掐断它连接公网的可能。当组建好 VLAN 后,再也不需要把端口映射到 0.0.0.0(允许所有网络接口访问),而是绑定在 Tailscale 分配的内网 IP 上。

实操演示(以 docker-compose.yml 为例):

修改前:

services:
  mysql:
    image: mysql:8.0
    ports:
      - "3306:3306" # 默认等同于 0.0.0.0:3306,全网皆可扫描

修改后:

services:
  mysql:
    image: mysql:8.0
    ports:
      - "100.x.x.x:3306:3306" # 替换为自己 VPS 的 Tailscale IP

效果: 修改重启后(记得重启啊),这个 MySQL 容器在互联网上彻底“蒸发”了。公网的扫描器连这个端口的存在都探测不到。只有处于同一个 Tailscale 虚拟局域网(VLAN)中并获得授权的设备,才能通过 100.x.x.x:3306 畅通无阻地直连数据库。

这不仅解决了动态 IP 下无法设置固定防火墙白名单的痛点,更是现代微服务架构中零信任网络(Zero Trust)的实践。无论是跑数据库、Redis 还是内部调用的后端 API,统统用这个方法藏进内网,再也无需为弱密码爆破而焦虑。

阶段四:进阶探索 —— Funnel 公网穿透本地 Vue 项目
#

在搭建好虚拟局域网后,尝试利用 Tailscale 的 Funnel(漏斗)功能,将本地正在开发的 Vue 项目临时暴露到公网,以便在不走 CI/CD 部署流程的情况下直接向他人展示 UI 效果。

1. 穿透操作与表象成功

  • 启动本地前端服务:在 VS Code 终端运行 npm run dev,Vite 服务正常启动在 http://127.0.0.1:5173/。
  • 开启公网穿透:在管理员模式的 cmd 中执行 tailscale funnel 5173。
  • 终端提示 Success,并分配了一个带有 HTTPS 的专属公网域名(如:https://idyq.tailxxxx.ts.net/)。 (首次使用可能需要二次登录确认)

2. 遭遇报错(“Blocked request”)

满怀期待地用浏览器打开该公网链接,却遭遇了直接拦截,页面抛出错误:

Blocked request.

This host (“idyq.tailxxxx.ts.net”) is not allowed.

To allow this host, add “idyq.tailxxxx.ts.net” to server.allowedHosts in vite.config.js.

3. 排查与底层原理

网络隧道已打通却被拦截,问题在于 Vite 框架的 DNS 重新绑定防御机制。

出于安全考虑,Vite 开发服务器默认只信任请求头(Host)中来源为 localhost 或 127.0.0.1 的请求。当我们通过 Tailscale 分配的 ts.net 域名访问时,Host 字段不符,Vite 直接将其判定为潜在的安全风险并切断了响应。

4. 解决办法

根据报错提示,在 Vue 项目根目录下的 vite.config.js 文件中,手动配置 server 选项,将该专属公网域名加入白名单:

import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'

export default defineConfig({
  plugins: [vue()],
  server: {
    // 允许 Tailscale 分配的公网域名访问本地开发服务器
    allowedHosts: [
      'idyq.tailxxxx.ts.net'
    ]
    // 也可以简单粗暴地写成 allowedHosts: true 或 'all' 来允许所有域名访问
  }
})

保存配置文件后,Vite 自动热更新。再次刷新浏览器的公网链接,报错消失,本地 Vue 页面完美呈现在公网环境中。展示完毕后,只需在终端敲下 tailscale funnel off 即可安全闭合大门。

相关文章

任务 7:部署限制内存的数据库

在服务器部署的进阶之路上,部署限制内存的数据库是一个必经的关卡 。对于这种小内存的 VPS(比如 RackNerd),如果直接裸跑默认配置的 MySQL 8.0,它会一口气吞掉 400MB 甚至更多的内存,极易导致服务器 OOM(内存溢出)死机。

任务 10:打通 CI&CD(持续集成与部署),全自动上云实战

过去写完一篇笔记,需要手动执行编译指令,打开 FTP 软件,再把文件生硬地拖拽到服务器上。现在可以通过 GitHub Actions,实现一套极致的极客工作流:只要在本地执行 git push 把代码推送到 GitHub 的主分支,云端就会自动完成编译,并通过 SSH 悄无声息地将最新博客更新到 VPS 上 。这不仅让我们彻底告别了手动上传的繁琐,更是掌握现代互联网大厂 DevOps 核心工作流的关键一步 。

番外:1G 小内存 VPS 部署 Java JSP 项目实战:Docker 本地构建 + 远程运行完美方案

在拥有了一台属于自己的 VPS(如 1核 1G内存,配置了 2G Swap)后,很多新手在尝试部署 Java 项目时,往往会选择直接在服务器上安装 Maven 或运行 docker build。但现实很残酷:Java 编译极其消耗内存,1G 的内存在构建瞬间就会被挤爆,导致系统卡死或触发 OOM (Out Of Memory) 杀掉进程。