使用树莓派4B作为一台服务器

本文最后更新于:2023年2月9日 下午

树莓派是个一直听说,但是一直没玩过的东西,所以在今年年初搞了一块折腾了一段时间。在上面起了一些服务,确实很好玩。在上面搭建了私有网盘、Minecraft、SageMath还有几个密码学题目。这篇博客记录了我折腾树莓派的经历~

解决公网IP问题

我想在公网连接到家里的树莓派,访问到上面的服务,所以需要有公网IP或者使用frp内网穿透。当然如果不需要让树莓派暴露在公网下,就不需要搞公网IP了。

静态公网IP

如果有角度可以搞到企业专线,那还是很香的,不仅拥有运营商分配的静态公网IP,还有上下行相等的大带宽,当然费用也很高,也需要注册的企业资质。所以我是搞不到静态的公网IP。

动态公网IP

没有静态的,可以退而求其次搞动态的,跟运营商交涉一下是可以得到动态的公网IP的,虽然是上下行不对等的家庭带宽,但是作为服务器来说绰绰有余,已经远超很多便宜的云服务器了。

第一次我直接跟人工客服打电话申请要一个静态公网IP被拒绝了;然后第二天我又一次联系了人工客服,询问能不能申请动态的公网IP(可以说装摄像头要用到,不能说在家里搭建服务器),结果很快就回电通知我已经给了动态的公网IP,还算顺利~

Dynamic DNS

后面使用发现,我们这边的动态的公网IP大约2~3天变动一次,这就可以通过 Dynamic DNS(DDNS) 将动态的IP解析到固定的域名,就能通过固定的域名访问到服务器了。

DDNS需要服务端和本地各运行一个服务:本地的树莓派或路由器上运行一个服务,每隔一段时间(几分钟)就获取一下自己的公网IP,判断一下有没有发生改变,如果发生改变就将新的IP发送给服务端,服务端得到新的IP,就重新将域名解析到新的IP,所以是动态DNS。

DDNS的服务端只提供域名的动态解析服务,所以服务端的带宽对访问树莓派的速度没有影响(但是可能会影响到延时?实际使用没有明显感觉到)

DDNS服务:

  1. 直接使用一些网站(oray.com/3322.org/Dyndns.com/No-ip.com)免费提供的DDNS服务,但是他们给的域名都是比较杂乱的三级域名,需要再把自己租的阿里云/腾讯云域名CNAME解析到这个三级域名。花生壳(oray.com)可以免费使用;而No-ip.com免费版需要每30天登录上去手动续约。
  2. 使用自己的vps运行脚本为树莓派提供DDNS服务,由于需要通过脚本进行域名解析,所以需要在vps上安装对应的SDK,然后开发对应的脚本,可以直接将自己的阿里云/腾讯云域名解析到家里的公网IP。

我现在是选择使用花生壳(oray.com)的DDNS服务,只需要注册即可得到一个三级域名,通过控制台->域名->壳域名来查看。我的路由器是小米4A,可以在后台设置DDNS,选择“花生壳”并输入账号密码域名和检查IP的时间间隔即可。路由器不支持的话就需要在树莓派上运行一个脚本来发送新的IP。

也有很多树莓派玩家通过每次变动IP时,让树莓派将新IP发送给自己的邮箱,使自己总是可以知道最新的IP地址。

frp内网穿透

如果也申请不到动态IP,那就只能用frp做内网穿透了,需要借助一台有公网IP的vps,流量也都需要经过这台vps,所以享受不到家庭宽带的低价大带宽了。

测试上下行带宽

可以在 speedtest.cn 测试下行带宽和上行带宽(最好直接连网线测)。

从公网访问树莓派下载文件需要的是家里的上行带宽,而家庭宽带的上行都是很低的,只能去升级下行带宽,上行才能对应的提升一点。。。。所以如果感觉带宽不太够还是要去升级一下宽带的。

我家的联通宽带就不太行,一开始是100M下行,20M上行,实测大约下行90~100M,上行20~40M。于是换了500M下行,50M上行的宽带,实测大约下行450~550M,上行60~70M

组装树莓派和安装Ubuntu

我的是树莓派4B。启动树莓派还需要有5V3A的电源和一张TF卡。

