Chapter 10 · 树莓派与嵌入式 Linux

Docker 容器

再也不用为"依赖装不上、版本冲突、装完系统一团糟"发愁。Docker 把应用连环境打包成容器,一条命令跑起 Home Assistant、数据库、Web 服务,删了也不留痕迹。

课程进度83%

关键概念

容器(Container)与镜像(Image)
镜像是打包好的"应用+运行环境"的只读模板(如 nginx 镜像含 nginx 及其依赖)。容器是镜像运行起来的实例,互相隔离。一个镜像可跑多个容器。类比:镜像是"程序安装包",容器是"运行中的程序"。删容器不影响镜像。
容器 vs 虚拟机
虚拟机模拟整套硬件+完整操作系统,笨重耗资源。容器共享宿主机内核,只隔离进程和文件系统,轻量、启动秒级、开销小。这对资源有限的树莓派至关重要——能同时跑多个容器化服务而不卡死。
多架构镜像与 ARM
树莓派是 ARM 架构(arm64/armhf),不是 PC 的 x86。拉镜像时 Docker 会自动匹配对应架构的版本——好的镜像都做了多架构(multi-arch)支持。若某镜像只有 x86 版,在树莓派上会报 exec format error。选镜像时留意是否支持 arm64。
Dockerfile
构建自定义镜像的"配方文件",一行行指令描述:基于哪个基础镜像、装什么依赖、拷贝什么代码、启动什么命令。docker build 按它生成镜像。把自己的 Python 应用容器化就靠写 Dockerfile。
数据卷(Volume)与持久化
容器默认无状态——删了数据就没了。要保留数据(数据库、配置、日志),用把宿主机目录挂载进容器(-v /host/path:/container/path)。这样容器可随意重建,数据留在宿主机。持久化是生产部署必须理解的核心。
端口映射与网络
容器有自己的网络命名空间,内部端口对外不可见。用 -p 8080:80 把宿主机 8080 端口映射到容器 80 端口,外部才能访问。多个容器可组成自定义网络互相通信(如 Web 容器连数据库容器),用服务名当主机名。
Docker Compose
用一个 compose.yaml 文件声明式定义多个容器(服务)及其配置、卷、网络、依赖,docker compose up 一键全部拉起。比一堆长长的 docker run 命令清晰得多,是部署多组件应用(如 Home Assistant + 数据库 + MQTT)的标准做法。
重启策略(restart policy)
容器版的"开机自启+崩溃自愈"。--restart unless-stopped 让容器崩溃或树莓派重启后自动拉起(除非你手动停了它)。嵌入式常驻服务必设,等价于第 9 章 systemd 的 Restart=always,但更简单。

容器化的价值

为什么树莓派尤其适合 Docker?看传统部署与容器部署的对比:

传统 apt/pip 部署 vs Docker 容器部署 ═══════════════════════════════════════════════════════════════ 传统方式:直接装在系统上 ┌───────────────────────────────────────────┐ │ apt install / pip install ... │ │ ├ 依赖冲突(A 要 py3.9,B 要 py3.11) │ │ ├ 装完系统一团糟,卸载残留 │ │ ├ 换台派要重新一步步装 │ │ └ 版本升级易搞崩 │ └───────────────────────────────────────────┘ Docker 方式:应用打包进容器 ┌───────────────────────────────────────────┐ │ 宿主机树莓派(只装 Docker) │ │ ┌────────┐ ┌────────┐ ┌──────────┐ │ │ │ HA容器 │ │MQTT容器│ │ 数据库容器│ │ │ │ 独立环境│ │ 独立环境│ │ 独立环境 │ │ │ └────────┘ └────────┘ └──────────┘ │ │ ├ 互不干扰,各带各的依赖 │ │ ├ 一条命令启停,删了不留痕 │ │ └ compose.yaml 一份,换派秒部署 │ └───────────────────────────────────────────┘

安装 Docker

官方一键脚本自动识别 ARM 架构,装对版本:

# 官方便捷安装脚本(自动适配树莓派 ARM)
curl -fsSL https://get.docker.com -o get-docker.sh
sudo sh get-docker.sh

# 把 pi 用户加入 docker 组,之后免 sudo 用 docker
sudo usermod -aG docker $USER
# 注销重新登录(或 newgrp docker)使组生效

# 验证安装(跑测试容器)
docker run --rm hello-world

# 查看版本与架构信息
docker version
uname -m           # aarch64 = arm64 架构

docker run:跑第一个容器

以部署一个 Web 服务器为例,理解常用参数:

# 后台运行 nginx,映射端口,命名,设重启策略
docker run -d \
    --name myweb \
    -p 8080:80 \
    --restart unless-stopped \
    -v /home/pi/html:/usr/share/nginx/html:ro \
    nginx:alpine

# 参数拆解:
#   -d              后台运行(detached)
#   --name          给容器起名,方便管理
#   -p 8080:80      宿主机 8080 → 容器 80
#   --restart       崩溃/重启后自动拉起
#   -v ...:ro       挂载宿主机目录(只读)
#   nginx:alpine    镜像名:标签(alpine 版体积小,适合树莓派)

