树莓派 Docker 化网页服务器:从系统镜像到容器化部署
2026/9/20 6:39:14 网站建设 项目流程

拿到一张新的树莓派 SD 卡,最爽的一刻是把自己选好的树莓派镜像写进去、通电、SSH 连上去,然后在不关机的角落里让它安安静静跑网页服务器。这种快乐通常能维持到第三个服务上线:先是 nginx 挂了个页面,接着想加个数据库,再想来个定时脚本,然后你会发现,“系统镜像”这东西在服务越装越多之后,越来越像一个开线了的毛线团。依赖冲突、版本不对、升级挂掉、重刷镜像后全部从头来一遍。如果你也走到这一步,那你就该认真了解一下 Docker 了。

这篇文章是这个系列的开篇,主题正是“树莓派 Docker 化网页服务器(1):当系统镜像遇上服务增长”。我会从最底层的痛点说起,解释为什么树莓派做长期服务的方案里,Docker 几乎是绕不开的一环,再带你把环境准备好,把第一个 nginx 容器跑起来。适合的对象很清楚:已经能让树莓派正常工作,但还没入 Docker 坑的玩家,或者已经在别的服务器上用 Docker、想在树莓派上复制这套玩法的朋友。

1. 树莓派的“服务增长”到底是怎么发生的

1.1 从“刷机一时爽”到“配置火葬场”

树莓派刚到手的时候,大家最常做的事就是去官网下一个官方镜像,拿写卡工具把镜像写进 SD 卡。整个过程很固定,设备型号选好、镜像选好、写卡写入,通电,第一次进系统初始化。这个流程单独看没有任何问题,Raspberry Pi OS 这类官方系统镜像把底层基础环境处理得算不错,至少你在国内能比较顺利地把系统跑起来再换个软件源,基本能用。

但问题来了:所谓“系统镜像”,本质上是一套静态的系统快照,它只负责把操作系统初始环境固定下来。你之后所有对系统的修改——安装包、改配置、加用户、跑服务,都是在这次快照之上“层层叠加”。头几个服务装完还没什么感受,到了第五第六个,各种依赖就开始互相打架。

我实际踩过的典型场景是这样:我先用 apt 装了一个 nginx,然后因为要跑一个 Python 网页小应用,又装了 python3-pip,pip 一装就带进来一堆依赖库。再后来某个小工具需要新版本 OpenSSL,我架不住教程里“直接编译安装”的诱惑,手动编了一个新版,结果系统里旧的 OpenSSL 库还留着,两个版本混在一起。过了两周系统安全更新一跑,nginx 直接起不来,网页服务器永远打不开,排查一晚上,最后发现是系统里的 libssl.so 被我自己编译的版本覆盖了关键路径。

这种问题不是树莓派独有,是任何“长周期运行的 Linux 设备 + 手工维护系统”的必然结局。树莓派和普通 VPS 不太一样的地方在于,它的资源更有限、SD 卡更容易坏、重刷系统的成本也高,所以一旦系统环境被搞乱,不是你忍一忍就能过去的,而是整个服务全部停摆,甚至要拔卡重刷。

1.2 树莓派扛不住“大而全”系统

树莓派的硬件天花板摆在那里,哪怕是最新的树莓派 5,内存也就 8GB,日常给网页服务用的往往还是 4GB 版本。再多服务同时跑、进程数量一多,空余内存就会迅速见底。树莓派默认用的 swap 又是建立在 SD 卡上的,频繁换页会显著加快缩卡寿命,卡一坏,系统镜像跟着一起完蛋,数据没备份的话整个服务直接归零。

另外一个很多人忽略的点是 SD 卡 IO。树莓派系统盘就是 SD 卡,容器的镜像、日志、数据都要读写它。如果你只在系统层面跑几个服务,一般没啥大问题;一旦服务多了,系统里各种日志文件、pip 缓存、apt 缓存、临时文件全都堆在卡上,卡本来就不快的 4K 随机读写会被放大成持续高负载,网页响应跟着变慢。

所以树莓派长期跑服务,天然不适合“在系统里堆一堆东西”。它需要的是另一种模式:服务本身可复制、可清理、可随时重建,而且重建之后不会在系统里留下垃圾。这就要求你把服务与系统环境解耦。这不是优化技巧,而是树莓派这种硬件条件下必须走的路线。

