使用Mac mini作为服务器的经验之谈

写在开头

由于 受到库老头远程蛊惑施法 在观察了一些视频以及发现原先使用的Orange Pi 5 Plus有一些性能不足的问题,因此趁着国补采购了一个Mac mini M4丐版(16G+256G)。建议各位理性购买,确认自己有需求后再下手。

本文章将从开箱,服务迁移与部署,问题排障展开。

开箱

Mac mini的采购来源是Apple京东自营店,叠加上海地区国补和教育优惠,原价3749元,于4月20日在高铁上2999元拿下。原本提示需要调货,4月25日送达,结果4月21日下午就送达了。

由于是国家补贴,需要当面和快递员拆箱。快递员似乎从来没有见过Mac mini,于是要求我插上屏幕登录Apple ID。我进行反复解释说明,他最终致电询问他人,对着我的包装,三包凭证,保修卡反复拍照,接着交货完成。擦锭开机,接入有线网络,显示器,进行默认初始化设置进入桌面。

如果要把Mac mini作为服务器,显然不会经常插入显示器,因此我参考这个Bilibili视频完成了虚拟显示器与iPad随航的配置。接着在Unifi后台为Mac mini指定静态IP并绑定内网mDNS名称。

服务迁移与部署

首先简单列出一下我目前需要部署的服务(其余支持软件将在下文列出)

其余支持软件

  • 1Password - 密码管理器
  • MCSManager - Minecraft服务器管理器
  • Orbstack - Docker Desktop替代
  • Sublime Text - 文本编辑器
  • Navicat Premium - 数据库查看编辑器
  • MySQL - 数据库
  • App Cleaner & Uninstaller - 应用卸载器
  • Surge - 代理与网络工具
  • Homerow - 键盘控制工具
  • Lingon X - 登录项与计划任务管理
  • Bartender 5 - 屏幕顶栏管理
  • SwitchHosts - Host文件管理
  • Tailscale - 异地组网
  • cloudflared - Cloudflare隧道
  • RustDesk - 远程控制
  • BetterDisplay - 虚拟显示器
  • BetterTouchTool - 键盘快捷键
  • rclone - 文件向对象存储备份

本次部署时尝试的方法优先级:Docker Compose -> Docker Run -> Binary - Application - Virtual Machine。我非常建议各位在自己的VPS或服务器上部署的时候也尝试类似的方案,个人认为这样有助于管理和下次迁移服务。

此外,强烈推荐在macOS上使用OrbStack来代替Docker Desktop,原因有很多。首先,OrbStack拥有比Docker Desktop更加小的占用(内存和储存)此外,OrbStack无须配置即可在macOS上使用net=hosts这个配置,并且在网络方面,自动接管容器内的流量走系统代理(docker pull等二进制操作依然需要增强模式)(关于不使用增强模式的原因将在排障部分讲解)并为每个容器分配一个自动分配HTTPS证书的*.orb.local的域名。

大部分服务都很快速的通过Docker Compose迁移上线,将原本在Orange Pi上通过.NET运行的Microsoft 365 E5 Renew X改为了Docker运行。

但是在部署MCSManager的时候遇到了阻碍,原因是因为官方没有对macOS进行适配,只能使用Linux或者Windows。官方建议使用Docker部署,但是我发现Docker容器内部的文件我难以管理,尤其是对于实例的文件管理更是难上加难。因此我退而求其次,选择由OrbStack提供的虚拟机功能实现。

OrbStack的虚拟机非常的神奇我称之为msl(macOS Sub-system Linux),他会自动透传Mac的用户文件夹,并且在运行效率很高,对于我轻量供朋友们使用的Minecraft服务器来说不造成性能损失。

在先前的Orange Pi上,我是用宝塔面板内置的备份功能,将部分文件进行本地+OneDrive双重备份来保证文件安全。在macOS上我经过调研后选择了rclone这款软件来实现类似的操作。为此我写了如下的脚本来处理备份问题,可以实现文件上传,保留N个最新的备份,排除文件,使用<script path> <source_path> <backup_dest_path> <retain_count> [exclude_file]。其中exclude_file是一个纯文本文件,里面保存着一行一个不需要被备份的文件/文件夹路径(相对于source_path)。需要将里面的onedrive:替换为自己的config名称。

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
#!/bin/bash

