树莓派服务器为何需要Docker?从环境混乱到容器化运维
2026/9/18 19:42:00 网站建设 项目流程

1. 起因:当树莓派服务越装越多,我开始不敢关机

手里的树莓派4B已经跑了小半年网页服务,从最初的 Nginx 静态页,到后来陆续加了 MySQL、Node.js API、内网穿透工具、定时爬虫脚本。系统是 Raspberry Pi OS Lite,没桌面,全靠 SSH 艹。最开始很爽,apt install 一条龙,一个 SD 卡撑起一个小网站。但是等到服务数量超过七八个之后,事情开始不对劲了:Python 版本冲突、Node 版本混乱、MySQL 的依赖库被某个安装脚本改掉、Nginx 的配置文件和另一个服务打架。最离谱的一次,我升级系统依赖,直接把 libssl 给搞挂了,整机 SSH 都进不去,最后只能拔卡重刷系统,所有配置全部重来。

那次事故之后我意识到一个道理:裸机装服务,本质上是在用一个正在运行的系统当作测试环境。对于一台需要长期稳定提供网页服务的树莓派来说,这是不可接受的。所以我决定把整套服务器环境迁到 Docker 上,也就是要给这篇博客的标题一个交代:为什么我们需要 Docker。这篇文章就是这个系列的第一篇,先把动机和核心概念讲清楚,后面再逐步拆解具体怎么把树莓派上的网页服务 Docker 化。

简单说,这篇内容是给那些和我一样,手里有一台树莓派,把它当服务器用,但是已经被环境问题折磨过的人看的。如果你正准备用树莓派跑点什么服务,或者已经在裸机上装了一堆东西但还没出过大事,这篇文章值得读完,因为能帮你省下未来至少一个周末的修复时间。

2. Docker 到底解决了什么问题:服务增长带来的混乱,本质是状态管理失控

2.1 系统镜像的“快照”思维:一台机器多个环境能共存吗

裸机安装服务的本质是什么?是在同一个系统镜像上不断叠加、修改、覆盖状态。你今天为服务 A 装了一个库,明天服务 B 依赖同一个库的另一个版本,apt 一升级,服务 A 挂了。这种问题的根源不在于你操作失误,而在于系统镜像本身是唯一的、共享的、全局的。所谓“系统镜像”,其实就是一个完整的根文件系统加内核,你的所有服务都共享这一份状态,谁都没法保证自己的改动不影响别人。

Docker 的思路不是改变服务本身,而是改变服务的运行边界。每个容器都拥有独立的文件系统视图、独立的进程空间、独立的网络栈和独立的用户权限范围。容器用的底层内核是同一个,但每个容器内部的 /usr、/etc、/var 这些都是自己独立的副本。这就相当于你在同一台物理机上虚拟出了多个互不干扰的“最小系统镜像”,每个服务都以为自己跑在一台全新的机器上。

打个比方:裸机装服务就像合租一套房子,所有租客公用厨房、卫生间、客厅,一个人搞乱厨房,其他人全部遭殃。Docker 则是给每个租客单独一个小隔间,厨房、卫生间、卧室全都有独立的过滤和隔离系统,外面再乱的装修也影响不到你。

2.2 服务增长的必然结果:环境冲突的临界点一定会到来

有人会觉得,我服务数量少,装几个服务而已,没必要上 Docker。确实,当你只有一两个服务时,裸机部署反而最简单。但是服务增长是一个必然趋势——你今天为博客加了评论数据库,明天又加了一个搜索索引服务,后天临时挂了一个微信机器人。每增加一个服务,潜在冲突的对数就增加一分。

我在树莓派4B上经历过的最典型场景:一个 Python 项目需要 Python 3.9,而系统默认是 3.7;另一个 Node 项目需要 npm 全局包,但版本又和其他项目要求的互相覆盖;还有一个旧项目用 MySQL 5.7,新项目要求 MySQL 8.0。在裸机上,这些需求无法同时满足,除非你手动管理多个版本的运行时,而这是一件极其痛苦的事。如果你把这些服务各自放进一个容器,每个容器里装自己需要的 Python、Node、MySQL,整个系统就恢复了清净。