1.3 网页服务器只是第一站

我见过很多树莓派玩家的路线图:先跑一个 nginx 静态页面试试水,然后很快会加一个 PHP 或 Node 的接口服务,接着上数据库,再整一个反代把几个服务串起来,再之后可能是内网穿透或者智能家居的集成服务。说直接一点,在“折腾”这个语境里,网页服务器几乎就是树莓派的第一站,但它永远不会是最后一站。

服务一旦多起来,真正消耗你精力的事情不是“写功能”,而是“保持环境不坏”。比如某天你升级系统后,PHP 版本被 apt 不小心升了,你的老接口开始 502;再比如你按某篇教程给树莓派装了个软件,那软件自动把 nginx 配置给改掉了;又比如你想把整套服务迁移到另一张 SD 卡,结果发现自己根本不记得一年前装过哪些依赖。

当你开始问“我能不能把现在这套环境原封不动搬到新卡上”的时候,你已经摸到 Docker 的门口了。Docker 的答案很直接:我不搬运整个系统,我只搬运服务本身以及服务运行所需的环境描述。环境变成代码,服务变成容器,系统镜像从此不再是你最关心的东西。

2. Docker 到底做了什么,把杂乱的“跑服务”变成了“关进盒子”

2.1 Docker 不是虚拟机,是进程隔离

要理解 Docker 为什么适合树莓派,先要破除一个常见的误解:Docker 不是虚拟机。虚拟机模拟一整套硬件,然后里面再跑一个完整操作系统,开销大不说,镜像体积通常也是几个 GB 到几十个 GB。树莓派的性能和存储根本不适合这样玩。

Docker 的做法是在 Linux 内核层面做隔离。它利用 namespace 给每个容器一个独立的视图——独立的文件系统、网络栈、进程表、用户空间;再用 cgroups 限制每个容器能使用的 CPU、内存和 IO。在树莓派上跑容器的时候,容器里的进程其实直接跑在树莓派的 CPU 上,跟普通进程没什么本质区别,只是它被“圈”在了一个独立的盒子里。

用生活化的类比来解释:虚拟机是给你一整套独立的“房子”,水电煤气都是独立接的,代价是要配一个“物业公司”;Docker 容器则是你在一栋大楼里隔出的“单间”,墙是有的、门是有的,但水电管道都是共享楼里的系统。它省掉了物业公司的开销,代价是隔离程度比虚拟机浅一点,但对树莓派跑网页服务来说,这种隔离程度已经完全足够了。

2.2 镜像:把你需要的环境固化成一个“可复用快照”

Docker 的镜像概念,其实和树莓派刷机时用的系统镜像有相似之处,都是一个静态文件、一套完整的环境。Docker 镜像的特殊之处在于它不是“单层整体”,而是一层一层堆叠出来的。

你可以在一个镜像基础上做修改,加一个软件、改一个配置,生成一个新的层。别人想复用你这个环境的时候,只需要基于同一个基础镜像,复用没有变化的底层,只下载多出来的层就行。这样带来的直接好处是:镜像体积小、分发快、存储省。

比如我在树莓派上跑 nginx,我会用一个nginx:stable或更精简的nginx:alpine作为基础镜像,然后把我自己的网站文件放进去,做出一个属于我自己的网页镜像。这个镜像放在任何装了 Docker 的 Linux 机器上,都能跑出一模一样的服务来,不管是树莓派 3B+ 还是树莓派 5,结果一致。这就是“环境固化”的含义:你不再依赖某张 SD 卡里的某个系统状态,而是依赖一个明确定义的镜像。

2.3 容器的生命周期:启动、停止、删除与重建

Docker 容器是个轻量级进程,有非常清晰的生命周期。你可以用docker run启动,docker stop停止,docker rm删除,然后基于同一个镜像再启动一个全新容器。

这句话听起来平平无奇,但它的意义极其重大:传统模式下,服务“坏了”是一件很严重的事情,因为修复的是系统本身,牵一发动全身;Docker 模式下,服务坏了,我做的事情就是“停掉容器、删掉容器、再跑一个新的”。整个修复过程不超过十几秒,而且不会对宿主机系统产生任何污染。

