ARTICLE DETAIL

资讯详情

深耕商务建站与企业官网运营的一线实战洞察。

树莓派Docker Compose全家桶:一键部署容器化服务器实战

树莓派Docker Compose全家桶:一键部署容器化服务器实战 1. 为什么这篇要把全家桶整合当成重头戏树莓派玩 Docker玩到第七篇很多东西已经不是能不能跑的问题而是怎么跑得省心的问题。系列前面的文章分别处理了容器化 Nginx、数据库容器化、存储挂载、日志方案、反向代理与端口规划这些单个模块。每一块单独拆开看都能在树莓派上稳定运行但真正把它们当成一个完整的网页服务器来使用时你会发现一个很现实的问题手动逐条执行docker run去管理六个、八个甚至十几个容器本身就是一种体力活而且特别容易出错。这篇文章要做的就是把前几篇拆散的零件重新组装起来从一个完全纯净的树莓派系统开始到 Docker 引擎就绪再到一套完整的网页服务器容器全家桶通过一份docker-compose.yml编排好最后用一个部署脚本实现一条命令从零启动整个服务集群。整个流程覆盖了系统烧录、基础环境初始化、Docker 安装与加速配置、镜像规划、容器编排、数据持久化、健康检查与故障排查。这套方案适合谁如果你手里有一块树莓派 4B2GB 内存以上版本想让它承担真实的网页服务任务——不只是玩一个 Hello World 容器——同时又不想每次重启或者搬家式迁移时手动敲十几个docker run这篇文章就是给你准备的。做完之后的体验大概就是TF 卡一插、上电、等几分钟整个服务栈自动拉起完全不需要盯屏幕输命令。2. 纯净底座准备从一张空白 TF 卡到可用的 Docker 主机2.1 系统镜像选型64 位是底线先聊最容易被忽略但影响最大的决定操作系统镜像。树莓派 4B 的 CPU 是 Cortex-A7264 位架构但很多教程还在推荐 32 位的 Raspberry Pi OS。如果是做桌面娱乐或者 GPIO 实验32 位问题不大但要做 Docker 化服务器直接上 64 位系统。原因很简单Docker Hub 上大量的官方镜像已经以arm64为主推架构你用 32 位系统拉arm/v7版本也不是不行但很多镜像的arm/v7构建已经进入维护甚至停止更新状态。比如 MySQL 8.x 的官方镜像对arm/v7的支持明显没有arm64积极。另一个现实问题是树莓派 4B 的 4GB 内存版本搭配 32 位系统时单个进程可寻址内存受限跑数据库或者构建镜像时总觉得憋屈。我推荐两个方案发行版优点缺点适用场景Raspberry Pi OS Lite64位官方维护、驱动最稳、社区资料最多软件包版本偏保守大多数人的首选Ubuntu Server for Raspberry Pi64位内核较新、软件包新、Docker 兼容性极好驱动偶尔有小问题想紧跟新特性、跑较新容器镜像的人我个人更倾向于 Ubuntu Server。原因是 Docker 官方安装脚本对 Ubuntu 的适配最成熟遇到问题时排查资料也更多。但如果你对 Raspberry Pi OS 更熟悉Lite 版完全够用只要确定下载的是 64 位镜像。2.2 烧录与开机初始化头十分钟决定后面顺不顺利烧录环节官方出的 Raspberry Pi Imager 是目前最省事的工具。它现在支持直接在烧录时预设 SSH 开关、用户名密码、Wi-Fi 信息这一步非常关键——省去了无头模式下必须接显示器和键鼠才能开 SSH 的尴尬。具体操作打开 Raspberry Pi Imager选择镜像选 64 位 Lite 或 Ubuntu Server。选择 TF 卡建议 32GB 以上16GB 装完系统加几个镜像就捉襟见肘。点击右下角设置按钮提前写入开启 SSH允许密码登录或配置公钥指定主机名比如rpisrv设置用户pi的密码配置 Wi-Fi如果走无线或者直接插网线烧录完成后TF 卡插入树莓派上电等一两分钟。上电后第一步是找到树莓派的 IP。最简单的方法是在路由器后台看 DHCP 客户端列表或者用ping rpisrv.local如果网络支持 mDNS。连上 SSH 后先做三件事改源、更新系统、检查固件。改源这件事很多人教条式地照抄网上的代码块结果换完发现源和目标系统对不上。Raspberry Pi OS 和 Ubuntu 的源格式不同Debian 系里又分 bookworm、bullseye、noble、jammy 等版本代号抄错了轻则更新慢重则直接apt update报错。正确做法是先看系统版本文件cat /etc/os-release lsb_release -cs # 看代号然后根据代号去配对应国内镜像站的源。这一步做完apt update和apt upgrade的速度会有质的提升后续装任何软件都不至于卡在下载上。系统更新完重启一次然后进入固件配置sudo raspi-config需要动的地方只有两处一是Advanced Options - Expand Filesystem虽然新版本系统镜像默认会自动扩容但确认一下没有坏处二是如果不需要桌面环境确保 boot 到命令行模式。另外建议把 GPU 内存调低到 16MBPerformance Options - GPU Memory这台机器如果纯做服务器GPU 完全用不上省下的内存给容器更划算。2.3 基础软件包与目录规划系统就绪后装一些后面会用到的工具。这一步不需要太多够用就好sudo apt update sudo apt upgrade -y sudo apt install -y curl wget git vim net-tools htop目录规划这里我先说一个超出常规教程的思路不要把数据直接散落在~/或者/opt里而是建立一个独立的目录结构把应用代码、容器配置、数据卷、备份全部归类放好。我习惯在树莓派上用这个布局/srv/server/ ├── apps/ │ ├── nginx/ │ ├── mysql/ │ ├── redis/ │ └── monitor/ ├── compose/ │ └── docker-compose.yml ├── scripts/ │ └── deploy.sh └── backups//srv是 Linux 中专门给服务数据的目录语义清晰也不容易被误清理。这套目录结构会和后面的整个方案绑定后续所有路径映射都以这里为根。3. Docker 引擎与镜像加速ARM 平台最容易翻车的地方3.1 Docker Engine 安装不走 Docker Desktop 路线树莓派上不要装 Docker Desktop它面向的是桌面环境且需要额外的虚拟机层。我们要的是纯粹的 Docker Engine 加 Compose 插件。安装方式我推荐用 Docker 官方脚本简单直接curl -fsSL https://get.docker.com -o get-docker.sh sudo sh get-docker.sh脚本会自动识别系统架构和发行版把 Docker Engine、containerd、Compose 插件一起装好。装完把用户加入 docker 组省得每次敲sudosudo usermod -aG docker $USER newgrp docker验证安装docker --version docker compose version一个容易被忽略的点docker compose带空格v2 插件和docker-compose带横线v1 独立二进制是两回事。新版 Docker 官方推荐用前者。网上大量旧教程还在要求你apt install docker-compose装出来的是一个老版本 Python 实现不仅语法支持不全性能也差一截。请认准docker compose命令。3.2 镜像加速配置让拉取速度回归正常这一步在国内网络环境下几乎是必修课。树莓派在国内用默认 Docker Hub 拉镜像速度经常让人怀疑 SD 卡是不是坏了。配置镜像加速器的原理不复杂Docker 在拉镜像时会先访问daemon.json里配置的 registry mirror这些加速器相当于 Docker Hub 的缓存节点。配置文件位置在/etc/docker/daemon.json如果不存在就新建一个{ registry-mirrors: [ https://docker.m.daocloud.io, https://dockerproxy.com, https://docker.nju.edu.cn ], log-driver: json-file, log-opts: { max-size: 10m, max-file: 3 }, data-root: /var/lib/docker }这里同时做了一件非常重要的事日志限额配置。树莓派的 TF 卡空间有限容器如果持续输出日志默认情况下/var/lib/docker/containers/xxx/xxx-json.log会无限增长。曾经见过一个跑了半年 Nginx 的容器日志文件膨胀到 7GB 直接写满 TF 卡。限定单个日志文件 10MB、最多保留 3 份基本保证日志不会成为容量杀手。配置完重启 Dockersudo systemctl restart docker sudo systemctl enable docker # 开机自启enable这一步值得强调。树莓派经常因为停电、误碰电源等原因意外关机如果 Docker 服务没有设为自启重启之后全家桶不会自动回来等你 SSH 上去才发现在裸奔。3.3 ARM 平台的镜像拉取细节树莓派 4B 装 64 位系统后Docker 默认拉取arm64镜像。大多数情况下你不需要手动指定平台。但有一个场景会踩坑某些镜像只有amd64版本或者开发者只发布了linux/arm/v7没有arm64。遇到这种情况Docker 会尝试通过模拟运行amd64容器性能会打折扣甚至直接失败。我的建议是在选择镜像时养成看架构标签的习惯。Docker Hub 上每个镜像的 Tags 页面都能看到不同架构的 digest。如果你不确定某个镜像是否支持arm64可以用docker manifest inspect快速查看docker manifest inspect mysql:8.0 | grep architecture这个方法不实际拉取镜像就能看到支持的架构列表选镜像前花十秒钟检查一下能避免启动之后才发现镜像不兼容的尴尬。4. 全家桶阵容规划网页服务器周边到底需要哪些容器4.1 我的全家桶组成与选型逻辑一套能承担真实业务的网页服务器显然不可能只有 Nginx 一个容器。结合树莓派的硬件条件4 核 CPU、4GB 内存我最终定下的全家桶阵容是这样容器镜像作用端口内存占用实测nginxnginx:stable-alpine前端入口反向代理80/443约 40MBmysqlmysql:8.0业务数据库3306仅内网约 280MBredisredis:7-alpine缓存、会话6379仅内网约 60MBportainerportainer/portainer-ce:latestDocker 可视化管理9000内网约 80MBwatchtowercontainrrr/watchtower:latest容器镜像自动更新无约 25MBcadvisorgcr.io/cadvisor/cadvisor:latest容器资源监控8080内网约 100MB为什么选这几个而不是更多我在规划时有一个铁律树莓派上的容器数量宁少勿多。每一个容器背后都是一个常驻进程都有内存开销和日志写入。2GB 内存版本如果硬上 Elasticsearch 全家桶Kafka启动阶段就能把 swap 吃爆。我的 4GB 版本跑这套阵容系统加容器整体内存占用约 1.8GB还有一半余量这算是一个比较健康的水平。镜像选型也有讲究Nginx 用alpine版本体积小、攻击面小MySQL 用官方8.0而不是latest因为latest的 tag 可能随着大版本升级而漂移到时候自动更新一跑数据库从 8.0 跳到了 8.4兼容性问题会让人措手不及。固定主版本号是数据库类容器的基本素养。4.2 为什么非要用 Portainer管理员的理智选择有人会说命令行本来就能管理 Docker为什么非要加一个 Portainer我的回答是树莓派服务器不是只服务你一个人。部署完一个完整系统之后如果家里其他人也想看服务状态、重启某个出问题的容器你不可能让每个人都去学docker ps和docker logs。Portainer 提供的图形界面让整个管理过程变得几乎是零门槛。另外Portainer 的 Stacks 功能原生支持 docker-compose可以直接用图形界面管理整套 Stack 的启停和更新和本文后面要写的部署脚本不冲突——脚本负责首次部署Portainer 负责日常运维。这种脚本初始化 面板管理的组合拳是我试出来的最佳配合。4.3 内存预算与 Swap 策略4GB 内存看着不小但 MySQL 默认配置能吃掉大量内存去做 buffer pool。树莓派上必须对 MySQL 做内存约束。用 Docker 的方式可以通过command参数覆盖 MySQL 的启动配置command: --innodb-buffer-pool-size256M --max-connections50 --performance-schemaOFFmax-connections从默认的 151 降到 50对家庭级应用绰绰有余performance-schema关闭能省掉约 200MB 内存开销。这是树莓派上跑 MySQL 最值得做的两个调整。Swap 方面树莓派实际跑这套方案时内存峰值可能冲到 2.5GB 左右剩余空间已经不太从容。建议在系统层面加 2GB 的 swapfile防止瞬时内存尖峰直接把进程杀掉。开启后free -h能看到 swap 总量变成 2GB 以上。注意把vm.swappiness设小一点比如 10让系统优先用物理内存swap 只兜底。5. docker-compose 编排把容器依赖关系和数据持久化理清楚5.1 compose 文件的核心结构所有容器最终通过一份docker-compose.yml统一管理放在/srv/server/compose/下。这是全家桶整合的核心文件我直接给出完整内容并逐段说明services: nginx: image: nginx:stable-alpine container_name: web-nginx restart: always ports: - 80:80 - 443:443 volumes: - /srv/server/apps/nginx/conf.d:/etc/nginx/conf.d:ro - /srv/server/apps/nginx/html:/usr/share/nginx/html:ro - /srv/server/apps/nginx/logs:/var/log/nginx - /srv/server/apps/nginx/cert:/etc/nginx/cert:ro networks: - webnet depends_on: - mysql - redis healthcheck: test: [CMD, wget, -q, --spider, http://localhost/healthz] interval: 30s timeout: 5s retries: 3 start_period: 10s mysql: image: mysql:8.0 container_name: web-mysql restart: always command: --innodb-buffer-pool-size256M --max-connections50 --character-set-serverutf8mb4 --collation-serverutf8mb4_unicode_ci environment: MYSQL_ROOT_PASSWORD: 换成强密码 MYSQL_DATABASE: webapp MYSQL_USER: webuser MYSQL_PASSWORD: 换成强密码 TZ: Asia/Shanghai volumes: - /srv/server/apps/mysql/data:/var/lib/mysql networks: - webnet healthcheck: test: [CMD, mysqladmin, ping, -h, localhost, -u, root, -p换成root密码] interval: 30s timeout: 5s retries: 5 redis: image: redis:7-alpine container_name: web-redis restart: always command: redis-server --appendonly yes --maxmemory 128mb --maxmemory-policy allkeys-lru volumes: - /srv/server/apps/redis/data:/data networks: - webnet healthcheck: test: [CMD, redis-cli, ping] interval: 30s timeout: 5s retries: 3 portainer: image: portainer/portainer-ce:latest container_name: web-portainer restart: always ports: - 9000:9000 volumes: - /var/run/docker.sock:/var/run/docker.sock - /srv/server/apps/portainer/data:/data networks: - webnet watchtower: image: containrrr/watchtower:latest container_name: web-watchtower restart: always environment: WATCHTOWER_CLEANUP: true WATCHTOWER_SCHEDULE: 0 0 4 * * * WATCHTOWER_TIMEOUT: 30s volumes: - /var/run/docker.sock:/var/run/docker.sock networks: - webnet cadvisor: image: gcr.io/cadvisor/cadvisor:latest container_name: web-cadvisor restart: always ports: - 8080:8080 volumes: - /:/rootfs:ro - /var/run:/var/run:ro - /sys:/sys:ro - /var/lib/docker:/var/lib/docker:ro - /cgroup:/cgroup:ro networks: - webnet networks: webnet: driver: bridge5.2 网络模式选择为什么统一桥接到 webnet这里有一个前面系列文章已经详细拆解过、但值得再强调的设计所有容器放在同一个自定义 bridge 网络webnet中并且只有 Nginx、Portainer、cAdvisor 对外开放端口。这个设计带来三个直接好处。第一MySQL 和 Redis 不暴露宿主机端口外部无法直接访问数据库降低被扫描攻击的风险。第二Nginx 可以通过容器名直接访问 MySQL 和 Redis比如 PHP 应用里配置数据库主机名直接写mysql而不是 IPDocker 内置 DNS 会处理解析。第三端口冲突问题彻底消失不需要为每个容器手动规划一个非标准宿主机端口。如果你想过用network_mode: host来获得更好性能在树莓派上其实没必要。bridge 网络模式在 Docker 内部的性能损耗非常小而 host 模式会带来端口冲突风险甚至可能和系统自身的 80 端口抢占。除非遇到极端性能需求一律用 bridge 加自定义网络。5.3 数据持久化volume 和 bind mount 的使用边界在这套方案里我用了两种不同的挂载方式使用边界很明确bind mount直接映射宿主机目录用于需要直接查看、修改、拷贝的数据。Nginx 的 HTML 目录、配置目录、证书目录用 bind mount因为你可能要经常直接编辑文件MySQL 数据目录也用 bind mount方便备份时直接tar整个目录。named volumeDocker 管理卷适合不想直接碰、只由容器读写的数据。如果某个服务的数据对宿主机透明性要求低应该用 named volume。比如 Portainer 的数据目录虽然写成了 bind mount但其实用 named volume 会更好因为它的数据目录内部结构很复杂没有直接操作的需求bind mount 还可能带来文件权限和 SELinux 方面的小问题。MySQL 数据目录用 bind mount 时需要注意权限容器内的mysql用户和宿主机的uid/gid并不一致直接映射后经常出现Permission denied。解决方案是在挂载前手动设置目录所有者sudo mkdir -p /srv/server/apps/mysql/data sudo chown -R 999:999 /srv/server/apps/mysql/data999 是 MySQL 官方镜像中mysql用户的 uid。这个细节能避免首次启动时报 Cant open file 的尴尬。6. 一键部署脚本从手动拉镜像到一条命令收工6.1 部署脚本要覆盖的完整阶段compose 文件写好后剩下的事情就是写一个一键部署脚本。这个脚本的意义在于面对一台全新的树莓派只需要执行一条命令就能完成环境检查、目录初始化、配置校验、拉镜像、启动服务、健康检查的完整流程。脚本我放在/srv/server/scripts/deploy.sh整体结构分六个阶段。这里我按阶段拆开讲每个阶段都有独立函数方便出了问题单独执行。阶段一环境预检。脚本跑起来先检查系统架构、Docker 是否可用、端口是否被占用check_environment() { echo [1/6] 检查运行环境 arch$(uname -m) if [ $arch ! aarch64 ] [ $arch ! armv7l ]; then echo 错误非 ARM 架构脚本仅适用于树莓派 exit 1 fi if ! command -v docker /dev/null 21; then echo 错误Docker 未安装请先完成 Docker 引擎安装 exit 1 fi if ! docker compose version /dev/null 21; then echo 错误Docker Compose 插件未安装 exit 1 fi echo 环境检查通过架构 $archDocker 已安装 }阶段二目录初始化。把整个/srv/server目录结构在当前机器上补齐init_dirs() { echo [2/6] 初始化目录结构 mkdir -p /srv/server/{apps/{nginx/{conf.d,html,logs,cert},mysql/data,redis/data,portainer/data},compose,scripts,backups} if [ ! -d /srv/server/apps/mysql/data ]; then echo MySQL 数据目录不存在开始创建 mkdir -p /srv/server/apps/mysql/data chown -R 999:999 /srv/server/apps/mysql/data fi if [ ! -d /srv/server/apps/redis/data ]; then mkdir -p /srv/server/apps/redis/data chown -R 999:999 /srv/server/apps/redis/data fi echo 目录结构就绪 }阶段三配置文件生成。如果 Nginx 的conf.d里还没有默认站点配置脚本顺手生成一份最小可用的反向代理配置让它能转发到webapp容器假设业务应用跑在另一个容器里。这一步的逻辑是不要假设每一台新机器上都已经有完整配置部署脚本要能在零配置的前提下生成一份可运行的最小配置后续再手动覆盖。generate_nginx_conf() { echo [3/6] 生成 Nginx 基础配置 if [ ! -f /srv/server/apps/nginx/conf.d/default.conf ]; then cat /srv/server/apps/nginx/conf.d/default.conf EOF server { listen 80; server_name _; location / { proxy_pass http://webapp:8080; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; } } EOF fi echo Nginx 配置生成完毕 }阶段四拉取镜像。手动触发docker compose pull提前把所有镜像拉到本地。这一步虽然up也会自动拉但单独跑一次的好处是错误信息更清晰可以逐镜像排查拉取失败的原因。阶段五启动服务。用--wait参数等待 compose 服务完全启动start_services() { echo [5/6] 启动服务栈 cd /srv/server/compose docker compose pull docker compose up -d --wait }--wait是 Docker Compose v2 的一个实用特性它会阻塞直到所有配置了 healthcheck 的服务通过健康检查。这样脚本可以在下一步判断服务是否真正就绪而不是只看到容器启动了。阶段六输出访问清单。脚本跑完后把服务地址、需要手动进行的后续步骤列出来省得部署完还要去翻笔记回忆端口。这一步虽然简单但在实际使用中非常提升体验——尤其是在树莓派 IP 变化后脚本输出能直接看到当前机器的所有访问入口。6.2 幂等性设计重复执行不踩雷部署脚本最容易被人忽视的需求是幂等性。什么叫幂等就是同一个脚本在同一台机器上执行两次、三次结果都正确不会因为重复执行导致数据被覆盖、目录被重建、配置被重置。我在这份脚本里做了三处幂等保护目录创建使用mkdir -p目录已存在时静默跳过不会报错。Nginx 配置只在文件不存在时生成再次执行不会覆盖已有配置。MySQL 数据目录的所有者修改只执行一次如果目录已经存在且所有者正确chown不会造成额外影响。为什么要做幂等因为部署脚本不只是新装机用的。你在日常使用中改了某一个容器的环境变量或者调整了 compose 文件里的某个卷映射会想重新执行一遍部署脚本让改动生效。如果脚本不具备幂等性第二次执行可能就是一场灾难——目录会被重建配置文件会被重置数据目录权限可能会变得混乱。6.3 健康检查与自检逻辑Compose 文件里我们已经给每个有检测能力的服务配置了healthcheck。部署脚本的最后一阶段就是对健康检查结果做汇总判断。一个实用的自检函数长这样check_health() { echo 服务健康状态汇总 failed0 for container in web-nginx web-mysql web-redis web-portainer; do status$(docker inspect --format{{.State.Health.Status}} $container 2/dev/null || echo status: not found) if [ $status healthy ]; then echo $container: healthy else echo $container: $status failed1 fi done if [ $failed -eq 0 ]; then echo 所有服务健康检查通过 else echo 存在异常服务请检查 docker logs container_name 查看原因 exit 1 fi }这段脚本的价值在于它把容器在跑和服务可用区分开。一个容器进程虽然活着但内部的应用可能还在启动中或者已经崩溃重启。通过健康检查判断比单纯看docker ps输出更可靠。有时候docker inspect查出来的健康状态会是starting这通常意味着start_period时间还不够等服务内部程序完全就绪后会变healthy。7. 实测与排障全家桶跑起来后遇到的真实问题7.1 启动耗时与资源占用实测这套方案在树莓派 4B4GB 版本Ubuntu Server 24.04TF 卡为 A2 规格上的表现我做了完整的实测记录。首次部署需要拉取全部镜像总耗时约 12 分钟其中镜像拉取占大头MySQL 8.0 镜像约 150MB在加速器正常情况下两分钟内能拉完。后续启动镜像已缓存从执行部署脚本到所有服务healthy实测约 55 秒。主要等待在于 MySQL 的初始化和健康检查通过需要时间。内存占用的真实分布总内存 3.8GB4GB 减掉 GPU 预留 系统基础进程约 300MB Docker 各容器约 1.1GB - 1.3GB 缓存与余量约 2.2GB一次正常的业务高峰并发 20 个请求打 Nginx同时读写 MySQLCPU 使用率在 30%~50% 之间空闲时几乎为 0温度稳定在 52°C 左右前提是加了一个几十块的铝制散热壳。如果你的树莓派还是裸板加小散热片建议跑这套方案前先把散热问题解决MySQL 在高温下会明显降速。7.2 三次典型故障的完整排查链路好消息是这套方案稳定跑了大半年坏消息是过程中踩过三个值得记录的坑每个都花了不少时间定位。故障一重启后 MySQL 起不来报错指向数据目录权限。现象是树莓派断电重启后docker compose up -d后 MySQL 容器一直在restarting状态docker logs web-mysql看到[ERROR] Cant open file /var/lib/mysql/ibdata1 ... Permission denied。排查过程先查挂载目录的所有者ls -ln /srv/server/apps/mysql/data发现数据目录的所有者变成了1000宿主机第一个用户的 uid。原因是我后来用rsync从旧 SD 卡迁移数据时tar 解包覆盖了原有的所有权信息。修复方式是重新执行chown -R 999:999 /srv/server/apps/mysql/data。这个坑让我意识到一个经验bind mount 目录的权限信息是部署容器的隐藏依赖任何涉及目录迁移的操作之后必须检查并恢复所有者和权限。故障二Portainer 和 cAdvisor 容器内能看到 Docker sock 挂载但界面无数据。现象是 Portainer 可以连接但容器资源使用图表全部空白cAdvisor 能访问但指标页面报错。排查发现两个容器都挂载了/var/run/docker.sock但 cAdvisor 还需要额外挂载宿主机的根目录、sys 目录、cgroup 目录才能采集到资源指标。compose 文件里漏掉了/sys和根目录映射导致数据采集失效。补齐卷映射后一切正常。这个问题的教训是某些容器的资源监控类功能不仅依赖 Docker API还依赖宿主机文件系统的直接访问挂载不全时功能会静默降级不会直接报错。故障三Watchtower 自动更新把 MySQL 从 8.0 拉到了 8.4。现象是某天早上发现业务报错数据库连不上。查日志发现 watchtower 夜里自动拉取了 MySQL 镜像的 latest tag然后重建了容器导致原本跑在 8.0 上的数据目录被 8.4 识别时出现了不兼容问题。这个坑的根源在我的 compose 文件里image: mysql:8.0这个 tag 虽然指定了主版本但 watchtower 仍然会检查这个 tag 指向的最新 digest只要官方把 8.0 系列的基础镜像重新构建它就会拉下来替换。修复方案有两种一是给 watchtower 配置WATCHTOWER_LABEL_ENABLEtrue然后在敏感容器上加com.centurylinklabs.watchtower.enablefalse标签二是干脆不用 watchtower 更新数据库类容器只让它更新 Nginx 这类无状态服务。我最终选择了后者数据库这种有状态服务的更新必须人工介入评估。7.3 性能调优与日常维护的几个小参数跑稳定之后我做了几处水磨工夫的调优每项改动都不大但日积月累对 TF 卡寿命和系统稳定性帮助很明显。一是 Docker 数据目录不要放在根分区。TF 卡的根分区空间本就有限镜像层、日志、容器可写层全都堆在/var/lib/docker里时间长了容易把根分区塞满。如果你的树莓派挂了一块移动硬盘或者 SSD把>0 3 * * 1 docker image prune -f docker container prune -f注意不要用docker system prune -a那是无差别清理所有未使用的镜像会把本地构建但暂时没跑的镜像也删掉。文章写到这里从系统底座、Docker 引擎、镜像规划、Compose 编排到一键部署脚本和排障复盘一套完整流程已经摆在面前。套用我在实际使用中养成的一个习惯每次新装一套机器跑完部署脚本后立刻把脚本和 compose 文件备份到 U 盘或者云盘一份。树莓派坏卡换新卡恢复到可用状态只需要烧录系统、执行脚本、等完健康检查三件事。
返回列表
PREV
查看更多资讯
NEXT
返回资讯列表