2.3 系统镜像的大小和复杂度:树莓派 SD 卡的脆弱性被放大了

树莓派的 SD 卡存储和完整性比普通服务器的硬盘差得多。频繁读写、突然断电、依赖库覆盖安装,这些操作都在让 SD 卡更接近损坏边缘。系统镜像越大、被修改得越频繁,风险越高。Docker 镜像虽然也占用存储,但它的数据是一层一层封装的,每次部署新服务不需要再往系统根分区里写一堆动态链接库。服务运行产生的数据也可以挂载到独立的 volume 里,和系统目录分离,方便管理和备份。

更重要的是,如果 SD 卡真的损坏了,裸机系统的恢复方式是重新刷整个镜像,然后手动把服务一个一个装回来。而 Docker 化的环境,只需要在另一台树莓派上执行 docker-compose up -d 即可一键恢复。这个差距在数据丢失和个人心力上的节省是巨大的。

3. Docker 的核心原理拆解:镜像层、容器层和挂载卷的关系

3.1 镜像:一套经过验证的“装机预演方案”

Docker 镜像可以理解为一个打包好的系统文件系统快照,它包含一个 service 运行所需的全部文件:二进制文件、库文件、配置文件和依赖项。镜像是分层的,每一层都是只读的。所有层叠加在一起,构成一个完整的、可执行的根文件系统视图。

镜像是怎么产生的?在 Dockerfile 里,每条指令(RUN、COPY、ENV 等)都会创建一个新层。这些层可以复用,例如两个服务都基于同一个基础镜像(比如 ubuntu:22.04),那么它们的前几层是完全一样的,不会重复存储。这意味着,哪怕你有十个容器,只要基础镜像相同,实际磁盘占用并不是十份完整镜像,而是一份基础层加上各自的小改动层。这个特性对 SD 卡容量有限的树莓派来说非常宝贵。

镜像还具备不可变性。一旦构建完成,镜像内容就不会被修改。你改代码,改配置,这些变更应该通过重新构建新镜像来完成,而不是进入容器内手动改文件。这里有一个深刻的意义:通过镜像,你得到了一套经过验证的、可重复执行的“装机预演方案”。团队里任何人拿到镜像,都能跑出完全相同的环境,彻底告别“在我机器上明明是好的”这类推诿。

3.2 容器:镜像的运行时实例,不是一台虚拟机

容器是镜像跑起来之后的实例,它是活着的、有状态的(相对镜像)。容器的文件系统是在镜像的只读层之上叠加了一个可写层,所有运行时的文件写入都在这一层。容器内部运行着你的进程(比如 Nginx),但它的进程是运行在宿主机内核之上的,只是通过 namespaces 和 cgroups 实现了隔离与资源限制。

容器和虚拟机的最大区别在于:虚拟机需要模拟硬件,跑一个完整的客户机操作系统(Guest OS),每个虚拟机都占用几百 MB 甚至几个 GB 的内存,对树莓派这种 ARM 小板上来说不可接受。而容器直接和宿主机共享内核,只额外消耗进程级别的系统资源。一个精简的 Nginx 容器在树莓派上内存占用可能只要 30~50 MB,这在同一台硬件上跑十个服务变得非常现实。

那容器的隔离是不是绝对安全的?不是。容器的隔离级别不是安全边界,而是运维边界。它隔离的是运行环境,不是内核。如果一个容器里的进程能获得宿主机的 root 权限,它仍然可能影响整个系统。所以不要把容器当作安全沙箱来用,它首先是一个环境隔离工具。

3.3 数据卷:容器消失后,你的数据还在

容器本身是可丢弃的。docker rm 之后,整个容器的文件系统就删掉了。如果服务产生的重要数据写在容器可写层里,就会跟着容器一起消失。所以 Docker 引入了 volume 机制,主动把容器内的某个目录映射到宿主机的一个持久化存储路径。