你只需要保证一件事:服务的数据和配置不在容器内部,而是挂载到容器外部(通过 volume 或 bind mount)。这样容器本身只是一个“临时工”,干完活丢了也不可惜,真正有价值的数据都保存在宿主机目录里。

2.4 为什么“删掉容器再跑一个”这句话最值钱

我在树莓派上运维的这些年,最受益的一句话就是:不要修容器,要重建容器。作为从传统 Linux 运维一路走过来的人,我早期总是喜欢钻进容器里,把某个配置改了再重启服务。后来发现这种方式看着像在“修”,实际上掩盖了“环境不一致”的真正问题。

如果配置有误,你应该改的,是生成容器所用的 Dockerfile 或配置文件,然后删掉旧容器,用新配置重新跑。如果新配置不对,无非是再改再跑一次,一分钟搞定。这种模式逼着你把环境定义清楚,而不是靠手动改来改去。时间一长,这套系统的可维护性、可复制性和可恢复性,会远远超过那套“靠记忆维护”的传统方案。

所以,当你决定在树莓派上使用 Docker 时,你其实是做了两个决定:第一,服务与系统解耦;第二,基础设施即代码。这两个决定,会让树莓派从“玩具”变成一个真正可靠的“迷你服务器”。

3. 方案选型与物料准备:把“要 Docker 化”做成具体清单

3.1 树莓派硬件选型:4B、5 与 Pico 别搞混

进入实操之前,先把硬件说明白。树莓派家族里适合跑 Docker 的,最低建议是树莓派 4B,内存 2GB 起步,最好 4GB 或以上。树莓派 5 性能更好,跑容器自然更舒服,但现在价格和散热片也是一笔开销。至于树莓派 Zero 2 W,内存只有 512MB,跑一个轻量容器勉强,要像跑 nginx + 数据库 + 后端服务这种组合就会有些吃力,不是很推荐作为网页服务器的载体。

有个常见的低级错误需要提醒:树莓派 Pico 是单片机,不是 Linux 系统,跑不了 Docker。网上有些热词把“树莓派 Pico 控制舵机”和“树莓派 Docker”混在一起,完全是两条线。做嵌入式单片机开发不用考虑 Docker,本文的场景全部针对能跑完整 Linux 系统的树莓派单板机。

我自己的主力机器是一块 4GB 内存的树莓派 4B,配一块 64GB 的 A2 级 SD 卡,跑 nginx、Node 后端、MySQL 容器组合,目前还有内存余量。如果你预算宽松,树莓派 5 的 8GB 版本会更从容,尤其是在容器里跑编译任务的时候,差距非常明显。

3.2 系统镜像选哪个:Raspberry Pi OS Lite 还是 Desktop

这个选择其实不该纠结。树莓派上跑 Docker 化网页服务器,只用命令行就够了。请直接选择 Raspberry Pi OS Lite 版本,也就是不带桌面环境的轻量版系统镜像。理由很简单:桌面环境会占用几百 MB 内存和大量 CPU 时间,对服务器场景没有任何帮助,还要承担桌面组件的安全更新负担。

我用过一个比喻来形容:如果你打算开一个小饭馆,你不会把饭店装修成“家庭客厅加开放式厨房”,你只需要后厨和收银台。树莓派跑 Docker 也一样,桌面环境就是那个豪华客厅,好看但完全没必要。

下载镜像建议用官方提供的 Raspberry Pi Imager 工具去烧录。它最方便的地方是可以提前在烧录阶段设置好 SSH、WiFi、用户密码等参数,不用你拔卡插显示器再配置。我每次烧卡都是这样:选 Lite 镜像,选 SD 卡,点设置,然后插电开机就能直接 SSH 上了。

3.3 写卡与第一次开机:从镜像到能 SSH

如果你第一次用官方 Imager,烧录流程是这样的:打开 Imager,选择操作系统,选择“Raspberry Pi OS Lite (64-bit)”,然后选择目标 SD 卡。点右下角的设置按钮,可以启用 SSH、设置用户和密码、配置 WiFi。把卡写入后,插到树莓派上通电,过几秒钟,你就能从局域网里找到这台设备。

