最近買了一個(gè)樹莓派 Model 3B放在了辦公室的桌子上,讓它平時(shí)替我跑一些腳本,好不愜意。但是當(dāng)下班回家后就與它失去了聯(lián)系,比較苦惱,為樹莓派做內(nèi)網(wǎng)穿透的想法應(yīng)運(yùn)而生。

使用到的開源項(xiàng)目或工具有:
- Docker
- ngrok 1.7
- openssl
- (非必需)國內(nèi)Docker云服務(wù)提供商 + 備案的域名
編譯 ngrok server 和 client
我的編譯機(jī)器是 VPS Ubuntu 16.06
首先安裝必要的工具:
sudo apt-get install build-essential golang mercurial git
clone 源碼,簽發(fā)自己域名的證書用于建立可信的連接。
git clone https://github.com/tutumcloud/ngrok.git ngrokcd ngrok
cd ngrok
NGROK_DOMAIN="sample_domain.com" //替換成你自己的域名
openssl genrsa -out base.key 2048
openssl req -new -x509 -nodes -key base.key -days 10000 -subj "/CN=$NGROK_DOMAIN" -out base.pem
openssl genrsa -out server.key 2048
openssl req -new -key server.key -subj "/CN=$NGROK_DOMAIN" -out server.csropenssl x509 -req -in server.csr -CA base.pem -CAkey base.key -CAcreateserial -days 10000 -out server.crt
cp base.pem assets/client/tls/ngrokroot.crt
開始編譯:
sudo make release-server release-client
//編譯運(yùn)行于macOS的可以使用如下命令
sudo GOOS=darwin GOARCH=amd64 make release-server release-client
//編譯運(yùn)行于樹莓派的可以使用如下命令
sudo GOOS=linux GOARCH=arm make release-server release-client
編譯完成后可以發(fā)現(xiàn) bin目錄出現(xiàn)一個(gè) ngrokd的二進(jìn)制可執(zhí)行文件,說明編譯是成功的。
連接 ngrok server 和 ngrok client
我在VPS上,使用如下命令開啟了一個(gè) ngrok server, 可以通過 8081 和 8082 端口訪問轉(zhuǎn)發(fā)到 ngrok client, 只差客戶端來連接了。
sudo ./bin/ngrokd -tlsKey=server.key -tlsCrt=server.crt -domain="sample_domain.com" -httpAddr=":8081" -httpsAddr=":8082"
在樹莓派上,建立一個(gè)簡單的配置文件 ngrok.cfg:
server_addr: sample_domain.com:4443
trust_host_root_certs: false
運(yùn)行命令:
./ngrok -subdomain pi -proto=http -config=ngrok.cfg 80
不出意外,可以看到終端會輸出 Tunnel Status onlie的字樣,然后我們通過 pi.sample_domain.com:8081訪問的請求就會轉(zhuǎn)發(fā)到樹莓派的 80端口上。
去掉該死的 :8081,我只想通過域名就能訪問
ngrok server 在不指定 httpAddr的情況下會使用 80端口
Docker鏡像制作
ngrok和 nginx都在搶 80端口該怎么辦??顯然 nginx更重要,所以我打算用 Docker欺騙 ngrok,讓它誤以為自己綁定到了機(jī)器的 80端口。
工程目錄結(jié)構(gòu)如下:
.├── Dockerfile
├── ngrok
└── run.sh
Dockerfile 如下:
FROM hub.c.163.com/public/ubuntu:16.04
MAINTAINER summerbabybiu admin@stephenw.cc
ENV NGROK /opt/ngrok
ENV DOMAIN sample_domain.com //替換你自己的域名
EXPOSE 80 443 4443 59900
COPY ngrok/bin/ngrokd $NGROK/
COPY ngrok/server.crt $NGROK/ssl.crt
COPY ngrok/server.key $NGROK/ssl.key
COPY ngrok/server.csr $NGROK/ssl.csr
COPY run.sh $NGROK/
RUN chmod +x $NGROK/run.sh
CMD .$NGROK/run.sh
其中 run.sh 內(nèi)容如下:
#!/bin/sh
/opt/ngrok/ngrokd -tlsCrt /opt/ngrok/ssl.crt -tlsKey /opt/ngrok/ssl.key -domain "sample_domain.com"
然后在工程目錄下執(zhí)行:
//$REPO_NAME 你的 repo名稱
//$TAG 你的鏡像 tag
docker build -t $REPO_NAME:$TAG .
不出意外地,鏡像 build成功,然后我把它 push 到了 Docker Hub。在 VPS上,把鏡像 pull下來,執(zhí)行命令:
docker run -d -p 127.0.0.1:8080:80 -p 127.0.0.1:8081:443 -p 4443:4443 -p 59900:59900 $REPO_NAME:$TAG
此時(shí)再次嘗試樹莓派連接,然后 VPS配置一下 pi.sample_domain.com proxy_pass 到 127.0.0.1:8080, 你就會發(fā)現(xiàn)可以不帶端口訪問到樹莓派了!
樹莓派的 ngrok client 自動啟動與 ssh 穿透
ssh 穿透和 http 原理差不多,不過它不需要分配子域名,只需要簡單的端口建立 tcp連接。編寫一個(gè) ngrok client的配置文件:
server_addr: sample_domain.com:4443
trust_host_root_certs: false
tunnels:
http:
proto:
http: 80
subdomain: pi
ssh:
proto:
tcp: 22
remote_port: 59900
然后再次通過命令啟動:
ngrok client 二進(jìn)制被我放在了 /opt/ngrok下,配置文件被我放在了 /opt/config下
/opt/ngrok/bin/linux_arm/ngrok -config=/opt/config/ngrok.cfg start http ssh
這樣啟動后,可以通過 59900端口 ssh到樹莓派上。
還不夠,目前的 ngrok client啟動處于阻塞態(tài),一旦 tty失去連接就會斷開。為此我配置了一個(gè) systemd service:
/etc/systemd/system/ngrok.service
[Unit]
Description=ngrok
After=network.target
[Service]
Type=simple
ExecStart=/opt/ngrok/bin/linux_arm/ngrok -config=/opt/config/ngrok.cfg start http ssh
[Install]
WantedBy=multc-user.target
然后通過 systemctl enable ngrok.service來使其開機(jī)自啟,service ngrok start來使其自動啟動。
優(yōu)化 ngrok server的連接速度
我的 VPS地理位置處于日本,我在家時(shí)訪問樹莓派會比較慢(150ms左右),但是單獨(dú)為一個(gè)樹莓派開一臺國內(nèi)的虛機(jī)很不劃算(月花費(fèi)65元左右),于是我想到可以托管在 Docker云服務(wù)商那邊。具體做法很簡單,在 Docker云服務(wù)商提供的控制臺運(yùn)行剛才 build好的鏡像,然后綁定你的域名(需要備案),根據(jù) docker容器運(yùn)行實(shí)際提供出來的端口對 client端的配置文件端口稍作修改就可以建立連接了。實(shí)測連接速度在 30ms左右,并且費(fèi)用降到了 10-15元每月左右。
這只是 流量密集型的操作,所以實(shí)際上非常低的云服務(wù)配置也能提供非常多穩(wěn)定的連接,性價(jià)比非常高。
手機(jī)SSH到遠(yuǎn)程服務(wù)器可以安裝
Serverauditor這一款免費(fèi)的 App,親測實(shí)用。
- EOF-