在树莓派场景下,数据卷挂在 SD 卡上即可,但更推荐挂在外部 USB 移动硬盘或 NFS 共享目录里。因为 SD 卡的写入寿命是个硬伤,MySQL 这种频繁写日志和数据的服务如果跑在 SD 卡上,会明显加速卡的损坏。我在自己的树莓派上把 MySQL 的数据目录挂到了移动硬盘上,用了两年也没有出现过数据库文件损坏的问题。

数据卷的第二种用途是共享配置或代码。比如,你可以把宿主机上的某个目录映射到容器里的 /app,这样改代码就不需要重构镜像,直接改宿主机的目录,容器内立即生效。这在开发调试阶段特别方便,但是生产环境我会建议用镜像固化代码版本,然后再映射日志和持久化数据目录。为什么要这样做?因为生产环境里的容器可能分布在多台机器上,如果依赖宿主机目录,代码不一致就失去了镜像的一致性意义。

4. 树莓派上 Docker 化网页服务器的实际收益:部署、回滚和资源占用

4.1 部署动作从“约等于重构系统”降级为“写一段配置”

裸机部署一个网页服务的典型动线是:apt 安装依赖、下载运行时、解压配置、准备 DB、修改 Nginx 站点配置、测试、重启服务。这个过程里,最磨人的不是步骤多,而是每一步都可能破坏系统的其他部分。今天你为了一个新服务把 MySQL 的端口改成了 3307,搞不好老服务就没法连数据库了。

Docker 将这一切变成了一份 docker-compose.yml。我来展示一个简化但真实的示例,这段配置可以在树莓派 4B 上直接跑一个 Nginx 加 MySQL 的网页服务器基础环境:

version: "3.8" services: web: image: nginx:1.25-alpine container_name: web-server ports: - "8080:80" volumes: - ./html:/usr/share/nginx/html:ro - ./nginx/conf.d:/etc/nginx/conf.d:ro depends_on: - db restart: unless-stopped db: image: mysql:8.0 container_name: web-db environment: MYSQL_ROOT_PASSWORD: example_pass MYSQL_DATABASE: webapp volumes: - db_data:/var/lib/mysql ports: - "3306:3306" restart: unless-stopped volumes: db_data:

这不是炫技,它就是日常工作的普通写法。你锁定镜像版本,定义端口映射,确定数据挂载关系,然后一条 docker compose up -d 启动全部。以后再部署一个新服务,不需要再考虑会不会影响系统上的老服务,只要你新定义一个 service 块即可,所有依赖都封装在镜像里。

4.2 回滚操作有了真正的“时光机”

裸机环境下,你改了某个配置文件然后服务起不来了,怎么回滚?如果记得住改了哪些行,可以还原;如果不记得,那就只能翻日志、查历史命令,甚至重新配置整个服务。这个过程非常耗时,而且你在现场操作时往往带着压力,更容易做错。

Docker 镜像天然带版本化。每次构建新版本时打一个新的 tag,比如 my-web-server:v2、my-web-server:v3。如果 v3 出了问题,只需要把 docker-compose.yml 里的镜像 tag 改回 v2,然后重新 up,几秒钟就回到上一个可用状态。它的背后原理很简单——镜像层是不可变的,每一版都保留着当时构建的完整文件状态。这个能力在网页服务频繁改版时非常值钱,因为网站依赖的配置、后端代码、静态文件是一个整体状态集合,而不是散落在一堆目录里的文件。

回滚还有一个高层层面的好处:因为你敢随意回滚了,就会更敢于做变更实验。我之前有一个项目的 HTTP 服务需要从 Nginx 迁移到 Caddy,放在裸机上要折腾一个下午,但在 Docker 里我新起一个 Caddy 容器,把端口改好,测试完直接切换流量入口,不理想就切回来,全程对系统零污染。