如果你没有显示器,建议在路由器后台看 DHCP 客户端列表,或者用 nmap 扫描局域网。我习惯在 Imager 里直接把 IP 固定下来,避免以后树莓派因为 DHCP 分配变化导致 SSH 连不上。

首次 SSH 进去之后,建议先把系统更新做完:

sudo apt update && sudo apt upgrade -y

树莓派官方源在国内一般稍慢,你可以视情况换成国内镜像源。这一步不是必须,但能让后续安装 Docker 更顺利。更新系统之前心里要有数:树莓派系统升级有时候会让你的旧配置失效,而 Docker 恰恰能帮你规避这类问题的扩展影响,这也是后话。

3.4 树莓派上安装 Docker 的两种方式:脚本与 apt

Docker 官方提供了一个安装脚本,一行命令装完:

curl -fsSL https://get.docker.com -o get-docker.sh sudo sh get-docker.sh

这个脚本会自动识别系统架构,下载并安装 Docker Engine,并帮你把当前用户加进 docker 用户组(如果脚本成功的话)。在树莓派上,这个脚本是官方推荐的装机方式,好处是版本比较新、安装路径标准。

另外一种方式是直接 apt 安装:

sudo apt install docker.io -y

这种方式简单方便,但 Debian/Ubuntu 的软件仓库里 docker.io 的版本通常滞后,尤其是树莓派 OS 更新节奏不一定跟得上 Docker 上游。对正常使用影响不大,但如果你想用最新的 Compose 插件或新特性,建议用官方脚本。

无论用哪种方式,装完以后都要运行一下验证命令:

sudo docker run hello-world

如果打印出 “Hello from Docker!”,说明引擎正常工作。如果提示权限问题,那就说明当前用户还没加入 docker 用户组,执行:

sudo usermod -aG docker $USER

然后重新登录 SSH 再试。这个坑我见过太多次,网上几乎所有教程都会让你执行这一行,但很多人还是忘了重新登录,导致权限不生效。

4. 实操:把树莓派变成 Docker 化网页服务器

4.1 安装 Docker 引擎并验证

先讲我实测下来最顺畅的一套流程。SSH 进入树莓派后,第一步是安装依赖,然后拉取官方脚本执行:

sudo apt update sudo apt install -y ca-certificates curl gnupg lsb-release curl -fsSL https://get.docker.com -o get-docker.sh sudo sh get-docker.sh

安装过程会持续几分钟,树莓派上如果没有散热片,CPU 温度可能会到 60 摄氏度以上,属正常现象。装完后 Docker 服务会自动启动并设置成开机自启:

sudo systemctl status docker

如果输出显示active (running),Docker 引擎这部分就算完成了。我从不用桌面版 Docker Desktop,树莓派上没这必要,而且 Docker Desktop 自带的那套 VM 方案和树莓派架构完全不搭。直接用 Docker Engine 命令行最干净。

验证部分多提一句:docker run hello-world这条命令需要拉镜像,如果这一步就超时或报错,大概率是网络不通或者镜像加速没配,请先跳到 4.2 节把加速源配置好再回来。

4.2 配置镜像加速并避免拉取卡住的坑

Docker 装好之后,在中国大陆网络环境下,第一件事就是配置镜像加速器。如果不配,你执行docker pull nginx的时候可能会卡在一个很低的速度上,甚至直接超时。网上很多教程里说“树莓派 docker 安装完成”,其实只是装完了引擎,镜像拉不下来照样没法用。

镜像加速器的配置路径是/etc/docker/daemon.json。我建议你先备份原文件,然后写入以下内容:

{ "registry-mirrors": [ "https://docker.m.daocloud.io", "https://dockerproxy.com", "https://hub-mirror.c.163.com" ] }

写完保存后,执行:

sudo systemctl daemon-reload sudo systemctl restart docker

docker info查看 Registry Mirrors 一项是否存在,确认生效。加速源的选择,原则是“多填几个,只要有一个能用就行”。实测下来,不同时间段加速源的稳定性差异很大,所以我一般会一次性填三个,防止一个挂了全盘卡死。

