去年我把一块树莓派 4B 从抽屉里翻出来的时候,SD 卡里还躺着一个大三装的 WordPress。当时搭它费了整整两天,后来因为搬家懒得迁移,就一直吃灰。今年决定重新把它用起来,方向很明确:用 Docker 把整个网页服务器容器化。这个决定改变的不只是部署方式,连我对待这台小主机的态度都变了。所以我打算把这个过程整理成一个专栏,而这篇文章就是整个专栏的地图。
如果你手里也有一块树莓派,或者单纯想用一个低功耗小机器跑点正经服务,那这个专栏可以给你一条非常清晰的路:从硬件选型、系统安装,到 Docker 部署网页服务器,再到域名、HTTPS、数据备份,全部按真实项目流程来写。我会直接把命令、配置、目录结构、坑位都摆在明面上,不绕弯子。
1. 吃灰树莓派的第二次生命:这个专栏到底在做什么
1.1 为什么是 Docker + 网页服务器,而不是其他玩法
树莓派能做的事情很多,GPIO 控制、机器人、软路由、复古游戏机、NAS,随便一个方向都能玩半年。但如果你只是想用它“稳定跑一个服务”,而且希望这个服务以后迁移到别的机器上还能一键恢复,那 Docker 化几乎是唯一省心的选择。
传统玩法是在系统里直接装 Nginx、MySQL、PHP,这样的问题在树莓派上尤其致命:系统本身跑在 SD 卡上,一旦卡片损坏,整个环境就全没了;时间久了系统依赖升级,某个扩展和 PHP 版本不兼容,排查起来非常费劲;换一块新板子想迁移,什么东西都得重新配一遍。容器化解决的就是这三个痛点:环境隔离、启动可重复、迁移成本极低。
而网页服务器恰恰是容器化收益最大的场景。一个像样的站点至少由 Nginx 反代、Web 应用、数据库、缓存组成,这么多组件如果用传统方式安装,每个都有自己的配置文件、进程管理方式、依赖关系,组合起来就是一场灾难。用 Docker 把每个组件装进独立容器,再用 docker-compose 把它们串起来,整个服务的生命周期都能用几条命令控制,这对树莓派这种小机器来说太合适了。
1.2 看完这个专栏你能得到什么
这个专栏不是讲零散命令,而是带着你从一块裸板跑到一个能对外提供访问的完整网页服务器。最终你会得到几个非常具体的东西:
- 一台装好 64 位 Linux 系统、SSH 可连、基础环境干净的树莓派;
- 一套正确安装并配置好的 Docker 引擎,以及自己手写的一批常用命令;
- 一个用 docker-compose 管理起来的网页项目,包含 Nginx 反代、一个动态 Web 应用、MySQL 数据库和 Redis 缓存;
- 一台只要插上电就能自动拉起所有服务的“家用服务器”,重启之后不用再手工敲命令;
- 你知道怎么在系统崩溃后从备份里把整个站点恢复回来,而不是抱着 SD 卡哭。
对我来说最有价值的其实是最后一条。树莓派这种设备,最大的敌人不是性能,而是 SD 卡突然损坏。把服务容器化之后,我只需要定期备份几个数据目录和一份 docker-compose.yml,恢复一台新机器只需要十分钟。
1.3 适合谁读,谁可以先划走
这个专栏适合三类人:第一类是有树莓派但一直吃灰,想用起来又不知道从哪下手的人;第二类是想学 Docker,但不想在虚拟机和云服务器上折腾,想用一个真实的小设备练手的人;第三类是做毕设或课程设计,需要快速搭一个“物联网 + Web 管理平台”的学生。
不太适合的人也有:如果你主要想在树莓派上做桌面办公、看视频、写文档,那容器化帮不了你太多;如果你要做的是实时性极强的硬件控制,比如舵机、电机、传感器闭环,那第一优先应该选裸机或实时系统,容器在这里只适合做旁路服务,不适合做控制主链路。
2. 先选对硬件和系统,再谈容器化
2.1 树莓派 4B、5、3B 怎么挑
我做这个专栏用的主力机是树莓派 4B,4GB 版本,因为这是目前保有量最大、资料最全、性价比最均衡的型号。如果你手头已经有树莓派,先不要急着买新的,看看手里的型号和内存再决定。
| 型号 | CPU 架构 | 常见内存 | Docker 适用度 | 主要限制 |
|---|---|---|---|---|
| 树莓派 3B/3B+ | armv7l | 1GB | 勉强可用 | 很多镜像没有 32 位 ARM 版本,跑内存密集型应用吃力 |
| 树莓派 4B | arm64 | 2GB/4GB/8GB | 很推荐 | 散热要做好,USB 启动需要较新 BootLoader |
| 树莓派 5 | arm64 | 4GB/8GB/16GB | 非常推荐 | 价格偏贵,但对 AI 和并发场景提升明显 |
树莓派 3B 不是不能用,只是很多 Docker 官方镜像只发布 linux/arm64 版本,没有 linux/arm/v7,你拉镜像的时候会直接报 “no matching manifest for linux/arm/v7”,这时候要么找第三方构建的 armhf 镜像,要么自己源码构建,时间成本很高。所以我建议如果是 3B,最多跑个 Nginx 或轻量容器,别指望跑全家桶。4B 起步才谈得上“网页服务器”。
内存方面,2GB 版本跑单容器还可以,如果要跑 Web 应用 + MySQL + Redis 再加反代,最好上 4GB。我实测一个典型 LEMP 容器栈,空闲时占 700MB 左右,跑起来之后轻松破 1.5GB,2GB 会很紧张。
2.2 系统镜像:Lite 版还是 Ubuntu Server
确定了硬件之后,系统选择也要认真。树莓派官网提供的 Raspberry Pi OS 分两个版本:带桌面的完整版和无桌面的 Lite 版。做服务器一定要选 Lite,因为桌面环境会吃掉大量内存和 CPU,而且没键盘没显示器时,桌面版反而更容易出问题。
如果你想用 Ubuntu,可以选 Ubuntu Server 24.04 的树莓派版本。它的优势在生态更接近云服务器,很多部署文档里的命令可以直接照抄;劣势是树莓派专属的一些硬件支持不如官方系统及时,比如摄像头、GPIO、硬件编解码,更新节奏也慢一些。
我给你的建议很简单:第一次用树莓派做服务器,选 Raspberry Pi OS Lite(64-bit)最稳,因为官方对 4B/5 的 64 位支持已经非常成熟,社区资料最多,换源、装 Docker、调内核参数都有现成答案。等你对这套流程很熟了,再换 Ubuntu Server 不迟。
2.3 无屏幕安装与初始连接
做服务器基本不会给它配显示器,所以“无头安装”是第一个要跨过的坎。现在官方烧录工具 Raspberry Pi Imager 已经做得非常友好,在烧录前可以点右下角的齿轮图标,直接预配置:
- 开启 SSH,设置用户名和密码;
- 配置 WiFi 或以太网;
- 设置地区、语言、时区;
- 给主机起一个固定名字,比如 webpi。
这样烧完系统,把卡插回树莓派开机,然后去路由器后台找到 webpi 的 IP,或者直接用ping webpi.local/ SSH 连接:ssh pi@webpi.local。如果你用的是 Windows 10/11 系统,可以直接在终端里用ssh pi@webpi.local连,不需要装 PuTTY。
这里有一个常被忽略的细节:树莓派首次开机会自动扩展分区,但如果用的是不带官方引导的第三方镜像,可能会有分区没扩完的情况。建议连接后先跑一下sudo raspi-config,在 Advanced Options 里确认“Expand Filesystem”已生效,否则后面 Docker 数据一多,根分区就满了。
2.4 换源、时区、基础依赖一次搞定
系统连上之后,第一步不是装 Docker,而是把基础环境收拾干净。
换软件源这个动作在国内网络环境下尤其重要。默认源在国外,跑apt update可能慢到怀疑人生。我的习惯是先备份/etc/apt/sources.list,然后用一个稳定的软件源镜像替换掉默认地址。这里不固定推荐哪个源,因为不同网络环境下的延迟差异很大,你只要选择一台在自己网络里实测速度最快的镜像就好。
换完源马上做三件事:更新系统、设置时区、装基础工具。
sudo apt update && sudo apt upgrade -y sudo timedatectl set-timezone Asia/Shanghai sudo apt install -y curl git vim htop tmux时区这个问题在 Docker 容器里更容易踩,后面你会看到很多镜像默认 UTC 时间,和宿主机差 8 小时。所以从一开始就把系统时区设好,至少容器启动时少一个变量。
3. Docker 化网页服务器的核心设计思路
3.1 传统部署方式为什么不推荐
我在文章开头说传统部署很麻烦,你可能觉得有点夸张。这里说一个我真实遇到过的场景:之前我在树莓派上直接apt install nginx php-mysql,一开始站点很正常,后来因为毕设要加一个 WebSocket 服务,我顺手把系统里的 PHP 升级了一下,结果 WordPress 的某个插件彻底打不开了。
类似这种“升级一个组件,带崩整个环境”的问题,在传统部署里几乎无法根治。容器化的思路是把每个组件锁在自己的镜像里,PHP 应用跑在 PHP 容器里,Nginx 跑在 Nginx 容器里,MySQL 跑在 MySQL 容器里,它们之间通过网络通信,而不是在同一个文件系统里互相踩脚。
生活化的类比是这样:传统部署像是把一家人全塞进同一个房间,有人要睡觉、有人要唱歌、有人要煮饭,互相干扰;Docker 是给每个人分了一间带独立家具的房间,需要交流时开门说话,不想被打扰时完全隔离。网页服务器天然是多组件协作的场景,容器化之后每个组件可以独立升级、回滚、重新部署,这套体验用传统方式很难做到。
3.2 一个网页服务器的容器化拆解
假设你的目标是一个带动态数据的站点,至少需要拆成四个角色:
- Nginx 容器:负责接收外部请求,把静态文件直接返回,把动态请求转发给 Web 应用;
- Web 应用容器:运行 Python/Node/PHP 代码,处理业务逻辑;
- MySQL 容器:持久化业务数据;
- Redis 容器:处理缓存和会话。
先拿 Nginx 举例,最小化的启动命令是:
docker run -d --name nginx-web -p 80:80 -v /home/pi/site:/usr/share/nginx/html:ro nginx:alpine这条命令做了三件事:-d表示后台运行;-p 80:80把宿主机 80 端口映射到容器 80 端口;-v /home/pi/site:/usr/share/nginx/html:ro把宿主机里的站点目录挂载进容器,ro表示只读,防止容器内部意外改动宿主机文件。
理解端口映射的方向很重要:-p 宿主机端口:容器端口,访问者永远先到宿主机 IP 的端口,再由 Docker 转发给容器内部端口。所以如果你以后要改对外端口,只动冒号左边,冒号右边默认是容器里服务监听的端口,轻易不要改。
3.3 docker-compose:用一份 YAML 管理整个站点
容器一多,纯docker run命令就会变得失控。创建一个网络、挂三个数据卷、设五个环境变量,每次手工敲都很容易错。这时候用 docker-compose 把整个服务栈的定义写进一份docker-compose.yml,好处是:可读、可版本管理、一条命令拉起或关掉全部服务。
一个基础示例:
services: nginx: image: nginx:alpine ports: - "80:80" volumes: - ./site:/usr/share/nginx/html:ro restart: unless-stopped web: build: ./app environment: - DB_HOST=db - REDIS_HOST=redis depends_on: - db - redis restart: unless-stopped db: image: mysql:8 environment: MYSQL_ROOT_PASSWORD: change-me MYSQL_DATABASE: webapp volumes: - db_data:/var/lib/mysql restart: unless-stopped redis: image: redis:7-alpine restart: unless-stopped volumes: db_data:这份文件并不复杂,但几个细节非常关键。第一,restart: unless-stopped保证了树莓派重启之后容器能自动拉起来,这是服务器长期稳定运行的基础。第二,db容器通过MYSQL_DATABASE这个环境变量自动创建数据库,省去初始化手工执行 SQL 的步骤。第三,depends_on只保证启动顺序,不保证数据库“已经就绪”,所以应用代码里最好有重连逻辑。
3.4 arm64 架构带来的镜像差异与兼容性
树莓派 4B/5 都是 arm64 架构,这本身不是问题,现在主流镜像几乎都有 arm64 版本。真正的问题是有些个人或小团队维护的镜像只提供了 x86_64 版本,你在树莓派上docker pull时会一直卡住,然后报no matching manifest。
遇到这种情况,第一步用docker manifest inspect 镜像名看看它支持哪些架构。如果确实没有 arm64,你有两条路:一是找替代镜像,很多常见软件都能找到官方或多架构镜像;二是自己在树莓派上用源码构建,但编译时间可能很长,而且内存小的版本容易直接 OOM。
我个人的经验是,在一个家庭服务器项目里,优先选择官方镜像或知名社区镜像,不要为了一个“看起来很酷”的小众镜像浪费大量时间。稳定压倒一切。
4. 专栏实操路线:六个阶段跑通一个真实站点
4.1 阶段一:烧录与初始化
这个阶段的目标是让树莓派通电就能连上,基础环境干净可操作。你需要准备:一张至少 32GB 的高质量 SD 卡、读卡器、一块树莓派 4B/5、电源和数据线。
用 Raspberry Pi Imager 选择 Raspberry Pi OS Lite(64-bit),预配置 SSH、WiFi、主机名。烧录完成后插卡开机,通过路由器后台或webpi.local找到设备地址,SSH 登录。然后执行系统更新、时区设置、基础依赖安装。整个过程不复杂,但至少预留 30 分钟,因为首次开机后系统会自动扩展分区并做初始化,SSH 可能前一两分钟连不上,这是正常现象。
我会建议你写一个简单的部署笔记,把 IP、用户名、安装日期、使用过的镜像源记下来。树莓派这种设备很容易被遗忘,等三个月后再去操作,没有笔记几乎等于重新开始。
4.2 阶段二:装好 Docker 引擎并跑通 hello-world
树莓派上装 Docker 最省事的方式是用官方安装脚本:
curl -fsSL https://get.docker.com | sh sudo usermod -aG docker $USER执行完第二条命令后,先注销再重新登录,让docker组权限生效。然后用docker version确认客户端和服务端都在运行。
跑一次 hello-world 不只是验证安装,还能顺便检查镜像拉取速度是否正常:
docker run hello-world如果这条命令卡住,说明网络到 Docker Hub 不通畅。这时候可以考虑配置 registry mirror,编辑/etc/docker/daemon.json,加入registry-mirrors配置,然后重启 Docker 服务。镜像加速地址要根据你所在网络实测选择,没有统一标准,但配置完之后docker run hello-world应该明显变快。
4.3 阶段三:Nginx 静态站点与局域网访问
这一步能让你快速获得成就感。在树莓派上建一个目录,写一个最简单的 HTML 页面:
mkdir -p ~/site echo '<h1>hello from raspberry pi</h1>' > ~/site/index.html然后启动一个 Nginx 容器挂载这个目录:
docker run -d --name web -p 80:80 -v /home/pi/site:/usr/share/nginx/html:ro nginx:alpine浏览器访问http://树莓派IP,看到页面的那一刻,你的树莓派就已经是一台正式的 Web 服务器了。这个阶段只有两个坑:一是防火墙或路由器没有放行 80 端口;二是容器启动后改了本地文件,但浏览器没刷新缓存。如果你要修改页面,直接改~/site/index.html,容器会实时读到,不需要重启。
4.4 阶段四:Web 应用 + MySQL + Redis 的容器化实战
静态页面不够,这个阶段进入真正有业务逻辑的动态站点。以一个简单的 Python Flask 应用为例,你的代码里要读取数据库连接信息,不能写死 IP,而是使用服务名db。这就是容器网络的作用:在同一个 Docker 网络里,容器之间可以通过服务名互相访问。
创建自定义网络:
docker network create webnet然后分别启动 MySQL 容器和应用容器,并加入同一个webnet。应用容器里配置DB_HOST=db,而不是127.0.0.1,因为在容器网络里127.0.0.1指向容器自己,不是数据库容器。
这个阶段的重点不是“应用代码怎么写”,而是理解容器之间的网络模型。很多新手第一次死在“为什么我在容器里连不上数据库”上,多半就是没搞懂这个逻辑。
4.5 阶段五:用 docker-compose 一键拉起全栈项目
把上一阶段手动执行的多条命令整理成docker-compose.yml,这是整个专栏最关键的一次升级。你不再需要记顺序,也不需要关心哪个容器先启动哪个后启动,只需要:
docker compose up -d然后观察日志:
docker compose logs -f这个阶段我会特别强调“数据卷”的重要性。MySQL 数据目录必须挂到 volume 或宿主机目录,否则删除容器就意味着删库跑路。资源有限的树莓派上,你还要学会用docker compose ps查看每个容器的状态和资源占用,及时发现某个容器的日志在疯狂增长。
4.6 阶段六:反代、域名与 HTTPS 收尾
到了这个阶段,你的站点已经可以用http://IP:端口访问了。但要让它看起来像一个真正正式的网站,还需要统一入口和加密。
我的推荐是加一个 Nginx 或 Caddy 作为反向代理容器,把所有外部请求收进来,再按路径转发到不同后端容器。Caddy 的好处是能自动申请和管理 HTTPS 证书,配置文件极短;Nginx 的好处是通用性更强,网上资料最多。
如果你有一个自己的域名,并且路由器支持端口映射,可以配置 A 记录指向家庭宽带的公网 IP,然后把 80 和 443 端口映射到树莓派。这个阶段要格外小心安全:不要用默认密码,不要暴露 SSH 到公网,不要开放不必要的端口。家庭服务器被扫到是常态,安全习惯必须从第一天就养成。
5. 踩坑清单:这些内容值得重点划线
5.1 电源和散热:性能不稳定的头号元凶
我见过太多“树莓派为什么老是重启”的案例,最后发现都是电源问题。树莓派 4B 满载时电流接近 3A,5V 供电如果不足,系统会瞬间掉压,轻则性能骤降,重则直接重启。
最明显的征兆是系统日志里出现under-voltage警告。排查方法:运行vcgencmd get_throttled,如果返回的不是0x0,说明发生过欠压或温度降频。
使用建议:用 5V/3A 以上、线材质量好的电源,不要让树莓派从 USB 口或老手机充电器取电。散热方面,4B 至少要加一个散热片加风扇的套装,长时间跑容器负载,温度控制在 60 度以内比较安全。5 的发热比 4B 更猛,如果你要跑多容器高负载,被动散热完全不够。
5.2 存储卡与数据卷:日志暴涨的清理方案
SD 卡是树莓派里最容易坏的部件,而 Docker 默认会把所有容器日志写到宿主机/var/lib/docker/containers/<id>/下,如果不做限制,一个日志文件轻松涨到几个 GB,直接把 SD 卡塞满。
我建议你在/etc/docker/daemon.json里加上日志轮转配置:
{ "log-driver": "json-file", "log-opts": { "max-size": "10m", "max-file": "3" } }配置后重启 Docker 服务,新的容器就会按这个策略写日志。对于已经运行很久的老容器,需要重新创建容器才会生效,所以我会在专栏里专门安排一课:如何在不影响数据卷的前提下重建容器。
另外,如果条件允许,把 Docker 的数据目录/var/lib/docker迁到 SSD 或高速 U 盘上。方法是在daemon.json中加一句"data-root": "/mnt/ssd/docker",然后把旧目录复制过去。迁移后容器的启动速度和数据库的稳定性都会明显提升。
5.3 docker 权限、容器时区、镜像架构等高频问题
这三个问题几乎每次上手都会遇到,我直接给结论。
一是权限。执行docker ps报permission denied,说明你的用户不在docker组里。执行sudo usermod -aG docker $USER后重新登录。
二是时区。容器默认 UTC,和北京时间差 8 小时。启动容器时加-e TZ=Asia/Shanghai,或者在 compose 文件里的每个服务中配置环境变量。如果应用内部依赖数据库时间,数据库容器也必须设。
三是镜像架构。拉镜像时报no matching manifest for linux/arm64,说明这个镜像没有 arm64 版本。先docker manifest inspect确认,再决定是换替代镜像还是自己构建。不要在 3B 上死磕 armhf,很多轮子早就不维护了。
5.4 Windows 环境连接树莓派时的常见误区
很多同学是在 Windows 笔记本上操作树莓派的,有一个非常常见的思路误区:为了管理树莓派上的 Docker,先在 Windows 上装一个 Docker Desktop,然后试图用图形界面连接远程节点。
这个思路本身没错,但 Docker Desktop 在 Windows 上经常因为虚拟化没有开启而启动失败,报错提示类似virtualisation support wasn't detected。这跟你的树莓派一点关系都没有,纯粹是 Windows 宿主机的虚拟化设置问题。在 BIOS 里开启虚拟化、开启 WSL2,才能解决。
如果你不想折腾这些,更省心的做法是:Windows 上只装一个 SSH 客户端,直接 SSH 到树莓派,所有的 Docker 操作都在树莓派终端里完成。图形化管理可以在树莓派上装 Portainer 之类的 Web 管理工具,用浏览器打开管理界面,比 Docker Desktop 轻得多。
6. 专栏之外的扩展:从网页服务器走向更多项目
6.1 摄像头、USB 设备与容器化
树莓派最常见的扩展之一是摄像头模块。如果你想把摄像头画面接入网页,可以在容器启动时把设备挂载进去:
docker run -d --name cam \ --device /dev/video0 \ -p 8080:8080 \ your-camera-image--device参数把宿主机上的/dev/video0设备节点直接暴露给容器,容器里的应用就能访问摄像头。这个方法对 USB 摄像头、串口设备都适用。需要注意,容器对设备的访问权限和宿主机用户组有关,如果报权限错误,可以在docker run时加--group-add video。
专栏里我会放一个实际案例:让树莓派摄像头拍到的画面通过网页实时预览,容器里跑的是一个轻量视频服务,宿主机只负责供电和网络。
6.2 本地 AI 模型与树莓派 5
树莓派 5 的性能比 4B 有明显提升,16GB 内存版本可以跑一些量化后的小参数语言模型。现在社区里有不少针对 ARM 平台优化的部署方案,我自己在树莓派 5 上试过轻量模型的推理,速度虽然比不上桌面级 GPU,但作为学习工具完全够用。
如果你想往这个方向走,建议先把基础打好:会写 docker-compose、会看容器日志、会管理数据卷。因为 AI 模型的镜像通常很大,如果不小心把镜像塞满 SD 卡,运维经验就是救命稻草。
6.3 毕设和课设的选题思路
很多学生朋友找到我,说毕设想做“树莓派 + 网页平台”,但不知道具体做什么。我推荐几个方向:
- 环境监测系统:传感器数据定时写入 MySQL,网页用图表展示,Docker 负责打包整个后端和数据库;
- RabbitMQ 消息队列管理平台:在树莓派上容器化部署 RabbitMQ,用它自带的管理后台做消息收发演示;
- 家庭媒体中心:Nginx 做反代,容器里跑媒体服务,网页端管理播放列表;
- 设备管理后台:多个树莓派上报状态,用 Docker 部署一个集中管理页面。
这些方向有一个共同点:不是单纯“跑一个网页”,而是把物联网数据流和 Web 展示串起来,既能体现工作量,又能把 Docker 技术点写进论文里。
6.4 数据备份与长期运行建议
网页服务器跑起来之后,最重要的事就是备份。我的做法是每周做一次全量备份,工具就是 tar 加 crontab:
tar -czf /mnt/backup/webpi-$(date +%F).tar.gz \ /home/pi/site \ /home/pi/app \ /var/lib/docker/volumes恢复时只需要在新系统上装好 Docker,把备份解压,回到项目目录执行docker compose up -d,数据卷里的内容原样恢复。这就是容器化最大的红利:系统和软件全部可重建,只有数据需要备份。
长期运行建议也一并写在最后:定期apt upgrade和升级镜像;不要在树莓派上同时跑太多无状态服务;给重要数据目录做独立备份;保持系统日志可查;记住,服务器稳定运行的秘诀不是“不重启”,而是“重启后能自动恢复”。
我在实际搭建这个环境的过程中,最大的体会是:树莓派 Docker 化网页服务器根本不是“跑一个容器”的事,而是一套完整的运维思维训练。从硬件供电、系统裁剪、网络模型,到日志、备份、安全,每一个环节都会在你真正遇到故障时体现出价值。这个专栏会把这些内容按顺序拆给你看,你可以把它当教程,也可以当手册,遇到问题再回来查对应章节。
最后再分享一个小技巧:每完成一个阶段,就把当前的 docker-compose.yml、配置文件和踩坑记录塞进一个 Git 仓库。树莓派上跑的服务总有一天要迁移或重构,到时候你会感谢自己留下的这些痕迹。