
1. 为什么要在树莓派 5 上折腾 Docker树莓派 5 这代硬件提升挺明显的四核 Cortex-A76 跑到 2.4GHz内存从 4GB 起步最高能上 8GBPCIe 接口也开放了。这意味着它不再只是跑跑脚本、点个灯的玩具板而是能真正承担起家庭服务器、轻量级容器主机的角色。我手头这台 8GB 版本日常跑着几个 Docker 容器包括一个反向代理、一个数据库、一个自建笔记服务负载长期在 1.5 以下温度控制在 55 度左右相当稳。Docker 在树莓派上的价值说白了就是把环境打包成标准件。以前在树莓派上装个 MySQL得手动编译、调参数、处理依赖冲突换台机器又得重来一遍。用 Docker 之后一条docker run就能拉起一个 MySQL 8.0数据目录挂载到宿主机配置文件用 volume 映射迁移的时候把目录一拷就完事。对于树莓派这种 ARM 架构的设备来说Docker 还有个额外好处官方镜像仓库里已经有大量 ARM64 架构的镜像不需要自己从头编译省了大量时间。这篇文章适合谁看如果你刚拿到树莓派 5想把它变成一台能跑多种服务的家庭服务器或者你之前只在 x86 电脑上用过 Docker想搞清楚 ARM 平台上的差异和坑那这篇内容就是给你准备的。我会从系统准备开始一步步讲到 Docker 安装、日常管理、常见故障排查中间穿插我自己踩过的坑和实测有效的解决方案。2. 安装前的系统准备与关键决策2.1 系统选择64 位是硬性前提树莓派 5 支持 64 位系统这一点非常重要。Docker 官方从 2020 年起就不再维护 32 位 ARM 镜像了如果你刷的是 32 位 Raspberry Pi OS装 Docker 的时候会遇到各种奇怪的依赖问题即使勉强装上很多镜像也拉不下来。所以第一步确认你的系统是64 位版本。用这条命令检查uname -m如果输出aarch64说明是 64 位没问题。如果输出armv7l那就是 32 位建议重新刷写 64 位系统。我推荐用Raspberry Pi OS Lite (64-bit)没有桌面环境省内存省 CPU跑 Docker 更合适。如果你需要桌面用带桌面的 64 位版本也行但记得把不必要的服务关掉。2.2 系统更新与基础依赖刷完系统后先做一次完整的系统更新sudo apt update sudo apt full-upgrade -y这一步别省。树莓派 5 刚发布那会儿内核和固件更新比较频繁新版本对 PCIe、USB 3.0 和电源管理的支持更好。我有一次偷懒没更新结果 Docker 跑起来之后 USB 外接硬盘时不时掉线更新完内核就再没出现过。更新完之后装几个基础工具sudo apt install -y ca-certificates curl gnupg lsb-release这几个包的作用分别是ca-certificates提供 HTTPS 证书链curl用来下载文件gnupg用来验证软件包签名lsb-release帮我们识别系统版本。后面添加 Docker 官方源的时候都会用到。2.3 存储方案SD 卡还是 SSD这个问题值得单独拿出来说。树莓派的 SD 卡读写速度有限而且频繁写入容易损坏。Docker 的镜像层、容器日志、数据库文件都是高频写入操作长期跑在 SD 卡上卡挂掉是迟早的事。我的建议是系统跑在 SD 卡上没问题但 Docker 的数据目录一定要挂到外接 SSD 上。树莓派 5 有 PCIe 接口可以接 NVMe SSD 扩展板速度比 USB 3.0 快很多。如果不想折腾 PCIe用 USB 3.0 的固态硬盘也行实测顺序读写能到 300MB/s 以上比 SD 卡快十倍不止。挂载 SSD 的步骤大致是这样# 查看磁盘信息 lsblk # 格式化假设是 /dev/sda1注意确认设备名 sudo mkfs.ext4 /dev/sda1 # 创建挂载点 sudo mkdir -p /mnt/ssd # 获取 UUID sudo blkid /dev/sda1 # 写入 fstab 实现开机自动挂载 echo UUID你的UUID /mnt/ssd ext4 defaults,noatime 0 2 | sudo tee -a /etc/fstab # 挂载 sudo mount -a注意noatime参数可以减少不必要的写入延长 SSD 寿命。格式化之前务必确认设备名别把系统盘给格了。挂载好之后把 Docker 的数据目录设到 SSD 上。这个操作要在安装 Docker之前做否则后面迁移数据比较麻烦。3. Docker 安装的三种方式与选型分析3.1 方式对比官方脚本 vs 手动添加源 vs 系统自带包在树莓派上装 Docker常见的有三种方式我列个表对比一下方式命令优点缺点推荐度官方一键脚本curl -fsSL https://get.docker.com | sh简单快速自动识别架构不透明出问题不好排查新手可用手动添加官方源添加 GPG key apt source可控性强版本可选步骤稍多推荐系统自带包apt install docker.io最省事版本旧功能缺失不推荐系统自带的docker.io包版本通常落后好几个大版本比如 Debian 仓库里可能还是 20.x而官方已经到 27.x 了。新版本在存储驱动、网络性能、安全特性上都有改进特别是对 ARM64 的优化所以我不建议用系统自带的。官方一键脚本虽然方便但它做的事情比较多包括自动添加源、安装、启动服务中间如果某一步失败错误信息不够清晰。我倾向于手动添加官方源每一步都看得见出问题也好定位。3.2 手动安装 Docker 的完整步骤先添加 Docker 的官方 GPG 密钥sudo install -m 0755 -d /etc/apt/keyrings curl -fsSL https://download.docker.com/linux/debian/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg sudo chmod ar /etc/apt/keyrings/docker.gpg这里用的是 Debian 的源因为 Raspberry Pi OS 本质上是 Debian 的衍生版。如果你用的是 Ubuntu 系统把debian换成ubuntu就行。然后添加软件源echo \ deb [arch$(dpkg --print-architecture) signed-by/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/debian \ $(. /etc/os-release echo $VERSION_CODENAME) stable | \ sudo tee /etc/apt/sources.list.d/docker.list /dev/null$(dpkg --print-architecture)会自动识别为arm64$(. /etc/os-release echo $VERSION_CODENAME)会取到系统代号比如bookworm。这样写的好处是不用手动改换系统版本也能用。更新源并安装sudo apt update sudo apt install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin这里装了五个包docker-ce是核心引擎docker-ce-cli是命令行工具containerd.io是底层容器运行时docker-buildx-plugin提供增强的构建功能docker-compose-plugin是 Compose V2 版本。装完这些Docker 和 Docker Compose 就都有了。3.3 安装后的必要配置装完之后把当前用户加入 docker 组这样不用每次敲sudosudo usermod -aG docker $USER执行完这条命令后需要退出当前终端重新登录或者执行newgrp docker让组权限生效。我见过不少人加完组发现还是提示权限不够就是因为没重新登录。接着配置 Docker 的 daemon.json把数据目录指到 SSD 上sudo mkdir -p /etc/docker sudo tee /etc/docker/daemon.json EOF { data-root: /mnt/ssd/docker, log-driver: json-file, log-opts: { max-size: 10m, max-file: 3 }, registry-mirrors: [] } EOF>sudo systemctl restart docker sudo systemctl enable docker验证安装docker version docker infodocker info里能看到Storage Driver是overlay2Architecture是aarch64Docker Root Dir是你设置的 SSD 路径就说明配置正确了。4. 日常管理容器、镜像与存储的实操要点4.1 容器生命周期管理Docker 容器的日常操作其实就那么几个命令但用熟了能省很多事。我按使用频率排个序# 查看运行中的容器 docker ps # 查看所有容器包括已停止的 docker ps -a # 启动/停止/重启容器 docker start 容器名 docker stop 容器名 docker restart 容器名 # 查看容器日志实时滚动 docker logs -f --tail 100 容器名 # 进入容器内部 docker exec -it 容器名 /bin/bash # 删除容器先停止再删 docker rm 容器名这里有个经验给容器起有意义的名字。用docker run的时候加上--name参数比如--name mysql-main后面管理的时候不用去查容器 ID直接敲名字就行。容器 ID 那一长串哈希值记是记不住的。还有一个技巧是docker logs的--tail参数。默认情况下它会输出全部日志如果容器跑了很久日志可能几万行刷屏刷到你看不清。加上--tail 100只看最后 100 行配合-f实时跟踪排查问题效率高很多。4.2 镜像管理拉取、清理与架构适配树莓派上拉镜像最常遇到的问题就是架构不匹配。比如你想拉mysql:8.0但某个版本没有 ARM64 的构建拉下来跑的时候会报exec format error。解决办法有两个一是换一个支持 ARM64 的标签二是用--platform参数指定。# 查看镜像支持的架构 docker manifest inspect mysql:8.0 | grep architecture # 拉取指定架构的镜像 docker pull --platform linux/arm64 mysql:8.0清理镜像也是个常规操作。Docker 用久了悬空镜像和停止的容器会占不少空间# 清理所有停止的容器、未使用的网络、悬空镜像 docker system prune # 更彻底的清理包括未使用的镜像 docker system prune -a # 查看磁盘占用 docker system dfdocker system df这个命令我建议定期跑一下能看到镜像、容器、卷、构建缓存各占了多少空间。我有一次发现构建缓存占了 20 多个 G清理完瞬间清爽。4.3 数据卷与目录挂载的取舍Docker 的数据持久化有两种方式数据卷volume和绑定挂载bind mount。两者用起来有区别选错了后面迁移会麻烦。数据卷由 Docker 管理存在/var/lib/docker/volumes/下面如果你改了>docker run -d \ --name mysql-main \ -e MYSQL_ROOT_PASSWORDyourpassword \ -v /mnt/ssd/mysql-data:/var/lib/mysql \ -v /mnt/ssd/mysql-conf:/etc/mysql/conf.d \ -p 3306:3306 \ --restart unless-stopped \ mysql:8.0--restart unless-stopped这个参数很关键它让容器在宿主机重启后自动拉起除非你手动停了它。树莓派偶尔会因为断电重启加上这个参数能省不少事。5. Docker Compose 在树莓派上的实战5.1 为什么树莓派上更需要 Compose单容器用docker run没问题但一旦服务多起来命令行参数会变得又长又难维护。比如你要跑一个 Web 服务加一个数据库加一个缓存三个容器的启动命令加起来几十行改一个参数得翻半天历史记录。Docker Compose 用 YAML 文件描述整个服务栈一条docker compose up -d全部拉起。在树莓派上这个优势更明显树莓派性能有限服务之间的依赖关系需要精确控制Compose 的depends_on和健康检查能保证启动顺序正确。5.2 一个完整的 Compose 配置示例我拿一个典型的组合来演示Nginx 反向代理 MySQL 数据库 Redis 缓存。这个组合在树莓派上跑起来毫无压力8GB 内存版本绰绰有余。version: 3.8 services: nginx: image: nginx:alpine container_name: nginx-proxy ports: - 80:80 - 443:443 volumes: - /mnt/ssd/nginx/conf.d:/etc/nginx/conf.d - /mnt/ssd/nginx/certs:/etc/nginx/certs depends_on: - app restart: unless-stopped app: image: your-app:arm64 container_name: my-app environment: - DB_HOSTmysql - REDIS_HOSTredis depends_on: mysql: condition: service_healthy redis: condition: service_started restart: unless-stopped mysql: image: mysql:8.0 container_name: mysql-main environment: MYSQL_ROOT_PASSWORD: yourpassword MYSQL_DATABASE: appdb volumes: - /mnt/ssd/mysql-data:/var/lib/mysql healthcheck: test: [CMD, mysqladmin, ping, -h, localhost] interval: 10s timeout: 5s retries: 5 restart: unless-stopped redis: image: redis:7-alpine container_name: redis-cache volumes: - /mnt/ssd/redis-data:/data command: redis-server --appendonly yes restart: unless-stopped这个配置里有几个细节值得说。depends_on配合condition: service_healthy让 app 容器等 MySQL 健康检查通过之后再启动避免启动时连不上数据库。healthcheck里用mysqladmin ping检测 MySQL 是否就绪间隔 10 秒重试 5 次。Redis 开了appendonly yes数据持久化到磁盘重启不丢。5.3 Compose 的日常操作与更新策略Compose 的常用命令# 启动所有服务后台运行 docker compose up -d # 查看服务状态 docker compose ps # 查看某个服务的日志 docker compose logs -f app # 停止并删除所有容器保留数据卷 docker compose down # 拉取最新镜像并重建 docker compose pull docker compose up -d # 只重建某个服务 docker compose up -d --no-deps --build app更新镜像的时候我习惯先pull再up -d这样能确保用的是最新版本。如果更新后出问题回滚也简单把镜像标签改回旧版本重新up -d就行。所以生产环境建议用具体版本标签别用latest否则哪天自动更新到不兼容的版本排查起来很头疼。6. 常见问题与排查技巧实录6.1 镜像拉取失败与网络问题树莓派上拉镜像失败最常见的原因是网络问题。表现是docker pull卡住不动或者报timeout、connection reset。排查思路是这样的先确认 DNS 解析正常nslookup registry-1.docker.io如果解析不出来检查/etc/resolv.conf里的 DNS 设置。树莓派默认可能用的是路由器分配的 DNS有时候不稳定可以改成公共 DNS。如果 DNS 正常但拉取还是慢可以考虑配置镜像加速。在daemon.json里加上{ registry-mirrors: [https://your-mirror.example.com] }改完重启 Docker 服务。注意镜像加速地址要选可靠的不稳定的加速源反而会让拉取失败。还有一个坑是磁盘空间不足。树莓派 SD 卡容量有限镜像拉多了容易满。用df -h检查根分区用docker system df检查 Docker 占用。如果根分区满了把>docker info | grep Storage Driver如果是vfs说明 overlay2 没启用。解决办法是把 SSD 重新格式化为 ext4。虽然 exFAT 在 Windows 和 Linux 之间兼容性好但跑 Docker 还是 ext4 最合适。另外树莓派 5 的 PCIe 接口如果接了 NVMe SSD要注意散热。NVMe 硬盘发热量不小长时间高负载写入可能触发降速。加个散热片或者小风扇能明显改善。6.4 内存与交换空间的管理树莓派 5 的内存从 4GB 到 8GB跑几个容器没问题但如果容器多了内存还是紧张。Docker 本身可以限制单个容器的内存使用docker run -d --memory512m --memory-swap1g ...--memory限制物理内存--memory-swap限制内存加交换空间的总量。设置合理的限制能防止某个容器吃光内存导致整个系统卡死。树莓派默认的交换空间swap比较小可以适当调大。编辑/etc/dphys-swapfile把CONF_SWAPSIZE改成 20482GB然后重启交换服务sudo systemctl restart dphys-swapfile不过要注意swap 是写在 SD 卡上的频繁交换会加速 SD 卡磨损。如果 SSD 空间充足可以把 swap 放到 SSD 上或者干脆加大物理内存买 8GB 版本。7. 性能调优与长期运行建议7.1 针对 ARM64 的镜像选择策略ARM64 镜像的选择有几个原则。优先选官方镜像因为官方镜像通常有完整的 ARM64 构建。其次选alpine标签的镜像体积小、资源占用低特别适合树莓派。比如nginx:alpine比nginx:latest小了将近 100MB启动也更快。如果某个镜像确实没有 ARM64 版本可以考虑自己构建。写个简单的 Dockerfile基于arm64v8/debian或arm64v8/alpine把需要的软件装进去。虽然麻烦点但一次构建长期使用值得。7.2 日志与监控的轻量方案树莓派资源有限不适合跑重量级的监控系统。我推荐用ctop或者lazydocker这种终端工具轻量直观。ctop可以实时看每个容器的 CPU、内存、网络占用安装也简单sudo apt install -y ctop或者用 Docker 跑一个docker run --rm -it --name ctop \ -v /var/run/docker.sock:/var/run/docker.sock \ quay.io/vektorlab/ctop:latest日志方面前面在daemon.json里配置了日志轮转限制单个日志文件 10MB最多保留 3 个。这样即使某个容器疯狂输出日志也不会把磁盘写满。如果某个容器日志特别重要可以单独配置日志驱动把日志发到外部系统。7.3 自动更新与备份的平衡容器镜像更新是好事但自动更新有风险。我建议手动更新关键服务自动更新非关键服务。关键服务比如数据库、反向代理更新前先看 changelog确认没有破坏性变更。非关键服务可以用watchtower自动更新docker run -d \ --name watchtower \ -v /var/run/docker.sock:/var/run/docker.sock \ containrrr/watchtower \ --interval 86400 \ --cleanup--interval 86400表示每天检查一次更新--cleanup表示更新后删除旧镜像。不过 watchtower 在 ARM64 上跑要注意有些镜像可能没有 ARM64 版本更新后会启动失败。所以用之前先确认镜像的架构支持。备份方面最重要的是数据卷和绑定挂载的目录。我写了个简单的备份脚本每周跑一次#!/bin/bash BACKUP_DIR/mnt/ssd/backups DATE$(date %Y%m%d) # 备份 MySQL 数据目录 tar -czf $BACKUP_DIR/mysql-$DATE.tar.gz /mnt/ssd/mysql-data # 备份 Compose 配置 tar -czf $BACKUP_DIR/compose-$DATE.tar.gz /home/pi/docker-compose # 删除 30 天前的备份 find $BACKUP_DIR -name *.tar.gz -mtime 30 -delete这个脚本配合 cron 定时执行简单可靠。备份文件放在 SSD 上如果条件允许再同步一份到 NAS 或云存储做到异地备份。8. 我踩过的几个坑和最终建议第一个坑是电源问题。树莓派 5 对电源要求比前代高官方推荐 5V 5A 的电源。我一开始用了个旧手机的充电头结果接上 SSD 之后频繁重启查了半天以为是系统问题换了官方电源就稳了。所以如果你也遇到莫名其妙的重启或掉盘先检查电源功率够不够。第二个坑是SD 卡质量。我最早用的一张杂牌 32GB 卡跑了两个月 Docker 就出现 I/O 错误容器频繁崩溃。后来换了三星的 endurance 卡专门为监控设备设计的耐写入跑了半年多没出过问题。如果预算允许直接上 SSD 最省心。第三个坑是镜像标签用 latest。有一次docker compose pull之后某个服务的新版本改了配置格式启动直接报错。幸好我有备份回滚了旧版本。从那以后所有生产容器的镜像标签都写具体版本号比如mysql:8.0.35更新的时候手动改标签确认没问题再上。最后分享一个小技巧用docker context管理多台设备。如果你不止一台树莓派或者还有别的 Docker 主机可以在本地配置多个 context用docker context use切换不用每次都 SSH 上去操作。这个功能在 Docker 19.03 之后就有了用起来很方便。# 添加远程 context docker context create pi5 --docker hostssh://pi192.168.1.100 # 切换 context docker context use pi5 # 之后所有 docker 命令都作用在树莓派上 docker ps这个方式比 SSH 进去再敲命令效率高特别是需要频繁查看容器状态的时候。不过要注意docker context走的是 SSH 连接网络不稳定的时候可能会卡本地网络环境下用没问题。