if [ "$#" -lt 3 ]; then
echo "Usage: $0 <source_path> <backup_dest_path> <retain_count> [exclude_file]"
exit 1
fi

SOURCE_PATH="$1"
DEST_PATH="$2"
RETAIN_COUNT="$3"
EXCLUDE_FILE="$4"

SOURCE_DIR_NAME=$(basename "$SOURCE_PATH")
BACKUP_FILE="/tmp/${SOURCE_DIR_NAME}_backup_$(date +%Y-%m-%d_%H-%M-%S).tar.gz"

EXCLUDE_OPTS=""
if [ -n "$EXCLUDE_FILE" ]; then
if [ ! -f "$EXCLUDE_FILE" ]; then
echo "Error: Exclude file '$EXCLUDE_FILE' not found!"
exit 1
fi
while read -r line; do
EXCLUDE_OPTS="$EXCLUDE_OPTS --exclude=$SOURCE_PATH/$line"
done < "$EXCLUDE_FILE"
fi

echo "Creating backup of $SOURCE_PATH..."

tar -czvf "$BACKUP_FILE" $EXCLUDE_OPTS "$SOURCE_PATH"

echo "Uploading $BACKUP_FILE to $DEST_PATH..."
rclone copy "$BACKUP_FILE" "onedrive:$DEST_PATH"

echo "Cleaning up old backups, keeping only the latest $RETAIN_COUNT..."
rclone lsl "onedrive:$DEST_PATH" | sort -n | tail -n +$((RETAIN_COUNT+1)) | awk '{print $NF}' | while read file; do
echo "Deleting old backup: $file"
rclone delete "onedrive:$DEST_PATH/$file"
done

rm "$BACKUP_FILE"

echo "Backup process completed successfully."

接着我使用Lingon X来将其添加为计划任务,不使用crontab的原因是因为在macOS上已经被Launch DaemonLaunch Agent替换掉了,因此使用新的总归没错。

问题排障

RustDesk提示远程主机关闭了连接

这个问题我先说结论,无论是手动编译的二进制文件还是Docker都存在这个问题,其核心原因是因为启用了代理软件的增强模式,我已测试了mihomo partySurge均存在此问题,这也是使用OrbStack其中的一个重要原因,因为他会自动接管容器内的流量走系统代理。

此外,还会出现提示ip/pk not match,原因是你可能在尝试替换密钥对,但是没有替换成功,在替换的时候需要确保容器/二进制已经关机

在此处放上根据1.1.14版本构建的macOS arm64的编译文件:点击下载

Tailscale Derper提示证书不匹配

由于我使用DDNS,因此我的域名类似于*.ddns.domain.com,每一个服务占用一个独立的三级域名,我在Let's Encrypt申请的是\*.ddns.domain.com,因此当使用derper.ddns.domain.com会显示证书不匹配,解决方案如下

找到~/go/pkg/mod/tailscale.com@xxxxx/cmd/derper/cert.go找到func (m *manualCertManager) getCertificate这段内容,把第一个if报错逻辑段注释掉,重新使用go build -o /path/to/derper构建即可,我这里同样放一个构建好的macOS arm64二进制:点击下载

macOS防火墙控制

不建议在macOS上使用系统自带的防火墙,经过一周左右的实验,通过launchctl启动的守护进程如RustDesk的hbbrhbbs有概率会因为重新启动且未经过签名被macOS自带防火墙反复拦截

在一番探索后,我选择了基于macOS自带的pf工具的GUI界面Murus Pro来进行管理,实现类似于Ubuntu上ufw的基于端口,来源的防火墙工具(而不是macOS自带防火墙基于应用)。以下是一张使用截图

特别提醒:在启用防火墙前,请确认已经放通远程桌面等端口,否则将丢失连接

Murus Pro