4.3 资源占用:容器并不是免费午餐,但值得这个开销

有人担心树莓派 4B 只有 4GB、8GB 内存,跑 Docker 会不会太吃力。我的实测数据是:基础系统(Raspberry Pi OS Lite + Docker Engine + Docker Compose)开机后内存占用约 400~500MB,其中 Docker 守护进程长期驻留约 100~150MB。一个 Nginx Alpine 容器约 30~50MB,一个 MySQL 8.0 容器约 250~400MB,一个 Node.js API 容器约 80~150MB。整体跑一个中小规模的网页服务加数据库,内存占用控制在 1.5GB 以内完全可行,4GB 版树莓派跑三四个网站实例绰绰有余。

CPU 方面,Docker 容器有 cgroups 的资源限制能力。你可以在 docker run 或 compose 文件中限制某个容器最多使用多少 CPU 配额、多少内存。我非常建议给数据库类容器设定高于其他容器的内存限制,同时给每个服务都设置 restart: unless-stopped,这样即使某个服务崩溃退出,守护进程也会自动拉起它,不需要人工干预。

存储层面,Docker 镜像占用的实际空间可能比预想的要小,因为镜像层复用的机制存在。举个实际例子:我有五个服务,其中三个都基于 node:18-alpine 基础镜像,那么这几个镜像的基础层只存一份。最终用 docker system df 查看,总占用可能比所有镜像尺寸加起来少 30%~40%。

5. 为什么偏要在树莓派上 Docker 化:ARM 平台的特殊考量

5.1 ARM 架构镜像选择和外部生态的适配

树莓派是 ARM 架构,跟大多数云服务器(x86_64)不一样。Docker Hub 上大部分官方镜像都提供了 multi-arch 支持,也就是说,同一个 nginx:1.25-alpine 标签,系统会根据你 CPU 架构自动拉取 ARM64 版本,不需要手动指定。你要注意的反而是一些非官方镜像,尤其是只有 amd64 架构的第三方镜像,在树莓派上是无法直接运行的,这时候你需要找替代镜像,或者自己用 Dockerfile 从源码构建。

另外一个考量是树莓派的系统镜像本身。Raspberry Pi OS 是 32 位为主的系统(即使是树莓派 4B,官方也推荐 32 位 Lite),如果你用的是 32 位系统,容器也只会跑 32 位版本。我自己后期换成了 64 位的 Raspberry Pi OS Lite,原因很简单:很多镜像是纯 64 位的,且单个进程地址空间超过 4GB 限制在某些内存密集场景下有优势。如果你从零开始,直接装 64 位系统,后面少很多麻烦。

5.2 GPIO 和硬件直通与 Docker 的结合

树莓派不同于普通服务器的另一个特点是 GPIO(通用输入输出接口)。很多树莓派项目会用到传感器、摄像头、继电器等外设,这些东西绕不开直接操作硬件资源。Docker 容器虽然隔离了环境和进程,但硬件访问默认是受限的。要让容器访问 GPIO,需要挂载 /dev/gpiochipX 等设备节点,并以特权模式运行容器(privileged: true)。这个操作要谨慎,特权模式会削弱容器的隔离性,相当于告诉内核“允许这个容器的进程访问更多宿主资源”。

我的实践结论是:纯网页服务器场景不建议直接用 Docker 管理 GPIO。如果业务同时涉及网页展示和硬件控制,我会把 GPIO 控制逻辑放在宿主机上的一个独立服务里,然后通过 HTTP API 或 MQTT 桥接给容器内的网页服务。这样做的好处是硬件访问和网页应用解耦,容器的重启不会导致引脚状态被系统重置。如果你一定要在容器里操作 GPIO,务必记住挂载设备节点和特权模式是必须的,同时你也要接受安全隔离性下降这一代价。

5.3 网络模式的选择:桥接、主机、还是 Macvlan

Docker 提供多种网络模式,但在树莓派上我最常用的是 bridge 和 host 两种。

