我平时经常在树莓派上使用 Docker,用来搭建官方软件仓库里没有的各类服务。容器方便快速测试,但使用久了难免出现各种故障。下面分享实用方法,让容器能够稳定长期运行。
配置得当的 Docker 容器可以持续稳定运行。想要保证稳定性,就要将容器互相隔离,各自使用独立的配置文件、文件夹和端口;日常维护中,建议开启自动重启、更新与备份功能。
这些运维理念大家都有所了解,但在 Docker 环境里具体该如何实现,很多人并不清楚。下面我会逐一讲解细节,保障服务器井然有序地运转。
放任容器异常关停
始终设置重启策略,让容器自动恢复运行。
先从常见问题说起。一次我准备在智能电视上观看电影《功之怒》,却发现无法访问 Plex 影音服务器里的影片。检查后发现,对应的 Docker 容器已经停止运行。
默认设置下,容器程序报错崩溃,或是重启树莓派后,Docker 都不会自动重启容器。需要全天候运行的服务,必须手动设置重启规则。
想要开机自动启动、故障后自动重启,在配置中添加代码:
restart: always
调试阶段不想强制重启,可以改用这条规则。除手动关停外,其余情况都会自动重启容器:
restart: unless-stopped
这套设置基本可以保障 Plex 服务稳定运行。如果是核心业务服务,还可以配置异常提醒,服务下线时及时收到通知。
将所有服务写入同一个配置文件
每个容器一个服务,每个服务一个compose文件。
Docker Compose 采用文本配置,简单直观,如今已是主流使用方式。我刚上手时,误以为把所有容器整合在一个文件里更好管理,事实证明这个想法并不正确。
集中式配置反而会增加维护难度:
无法单独对某个容器进行版本更新
配置参数容易混淆,引发端口、存储卷冲突
单个容器配置出错,会导致全部服务启动失败
分开管理才是最优方案,每项服务单独创建配置文件。
我日常会为每个容器新建专属文件夹,Immich 相册、qbittorrent 下载工具都分开存放,每个文件夹只保留自身服务的 compose.yaml 配置。
注意: 有时候将多个服务合并到一个Docker文件中是有道理的。例如,当容器需要共享卷或共享虚拟网络以实现特定目标时。
囤积容器垃圾
定期清理未使用的容器、镜像和卷。
Docker 没有可视化的资源管理界面,老旧镜像、停用容器和缓存碎片很容易不断累积。
要更好地了解情况,运行这个命令:
docker system df
或者获取更详细的信息:
docker system df -v
例如,你可能有以下残留物:
没有被任何东西引用的旧镜像。
不再使用的已停止容器。
旧设置留下的非活跃虚拟网络。
被遗弃的卷。
构建缓存残留。
容器垃圾会导致树莓派变慢,因为它必须扫描所有这些东西。
如果它不能给你带来快乐,就丢掉它。当我完成测试并达到想要保留的稳定设置后,我会用这个命令清理:
docker system prune
有更细粒度的清理命令,但我觉得上面的语法容易记住,日常使用完全够用。
https://docs.docker.com/engine/manage-resources/pruning/
允许容器独占资源
始终为树莓派上运行的容器定义资源限制(CPU、RAM)。
多数容器运行时资源占用合理,不会超额消耗硬件资源。但像 PostgreSQL 这类数据库程序,常会大量占用树莓派 5 的处理器与内存。
无限制运行的容器会拖累整机性能,部署服务越多,故障概率越高。严重时系统会弹出内存不足提示,随意终止进程释放空间,造成运行紊乱。
提前划定资源使用上限,约束高负载容器。用例子更容易解释,这里是一个配置,相关部分(6-14行):
services:stress-test:image: jfleach/docker-arm-stress-ng:latestprivileged: truecpus: 0.5mem_limit: 512mmemswap_limit: 512mlogging:driver: "local"options:max-size: 50mmax-file: 3command: >--cpu 4--vm 2--vm-bytes 800m--vm-populate--timeout 60s--verbose
从这个命令可以看出,我已经成功限制了这个容器的资源使用:
docker stats
让我们来看看这些限制的作用:
处理器 – cpus: 0.5 表示容器最多允许使用0.5个CPU核心。
内存 – mem_limit: 512m, memswap_limit: 512m 表示容器最多可以使用512MB RAM。在树莓派上要正确生效,这两行都是必需的。
日志 – 可以用来限制日志占用的磁盘空间。
driver: local – 指定使用系统的日志轮转功能。
max-size: 50m – 表示主日志文件最多占用50MB磁盘空间。
max-file: 3 – 在删除旧文件之前轮转3个日志文件。
可根据自身需求调整参数。不必给所有容器统一加限制,避免产生性能瓶颈,仅针对高占用程序单独约束即可。
提示:Docker在树莓派上开箱即用时不会准确报告内存使用情况。如果你需要这个功能,打开 /boot/firmware/cmdline.txt,在那行长命令的末尾添加这些参数:
cgroup_enable=cpuset cgroup_enable=memory cgroup_memory=1
制造端口冲突
为每个容器映射唯一的主机端口,以避免冲突。
配置Docker容器时,通常会定义一个网络端口来连接服务。许多服务会创建一个Web面板,默认为端口80。如果你有三个不同的服务都设置为使用端口80,你的树莓派怎么知道该提供哪一个?
为了防止冲突,为每个容器映射不同的端口,使它们不会重叠。例如,下面是Pi-Hole广告拦截器Docker配置的相关部分:
pihole:container_name: piholeimage: pihole/pihole:latest ports:- "53:53/tcp"- "53:53/udp"- "80:80/tcp"- "443:443/tcp"
当我看到 80:80 和 443:443 时,我的第一反应就是修改它们。这些是常用端口,可能会与其他容器冲突,比如我的Caddy网页服务器。要重新映射端口,你只需要修改冒号(:)左边的数字,像这样:
6060:80/tcp6443:443/tcp
现在,我的Pi-Hole网页面板可以通过端口6060访问。确实不太好记,但初始设置后我很少去那里,一个书签就轻松解决了。
使用过时的镜像
定期更新你的容器镜像,保持安全和最新。
容器与系统相互隔离,常规系统更新无法同步升级容器内部程序。长时间不更新,Home Assistant 这类服务会存在安全漏洞,容易遭到网络攻击。
让我们讨论几种让容器更新更可持续的方法。
如何更新Docker容器?
导航到你存储Docker compose.yaml文件的文件夹。然后,运行以下命令来更新容器:
docker compose pulldocker compose up
第一个命令下载最新镜像,第二个命令使用更新后的镜像重启容器。如果你更喜欢定期更新所有容器而不必手动一个个操作,你可以创建一个脚本,每月自动运行一次这些命令。
或者,你也可以从Portainer操作(Portainer是一个图形化容器管理器,本身也可以作为容器运行)。进入服务的"容器详情"页面,点击"重建"按钮来更新容器。
何时使用"latest"标签?
我经常使用"latest"标签(例如 image: wg-easy:latest),这样更新容器时总是拉取最新版本。但有些情况下,我运行的是关键任务服务,比如生产服务器上的,我需要更稳定的版本。
当需要稳定性时,将Docker配置固定到特定的镜像版本。例如,我的WireGuard VPN服务器的容器配置使用了这个镜像标签:
image: ghcr.io/wg-easy/wg-easy:14
":14"标签告诉Docker获取最后一个v14版本,我知道它已经成熟,只接收安全更新。如果我用了"latest"标签,它会获取最新的v15版本,该版本仍在进行重大更新,可能会破坏我的设置。
忘记保存用户数据
配置卷,让你的数据保存在容器之外。
许多容器需要存储数据,比如设置或用户文件,就像任何其他程序一样。但如果你不告诉它怎么做,你的东西最终会消失。在Docker配置中,"volumes"部分告诉容器把文件保存在哪里。
推荐这套稳妥的存储方案:
每个容器存储在自己独立的项目文件夹中。
用户文件的卷相对于项目文件夹进行映射。
例如,我有一个 ~/containers 文件夹来存放所有Docker相关内容。里面有一个子文件夹给我的项目 paperless-ngx,一开始只包含它的Docker配置。
下面是compose.yaml中的相关部分:
paperless-ngx:image: ghcr.io/paperless-ngx/paperless-ngx:latest volumes:- data:/usr/src/paperless/data- media:/usr/src/paperless/media- ./export:/usr/src/paperless/export- ./consume:/usr/src/paperless/consume
文档说明上面两个卷('data'和'media')是应用默认值,用于基本功能,所以为了教学目的我们忽略它们。然而,下面两个卷('export'和'consume')指的是用户文件,比如我的扫描文档,以及它们应该保存在哪里。没有这几行,当容器宕机时,文件就会烟消云散!
顺便说一下,通过在前面加上"./",我是在告诉Docker把这些卷映射到当前文件夹——而不是Linux文件系统中其他随机位置,比如 /etc 或 /usr。这意味着我的paperless-ngx所有用户文件都会保存在与其Docker配置相同的地方。
为什么这样更简单?因为当我需要备份所有容器时,我只需压缩 ~/containers 文件夹,就完成了。
如果我需要从备份恢复,我启动容器,它已经在一个文件夹里拥有了重新工作所需的一切。这也是我喜欢使用Docker的另一个原因:我所有的工作都是可复现的,所以我不需要完美的记忆力就能把东西恢复到以前的样子。
掌握正确的使用方式,就能搭建稳固可靠的容器服务,即便无人值守也可以持续平稳运行。
Tip:对于刚接触树莓派的新手来说,面对英文文档、零散教程和复杂配置,很容易无从下手。pidoc.cn就是为解决这些痛点而生的树莓派中文一站式学习平台,界面清晰、内容系统、更新及时,堪称新手入门的“保姆级” 网站,让零基础用户也能轻松上手树莓派。
官方网站:https://edatec.cn/zh/cm0
淘宝店铺:https://edatec.taobao.com/
208