七夕节开箱了一个孤寡的 RaspberryPi 单板,孤寡到没有显示器没有键盘没有鼠标。
其实很早就想搭个类似云盘的东西来当作 NAS,备份照片存文件之类的。阿里云和 BWH 的主机空间都太小,几十 G 存不了什么文件。偶然想起来我还有一块 2016 年买的东芝 2T 移动硬盘(虽然是 HDD),顺便也想实践一下物理的服务器的部署……于是购入了这个 RaspberryPi 4G 版本。

系统安装与准备

一些链接

RaspberryPi 官网 | Nextcloud Docker | MariaDB Docker | MySQL Docker

树莓派官网推荐了 Raspberry Pi Imager 这个软件,还是很好用的,电脑上插入 SD 卡就可以直接选择系统烧录。
为了后续玩机的方便,也是为了尽量避免出现我力所不能解决的奇怪问题,我还是决定了刷入默认的 RaspbianOS。

RaspiOS on arm64

明明这个树莓派是 64 位的 soc,为什么 Imager 软件里只提供了 32 位的 RaspbianOS 下载?
查了一下资料,大概是从很早开始 RaspbianOS 一直是 32 位(arm32v7 架构),最近官方才有 64 位系统的测试版,不过还处于测试阶段,考虑到从 32 位转为 64 位会导致很多系统内部结构的变化,很多程序还未兼容,存在若干问题,所以官方不推荐使用
不过拿着 64 位的 CPU 用着 32 位的系统,总感觉怪怪的……

然而,我们肯定要用到的 MySQL 和 MariaDB 都是不支持 arm32v7 架构的。所以我们不得不去尝试一下 arm64 架构的测试版。官方的 Known issues 很多是某些特定程序的问题(比如 Wolfram Mathematica 和 Minecraft)(真有人用树莓派玩 Minecraft 么),似乎对我们搭建 Nextcloud 来说问题不大。那就冲吧!!

(之所以不选择诸如 Ubuntu Server 这样的其他 arm64 操作系统,是因为它们都没法无屏幕联网启动,并且兼容性也不见得好。)

最新的下载地址可以在这里找到。

Raspbian 连接与基础配置

无屏幕启动

RapbianOS 提供了开机自动连接 Wi-Fi 的设置,只要在 sd 卡根目录下放一个 wpa_supplicant.conf 文件,写如下设置:

country=CN
ctrl_interface=DIR=/var/run/wpa_supplicant GROUP=netdev
update_config=1

network={
  ssid="你的 Wi-Fi 名字"
  psk="你的 Wi-Fi 密码"
  key_mgmt=WPA-PSK
  priority=1
}

开机就可以自动连接 Wi-Fi。在路由器后台找到这个设备的 ip 就可以 ssh 了。

至于 ssh 的开启,同样在 sd 卡根目录下放一个名为 ssh 的空白文件即可。

ssh 默认的用户是 pi,密码是 raspberry,虽然这个用户有 root 权限,但是我还是一般习惯直接用 root 用户登录。第一次登录之后进入 /etc/ssh/sshd_config,增加 PermitRootLogin yes,之后就可以直接用 root 登录。

VNC 连接

ssh 连接之后运行 raspi-config,可以看到一个命令行里的 GUI 界面……
进入 Interface Options 里面可以打开 VNC 服务。
如果 VNC 桌面连接不上,一般是分辨率问题,要进入 Display Options 修改 Resolution(分辨率)。

使用清华源

虽然 RaspbianOS 里的 apt 似乎内置了类似 fastest-mirror 插件,但是我这里最近的镜像会选择浙大的镜像(mirrors.zju.edu.cn),而浙大的镜像在我使用期间似乎挂了 =_= 我们最好手动去换清华的镜像。

清华的 RaspiOS 镜像页面是这么说的:

# 编辑 `/etc/apt/sources.list` 文件,删除原文件所有内容,用以下内容取代:
deb http://mirrors.tuna.tsinghua.edu.cn/raspbian/raspbian/ buster main non-free contrib rpi
deb-src http://mirrors.tuna.tsinghua.edu.cn/raspbian/raspbian/ buster main non-free contrib rpi

# 编辑 `/etc/apt/sources.list.d/raspi.list` 文件,删除原文件所有内容,用以下内容取代:
deb http://mirrors.tuna.tsinghua.edu.cn/raspberrypi/ buster main ui

然而!!!arm64 的 RaspiOS 改了,直接这么改是不行的,/etc/apt/sources.list文件里应该直接用 debian 的镜像

# 默认注释了源码镜像以提高 apt update 速度,如有需要可自行取消注释
deb https://mirrors.tuna.tsinghua.edu.cn/debian/ buster main contrib non-free
# deb-src https://mirrors.tuna.tsinghua.edu.cn/debian/ buster main contrib non-free
deb https://mirrors.tuna.tsinghua.edu.cn/debian/ buster-updates main contrib non-free
# deb-src https://mirrors.tuna.tsinghua.edu.cn/debian/ buster-updates main contrib non-free