各版本树莓派的对照表:https://shumeipai.nxez.com/wp-content/uploads/2017/03/raspberrypi-version-compare-4b.png

然后就是愉快的组装环节,树莓派PCB边缘是有毛糙的,最好用砂纸磨光滑再装到壳子里。

写入Ubuntu镜像

树莓派支持安装很多种系统,我选择的是 Ubuntu Server 20.04.2 LTS 64-bit,可以在这里下载:https://ubuntu.com/download/raspberry-pi,先把镜像下载到本地,把TF卡插到读卡器里,再把读卡器插在这台电脑上。

电脑上需要安装 balenaEtcher 来为TF卡写入Ubuntu镜像,balenaEtcher 支持MacOS/Windows/Linux,可以在官网下载:https://www.balena.io/etcher/

写入镜像过程很简单,跟着点就可以,下图这样就是写入成功了。

写入完成后不需要手动推出磁盘(TF卡读卡器),可以直接拔掉,然后将TF卡插入树莓派即可。

树莓派的Wi-Fi网卡性能较弱,而搭NAS比较需要速度,所以我直接给树莓派插上了网线。这时就可以直接通电了,树莓派会自动开机。

内网SSH连接树莓派

现在就可以用电脑连接Wi-Fi并进入无线路由器的后台,应该可以看到连接路由器的所有设备的内网IP地址MAC地址,等待几分钟可以看到一个设备的名称是ubuntu,接入方式是网线连接,那么这个就是树莓派了。可以通过 ssh ubuntu@内网IP来连接树莓派,默认密码是ubuntu

公网SSH连接树莓派

接着可以设置一下路由器,为了防止树莓派的内网IP发生改变,需要在路由器后台,高级设置->DHCP静态IP分配 把树莓派的MAC地址和当前的内网IP地址绑定起来。然后为了让公网对路由器的访问都转向树莓派,高级设置->端口转发->开启DMZ,把DMZ这边的IP地址设置为树莓派的内网IP地址。这样设置完成之后,就可以通过 ssh ubuntu@公网IP 来远程控制树莓派了,公网IP地址可以通过访问 http://testipv6.com/来查看。

较为遗憾的是运营商一般会屏蔽掉80端口和443端口。。。所以如果在树莓派上起服务,只能通过端口访问,这个问题可以通过在一台有公网IP并且开着80端口的vps上配置Nginx反向代理来解决,不过这样的话流量还是要走vps,所以为了快速的下载上传文件,只能通过加着端口这种方法来访问了,不过好在最终速度理想。

初始配置

在同一文件夹下备份默认的源:

1
sudo cp /etc/apt/sources.list /etc/apt/sources.list.bak