bridge 模式是默认的,每个容器会分配一个独立的内部 IP,宿主机通过端口映射把外部流量转发到容器。这种方式灵活,多个容器可以用相同端口(比如各个容器内部都用 80),只是外部映射不能冲突。在前面的 docker-compose 示例里,我就是把 web 容器内部的 80 端口映射到宿主机的 8080,这就是 bridge 模式。

host 模式则让容器直接使用宿主机的网络栈,没有端口映射这一层。这个模式在两种场景下很好用:一是服务需要大量使用主机端口(比如 UDP 服务);二是希望容器内看到的外部 IP 就是宿主机实际的 IP,方便一些鉴权或者日志工具。缺点是多个容器不能同时监听同一个端口,而且容器和宿主机网络环境混在一起,隔离性降低。在树莓派的场景中,如果你只跑少量服务,host 模式可能让人感觉更简单;但如果服务数量上来了,我建议还是统一用 bridge 模式加反向代理来管理入口流量。

还有一点经验值得提:不要把树莓派直接暴露在公网端口上。如果网页服务需要公网访问,用一个带 TLS 的 Nginx/Caddy 作为入口容器,后面挂内部网络里的各应用容器。这既是安全习惯,也是工程组织上的清晰边界。

6. 当系统镜像“一路升级”到 Docker 化之后:运维习惯的转变

6.1 从“登录进系统敲命令”到“声明式描述状态”

裸机运维的习惯是登录到系统,用命令直接修改系统的状态。Docker 化之后,运维对象从“这台机器上的文件与进程”变成了“描述期望状态的编排文件”。你不再关心某台树莓派上到底装了哪个版本的 Nginx,因为镜像 tag 已经把版本钉死了。你关心的是 docker-compose.yml 里写了什么、镜像仓库里存了哪些版本。

这个转变最直接的收获是——你不需要再手动记录系统里改了什么。因为一切可重复的配置都写在文件里了,有据可查。全新的机器,拉一个仓库、执行 compose up,整个环境就回来了。如果你的服务是交给别人维护的,别人也不需要翻你脑内的记忆,他只需要读配置文件和 README。

为了这个转变顺畅,我强烈建议从一开始就做两件事:第一,所有 Dockerfile 和 docker-compose.yml 全部纳入 git 仓库管理;第二,使用 .env 文件统一管理变量(数据库密码、域名、端口),不要把这些信息写死在 compose 文件里。这样,每一次改动都有历史记录,回滚的尴尬少一半。

下面是 .env 文件的一个示例:

# .env MYSQL_ROOT_PASSWORD=change_me_to_long_random MYSQL_DATABASE=webapp WEB_PORT=8080

然后在 docker-compose.yml 里通过 ${MYSQL_ROOT_PASSWORD} 引用,Docker Compose 启动时会自动从 .env 读取并插入。但注意 .env 不要提交到 git 仓库,避免泄露密码。你需要维护一份 .env.example 作为模板,提交到仓库供别人参考。

6.2 备份和迁移逻辑的变化

裸机环境下,备份 = 整个 SD 卡的 dd 镜像,或者 cp 关键目录,再或者 mysqldump 导出数据库。这套方式的问题在于:全卡镜像太大、太慢;局部备份又容易漏。Docker 化之后,备份思路变成:数据卷备份 + 配置文件备份 + 镜像清单备份。

具体来说,我会用 tar 直接打包 MySQL 数据卷(或者用 mysqldump 导出 SQL),再打包外部挂载的网站文件和 Nginx 配置。镜像本身不需要备份到 tar 文件里,因为镜像可以通过 Dockerfile 随时重新构建,只需要把 Dockerfile 和依赖的源码仓库备份好即可。这个思路本质上参考了“Cow(Copy-on-Write)镜像层 + Volume 持久化”的设计哲学:镜像负责描述,卷负责存储。

