FRP 实战上手:原理、部署、解决什么问题¶
写在前面¶
很多运维同学都听过 FRP 这个名字,但真正动手部署过的不多。典型场景:
研发:「我家里跑了个本地服务,想让外面的同事访问。」
运维:「FRP 一下就能搞定。」
研发:「具体怎么搞?」
运维:「……我看一下文档。」
或者面试场景:
面试官:「你们生产环境怎么从云上访问 IDC 内网的服务?」
你:「用 FRP。」
面试官:「能讲一下 FRP 的工作原理吗?穿透 NAT 是怎么做的?」
你:「……」
这篇文章的目标,是让一个没动手部署过 FRP 的运维,用一两个小时把"原理 + 部署 + 生产实战 + 面试问答"全部打通。
不会展开协议层的所有细节,重点放在:
- FRP 解决什么问题,跟 OpenVPN/WireGuard/Nginx 反代是什么关系
- 工作原理(一张图就讲完)
- 一套最小可用的部署流程(云上 + 内网两端)
- 常见用法:暴露 HTTP、SSH、MySQL、K8s API
- 安全加固和生产坑
- 面试问答
一句话理解 FRP¶
FRP = 在公网服务器上打个洞,让你内网的服务能从这个洞被外界访问。
更准确点:
内网客户端主动连出到公网服务器建立长连接,外部访问公网服务器某个端口时,流量被反向转发到内网客户端,再转给真正的内网服务。
跟 OpenVPN 的关键区别:
FRP 把"服务"搬到公网;OpenVPN 把"用户"塞进内网。
跟 Nginx 反向代理的关键区别:
Nginx 要求自己能直接连到后端服务,所以后端必须有公网 IP 或在同一网络里;FRP 不要求,它的精髓就是让没有公网 IP 的内网服务也能被访问。
它解决什么问题¶
flowchart LR
Inet[公网用户] -.访问不到.- Home[家庭/公司内网]
Home --> Service[内网服务<br/>Web / SSH / MySQL]
NAT[家用路由器/企业 NAT<br/>没有公网 IP] -.挡住入站.- Inet
Home --- NAT 家用宽带、办公室网络、IDC 机房都有同样的问题:能主动连出去,但外面进不来。原因是 NAT、运营商不给公网 IP、防火墙默认拒绝入站。
FRP 的解法:在公网租一台便宜云服务器(一年几十块),FRP 客户端在内网主动连这台云服务器,建立一根长连接当成"反向通道"。外面访问云服务器的某个端口时,云服务器把流量沿着这根反向通道送到内网,再转给真实服务。
flowchart LR
Inet[公网用户] -->|访问 cloud.com:80| Cloud[云服务器<br/>跑 frps]
Cloud -.反向通道.- Client[内网机器<br/>跑 frpc]
Client --> Service[内网服务<br/>192.168.1.10:80] FRP 在远程接入家族里的位置¶
| 方案 | 解决什么 | 适合 |
|---|---|---|
| FRP | 把内网服务暴露到公网 | 临时调试、单服务对外、家庭实验室 |
| OpenVPN / WireGuard | 把人虚拟接入内网 | 多人远程办公、要访问大量内网资源 |
| Nginx 反向代理 | 已经有公网入口的流量分发 | 同一网络/直连场景的流量编排 |
| Tailscale / Zerotier | 自动组网,每台机器互通 | 个人多设备、小团队 |
FRP 的定位:轻量、专一、好搭、稳定。最擅长"我有个内网服务,想给少量外部用户访问" 这种场景。
工作原理:一张图讲完¶
sequenceDiagram
participant U as 公网用户
participant S as 公网服务器<br/>frps(端口 7000+暴露端口 80)
participant C as 内网客户端<br/>frpc
participant I as 内网真实服务<br/>192.168.1.10:80
Note over C,S: 启动阶段
C->>S: 主动建立 TCP 长连接(连 7000)<br/>带 token 鉴权
S->>C: 鉴权通过 + 注册"暴露 80 端口给 web 服务"
Note over U,I: 运行时
U->>S: HTTP 请求 cloud.com:80
S->>C: 通过反向通道转发请求
C->>I: 转给本地 192.168.1.10:80
I->>C: 响应
C->>S: 响应回送
S->>U: 返回响应 关键概念:
- frps(s = server):跑在公网服务器上,它是被连的那一端
- frpc(c = client):跑在内网,它是主动连出去的那一端
- 控制连接:frpc 启动后跟 frps 建立的那根 TCP 长连接,平时只用来传控制信令、心跳
- 数据连接:每来一个外部访问,frps 会通知 frpc 新建一根数据连接来转发数据
- token 鉴权:frps 和 frpc 共享一个 token,防止任意人连你的 frps 把流量打进来
部署:最小可用流程¶
下面用最经典的「公网 1 台 + 内网 1 台」过一遍。
1. 在公网服务器装 frps¶
# 下载(注意选你机器的架构,常见 amd64)
wget https://github.com/fatedier/frp/releases/download/v0.62.1/frp_0.62.1_linux_amd64.tar.gz
tar -xzf frp_0.62.1_linux_amd64.tar.gz
cd frp_0.62.1_linux_amd64
# 把可执行文件和配置放规范位置
install -m 755 frps /usr/local/bin/
mkdir -p /etc/frp
cp frps.toml /etc/frp/frps.toml
/etc/frp/frps.toml(FRP 0.52+ 推荐 TOML 配置):
# frpc 连接 frps 用的端口
bindPort = 7000
# 鉴权 token:frpc 必须配同一个
auth.method = "token"
auth.token = "your-strong-random-token"
# 用 HTTP 端口暴露内网 web 服务(可选)
vhostHTTPPort = 80
# 监控面板(生产不要直接对公网开)
webServer.addr = "127.0.0.1"
webServer.port = 7500
webServer.user = "admin"
webServer.password = "admin-strong-password"
# 日志
log.to = "/var/log/frps.log"
log.level = "info"
log.maxDays = 7
systemd 单元 /etc/systemd/system/frps.service:
[Unit]
Description=Frp Server Service
After=network.target
[Service]
Type=simple
ExecStart=/usr/local/bin/frps -c /etc/frp/frps.toml
Restart=on-failure
RestartSec=5s
LimitNOFILE=1048576
[Install]
WantedBy=multi-user.target
启动:
防火墙记得放行 7000(控制连接)和你想暴露的端口(这里是 80)。
2. 在内网机器装 frpc¶
/etc/frp/frpc.toml:
# 公网 frps 的地址
serverAddr = "cloud.example.com"
serverPort = 7000
# 必须和 frps 配同样的 token
auth.method = "token"
auth.token = "your-strong-random-token"
# 暴露一个 TCP 服务(比如 SSH)
[[proxies]]
name = "ssh"
type = "tcp"
localIP = "127.0.0.1"
localPort = 22
remotePort = 6000 # 在公网服务器上开 6000 端口
# 暴露一个 HTTP 网站(用 vhostHTTPPort 复用 80)
[[proxies]]
name = "blog"
type = "http"
localIP = "192.168.1.10"
localPort = 80
customDomains = ["blog.example.com"] # 域名解析到 frps 服务器
# 暴露 MySQL(高敏感,加密码)
[[proxies]]
name = "mysql"
type = "tcp"
localIP = "192.168.1.20"
localPort = 3306
remotePort = 6306
同样写 systemd unit:
[Unit]
Description=Frp Client Service
After=network.target
[Service]
Type=simple
Restart=on-failure
RestartSec=5s
ExecStart=/usr/local/bin/frpc -c /etc/frp/frpc.toml
[Install]
WantedBy=multi-user.target
启动:
3. 验证¶
# 从外网试 SSH(走公网 frps 的 6000 端口)
ssh -p 6000 user@cloud.example.com
# 浏览器访问 http://blog.example.com(DNS 解析到 cloud.example.com)
# 从外网连 MySQL
mysql -h cloud.example.com -P 6306 -u user -p
通了说明全链路 OK。
几种常见 proxy 类型¶
| type | 作用 | 典型用法 |
|---|---|---|
tcp | 暴露任意 TCP 端口 | SSH、MySQL、Redis、K8s API |
udp | 暴露 UDP 端口 | DNS、游戏服务器 |
http | 复用 80 端口 + 域名分发 | 多个内网网站 |
https | 复用 443 端口 | 跟 http 配套 |
stcp / xtcp | 点对点,需要双方都装 frpc | 安全场景,不暴露公网端口 |
stcp(Secret TCP)值得单独讲:它不在 frps 上开公网端口,只有同样持有密钥的 frpc 客户端能从隧道里访问。生产里"我只想让运维自己能从家访问公司内网 K8s API"这种场景非常合适——对外完全不可见。
# 内网侧(暴露方)
[[proxies]]
name = "k8s-api"
type = "stcp"
secretKey = "shared-secret-only-ops-knows"
localIP = "10.0.0.1"
localPort = 6443
# 访问方(运维自己电脑也跑一个 frpc)
[[visitors]]
name = "k8s-api-visitor"
type = "stcp"
serverName = "k8s-api"
secretKey = "shared-secret-only-ops-knows"
bindAddr = "127.0.0.1"
bindPort = 16443
# 之后本地 kubectl --server=https://127.0.0.1:16443 就能用
安全加固(生产必做)¶
裸跑 FRP 几乎等于"在公网开门"。生产里至少做这 6 件事:
- 强 token:32 位以上随机串。
openssl rand -hex 32 - 关闭 dashboard 公网访问:
webServer.addr = "127.0.0.1",要用就 SSH 隧道连 - 限制白名单:用
allowPorts限定 frpc 能开的远程端口范围,防止被篡改的 frpc 配置乱开端口 - TLS 加密控制连接:
transport.tls.enable = true(双向 TLS 更好) - 防止 frps 被人乱蹭:在 frps 前面挂 iptables/安全组,只放行 7000 给受控的内网公网出口 IP
- 暴露的服务本身要有认证:暴露 MySQL 必须强密码,暴露 SSH 强烈建议改用密钥登录、禁 root
# frps.toml 加固示例
bindPort = 7000
auth.method = "token"
auth.token = "your-32-byte-random-token"
allowPorts = [
{ start = 6000, end = 6010 },
{ single = 6306 }
]
transport.tls.force = true
transport.tls.certFile = "/etc/frp/server.crt"
transport.tls.keyFile = "/etc/frp/server.key"
transport.tls.trustedCaFile = "/etc/frp/ca.crt"
webServer.addr = "127.0.0.1"
webServer.port = 7500
webServer.user = "admin"
webServer.password = "strong-password"
生产里的几种常见用法¶
用法一:临时让外部访问内网调试服务¶
研发本地起了一个服务,想让产品经理在外面看一眼。FRP 一行配置 + 5 分钟就能搞定,比运维拖一个公网域名快得多。
用法二:从云上反向访问 IDC 内网¶
阿里云上的应用想访问公司 IDC 机房的 MySQL。让 IDC 机房的一台机器跑 frpc 主动连阿里云上的 frps,云上应用通过 frps:6306 就能连到 IDC 的 MySQL。省掉拉专线和打通 VPN 的成本。
(写这一段就是因为这正是"基于 FRP 搭建混合云远程接入通道"的典型场景。)
用法三:家庭实验室对外¶
家里跑了 K8s 集群、博客、NAS,想在外面访问。家里没公网 IP,用 FRP + 一台 30/月的云服务器搞定。
用法四:替代向跳板机做端口转发¶
以前做法是用 SSH 端口转发:ssh -L 16443:k8s-api:6443 jump-host,每次都要先 SSH 上跳板机。用 FRP 的 stcp 模式可以变成永久后台连接 + 本地直接 kubectl。
常见故障排查¶
| 现象 | 多半原因 |
|---|---|
frpc 起不来:login to server failed: i/o timeout | 公网 frps 端口 7000 没开放 / 公网安全组没放行 |
frpc 起不来:auth failed | token 不一致 |
| 外部访问 frps 端口连不上 | frps 安全组没放行远程端口;或 allowPorts 没把这个端口加进去 |
| HTTP 类型 proxy 不通 | 域名解析没指到 frps,或 frps 没开 vhostHTTPPort |
| 偶尔断流 | frps/frpc 心跳超时;调大 transport.heartbeatTimeout |
| 流量异常增大 | 可能 token 泄露被人滥用了 → 立刻换 token + 检查 dashboard 流量 |
调试命令:
# frps 日志
tail -f /var/log/frps.log
# frpc 日志
journalctl -u frpc -f
# 看连接状态(dashboard)
curl -u admin:admin-password http://127.0.0.1:7500/api/proxy/tcp
# 抓控制连接包
tcpdump -i any tcp port 7000 -nn
面试可能被问到的点(重点)¶
Q1:FRP 和 OpenVPN 有什么区别?¶
- FRP 暴露服务:内网服务被反向代理到公网某个端口,访问方拿到的是公网入口
- OpenVPN 接入用户:用户连上 VPN 后获得内网 IP,可以访问内网所有可达资源
- 选型:少量服务对外、临时调试 → FRP;多人远程办公、要访问大量内网服务 → OpenVPN/WireGuard
Q2:FRP 怎么实现 NAT 穿透的?¶
它不靠 NAT 穿透,而是靠"内网主动连出"。NAT 默认允许出向连接,所以 frpc 能连出去;连接建立后双向数据都走这根已建立的连接,不需要 NAT 设备允许入向。这跟 STUN/UDP 打洞那类真正的穿透技术不一样。
Q3:你怎么保证 FRP 通道的安全?¶
至少 4 件事:
- 强 token + TLS 加密控制连接
- 限制 frps 暴露端口范围(
allowPorts) - 暴露的服务本身有认证(密码、密钥、白名单)
- 用 stcp 模式让通道对公网完全不可见,只有持密钥的访问方能进来
Q4:FRP 和 Nginx 反向代理什么区别?¶
Nginx 必须能直接连到后端,所以后端要么有公网 IP,要么和 Nginx 在同一网络;FRP 不要求,靠"内网主动连出"打反向通道。两者其实是配合的——很多生产里 frps 上层挂 Nginx 做 TLS 卸载和域名分发,frps 只负责跨网通道。
Q5:FRP 适合做生产高并发流量入口吗?¶
不适合。FRP 设计偏轻量,单实例几千并发没问题,做主流量入口(每秒几万 QPS)就力不从心了,且单点风险高。生产高流量场景应该上专线 / VPC 互联 / 云厂商提供的网关。FRP 适合:内部工具访问、调试通道、小流量服务暴露、家庭实验室。
Q6:stcp 模式相比 tcp 模式的核心优势?¶
tcp 模式在 frps 上开一个公网端口,全网可见,任何人都能尝试连接(拼的就是后端服务自己的认证强度)。stcp 模式 frps 上不开任何公网端口,访问方也必须装 frpc 并持有密钥才能进入隧道——不可见 = 不可被扫描 = 不可被穷举。运维内部通道首选 stcp。
Q7:FRP 高可用怎么做?¶
- frps 单点是大问题,可以多节点 + DNS 轮询 / 公网 LB
- frpc 端配置多 frps 地址自动 failover
- 监控告警:dashboard
/api/proxy/...接口拉指标进 Prometheus - 配置版本化、一键下发,避免手改 frpc 出错
一句话总结¶
FRP = 在公网租一台机器开一个"反向门",让你内网的服务能从这扇门被外界访问,靠的是"内网主动连出 + 长连接反向转发",不靠 NAT 穿透。
把「为什么要它、它和 OpenVPN/Nginx 的差异、token+TLS+stcp 安全模型、生产用法」这四块讲清楚,面试这块基本不会被卡。