Ngrok服务器部署

前言

自己使用Google Cloud,阿里云,AWS有好几年了,在云服务器上花了不少钱,有段时间做爬虫,需要较大的磁盘空间(几百G),每个月大概要5,600元。
一年下来就6,7钱了。想想,还是直接买一台主机或服务器在家里放着好了,也就几千块钱。于是我买了台主机在家里放着,配置远远比原来ECS的配置要高,
而且还更便宜。把数据全部迁移到了自己的主机,但很多时候自己都不在家,不可能把笨重的主机带出去外面。于是得想办法在其它地方通过网络连接到主机。
刚开选择用Teamviewer,可以远程连接到主机的桌面,我的主机的操作系统是Ubuntu的,感觉还行。但是,慢慢感觉效率不是很高,因为是用桌面远程,

所以网速要求,还是有点高,更大的问题是Teamviewer是收费的,而且价格不菲,这样成本算下来一年下来也要花费挺多钱。
于是,我想到了以前的花生壳 内网穿透映射,但那个也是收费的。终于找到了一个令我满意的解决方案—-Ngrok!

正文

Ngrok到底好在哪里呢? 首先,也是最重要的是它的成本底。
主机买服务的话也就10多块钱一个月,网上一搜就很多Ngrok的提供商;自己搭建的话,也就买一台虚拟机的价格,我买的是某云的ECS。大概也就几十来块钱。

其次,Ngrok可以把你家里的电脑当成服务器来使用,远程登陆SSH;或者作为数据库,暴露对应的端口提供服务;或者直接作为Web服务,直接提供http访问。
这使得不用买昂贵的云服务,省了不少钱。不够配置的话可以自己买内存,SSD增加机器配置。

准备工作:

需要购买的服务

  1. 有一个外网的虚拟机,我用的是国内的阿里云的,操作系统是Ubuntu。
  2. 有个自己注册的域名。
    需要添加2条域名解析:

部署工作:

  1. apt-get install golang
    因为ngrok是用Go 来写的,所以要安装一下Go环境

  2. git clone https://github.com/inconshreveable/ngrok.git
    然后把ngrok的源码clone下来。

证书安装:

使用ngrok.com官方服务时,我们使用的是官方的SSL证书。自己建立ngrok服务,需要我们生成自己的证书,并提供携带该证书的ngrok客户端。首先指定域名:

进入ngrok目录后,运行下面的指令

1
2
3
4
5
6
7
8
export NGROK_DOMAIN="ngrok.xfl.host"
//这里换成你的自己注册的域名,比如你注册的域名为abc.com。这里可以填 ngrok.abc.com
openssl genrsa -out rootCA.key 2048
openssl req -x509 -new -nodes -key rootCA.key -subj "/CN=$NGROK_DOMAIN" -days 5000 -out rootCA.pem
openssl genrsa -out device.key 2048
openssl req -new -key device.key -subj "/CN=$NGROK_DOMAIN" -out device.csr
openssl x509 -req -in device.csr -CA rootCA.pem -CAkey rootCA.key -CAcreateserial -out device.crt -days 5000

我们在编译可执行文件之前,需要把生成的证书分别替换到 assets/client/tls和assets/server/tls中,这两个目录分别存放着ngrok和ngrokd的默认证书。

1
2
3
cp rootCA.pem assets/client/tls/ngrokroot.crt
cp device.crt assets/server/tls/snakeoil.crt
cp device.key assets/server/tls/snakeoil.key

ngrok部署安装

编译ngrokd 和 ngrok

首先需要知道,ngrokd 为服务端的执行文件,ngrok为客户端的执行文件。

有没有release的区别是,包含release的编译结果会把assets目录下的内容包括进去,从而可以独立执行。
如果你今后还要更换证书,建议编译不包含release的版本。。首先编译ngrok服务端(ngrokd),默认为Linux版本:

1
2
make clean
make release-server

编译ngrokd后,我们来编译ngrok(客户端)
在编译客户端的时候需要指明对应的操作系统和构架:

Linux 平台 32 位系统:GOOS=linux GOARCH=386
Linux 平台 64 位系统:GOOS=linux GOARCH=amd64
Windows 平台 32 位系统:GOOS=windows GOARCH=386
Windows 平台 64 位系统:GOOS=windows GOARCH=amd64
MAC 平台 32 位系统:GOOS=darwin GOARCH=386
MAC 平台 64 位系统:GOOS=darwin GOARCH=amd64
ARM 平台:GOOS=linux GOARCH=arm

我的Ubuntu属于linux 64,所以我执行如下指令:

1
GOOS=linux GOARCH=amd64 make release-client

启动ngrokd

编译后生成两个文件分别为服务端(ngrokd)和客户端(ngrok)。切换到对应的文件夹,运行服务端:

1
2
./ngrokd -domain="$NGROK_DOMAIN" -httpAddr=":801" -httpsAddr=":802"
//801 是访问的http端口,比如,在本例子中,访问http://ngrok.xfl.host:801 就可以看到映射出的网页

参数-domain表示服务器域名,请改成你自己的域名;-httpAddr表示默认监听的HTTP端口,-httpsAddr表示默认监听的HTTPS端口,
因为我用不到所以都设置成空字符串”“来关闭监听,如果需要打开的话记得格式是:12345(冒号+端口号)这样的;-tunnelAddr表示服务器监听客户端连接的隧道端口号,格式和前面一样;
-log表示日志文件位置;还有个-log-level用来控制日志记录的事件级别,选项有DEBUG、INFO、WARNING、ERROR。

