[NOTE] Frp原理深入实践、Nginx容器实践
近期将阿里云上的博客服务迁移到了本地机房服务器,同时oj平台的搭建也开始启动,目前多个服务通过不同端口访问的方式显得太过粗糙,本文将通过frp、Nginx、docker等工具以实现阿里云主机对多个本地服务的反向代理,对每个服务都分出对应的二级域名。
在frp的使用上,本文相较之前的文章更加深入,对frp的多个功能作出更多的探索。另外,也将初步地学习实践Nginx引擎。
一、相关说明
本文涉及到的环境:
- Ubuntu Server 18LTS (local)
- CentOS 7.3 (Aliyun)
- frp 0.28.0
- go 1.10.4
二、frp原理
内网穿透的原理在前文中已有说明,但基于需求的变化,我们需要使用frp更多的特性,了解相关的原理有助于减少在实现过程中的错误。
第一步:配置无误的情况下,frp服务端和frp客户端先后启动,建立通信隧道
,其中:
- frp服务端监听http
7071
端口(此端口可自定义),接收此端口下所有外网用户请求,注意此处的7071
端口要和通信隧道所用的端口7000
作出区别 - frp客户端代理本地想要暴露给外网的web服务端口,本文以
80
,8080
端口为例
第二步:通过配置nginx
反向代理,将指向本台公网服务器的nuzar.top
下的子域名,映射到服务器的7071
端口,也就是frp监听的那个端口。 外网用户访问gitlab.nuzar.top
、wordpress.nuzar.top
下的子域名,例如 :
gitlab.nuzar.top
wordpress.nuzar.top
等同于访问nuzar.top:7071
,会触发frp服务端和客户端的互动,从而http请求由frp服务端传递到frp客户端
第三步:frp客户端收到http请求后,基于自定义配置,则做如下处理:
- 监听到http请求中的域名为
gitlab.nuzar.top
,则将请求转发到我本地的8585
web服务端口 - 监听到http请求中的域名为
wordpress.nuzar.top
,则将请求转发到我本地的8686
web服务端口
第四步:本地的web服务收到http请求后,对请求做处理,并完成响应
第五步:frp客户端将响应结果回传给frp的服务端。服务端最终将响应回传给外网用户
第六步:最终的实测效果为:
- 访问
gitlab.nuzar.top
,等同于访问我本地的192.168.1.253:80
- 访问
wordpress.nuzar.top
,等同于访问我本地的192.168.1.253:8080
三、Configuration
话不多说,直接上配置信息,相关的属性会分别进行说明。
1.frps.ini
1 | [common] |
bind_port
为frp服务端与客户端之间通讯的端口,为事实上数据传输的端口;
vhost_http_port
为http服务的代理端口,代理所有发往7071
端口的http请求,之后通过bind_port
将包装后的请求发送给bing_port
相连接的服务器;
subdomain_host
指定父域名,便于划分子网(但事实上最后还是通过nginx来划分的子网,一直到都弄完之后,才发现前面多打了个d,前面frp方案走不通,恐怕是这个的锅)。
为了便于调试,你也开启frp的dashboard界面,参照官网Dashboard,在frps.ini中加入:
1 | [common] |
如果你之前有通过systemctl将frps加入到系统服务中的话,那么此时通过systemctl restart frps
就可以让新配置生效了。
dashboard的效果图如下:
通过dashboard面板,可以很容易的观察到自己的配置是否生效。
2.frpc.ini
1 | [common] |
frpc.ini配置文件,见名知义。需要注意的两点:1.配置文件中不要出现相同的名称,中括号中的名称只作代称,并不具有实际的意义;2.肯定有人会奇怪为什么明明是使用的http协议,但类型却标注的type = tcp
,原因是官方并没有提供多web服务的穿透,在一些issue中有人发现使用tcp可以绕过单个web穿透的限制,最终达到理想的效果,具体的讨论可以前往#914 (comment).
3.nginx
本文中nginx采用docker的方式进行部署,由于nginx在启动时会加载相关的配置文件,因此在部署前需要在相应路径(之后挂载到容器中)编写一些配置文件。
/root/nginx/nginx.conf
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20user nginx;
worker_processes auto;
error_log /var/log/nginx/error.log warn;
pid /var/run/nginx.pid;
events {
worker_connections 2048;
}
http {
include /etc/nginx/mime.types;
default_type application/octet-stream;
sendfile on;
keepalive_timeout 65;
client_max_body_size 10M;
include /etc/nginx/conf.d/*.conf;
}nginx.conf
常规配置没什么好说的。
/root/nginx/conf.d/default.conf
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22server {
listen 80;
listen [::]:80;
server_name nuzar.top;
location / {
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header Host $http_host;
proxy_pass http://nuzar.top:8080;
}
}
server {
listen 80;
listen [::]:80;
server_name gitlab.nuzar.top;
location / {
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header Host $http_host;
proxy_pass http://127.0.0.1:6080;
}
}nginx监听80端口,将对应的服务请求转发到对应的端口,由于目前我个人对于nginx也是个拿来即用的状态,了解不多。
四、添加域名解析
如果你也是使用的阿里云的云服务,可以直接登录域名解析管理页面。
以本文中的gitlab.nuzar.top
为例:
- 添加记录:A
- 主机记录:gitlab
- 解析路线:默认
- 记录值:SERVER_IP_ADDR
如果使用的其他厂商的主机,也有相应的操作可以实现。
五、部署nginx容器
nginx部署的方式还是采用docker run
,在部署时需要将宿主机的一些路径挂载到容器中的对应路径,因此,可以参考下我的文件树:
1 | |-- conf.crt |
conf.crt
用于存放证书,这个暂时不会用到;
nginx.conf
为nginx配置文件;
default.conf
为配置文件的补充文件,对于nginx主要的配置工作也是集中在这个文件之中;
index.html
顾名思义,一个普通的页面,之后用certbot来申请证书的时候需要用到这个页面。
1 | docker run -d -p 80:80 -p 443:443 \ |
需要注意的一点是,标注--network=host
后,nginx容器才有能力代理主机的请求,否则nginx只在容器内部有效,host
表示容器网络为主机模式;
ro
表示挂载的文件为只读状态,另外在docker社区中,文件级别的挂载好像是不太推荐的,但连官网上对nginx的示例也是如此,所以就先这样用了。
五、总结
对于二级域名的配置,总的来说还是比较简单的,总的来说存在两套方案:一者是直接通过frp提供的subdomain
进行配置,但前文也提到,由于后来才发现的一些错误,导致我过早地认为这套方案不适用;二是在frp实现内穿的基础之上,通过一个nginx引擎来完成一个类似反向代理的工作,这层nginx在现在看来有些多余,但出于熟悉nginx和之后加入https支持的目的,我将二级域名到指定端口的工作交由nginx来完成了。
另外,frp的功能远不止如此,目前项目比较活跃,加之也是由Golang编写,值得持续跟进。