# 浏览器访问 http://raspberrypi.local:8080 即可看到网页
# === 容器管理常用命令 ===
docker ps                    # 看运行中的容器
docker ps -a                 # 看所有容器(含已停止)
docker logs -f myweb         # 实时看容器日志
docker exec -it myweb sh     # 进入容器内部 shell
docker stop myweb            # 停止
docker start myweb           # 启动
docker rm -f myweb           # 强制删除容器
docker images                # 看本地镜像
docker stats                 # 实时看各容器资源占用(树莓派必看)
树莓派选镜像认准 arm64

拉镜像报 no matching manifest for linux/arm64 或运行报 exec format error,说明该镜像没有 ARM 版本。解决:① 优先选官方/知名镜像(多做了多架构);② 选 alpine 等轻量标签省内存;③ 用 docker manifest inspect 镜像名 查支持的架构。Home Assistant、Portainer、Mosquitto 等主流镜像都完美支持树莓派。

把自己的 Python 应用容器化

给第 5 章的传感器脚本写 Dockerfile,打包成镜像:

# Dockerfile(放在项目根目录)
FROM python:3.12-slim          # 基础镜像(自动多架构)

WORKDIR /app                    # 容器内工作目录

COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt

COPY . .                        # 拷贝代码进镜像

CMD ["python", "sensor.py"]   # 容器启动命令
# 在树莓派上构建镜像(. 表示用当前目录的 Dockerfile)
docker build -t my-sensor:1.0 .

# 运行(访问硬件需 --privileged 或映射设备)
docker run -d --name sensor \
    --restart unless-stopped \
    --device /dev/i2c-1 \
    --device /dev/gpiochip0 \
    my-sensor:1.0
容器里访问 GPIO/I2C 硬件

容器默认隔离,摸不到硬件。要让容器内程序访问 GPIO/I2C/摄像头,用 --device 映射具体设备(如 --device /dev/i2c-1--device /dev/gpiochip0),或图省事用 --privileged(给全部设备权限,但安全性差,仅内网信任环境用)。摄像头还需映射 /dev/video0。这是嵌入式容器化与普通服务器的关键差异。

Docker Compose 一键部署 Home Assistant

Home Assistant 是最流行的开源智能家居中枢。用 compose 定义,一键部署并自启:

# 安装 compose 插件(get.docker.com 脚本通常已带)
sudo apt install -y docker-compose-plugin
# compose.yaml — Home Assistant + Mosquitto MQTT
services:
  homeassistant:
    image: ghcr.io/home-assistant/home-assistant:stable
    container_name: homeassistant
    volumes:
      - ./ha-config:/config       # 配置持久化
      - /etc/localtime:/etc/localtime:ro
    restart: unless-stopped
    network_mode: host             # HA 需主机网络发现设备
    privileged: true

  mqtt:
    image: eclipse-mosquitto:2
    container_name: mqtt
    ports:
      - "1883:1883"              # MQTT 端口(第11章用)
    volumes:
      - ./mqtt/config:/mosquitto/config
      - ./mqtt/data:/mosquitto/data
    restart: unless-stopped
# 在有 compose.yaml 的目录里执行:
docker compose up -d        # 后台拉起所有服务
docker compose ps           # 看服务状态
docker compose logs -f      # 看全部日志
docker compose down         # 停止并删除(数据卷保留)
docker compose pull         # 拉取镜像更新

# Home Assistant 起来后访问 http://raspberrypi.local:8123
部署方式隔离性多服务管理换机迁移适用
裸装 + systemd各自配置逐个重装单一简单脚本
docker run命令繁琐记命令单个容器
docker compose一份 yaml拷 yaml 即可多组件应用,首选
SD 卡写入与镜像体积

Docker 镜像和容器层会占不少 SD 卡空间,数据库类容器频繁写入还会加速 SD 卡损耗。建议:① 定期 docker system prune -a 清理无用镜像/容器;② 数据卷(尤其数据库、HA 配置)放到外接 USB SSD而非 SD 卡,既快又耐用;③ 用 docker stats 盯内存,树莓派内存小,容器开太多会 OOM。

本章小结

Docker 把"应用+环境"打包成镜像,运行为隔离的容器,比虚拟机轻量得多,特别适合资源有限的树莓派。装 Docker 用官方脚本(自动适配 ARM 架构),把用户加入 docker 组免 sudo。docker run 核心参数:-d 后台、-p 端口映射、-v 数据卷持久化、--restart unless-stopped 自启自愈、--device 访问 GPIO/I2C 硬件。选镜像认准 arm64 多架构支持,优先 alpine 轻量版。自己的 Python 应用写 Dockerfiledocker build 打包。多组件应用(Home Assistant + MQTT + 数据库)用 Docker Compose(一份 compose.yamldocker compose up -d 一键部署,换机拷 yaml 即可)。注意数据卷放外接 SSD 护 SD 卡、定期 prune 清理、盯内存防 OOM。下一章我们让树莓派联网上云——MQTT 物联网通信。