deb https://mirrors.tuna.tsinghua.edu.cn/debian/ buster-backports main contrib non-free
# deb-src https://mirrors.tuna.tsinghua.edu.cn/debian/ buster-backports main contrib non-free

deb https://mirrors.tuna.tsinghua.edu.cn/debian-security buster/updates main contrib non-free
# deb-src https://mirrors.tuna.tsinghua.edu.cn/debian-security buster/updates main contrib non-free

/etc/apt/sources.list.d/raspi.list 文件还是按上文所述不变。

编辑后用 sudo apt-get update 命令更新一下。

导入公钥

哎呀,出错了:

Get:1 http://mirrors.tuna.tsinghua.edu.cn/raspbian/raspbian buster InRelease [15.0 kB]
Hit:2 http://mirrors.tuna.tsinghua.edu.cn/raspberrypi buster InRelease
Err:1 http://mirrors.tuna.tsinghua.edu.cn/raspbian/raspbian buster InRelease
  The following signatures couldn't be verified because the public key is not available: NO_PUBKEY 9165938D90FDDD2E
Reading package lists... Done
W: GPG error: http://mirrors.tuna.tsinghua.edu.cn/raspbian/raspbian buster InRelease: The following signatures couldn't be verified because the public key is not available: NO_PUBKEY 9165938D90FDDD2E
E: The repository 'http://mirrors.tuna.tsinghua.edu.cn/raspbian/raspbian buster InRelease' is not signed.
N: Updating from such a repository can't be done securely, and is therefore disabled by default.
N: See apt-secure(8) manpage for repository creation and user configuration details.

(32 位的 PaspiOS 没有这个问题)似乎 64 位版本的用了不一样的安全机制,搜了一圈找到了解决办法

sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys 9165938D90FDDD2E
sudo gpg --export --armor 9165938D90FDDD2E | sudo apt-key add -

初步配置与安装更新

VNC 桌面连接以后就可以根据图形界面的提示进行初步的配置啦。

(如果之前没配置镜像,这里的 update 就会需要很久……很久……很久……)

磁盘挂载

fdisk -l 查看移动硬盘的盘符:

Disk /dev/sda: 1.8 TiB, 2000398931968 bytes, 3907029164 sectors
Disk model: External USB 3.0
Units: sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disklabel type: dos
Disk identifier: 0x00000000

Device     Boot Start        End    Sectors  Size Id Type
/dev/sda1        2048 3907028991 3907026944  1.8T  7 HPFS/NTFS/exFAT

看到是 /dev/sda1

为了方便,把移动硬盘挂载到 /mnt/sda1/ 目录下:

mount /dev/sda1 /mnt/sda1/

本地 Nextcloud 搭建

两种方式

我尝试了两种方式搭建这个 Nextcloud(可实在是太折腾了(吐血)):

方式一:Docker 部署 Nextcloud

这种方法很大的一个优点就是部署简单。缺点就是由于这个 Docker 里面附带了 Apache,如果需要一些拓展的配置就会很麻烦(下文会看到)。并且需要另外装数据库。

方式二:LNMP + Nextcloud

这种方法是最折腾的,但是是我比较熟悉的(并没有),因为我的阿里云服务器(就是运行了当前这个网站的服务器)就是 LNMP 的架构。配置起来确实很麻烦,但是有一种 Everything in control 的感觉。

由于第二种方法实在是太复杂,配置的时候 MySQL 出现了各种玄学的问题(事实上是第一次实验的时候用的 32 位系统,MySQL 高版本不支持,所以用脚本死活装不上,后来才想起来),Nginx 也要进行很多复杂的配置,决定还是用第一种方法吧。

安装配置 Docker

对了,先最好安装个 screen,养成好习惯。

脚本自动安装 Docker:

curl -fsSL https://get.docker.com | bash -s docker --mirror Aliyun

Docker 安装 MariaDB

Nextcloud 支持 MySQL 和 MariaDB。后者没有体验过,据说是 MySQL 的一个分支,性能比 MySQL 更好,这次尝试一下吧。

首先

docker pull mariadb:latest

然后运行容器创建一个数据库:

docker run --name mariadb -d \
  -p 127.0.0.1:3306:3306
  -e MYSQL_ROOT_PASSWORD="你的密码" \
  -v /mnt/sda1/SkyCloud/database:/var/lib/mysql \
  --restart=always \
  -d mariadb:latest

-v /mnt/sda1/SkyCloud/database:/var/lib/mysql 的意思是把默认的数据卷存放位置映射到 /mnt/sda1/SkyCloud/database 也就是我的硬盘里的一个目录下,让数据库也存在硬盘里。

Docker 安装 phpMyAdmin

为了测试数据库是否正常,以及为了让我这个完全不会 SQL 命令的人进行创建数据表、更改权限等操作,再安装一个 phpMyAdmin 吧。

按照说明一样 pull 之后直接 link 那个 mariadb 的进程:

docker run --name phpmyadmin -d --link mariadb:db -p 8080:80 phpmyadmin

访问 你的IP:8080 登录就可以管理数据库。进入后台创建一个叫做 nextcloud 的用户和同名数据库。

Docker 安装 Nextcloud

重头戏来啦!