如果编译的是不带release的版本,还可以通过-tlsCrt和-tlsKey选项来指定证书文件的位置。

出现类似以下内容,则说明我们的服务器端ngrokd正常运行了:

1
2
3
4
5
6
7
8
[16:41:56 CST 2017/04/20] [INFO] (ngrok/log.(*PrefixLogger).Info:83) [registry] [tun] No affinity cache specified
[16:41:56 CST 2017/04/20] [INFO] (ngrok/log.(*PrefixLogger).Info:83) [metrics] Reporting every 30 seconds
[16:41:57 CST 2017/04/20] [INFO] (ngrok/log.Info:112) Listening for public http connections on [::]:80
[16:41:57 CST 2017/04/20] [INFO] (ngrok/log.Info:112) Listening for public https connections on [::]:443
[16:41:57 CST 2017/04/20] [INFO] (ngrok/log.Info:112) Listening for control and proxy connections on [::]:4443
[16:41:57 CST 2017/04/20] [INFO] (ngrok/log.(*PrefixLogger).Info:83) [tun:627acc92] New connection from 42.53.196.242:9386
[16:41:57 CST 2017/04/20] [DEBG] (ngrok/log.(*PrefixLogger).Debug:79) [tun:627acc92] Waiting to read message
[16:41:57 CST 2017/04/20] [DEBG] (ngrok/log.(*PrefixLogger).Debug:79) [tun:627acc92] Reading message with length: 159

配置ngrok客户端

将之前编译好的客户端文件(ngrok 文件)拷贝到需要使用服务的设备(自己买的那台笨重的主机)上。

启动ngrok客户端

在ngrok同路径下建立配置文件ngrok.yml

1
2
3
4
5
6
7
server_addr: "ngrok.xfl.host:4443"
trust_host_root_certs: false
tunnels:
ssh:
remote_port: 6666
proto:
tcp: 22

server_addr端口默认4443,可通过ngrokd服务端启动修改端口。在tunnels里配置隧道信息,
注意http和https隧道可设置subdomain和auth,而tcp里只能设置remote_port。

使用如下命令启动ngrok客户端:

1
ngrok -log=stdout -config=./ngrok.yml start ssh

正常启动,你将会看到如下日志:

1
2
3
4
5
6
7
8
9
ngrok (Ctrl+C to quit)
Tunnel Status online
Version 1.7/1.7
Forwarding http://demo.ngrok.xfl.host -> 127.0.0.1:19999
Forwarding https://demo.ngrok.xfl.host -> 127.0.0.1:19999
Web Interface 127.0.0.1:4040
# Conn 0
Avg Conn Time 0.00ms

Notice:如果显示reconnecting说明连接有错,在运行时加入-log=stdout来进行debug。

  1. 有可能是因为你的Ngrok服务器没有开 4443端口
  2. 有可能是域名没有解析成功

TL;DR

ngrok 的配置文件是完全可选的非常简单 YAML 格式文件,他可以允许你使用 ngrok 一些更高级的功能,例如:

同时运行多个隧道

  • 连接到自定义的 ngrok 服务器
  • 调整 ngrok 一些很神秘的功能
  • ngrok 的配置文件默认从 ~/.ngrok 加载。你可以通过 -config 参数重写配置文件的地址

  • 同时运行多个隧道
    为了运行多个隧道,你需要在配置文件当中使用 tunnels 参数配置每个隧道。隧道的参数以字典的形式配置在配置文件当中。
    举个例子,让我们来定义三个不同的隧道。第一个隧道是一个有认证的只转发 https 的隧道。第二个隧道转发我们自己机器的 22 端口以便让我可以通过隧道连接到自己的电脑。
    最后,我们使用自己的域名创造了一个隧道,我们将要在黑客马拉松中展示这个。

1
2
3
4
5
6
7
8
9
10
11
tunnels:
client:
auth: "user:password"
proto:
https: 8080
ssh:
proto:
tcp: 22
hacks.inconshreveable.com:
proto:
http: 9090

通过 ngrok start 命令,我们可以同时运行三个隧道,后面要接上我们要启动的隧道名。

1
ngrok start client ssh hacks.inconshreveable.com
  • 隧道设置
    每一个隧道都可以设置以下五个参数:proto,subdomain,auth,hostname 以及 remote_port。
    每一个隧道都必须定义 proto ,因为这定义了协议的类型以及转发的目标。当你在运行 http/https 隧道时, auth 参数是可选的,
    同样, remote_port 也是可选的,他声明了某个端口将要作为远程服务器转发的端口,请注意这只适用于 TCP 隧道。
    ngrok 使用每个隧道的名字做到子域名或者域名,但你可以重写他:
1
2
3
4
5
6
tunnels:
client:
subdomain: "example"
auth: "user:password"
proto:
https: 8080

对于 TCP 隧道,你可以会通过 remote_port 参数来指定一个远程服务器的端口作为映射。如果没有声明,服务器将会给你随机分配一个端口。

后记

如果使用国内IP或者国内的域名的话,用http的话貌似因为要备案,所以访问不了。前两天我是可以正常访问的,但是过了两天后就突然访问不了了。
可以改为用TCP的方式比如:

1
2
3
4
netdata:
remote_port: 801
proto:
tcp: 19999

访问 ngnrok.xfl.host:801 即可。

Reference

https://morongs.github.io/2016/12/28/dajian-ngrok/
https://luozm.github.io/ngrok
https://imlonghao.com/28.html