迁移到另一台树莓派时,新机器上只需要装好 Docker Engine,把代码仓库和 .env 文件复制过来,执行 docker compose up -d --build,等镜像拉取和构建完成,服务就回来了。这个过程里不需要手动安装 Node、MySQL、Nginx,省下的是大量重复劳动和历史遗留问题。

6.3 安全更新的节奏和坑

Docker 化有一个容易忽视的安全问题:镜像更新了,本地仓库里的镜像也更新了,但是已经运行的容器还是旧环境。裸机部署时,系统提示安全更新你可能顺手就 apt upgrade 了;Docker 化之后,如果不主动重新拉取镜像并重建容器,容器内部的系统和依赖其实一直停留在初始版本。

所以,我会在部署记录里做一个“镜像依赖清单”表格,记录每一个服务使用的基础镜像、镜像 tag、最近检查时间。每三个月检查一次官方镜像是否有新版本,如果有,就构建新版发布。以下是一份简化的示例表格:

服务镜像当前版本检查时间是否有更新
Nginx 入口nginx:1.25-alpine1.25.x2025-01-10
MySQL 数据mysql:8.08.0.x2025-01-10是,8.0.41
Node APInode:20-alpine20.x2025-01-10是,20.12

另一个值得注意的点:镜像更新后,重建容器那一下会造成短暂的服务中断。如果你是在一个真正对外提供服务的环境下,需要设计优雅停机或者滚动更新。树莓派家庭服务器场景不用太纠结,选择凌晨自动执行更新即可,我有一个定时任务每晚 3 点跑 docker compose pull && docker compose up -d,再配合健康检查,出问题的概率很低。

7. 实践中的常见问题与排查技巧实录

7.1 Docker 服务开机不自启,重启后全挂

树莓派作为服务器,经常断电重启。如果你没有开启 Docker 服务的开机自启,也没有给容器设置 restart 策略,那么重开机之后,所有容器都处于停止状态,网页服务自然起不来。解决方法是两条命令配合使用:

sudo systemctl enable docker

同时,docker-compose.yml 里每个服务都加 restart: unless-stopped。两者的区别是,systemctl 负责 Docker 引擎本身的开机自启,restart 策略负责引擎起来后自动拉起容器。光有前者,容器不会自动恢复;光有后者,Docker 引擎不启动也没用。别遗漏任何一条。

我在排查这类问题时有一个习惯:先跑 docker ps -a 看容器状态,再看 docker logs 看启动报错,最后才去改配置。有一次我以为容器没起来是配置问题,整整看了二十分钟日志,最后发现是宿主机的移动硬盘没挂载,MySQL 数据卷挂载点不存在,容器启动了但立刻退出了。这类问题靠日志说话,最直接。

7.2 容器里时间不准确,日志时间戳飘了

容器的默认时间通常是 UTC 而不是本地时间。如果你看容器日志时发现时间和宿主机差了八个小时,别慌,这不是容器坏了,而是时区没配置。解决办法是给容器设置环境变量 TZ=Asia/Shanghai。在 docker-compose.yml 里这样写:

services: web: environment: - TZ=Asia/Shanghai

如果是 docker run 命令,加 -e TZ=Asia/Shanghai 即可。你可能会想,为什么镜像不自带正确的时区?因为容器是可移植的,镜像作者不知道你运行在哪时区,所以默认用 UTC 是合理的。这是容器的设计哲学——不预设外部环境,一切由运行者注入。

7.3 日志量爆炸,SD 卡空间被日志填满

Docker 默认没有限制容器日志文件大小,如果一个服务打印日志比较激进,/var/lib/docker/containers/... 下的日志文件会无限增长。树莓派 SD 卡本来容量就有限,这个问题必须处理。我的建议是在 Docker daemon 配置里加上日志轮转:

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

修改 /etc/docker/daemon.json 后,重启 Docker 服务。这个配置的意思是:每个容器最多保留三个日志文件,单个不超过 10MB,超过就滚动覆盖。这样日志占用的空间被严格限制在一个可控范围内。