刚才把移动硬盘挂载到了 /mnt/sda1,为了让文件完全存储在移动硬盘里:

docker run -d --name nextcloud \
  -p 80:80 \
  --restart=always \
  -v /mnt/sda1/SkyCloud/html:/var/www/html \
  -v /mnt/sda1/SkyCloud/data:/var/www/data \
  -v /mnt/sda1:/SkyDisk \
  --link mariadb:mysql \
  nextcloud

访问 http://你的IP 应该就可以看到 Nextcloud 的配置界面了。

需要注意的是这里把 mysql 映射到了 mariadb 的进程,所以(根据官方的 Docker 文档)在 Nextcloud 的配置界面,数据库主机的 localhost 应该改成 mysql

玄学错误

Doctrine\DBAL\Exception\DriverException: An exception occurred while executing a query: SQLSTATE[HY000]: General error: 4047 InnoDB refuses to write tables with ROW_FORMAT=COMPRESSED or KEY_BLOCK_SIZE.

看起来某个地方没有权限呢,Nextcloud 论坛上有人遇到了一样的问题(参考这里),只要修改 innodb read only compressed 就可以了。

唉,MariaDB 还真是麻烦。

 title=

之后 Nextcloud 自动配置,进入仪表盘,就代表本地的安装成功啦!!

内网穿透与反向代理

Frp 内网穿透

本地的这个 Nextcloud 只有内网可以访问,我们需要用一台 VPS 做内网穿透。

release 页面下载 frp 的对应架构的版本,我这里树莓派下载 linx_arm64 版本,阿里云的 VPS 上下载 amd64 的版本(相当于 x86_64,具体架构啥的我也不太懂),树莓派是客户端,阿里云是服务端。

服务端设置

编辑 frps.ini:

[common]
bind_port = 7000
dashboard_port = 7500
token = 和客户端一致的 token
dashboard_user = admin
dashboard_pwd = yourPassword
vhost_http_port = 10080
vhost_https_port = 10443

bind_port 是监听端口,和客户端一致;dashboard_port 是控制面板的端口,可以直接访问查看 frp 的状态;token 用于验证,也要喝客户端一致;之后是控制面板的用户名密码端口设置。

之后用这个配置文件开启 frp 服务器:

./frps -c frps.ini

访问 IP:7500 可以看到一个面板,显示 frp 的各种运行状态。

客户端设置

[common]
server_addr = 服务端的 IP
server_port = 7000
token = 和服务端一致的 token

[nextcloud]
type = tcp
local_ip = 127.0.0.1
local_port = 80
remote_port = 7001

同样本地开启:

./frps -c ./frpc.ini

Nginx 反向代理

大概直接转发就好了,我用的配置文件是这样的:

server
    {
        listen 80;
        #listen [::]:80;
        server_name cloud.skywt.cn ;
        return 301 https://$server_name$request_uri;
        access_log off;
    }

server
    {
        listen 443 ssl http2;
        #listen [::]:443 ssl http2;
        server_name cloud.skywt.cn ;
        index index.html index.htm index.php default.html default.htm default.php;
        root  /home/wwwroot/cloud.skywt.cn;

        ssl_certificate /usr/local/nginx/conf/ssl/cloud.skywt.cn/fullchain.cer;
        ssl_certificate_key /usr/local/nginx/conf/ssl/cloud.skywt.cn/cloud.skywt.cn.key;
        ssl_session_timeout 5m;
        ssl_protocols TLSv1 TLSv1.1 TLSv1.2 TLSv1.3;
        ssl_prefer_server_ciphers on;
        ssl_ciphers "TLS13-AES-256-GCM-SHA384:TLS13-CHACHA20-POLY1305-SHA256:TLS13-AES-128-GCM-SHA256:TLS13-AES-128-CCM-8-SHA256:TLS13-AES-128-CCM-SHA256:EECDH+CHACHA20:EECDH+CHACHA20-draft:EECDH+AES128:RSA+AES128:EECDH+AES256:RSA+AES256:EECDH+3DES:RSA+3DES:!MD5";
        ssl_session_cache builtin:1000 shared:SSL:10m;
        # openssl dhparam -out /usr/local/nginx/conf/ssl/dhparam.pem 2048
        ssl_dhparam /usr/local/nginx/conf/ssl/dhparam.pem;

        location ~ /.well-known {
            allow all;
        }

        location / {
            index  index.html index.htm;
            proxy_set_header Host $http_host;
            proxy_set_header Upgrade $http_upgrade;
            proxy_set_header X-Real-IP $remote_addr;
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
            proxy_pass http://47.97.208.20:7001/;
        }

        access_log off;
    }

反思

折腾的过程中实在是遇到了非常多的困难,感觉自己的问题主要是各种知识的学习不系统,关于文件权限、SQL 命令、数据库等等基础知识都是不懂的,在折腾的过程中也没耐心学。

学习的过程本身是很枯燥的,只有这种应用的过程由于成就感会吸引人一点,但是应用实践却是要以学习为基础的,没有基础各种知识的系统的学习,在应用实践的时候肯定也是不顺利的。有计划、有系统的学习,而非服务于应用的学习,才是进步的最快方式。