面向教室与创客空间:使用 LTSP 替代 PiServer 搭建树莓派无盘课堂环境


Raspberry Pi 5

一、前言

之前我一直使用 PiServer 来做为创客空间的树莓派设备部署方案,有很多好处,不需要使用 SD 卡、统一系统镜像、集中管理学生账户,所有人的 home 文件夹都挂载在 Server 上,学生可以使用任一台树莓派登录自己的账户,就可以看到自己的文件内容。

Githun 上的 PiServer PiServer 虽然好用,它有图形界面,便于设置使用,但是由于它太久没有维护更新,当我把 Server 树莓派上的系统升级为 Raspberry Pi OS Trixie 后,它无法正常工作了,不得已,我不得不尝试寻找一个 PiServer 的替代方案,这就是本文的初衷。

后来我发现了 LTSP,与 PiServer 不同,LTSP 没有图形界面,在使用方面没有那么便捷,而且它也不是专为树莓派设计的,需要修改很多的配置,还会遇到一些问题。 本篇文章就是记录我的完整部署过程,希望可以给你提供一些有价值的参考。

在你开始部署之前,建议你先看一些本文的 [[面向教室与创客空间:使用 LTSP 替代 PiServer 部署树莓派无盘课堂环境#五、问题与排错]] 部分,再决定要不要开始。

二、整体架构概览

graph LR
    subgraph Internet
        WAN[互联网]
        WAN --- RA[路由器 A]
    end

    subgraph LAN[教室内局域网]
	    RA --- RB[路由器 B]
	    RB --- SW[交换机(可选)]
        RB --- RC[路由器 C(可选)]

        SW --- S[树莓派 Server]
        SW --- C1[树莓派 Client 1]
        SW --- C2[树莓派 Client 2]
        SW --- C3[树莓派 Client n...]
    end

其中:

  • 路由器 A 正常接入互联网。
  • 路由器 B 是一台多网线插口的路由器,传输速率越高,树莓派 Client 启动速度越快,本路由器需要关闭 DHCP 功能,所以不建议省略路由器 A,为了不影响其他设备联网,建议通过路由器 B 把教室内的设备组成单独的局域网。
  • 交换机 为可选设备,全部树莓派必须使用网线连接,不可以使用 Wi-Fi,当路由器 B 接口不够用时可以加入交换机
  • 路由器 C 为可选设备,当有其他设备需要 Wi-Fi 连接,而路由器 B 又没有 Wi-Fi 功能时可以额外加入一台带有 Wi-Fi 功能的路由器 C
  • 树莓派 Server 应该使用安装了 SSD 的树莓派 5,这里因为读写速度限制,不应该使用 SD 卡作为树莓派 Server 的硬盘。
  • 树莓派 Client 可以是树莓派 4/5,理论上树莓派 3 也可以,不过我没有测试,而且树莓派 3 与树莓派 4/5 相比需要额外的配置。

三、准备工作

硬件部分
  • 树莓派 5 一台作为树莓派 Server
  • 树莓派 M.2 HAT+ 扩展版和与之适配的 SSD 硬盘。
  • 其他作为 Client 的树莓派们,设置好 Network Boot。
  • 路由器和交换机等,参考 二、整体架构概览 部分。
软件部分
  • 树莓派 Server 安装操作系统,Raspberry Pi OS Trixie 64-bit,建议提前下载好镜像文件,然后使用 Raspberry Pi Imager 烧录到 SSD,这样可以保留镜像文件在为树莓派 Client制作 Chroot 时使用。
  • 提前下载好用于树莓派 Client 的系统镜像文件,我使用的是和树莓派 Server 相同的Raspberry Pi OS Trixie 64-bit 系统。
网络部分
  • 路由器 B 设置静态 IP:192.168.5.1,当然你也可以设置成你自己的 IP,只不过后面涉及到 IP 地址的命令会以我的设置为例,如果你设置成和我相同的 IP,复制命令时会很方便,不需修改。
  • 树莓派 Server 编辑网络设置,IPv4 设置为手动(Manual),添加条目 192.168.5.2 | 24 | 192.168.5.1 DNS:192.168.5.1。
  • 路由器 B 关闭 DHCP 功能。
  • 所有树莓派设备与路由器或交换机连接必须使用网线。

四、部署过程

以下步骤是在 LTSP 官方文档的基础上,加入了我自己的实践。

1. 切换 root 用户

以下步骤中的命令都应该使以 root 身份执行,所以先运行:

sudo -i
2. 添加软件包仓库

官方推荐,可以获取 LTSP 最新的稳定版。

wget https://ltsp.org/misc/ltsp-ubuntu-ppa-focal.list -O /etc/apt/sources.list.d/ltsp-ubuntu-ppa-focal.list
wget https://ltsp.org/misc/ltsp_ubuntu_ppa.gpg -O /etc/apt/trusted.gpg.d/ltsp_ubuntu_ppa.gpg
apt update
3. 安装 LTSP server 软件包
apt install --install-recommends ltsp ltsp-binaries dnsmasq nfs-kernel-server openssh-server squashfs-tools ethtool net-tools epoptes
gpasswd -a administrator epoptes

请将 administrator 替换为实际的管理员用户名。
如果你未使用 PPA,请将 ltsp-binaries 替换为 ipxe。 上述软件包说明

ltsp 包含 LTSP 主体代码,是 LTSP 服务器和 LTSP 客户端共同使用的核心组件。

ltsp-binaries 包含 iPXE 与 memtest 的二进制文件。

dnsmasq 提供 TFTP,以及可选的(代理)DHCP 与 DNS 服务。
可替代方案包括 isc-dhcp-servertftpd-hpa,但只有 dnsmasq 支持 proxyDHCP,因此为推荐默认选项。

nfs-kernel-server 通过 NFS 向客户端导出虚拟磁盘映像。

openssh-server 允许客户端通过 SSHFS 认证并访问 /home

ethtool、net-tools 可用于禁用以太网流控(Ethernet flow control),在服务器是千兆网、客户端为百兆网时可提升局域网性能。

epoptes(可选) 提供客户端监控与远程控制功能。
gpasswd 用于赋予系统管理员运行 Epoptes 的权限。

4. 准备用于 Client 的 Chroot

将下载好的 Raspberry Pi OS 镜像压缩文件拷贝到 server 上,打开终端,在同一目录里运行解压命令:

xz -dk 2025-11-24-raspios-trixie-arm64-full.img.xz

你要把文件名换成你自己下载的镜像压缩文件,解压完成后你会得到一个同名的 .img 镜像文件。 然后运行以下命令,别忘了切换 root 身份,别忘了切换到镜像文件所在目录。

# 安装 time,可选。
apt install time
# 创建 chroot 根目录。
mkdir -p /srv/ltsp/raspios
# 挂载镜像并拷贝 root 文件,如果挂载失败可以尝试修改 loop8 为其他数字。
losetup -rP /dev/loop8 2025-11-24-raspios-trixie-arm64-full.img
mount -o ro /dev/loop8p2 /mnt
time cp -a /mnt/. /srv/ltsp/raspios
umount /mnt
# 如果不删除 boot 文件夹,后面的拷贝命令无法顺利执行。
rm -r /srv/ltsp/raspios/boot
# 挂载并拷贝 boot 文件。
mount -o ro /dev/loop8p1 /mnt
cp -a /mnt/. /srv/ltsp/raspios/boot/
umount /mnt
losetup -d /dev/loop8

此时,Raspberry Pi OS 应当已经位于 /srv/ltsp/raspios
但该 chroot 仍未准备好用于网络启动,需要执行以下命令:

# 进入 chroot,以便使用相对路径
cd /srv/ltsp/raspios
# 屏蔽在 chroot 环境中不需要的服务,屏蔽的服务将来在 Client 端不会运行。
systemctl mask --root=. dhcpcd dphys-swapfile raspi-config resize2fs_once NetworkManager-wait-online
# 移除与 SD 卡相关的 fstab 条目
echo 'proc /proc proc defaults 0 0' >./etc/fstab
# 为 NFS_RW 网络启动模式写入适用的内核命令行参数
echo 'ip=dhcp root=/dev/nfs rw nfsroot=192.168.5.2:/srv/ltsp/raspios,vers=3,tcp,nolock console=serial0,115200 console=tty1 elevator=deadline fsck.repair=yes rootwait quiet splash plymouth.ignore-serial-consoles modprobe.blacklist=bcm2835_v4l2' >./boot/cmdline.txt
5. 准备 Server
dnsmasq

使用以下命令生成默认的 dnsmasq 配置文件。

ltsp dnsmasq

修改 /etc/dnsmasq.d/ltsp-dnsmasq.conf 文件。以下是全部需要修改或新增的地方,默认配置文件中的其他部分不需要改动。

# IP ranges to hand out, usually on the internal LTSP subnet of 2-NIC setups
dhcp-range=192.168.5.101,192.168.5.200,12h

# Specify the DNS server. 0.0.0.0 means the machine running dnsmasq.
# DNS_SERVER in ltsp.conf is preferred as it reaches proxy DHCP clients.
dhcp-option=option:dns-server,192.168.5.1
dhcp-option=option:router,192.168.5.1

#这里填入全部 Client 设备的 Mac 地址,每行一个。
dhcp-mac=set:rpi,d8:3a:*:*:*:*
ltsp.conf

使用以下命令生成 ltsp.conf 文件

install -m 0660 -g sudo /usr/share/ltsp/common/ltsp/ltsp.conf /etc/ltsp/ltsp.conf

/etc/ltsp/ltsp.conf 中添加以下内容:

[server]
# ltsp kernel 会依据该名称,从 /srv/ltsp/raspios/boot/* 生成指向 /srv/tftp/* 的相应符号链接。
RPI_IMAGE="raspios"
# 通过 NFS 挂载 /home。
NFS_HOME=1

[clients]
# 配置文件中已有,取消注释即可。通过 NFS 挂载 /home。
FSTAB_HOME="server:/home /home nfs defaults,nolock 0 0"
# 用于隐藏登陆界面中的用户名列表
LIGHTDM_CONF=”greeter-hide-users=true“
初始化

接下来,运行以下构建命令:

ltsp kernel raspios
ltsp initrd
ltsp nfs
6. 以 NFS_RW 模式从网络启动

此时,我们已经可以以 NFS_RW(读写 NFS)模式启动单个客户端。 这意味着在该客户端上进行的任何更改,如安装新软件,都会直接保存到服务器的 /srv/ltsp/raspios 中。 首先,以 NFS 可读写模式发布该 chroot:

echo '/srv/ltsp/raspios *(rw,async,crossmnt,no_subtree_check,no_root_squash,insecure)' >/etc/exports.d/ltsp-raspios.exports
exportfs -ra

现在启动一台 Client,向其软件源中 添加 LTSP PPA(参考[[面向教室与创客空间:使用 LTSP 替代 PiServer 部署树莓派无盘课堂环境#2. 添加软件包仓库]]),并安装客户端所需的软件包:

apt install --install-recommends ltsp epoptes-client

修改 /etc/hosts 文件,添加以下内容:

sudo nano /etc/hosts
192.168.5.2        server

从服务器获取 OpenSSL 证书

sudo epoptes-client -c

一切正常的话,你可以看到 Successfully

接下来你可以在 Client 安装需要的软件,比如,输入法,进行一些设置,比如,键盘布局,语言地区。

Client 设置好以后,关机。

7. 切换到 LTSP 模式启动

此时,chroot 中已经包含 LTSP 代码,并已具备网络启动的条件。
但它所需的内核命令行与 NFS_RW 模式不同,因此请执行以下命令:

echo 'ip=dhcp root=/dev/nfs nfsroot=192.168.5.2:/srv/ltsp/raspios,vers=3,tcp,nolock init=/usr/share/ltsp/client/init/init ltsp.image=images/raspios.img console=serial0,115200 console=tty1 elevator=deadline fsck.repair=yes rootwait quiet splash plymouth.ignore-serial-consoles modprobe.blacklist=bcm2835_v4l2' >/srv/ltsp/raspios/boot/cmdline.txt

# 最后,制作只读镜像。
ltsp image raspios --mksquashfs-params='-comp lzo'
8. 添加用户

在 Server 端使用命令添加用户,即可在 Client 端登录。

adduser user1

添加用户后,需要重新执行构建命令,不需要重新生成镜像。用户修改密码可以在 Server 端,也可以在 Client 端执行,修改密码后需要执行构建命令。

ltsp kernel raspios
ltsp initrd
ltsp nfs

五、问题与排错

以下是目前我发现的一些问题,以及部分问题的解决办法,希望可以让你少走些弯路,也可以先看一下这部分问题,你是否能够接受,再决定要不要开始部署 LTSP。

问题 1 Scratch 中与 GPIO 相关的扩展积木问题
问题描述

在 Client 端使用 Scratch 时,当添加或运行与 GPIO 相关的扩展积木时,例如,扩展 Simple Electronics 中的 When button 0 is pressed,会出现 Scratch 白屏崩溃的情况。

解决办法

这一版是由于 Client 端登录的用户的权限不正确导致的,在 Server 端创建的用户默认不属于 gpio 用户组,需要将用户加入组。 先在 Server 或 Client 上检查用户是否在 gpio 组:

id user

你需要看到里面包含:

... groups=...,20(dialout),29(audio),44(video),... ,997(gpio),...

如果没有 gpio (以及你想用 I2C/SPI 也最好在 i2cspi),在 Server 上执行:

sudo usermod -aG gpio,i2c,spi 你的用户名

添加用户组后执行构建命令。

问题 2 epoptes 无法启动
问题描述

在 Server 端打卡 epoptes 时没有任何反应。

解决办法

在 Server 终端中运行 epoptes,

epoptes

查看终端中是否出现错误信息:

user@raspberrypi:~$ epoptes cert
Traceback (most recent call last):
  File "/usr/bin/epoptes", line 20, in <module>
    from epoptes.ui import gui
  File "/usr/lib/python3/dist-packages/epoptes/ui/gui.py", line 8, in <module>
    from distutils.version import LooseVersion
ModuleNotFoundError: No module named 'distutils'

如果出现此错误信息,说明是由于 Python 版本不适配导致的,可以通过直接修改 epoptes 源码来解决。把 /usr/lib/python3/dist-packages/epoptes/ui/gui.py 里对 distutils.versionpipes 的依赖换掉。

  1. 打开文件(路径来自你的报错栈):
sudo nano /usr/lib/python3/dist-packages/epoptes/ui/gui.py
  1. 使用 Ctrl+F 查找文件中的 pipes 并全部替换成 shlex 共有两个地方需要修改,其中之一是 import 部分,另一个是 pipes.xxx 方法。

  2. 保存并退出。

现在 epoptes 可正常运行。

问题 3 epoptes 中无法广播屏幕
问题描述

在 Server 端无法向 Client 广播屏幕,也无法查看 Client 端屏幕。这是由于安装的 TigerVNC 1.15.0 不支持树莓派的 Wayland 服务。理论上安装 TigerVNC 1.60.0 beta 应该可以解决,不过我还没有尝试。

问题 4 Client 启动时卡在 wayvnc-control.service
问题描述

当 Client 启动时会会卡咋某些 service 上,停留时间比较久,虽然最终可以进入系统,但是耗时较长,整个过程大约需要数分钟,当然可能与我的网线和路由器网口速率比较慢有一定关系,但是 wayvnc-control.service 和 cloud-init.service 卡住较长时间不是网络速率慢的原因,我没有尝试在生成 chroot 镜像时屏蔽这些卡住的 service,因为我担心带来更多的问题。目前没有好的解决办法。