编辑 /etc/apt/sources.list 并替换成下面的内容:(sudo vim /etc/apt/sources.list

1
2
3
4
5
6
7
8
9
# 默认注释了源码镜像以提高 apt update 速度,如有需要可自行取消注释
deb https://mirrors.tuna.tsinghua.edu.cn/ubuntu-ports/ focal main restricted universe multiverse
# deb-src https://mirrors.tuna.tsinghua.edu.cn/ubuntu-ports/ focal main restricted universe multiverse
deb https://mirrors.tuna.tsinghua.edu.cn/ubuntu-ports/ focal-updates main restricted universe multiverse
# deb-src https://mirrors.tuna.tsinghua.edu.cn/ubuntu-ports/ focal-updates main restricted universe multiverse
deb https://mirrors.tuna.tsinghua.edu.cn/ubuntu-ports/ focal-backports main restricted universe multiverse
# deb-src https://mirrors.tuna.tsinghua.edu.cn/ubuntu-ports/ focal-backports main restricted universe multiverse
deb https://mirrors.tuna.tsinghua.edu.cn/ubuntu-ports/ focal-security main restricted universe multiverse
# deb-src https://mirrors.tuna.tsinghua.edu.cn/ubuntu-ports/ focal-security main restricted universe multiverse

然后更新:

1
2
sudo apt update
sudo apt upgrade

安装KodExplorer

私有云有很多种选择:seafile、nextcloud、KodExplorer等,我使用的是KodExplorer(可道云),比较美观并且可以创建低权限用户支持预览的格式也很多。

KodExplorer

官网链接:https://kodcloud.com/,官网的文档很详细。

为树莓派安装好lamp环境,然后将KodExplorer的文件夹放到web目录下即可,开放好对应的端口之后,我们就可以通过h ttp://[域名]/[目录名] 来访问到KodExplorer了。

不过要注意的是,服务要开在其他开放了的端口(80,443会被屏蔽)。

优化上传和下载的速度,可以按照这个方法操作一下:https://teddysun.com/489.html,速度提升非常明显。

挂载硬盘

安装好KodExplorer之后,就可以上传下载文件了,不过是存到TF卡里的,作为NAS当然需要给树莓派外挂硬盘,树莓派上有两个USB3.0接口和两个USB2.0接口,可以在这里挂上移动硬盘或者是硬盘加硬盘盒。

我使用的方法是给树莓派的USB3.0接口插了一个硬盘盒,可以自己选择需要大小的硬盘放在硬盘盒里,缺点是硬盘需要12v的供电,而树莓派的USB3.0仅有5v,所以需要给硬盘盒外接一个12v电源,不是很优雅,但硬盘的容量选择很多。

首先为了让树莓派支持ExFatNTFS格式的硬盘,需要安装下面两个依赖:

1
2
sudo apt-get install exfat-fuse
sudo apt-get install ntfs-3g

然后就可以挂载硬盘了,插上硬盘之后可以 sudo fdisk -l 来查看到自己的硬盘,然后 df -h 查看已经挂载的磁盘,这时这里是没有自己的硬盘的。

1
2
sudo mkdir /www/wwwroot/[站点名称]/nas
sudo mount /dev/sda1 /www/wwwroot/[站点名称]/nas

[站点名称]换成自己的站点名称。

就可以通过 df -h 查看到已经挂载到这个目录的硬盘了,加载内核模块modprobe fuse,然后设置开机自动挂载,先 sudo vim /etc/fstab,然后在底部添加这一行:

1
/dev/sda1  /www/wwwroot/[站点名称]/nas ntfs-3g defaults,nofail,noexec,umask=0000 0 0

安装SageMath

只需要一行命令,就可以在树莓派上安装好SageMath了,也可以很方便的为SageMath里的Python环境安装第三方包。

1
2
sudo apt-get install sagemath --fix-missing
sage -python3 -m pip install pycryptodome

sage exp.sage 来直接运行sage脚本。

散热风扇改造

店家送的风扇只能直接连接树莓派的供电引脚,然后满转运行,我觉得这样很不优雅,至少应该像机箱的风扇一样,可以随着当前CPU温度改变转速,温度不太高的时候完全可以停下,可以尽可能的减小噪声,所以我开始动手改装了树莓派的风扇。

在寻找合适的树莓派风扇的过程中,偶然发现一种很强劲的小风扇:悬浮轴承、18128RPM、5V、直径3CM,这也太适合树莓派了,就选择它了。

然后是写脚本来控制转速,要利用到树莓派可玩性最高的东西——GPIO,可以用Python写脚本,并且GPIO支持pwm输出,通过调节占空比来调节转速。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
import RPi.GPIO as GPIO
import os
import time

# Configuration
PWM_BOARD_IN1 = 8 # BOARD pin used to drive PWM fan
PWM_BOARD_IN2 = 10 # BOARD pin (keep it LOW except want the fan to reverse)
WAIT_TIME = 1 # [s] Time to wait between each refresh
PWM_FREQ = 25000 # [Hz] 25kHz for PWM control

MIN_TEMP = 35
MAX_TEMP = 65
FAN_LOW = 5
FAN_HIGH = 100
TEMP_COEFFICIENT = float(FAN_HIGH - FAN_LOW)/float(MAX_TEMP - MIN_TEMP)

def setFanSpeed(speed):
fan.start(speed)

def getCpuTemperature():
res = os.popen('cat /sys/class/thermal/thermal_zone0/temp').readline()
return float(res)/1000

def handleFanSpeed(temperature):
fanSpeed = round(FAN_LOW + (round(max(0, temperature-MIN_TEMP)) * TEMP_COEFFICIENT), 3)
setFanSpeed(fanSpeed)
with open("fan_speed", "w") as f:
f.write(str(fanSpeed))

try:
GPIO.setwarnings(False)
GPIO.setmode(GPIO.BOARD)
out_list = [PWM_BOARD_IN1, PWM_BOARD_IN2]
GPIO.setup(out_list, GPIO.OUT)
GPIO.output(out_list, GPIO.LOW)
fan = GPIO.PWM(out_list[0], PWM_FREQ)
while True:
handleFanSpeed(getCpuTemperature())
time.sleep(WAIT_TIME)
except KeyboardInterrupt:
setFanSpeed(50)

为了方便实时监控到树派的转速,脚本会一直吧速度写进fan_speed文件里,供另外一个程序获取。

开发树莓派资源监控页面

资源监控这边希望可以看到树莓派的公网IP、CPU温度、CPU的资源占用情况,ROM/RAM的资源占用情况,风扇的转速。

风扇的转速写在了文件里,并且实时更新,所以这边去获取一下即可。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
.
├── assets
│   ├── css
├── images
│   ├── js
│   └── vendors
├── fan_control
│   ├── fan_control.py
│   └── fan_speed
├── pi_dashboard.py
└── templates
├── assets
│   ├── css
│   ├── images
│   ├── js
│   └── vendors
└── index.html

后端使用flask,前端用的是mazer组件库。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
from flask import Flask, render_template
import os
import re
import requests

app = Flask(__name__,template_folder='templates',static_folder="assets")

def get_cpu_temperature():
try:
res = os.popen('cat /sys/class/thermal/thermal_zone0/temp').readline()
temp = float(res)/1000
return temp
except:
return "Error"


def get_cpu_info():
try:
res = os.popen("top -bn1 1 | grep Cpu").read()
us = [float(i[:-3].strip(" ")) for i in re.findall(r"[0-9. ]*us", res)]
sy = [float(i[:-3].strip(" ")) for i in re.findall(r"[0-9. ]*sy", res)]
cpu_used = [round(x[0]+x[1], 1) for x in zip(us, sy)]
assert len(cpu_used)==4
return cpu_used
except:
return ["Error" for _ in range(4)]


def get_rom_info(Filesystem):
'''return list: [Size, Used, Avail, Use%]'''
try:
res = os.popen("df -h").read()
card_data = re.findall(Filesystem+"[0-9. KMG%]*", res)[0].split()[1:]
card_data = [i[:-1] if (i[-1]=="G" or i[-1]=="%") else i for i in card_data]
assert len(card_data)==4
return card_data
except:
return ["Error" for _ in range(4)]


def get_public_ip():
try:
res = requests.get("http://members.3322.org/dyndns/getip")
return res.text.strip()
except:
return "Error"


def get_intranet_ip():
try:
res = os.popen("hostname -i").read().split(" ")[0]
return res
except:
return "Error"


def get_ram_info():
try:
res = os.popen('free').read()
ram_info = re.findall("Mem:[0-9 ]*", res)[0].split()[1:3]
return [str(int(i)//1024) for i in ram_info]
except:
return ["Error" for _ in range(2)]


def get_fan_speed():
try:
with open("fan_control/fan_speed", "r") as f:
fan_speed = f.read()
return fan_speed
except:
return "Error"

@app.route('/')
def dashboard_page():
context = {
"cpu_used":get_cpu_info(),
"cpu_temp":get_cpu_temperature(),
"fan_speed":get_fan_speed(),
"ip_adress":{"intranet":get_intranet_ip(),"public":get_public_ip()},
"ram":get_ram_info(),
"rom":get_rom_info("/dev/mmcblk0p2"),
"external_storage":get_rom_info("/dev/sda1")
}
return render_template('index.html', **context)


if __name__ == '__main__':
app.run("0.0.0.0", 82)
# nohup gunicorn -w 4 -b 0.0.0.0:82 pi_dashboard:app &

按自己的需求写了一个简单的面板

感觉对我来说,树莓派是个比较有意思的玩具,折腾了一段时间学到了一些杂七杂八的东西,算是填补了一直以来对树莓派的好奇心。

用树莓派跑了一些脚本,虽然可以24h稳定运行,但是性能差点意思,还有一些服务由于各种原因导致在树莓派上兼容不是很好。

为了更强的性能、更稳定的运行一些服务,我后面又装了一台裸金属服务器~然后把树莓派挂咸鱼出掉了,由于树莓派在涨价,运行了半年的树莓派竟然没赔,理财产品了属于是。