当然,如果你有外部日志采集需求,可以给容器配置 gelf 或 syslog 日志驱动,把日志发送到集中日志平台。家庭服务器场景轮转就够了,别为了一个玩具项目把架构搞复杂。

7.4 端口占用导致容器启动失败

这个错误场景很经典,树莓派上如果已经有一个进程占了 80 或 8080 端口,你新建的容器想映射这个端口就会报 bind: address already in use。这时候排查顺序是:确认哪个进程占用了端口,再用 lsof 或 ss 定位,然后决定是杀进程、改端口映射,还是干脆用 host 网络模式。

还有一个容易掉坑的情况:你看 docker ps -a 发现容器是 Exited 状态,然后手动 docker start 起不来,日志提示端口绑定失败。这可能是因为容器内部的服务监听的是宿主机某端口,但是容器本身没起来导致端口还占着。我的建议是统一用一个入口反向代理容器管理 80/443,所有业务容器映射到内部随机端口或者干脆不映射宿主机端口,让代理容器通过 Docker 内部网络转发。这样端口冲突的概率会降到最低。

7.5 容器内网络验证:如何区分是“容器没跑好”还是“容器网络不通”

排查容器网络问题的思路和裸机排查稍有不同。裸机上,你直接 curl 一个地址,看通不通;容器里,你需要先进入容器,再发起请求。注意,容器内的 localhost 是容器自己的 loopback,不是宿主机的。很多新手在这上面栽过跟头:容器内 curl 本机 3306 端口的 MySQL,发现连不上,以为 MySQL 没起来,其实是自己在容器里访问的是自己的 3306 端口,而 MySQL 跑在另一个容器里。

你可以用 docker compose exec 进入容器内部做测试,也可以用 docker inspect 查看容器的网络 IP,然后在宿主机 curl 那个 IP。但最直接的方式是,在同一个 Docker 网络里,直接使用服务名作为主机名访问,比如从 nginx 容器访问 mysql 容器,URL 应该是 jdbc:mysql://db:3306/webapp,其中 db 是 compose 文件里定义的服务名。Docker 内置 DNS 会把这个服务名解析到对应的容器 IP 上——前提是这两个容器在同一个用户自定义网络里。

强调一下,默认的 bridge 网络没有启用容器名自动解析,所以你需要用 docker network create 自定义一个网络,然后让 compose 服务都加入这个网络。在 docker-compose.yml 里,如果指定了 networks,Compose 会自动创建一个项目级别的网络,服务名和容器名都可以在这个网络里被解析。

8. 我个人在树莓派 Docker 化过程中的体会

如果你现在问我,树莓派上跑网页服务器,能不能不用 Docker,直接裸机也挺好?答案是可以,但是有一个隐含前提:你确定你的服务数量长期保持在一两个,且你不在意未来可能的灾难性环境问题。这就像住宿舍,一个人住确实自由,但一旦合租的人多了,公共空间一定出乱子。Docker 本质上不是“多此一举”,而是为服务增长预留的“房间隔断”。

在我自己的实践经历中,Docker 化带来的最大价值并不是“部署快”或“回滚方便”,而是心态上的安稳。以前我半夜收到服务告警短信,心里第一反应是“完了,是不是系统哪里又坏了”;现在我会想“先看日志,如果环境坏了,直接重新构建容器,最多几分钟”。这份淡定,是用一次惨痛的 SD 卡系统崩溃换来的,也提醒我永远不要低估一个共享系统被多个服务反复修改所带来的复杂性。

这个系列接下来会拆解更多的实操内容:如何在树莓派上安装 Docker Engine、怎么写一个适合 ARM 平台的 Nginx + Node.js + MySQL 的 Dockerfile、如何组织 compose 多服务编排、如何做数据卷备份和恢复。如果你正在规划树莓派上的新服务,或者在痛苦的维护旧环境,真的可以趁早把 Docker 搬家提上日程。一步一步来,第一篇只需要理解为什么,后面的命令都是水到渠成。

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

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

立即咨询