需要特别提醒的是:加速源只对 Docker Hub 的公开镜像有效。如果你后面要拉一些存放到私有仓库的镜像,那是另一个话题,这里先不展开。另外,镜像加速不等于“万能药”,有些大型镜像(比如编译工具链)本身就有几百 MB,无论走什么下载源,在树莓派这种网络条件下都需要耐心等。

4.3 用 nginx 容器把“网页服务器”跑起来

环境准备好之后,我们来做第一件正事:跑一个 nginx 容器。

最简单的命令如下:

docker run -d --name web-nginx -p 8080:80 nginx:stable

拆解一下这条命令:

  • -d表示后台运行;
  • --name web-nginx给容器起个名字;
  • -p 8080:80把宿主机的 8080 端口映射到容器内部的 80 端口;
  • nginx:stable表示使用 nginx 官方稳定版镜像。

拉镜像可能有些慢,等它完成后,你会得到一个正在后台运行的 nginx 容器。此时你可以从局域网任意一台电脑访问http://树莓派IP:8080,如果看到 nginx 的欢迎页面,你的网页服务器就算跑起来了。

这里为什么要映射到 8080 而不是直接 80?因为树莓派系统本身可能还留着一些软件占着 80,或者你以后想在宿主机上直接跑另一个服务。端口映射的灵活性,正是 Docker 的优势之一:宿主机 80 被别的程序占了,容器换个端口照样跑。不用为了端口不冲突去重装系统。

查看容器状态:

docker ps docker logs web-nginx

docker ps会显示运行中的容器列表,docker logs则输出 nginx 的访问日志和错误日志,排查问题很有用。

4.4 让网页内容“活”起来:挂载目录与持久化

现在这个 nginx 容器虽然能跑,但里面显示的内容是镜像自带的默认页面。如果我们想放自己的网页文件,常规思路是把文件docker cp进去,但这种做法很糟:容器一删,文件就没了。

正确做法是用 bind mount,把宿主机的目录与容器内的目录关联起来。我一般会在宿主机上建一个专门的目录存放网站文件:

mkdir -p ~/html echo "<h1>Hello from Raspberry Pi Docker</h1>" > ~/html/index.html

然后重新创建一个容器,挂载这个目录:

docker run -d --name web-nginx -p 8080:80 -v ~/html:/usr/share/nginx/html:ro nginx:stable

这里的-v参数把~/html挂载到容器内的 nginx 网页目录,:ro表示只读,防止容器内的进程意外篡改宿主机文件。

做完之后,刷新浏览器,你会看到自己的网页内容。以后只要修改宿主机~/html里的文件,再刷新浏览器就能看到变化,不需要进容器。这个模式叫作“配置与数据外置”,是长期运行服务的前提。以后如果你要备份网页服务器,只需要备份这个目录和容器启动命令,不用备份整个系统。

4.5 用 docker-compose 固定服务:以后不会再弄丢

单纯一个docker run命令确实简单,但时间一长问题会很明显:容器越来越多,命令参数越来越长,哪天换了一张 SD 卡,你根本记不住当初是怎么启动的。

解决方案是使用 Docker Compose。它是一个把容器编排定义写成配置文件的工具。树莓派上如果按官方脚本装 Docker,一般会自带 Compose 插件。运行docker-compose versiondocker compose version验证一下。如果提示没有,可以补装:

sudo apt install docker-compose-plugin -y

然后在自己的项目目录里写一个docker-compose.yml

version: "3.8" services: web: image: nginx:stable container_name: web-nginx ports: - "8080:80" volumes: - ~/html:/usr/share/nginx/html:ro restart: unless-stopped

保存后,启动命令简化为:

docker compose up -d

以后想把整套服务停掉,就是docker compose down。迁移到新设备,也只需要把docker-compose.ymlhtml目录复制过去,再执行一次docker compose up -d,服务就起来了。

我在实际使用中强烈建议,即使是单个容器,也尽量写 compose 文件,不要直接敲docker run。理由很简单:compose 文件就是“可复制的环境说明书”,它可以放进 Git 仓库,可以备份,可以审查。你不再需要依赖“自己的记忆力”来还原环境,所有配置都摆在明面上。

5. 常见问题与排查技巧实录

5.1 镜像拉不下来或超时:先检查网络和加速源

