一、什么是 frp
1.1 内网穿透
如下图所示, 一般情况下, 公网内的设备都能够被任意一台设备访问到!! 而不同局域网内的设备是相互隔离的, 局域网 A
的设备是无法访问到局域网 B
内的设备
而内网穿透技术, 顾名思义就是能让公网、或者当前局域网外的任意设备访问到局域网内某个设备! 如下图, 设备 C
实现了内网穿透技术, 所以局域网内任意设备都能够访问到当前设备
那么有了内网穿透我们可以做啥呢?
- 远程访问和管理: 内网穿透允许您从任何地方通过互联网连接到位于其他局域网内中的设备或服务器, 举个最简单的例子就是, 当你在家里想远程访问公司的电脑时, 因为你的两个设备是处于不同局域网内, 默认情况下是无法相互访问的, 这时就可以通过内网穿透来实现
- 游戏娱乐: 比如你在家用电脑开游戏服, 想邀请朋友加入联机, 因为你家里的网络与朋友的网络处于不同的局域网, 当朋友想要联机加入你的游戏服时, 就需要你的电脑使用内网穿透技术将设备访问权暴露出去
- 共享本地服务: 实现文件共享, 如文件共享、打印机访问或监控摄像头; 通过内网穿透技术, 我们可以随时随地访问家里设备的资源、服务
- 测试和开发环境: 开发人员可以使用内网穿透将本地开发环境暴露给外部世界; 比如我们要本地测试
github webhooks
就可以通过内网穿透来让外部的服务访问到我们本地服务
1.2 frp 简介 & 原理简述
简单地说, frp
就是一个反向代理软件, 它的作用是将内网中的服务器暴露到互联网上, 它体积轻量但功能很强大!!! 通过它我们可以很方便实现内网穿透功能!!!
它其实有两个服务:
- 客户端
frpc
, 安装在我们内网中某台物理机上 - 服务端
frps
, 安装在一个公网服务器上
如下图:
- 首先公网上先部署了
frps
服务, 并设置了连接端口 - 然后在内网中运行
frpc
服务, 启动时会连接到公网上的frpc
服务, 并保持保持住这个长连接(如果断开了会进行重试) - 当用户公网地址时, 会先在本地看是否有可用的连接, 如果没有, 那么
frps
服务就会将请求转发到frpc
服务上 - 再由
frpc
将请求转发到内网中任意一个服务上
FRP的优点: 它可以隐藏内网中的服务器, 避免因直接暴露内网服务器导致的安全问题。此外, FRP还支持动态端口映射, 可以方便地实现内网的服务器负载均衡
二、公网服务器 frps 部署
上文提到, frp
其实是有两个服务的, 一个存在于公网的 frp
服务端, 也是就 frps
; 一个就是部署在局域网物理机上的 frp
客户端, 也就是 frpc
那么本节将介绍下 frps
的一个部署, 这里使用到的 docker
镜像是 snowdreamtech/frps, 需要注意的是本文使用的是最新的版本, 所以配置文件和目前网上大部分教程是有所出入的!!!
如下图, 从 docker
镜像的详细信息可以看出, 该镜像发布时间的一个时间, 以及使用的配置文件路径, 配置文件具体信息可以看 frp 官网
下面开始正式部署 frps
服务...
2.1 添加配置文件
首先找个位置, 添加一个配置文件 frps.toml
, 我这里配置文件完整路径为 /home/moyuanjun/frp/frps.toml
, 配置文件内容如下, 具体每个配置项参考注释, 更多配置可查阅 frp 官网
bindPort = 7000
log.to = "console"
vhostHTTPPort = 7100
vhostHTTPSPort = 7200
auth.method = "token"
auth.token = "password"
webServer.port = 7300
webServer.addr = "0.0.0.0"
webServer.user = "admin"
webServer.password = "admin"
2.2 Docker 容器部署
配置文件整完, 下面我们开始部署 docker
!!!
- 首先先拉取下最新的
docker
镜像 snowdreamtech/frps
sudo docker pull snowdreamtech/frps
这里提前拉取了下镜像, 主要目的就是要确认下拉取下来的 snowdreamtech/frps 镜像的版本是否是最新的, 这里我就是简单看下镜像的创建时间(没办法, 不同的 docker
源上最新版本可能存在差异, 我这次就被坑惨了!!!)
docker
运行: 如下命令, 运行snowdreamtech/frps
, 这里唯一要调整的是-v /home/moyuanjun/frp/frps.toml:/etc/frp/frps.toml
, 要将前面我本地配置文件路径改成你自己的
sudo docker run -d \
--network host \
-v /home/moyuanjun/frp/frps.toml:/etc/frp/frps.toml \
--name frps \
snowdreamtech/frps
2.3 日志查看
上文我们已经完成了 frps
docker
容器的部署, 但实际上我们只看到容器起来了!! 但是 frps
具体运行情况我们是无法知道得知的!!
其实我们在 frps.toml
中配置了 log.to = "console"
, 日志实际上已经输出了, 这里我们直接通过 docker logs frps
就可以查看到日志信息了:
当然如果我们想要查看实时的日志, 可以使用 docker logs -f frps
来开启一个实时的终端进程, 就能够实时监控到日志的输出:
2.4 frps 仪表盘
我自己的服务器是阿里云的, 默认情况下防火墙只开启了几个常用端口, 所以在开始前, 我这边还需要设置下阿里云的防火墙, 为
frp
开放了一批端口出来
还记得我们在 frps.toml
中配置了仪表盘信息嘛, 下面我们可以通过 ip/域名:[webServer.port]
来访问仪表盘页面!!!
首次需要登录, 用户名密码就是 frps.toml
设置的内容:
如下图, 就是仪表盘的界面了:
三、内网 frpc 部署
下面我们还需要一个 frpc
客户端, 当我们运行 frpc
服务时将和公网上的 frps
建立一个长连接, 当我们访问公网不存在的服务时会转发到 frpc
, 然后 frpc
再做一个二次转发
那么本节将介绍下 frpc
的一个部署, 这里使用到的 docker
镜像是 snowdreamtech/frpc, 需要注意的是本文使用的是最新的版本, 所以配置文件和目前网上大部分教程是有所出入的!!!
如下图, 从 docker
镜像的详细信息可以看到, 该镜像的一个发布时间, 以及使用的配置文件路径, 配置文件具体信息可以看 frp 官网
下面开始正式部署 frpc
服务...
3.1 添加配置文件
还是一样, 我们需要先找个位置, 添加一个配置文件 frpc.toml
, 我这里配置文件完整路径为 /Users/qianyin/frp/frpc.toml
, 配置文件最简内容如下, 具体每个配置项参考注释, 更多配置可查阅 frp 官网
serverPort = 7000
serverAddr = "www.kunlunxu.cc"
log.to = "console"
auth.token = "password"
3.2 Docker 容器部署
配置文件整完, 下面我们开始部署 docker
!!!
- 首先先拉取下最新的
docker
镜像 snowdreamtech/frpc
docker pull snowdreamtech/frpc
这里提前拉取了下镜像, 主要目的就是要确认下拉取下来的 snowdreamtech/frpc 镜像的版本是否是最新的, 这里我就是简单看下镜像的创建时间(没办法, 不同的 docker 源上最新版本可能存在差异, 我这次就被坑惨了!!!)
docker
运行: 如下命令, 运行snowdreamtech/frpc
, 这里唯一要调整的是-v /Users/qianyin/frp/frpc.toml:/etc/frp/frpc.toml
, 要将前面我本地配置文件路径改成你自己的
docker run -d \
--network host \
-v /Users/qianyin/frp/frpc.toml:/etc/frp/frpc.toml \
--name frpc \
snowdreamtech/frpc
- 同样的, 这里我们可以通过
docker logs frpc
来查看启动日志
- 当然我们也可以通过查看
frps
仪表板中客户端连接数, 来确定frpc
的连接情况
3.3 将内网上本地 html 服务暴露到公网(tcp 版本)
- 首先本地我们先起一个服务, 我这里直接使用
vscode
插件 Live Server 起了一个静态服务
- 下面我们修改
frpc
配置文件, 添加一个代理配置, 我们希望的是, 当访问公网ip/域名:7001
能够通过frps
转发到内网上的frpc
服务上, 然后再通过fprc
代理到内网的192.168.0.108:5500
上; 配置完整内容如下, 参数介绍看注释, 主要就是加了[[proxies]]
配置:
# frpc.toml
serverPort = 7000 # [必选] 要连接的 frps 端口
serverAddr = "www.kunlunxu.cc" # [必选] 要连接的 frps 地址
log.to = "console" # [可选] 日志配置, 通过打印的方式输出日志
auth.token = "password" # [可选] token 设置, frps 设置的 token, 其实就是密码
+ [[proxies]]
+ name = "web" # 代理名称(随便填)
+ type = "tcp" # 代理类型
+ localIP = "192.168.0.108" # 代理地址, 要转发到哪个地址
+ localPort = 5500 # 代理端口, 要转发到哪个端口
+ remotePort = 7001 # 远程端口(和远程 frps 哪个端口绑定在一起, 访问对应端口将使用该代理)
重启 frpc
: 其实就是重启 docker
容器
docker stop frpc
docker start frpc
访问 http://www.kunlunxu.cc:7001
将正常展示内网上本地项目:
下面是一个简易流程图:
3.4 将内网上本地 html 服务暴露到公网(html 版本)
下面我们换一种配置方式, 下面是完整配置内容如下, 参数介绍看注释; 因为我们在 frps
上设置了 vhostHTTPPort = 7100
那么当我们访问公网服务器 7100
端口时, 转发到 frpc
后会走 type = "http"
的配置:
# frpc.toml
serverPort = 7000 # [必选] 要连接的 frps 端口
serverAddr = "www.kunlunxu.cc" # [必选] 要连接的 frps 地址
log.to = "console" # [可选] 日志配置, 通过打印的方式输出日志
auth.token = "password" # [可选] token 设置, frps 设置的 token, 其实就是密码
[[proxies]]
name = "web - tcp" # 代理名称(随便填)
type = "tcp" # 代理类型
localIP = "192.168.0.108" # 代理地址, 要转发到哪个地址
localPort = 5500 # 代理端口, 要转发到哪个端口
remotePort = 7001 # 远程端口(和远程 frps 哪个端口绑定在一起, 访问对应端口将使用该代理)
+ [[proxies]]
+ name = "web - html" # 代理名称(随便填)
+ type = "http" # 代理类型
+ localIP = "192.168.0.108" # 代理地址, 要转发到哪个地址
+ localPort = 5500 # 代理端口, 要转发到哪个端口
+ customDomains = ["www.kunlunxu.cc"] # 限制公网地址, 只有对应地址上
frps 转发了 html 才会走到这里
重启 frpc
: 其实就是重启 docker
容器
docker stop frpc
docker start frpc
访问 http://www.kunlunxu.cc:7100
将正常展示内网上本地项目:
下面是一个简易流程图:
四、遇到问题
1、版本问题: 目前网上大部分文章的配置文件还是 frps.ini
或 frpc.ini
, 写法上也是老的写法, 所以这里如果你安装的是最新版本那么一定请以 frp 官网 为准
2、如何确定 Docker
容器使用的配置文件是哪个? 答案是可直接查看容器的信息来进行确认
如何查看日志? 正如上文可在配置文件中设置 log.to = "console"
并配合 docker logs
来查看服务输出的日志
- 上文中
webServer.addr
设置了"0.0.0.0"
, 是因为默认情况下是该值为127.0.0.1
, 在测试过程中发现如果保持默认值无法访问到仪表盘页面了!!! - 在最开始我是跑了一个
React
项目, 然后试图在frpc
中将项目暴露出去, 最后发现一直无法代理成功!!! 经排查发现原来该项目无法通过内网IP
进行访问的, 当然相对的解决办法就是需要调整webpack
中的配置!! 后来省方便就直接使用vscode
插件 Live Server 起了一个静态服务来进行测试!! 所以这里主要就是想提醒下, 在测试前请确保你的本地服务能够正常通过内网IP
进行访问, 同时配置中尽量不要直接写127.0.0.1
, 尽量使用具体的内网IP
- 在阿里云上, 拉取 snowdreamtech/frps 镜像时总是发现最新版本和
hub.docker
上对不上, 经排查发现问题出在Docker
配置的源上, 由于我这边使用了 阿里云加速器, 但是由于Docker Hub
的限制, 导致使用镜像加速器后无法获取最新官方镜像, 暂时解决办法就是去掉加速器配置, 直接连接Docker Hub
进行获取:
rm /etc/docker/daemon.json
sudo systemctl daemon-reload
sudo systemctl restart docker