docker pull卡住是最常见的问题。解决思路按顺序来:先确认宿主机能访问外网,能 ping 通公网 IP 说明网络通;再确认/etc/docker/daemon.json里的镜像加速器是否生效;最后看是不是单个镜像太大导致等不耐心。

还有一个小技巧:不要一直盯着终端等,直接用docker pull nginx:stable,如果五分钟还没动静,果断 Ctrl+C,重新检查配置,再试。很多时候加速源会临时抖动,换一个即刻就好。

排查命令参考:

docker info | grep -A 4 "Registry Mirrors" ping -c 4 baidu.com

5.2 端口被占用:80 被系统 nginx 占住怎么办

很多树莓派用户在一开始的时候会用 apt 直接装过 nginx,再想通过 Docker 起一个 80 端口的容器时,会报bind: address already in use

处理方式有两个:一是把系统自带 nginx 彻底卸载:

sudo systemctl stop nginx sudo apt remove nginx nginx-common -y

二是像我上面用的,把 Docker 容器映射到别的端口,比如 8080。我一般建议先换端口,等确认 Docker 模式稳定了,再回头清理系统里的旧服务。这个过程中 Docker 容器不受任何影响。

5.3 容器一重启就没了:加上 restart 策略

如果你在树莓派上重启系统后,发现容器没自动起来,多半是启动容器时没加--restart参数。解决方案是在docker run时加上:

docker run -d --name web-nginx --restart unless-stopped -p 8080:80 nginx:stable

如果用的是 compose 文件,就在服务定义里写restart: unless-stopped。这个策略的含义是:除非你手动停掉容器,否则系统重启后 Docker 会自动把它拉起来。但要注意,如果容器本身崩溃退出了,除非有unless-stoppedalways策略,否则它也可能不会自动重启。对长期服务器场景,我通常直接写unless-stopped

5.4 权限不足:docker: permission denied 的解决

装完 Docker 后执行docker ps,如果报permission denied while trying to connect to the Docker daemon socket,说明当前用户不在 docker 用户组里。执行:

sudo usermod -aG docker $USER

然后一定记得重新登录 SSH,或者执行newgrp docker临时生效。这个问题几乎所有新用户都会遇到,所以单独列出来。我再多说一句:不要为了省事直接用sudo docker走天下,这会造成文件权限混乱,后患无穷。

5.5 树莓派发热与 SD 卡寿命:日志与数据落盘策略

Docker 化之后,树莓派的 SD 卡压力其实并不会自动变小,容器日志默认也会写入宿主机。长时间运行的高流量容器,日志文件可能膨胀到好几 GB,把 SD 卡塞满。建议给 Docker 配置日志轮转。

/etc/docker/daemon.json里加一段:

{ "log-driver": "json-file", "log-opts": { "max-size": "10m", "max-file": "3" } }

重启 Docker 生效。这样每个容器最多保留 3 个 10MB 的日志文件,日志不会无限膨胀。这个配置对 SD 卡寿命有很大帮助,是最容易被忽略但很重要的一条实践经验。

另外,数据库或需要读写的业务数据,尽量通过 volume 挂载到宿主机目录。数据落盘策略要结合自己的场景,但原则不变:容器无关数据不要放在容器内,要放在宿主机和卷里。

最后的一点体会

根据我个人的实操经验,树莓派用 Docker 最大的收益不是“看上去很酷”,而是“我不再害怕系统坏了”。系统镜像还是原来那张刷好的镜像,但服务运行方式已经彻底变了。原来我会花一整晚去修 nginx 依赖、PHP 扩展冲突;现在我会直接看容器日志,如果解决不了,就改配置重跑一个容器,干净、利落、快。

这个系列的文章后面会继续讲怎么把一整套网页服务器方案完善起来,包括多容器配合、反向代理、数据持久化、备份恢复等。但你在开始那些内容之前,最好先把本文里的基础做扎实:系统镜像烧好、Docker 装好、至少能跑起来一个 nginx 容器。后面的所有技巧,都是建立在“你已经会用 Docker 思维来管理树莓派服务”的前提上。等你把第一个容器跑起来,再回头看看“为什么我们需要 Docker”这个问题,答案已经在手上了。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询