Docker 在嵌入式设备中的常用玩法

Docker 在嵌入式设备中的常用玩法

目录

  1. 基础概念
  2. 交叉编译环境
  3. 固件构建与打包
  4. [CI/CD 流水线](#CI/CD 流水线)
  5. 设备端容器化运行
  6. 模拟与测试
  7. 常用工具与镜像
  8. 实战示例
  9. 注意事项与最佳实践

1. 基础概念

什么是嵌入式 Docker?

在嵌入式开发中,Docker 主要用于:

  • 环境隔离:为不同项目/芯片维护独立的编译环境
  • 可复现构建:确保团队成员使用完全一致的工具链
  • CI/CD 集成:在服务器上自动化构建嵌入式固件
  • 设备端部署:在资源受限的设备上运行容器化应用

Docker 多架构支持

bash 复制代码
# 注册 QEMU 模拟器(关键步骤,用于构建 ARM 镜像)
docker run --rm --privileged multiarch/qemu-user-static --reset -p yes

# 验证
docker run --rm arm64v8/ubuntu uname -m
# 输出: aarch64

2. 交叉编译环境

2.1 统一工具链容器

最常见的用法------把交叉编译工具链封装成 Docker 镜像:

dockerfile 复制代码
# Dockerfile.arm-gcc
FROM ubuntu:22.04

RUN apt-get update && apt-get install -y \
    gcc-arm-none-eabi \
    gdb-multiarch \
    make \
    cmake \
    ninja-build \
    python3 \
    python3-pip \
    && rm -rf /var/lib/apt/lists/*

# 设置工作目录
WORKDIR /workspace

# 默认进入 bash
CMD ["/bin/bash"]

使用方式:

bash 复制代码
# 构建镜像
docker build -t arm-gcc -f Dockerfile.arm-gcc .

# 挂载项目目录并编译
docker run --rm -v $(pwd):/workspace arm-gcc \
    make CROSS_COMPILE=arm-none-eabi- all

2.2 多工具链切换

dockerfile 复制代码
# Dockerfile.multi-toolchain
FROM ubuntu:22.04

# ARM Cortex-M (STM32, NRF52 等)
RUN apt-get update && apt-get install -y gcc-arm-none-eabi

# ARM Cortex-A (Linux 应用)
RUN apt-get install -y gcc-aarch64-linux-gnu g++-aarch64-linux-gnu

# RISC-V
RUN apt-get install -y gcc-riscv64-unknown-elf

# MIPS
RUN apt-get install -y gcc-mips-linux-gnu

RUN rm -rf /var/lib/apt/lists/*
WORKDIR /workspace

2.3 Yocto / Buildroot 环境

dockerfile 复制代码
# Dockerfile.yocto
FROM ubuntu:22.04

RUN apt-get update && apt-get install -y \
    gawk wget git diffstat unzip texinfo gcc build-essential \
    chrpath socat cpio python3 python3-pip python3-pexpect \
    python3-git python3-jinja2 python3-subunit \
    xz-utils debianutils iputils-ping \
    libsdl1.2-dev xterm \
    locales \
    && rm -rf /var/lib/apt/lists/*

RUN locale-gen en_US.UTF-8
ENV LANG=en_US.UTF-8

RUN useradd -m builder
USER builder
WORKDIR /home/builder

# 克隆 Yocto
RUN git clone -b kirkstone \
    https://git.yoctoproject.org/poky.git /home/builder/poky

3. 固件构建与打包

3.1 可复现固件构建

dockerfile 复制代码
# Dockerfile.firmware-build
FROM ubuntu:22.04

# 固定版本的工具链
RUN apt-get update && apt-get install -y \
    gcc-arm-none-eabi=15:10.3-2021.10-* \
    make cmake \
    && rm -rf /var/lib/apt/lists/*

# 固定版本的构建脚本
COPY build.sh /opt/build.sh
RUN chmod +x /opt/build.sh

WORKDIR /firmware
ENTRYPOINT ["/opt/build.sh"]
bash 复制代码
# 构建固件
docker run --rm -v $(pwd):/firmware firmware-build:v1.0

# 产出物自动落在当前目录
ls -la build/*.bin

3.2 OpenWrt 构建

dockerfile 复制代码
# Dockerfile.openwrt
FROM ubuntu:22.04

RUN apt-get update && apt-get install -y \
    build-essential ccache ecj fastjar file g++ gawk \
    gettext git java-propose-class-default libelf-dev \
    libncurses5-dev libncursesw5-dev libssl-dev \
    python3 python2.7-dev python3-distutils python3-setuptools \
    rsync subversion swig time unzip wget xsltproc zlib1g-dev \
    && rm -rf /var/lib/apt/lists/*

RUN git clone https://github.com/openwrt/openwrt.git /opt/openwrt
WORKDIR /opt/openwrt

RUN ./scripts/feeds update -a && ./scripts/feeds install -a

4. CI/CD 流水线

4.1 GitHub Actions 集成

yaml 复制代码
# .github/workflows/firmware.yml
name: Build Firmware

on:
  push:
    branches: [main]
  pull_request:

jobs:
  build:
    runs-on: ubuntu-latest
    container:
      image: ghcr.io/yourorg/arm-gcc:latest

    steps:
      - uses: actions/checkout@v4

      - name: Build
        run: make CROSS_COMPILE=arm-none-eabi- all

      - name: Upload artifacts
        uses: actions/upload-artifact@v4
        with:
          name: firmware
          path: build/*.bin

4.2 GitLab CI 集成

yaml 复制代码
# .gitlab-ci.yml
stages:
  - build
  - test
  - deploy

build:
  stage: build
  image: yourregistry/arm-gcc:latest
  script:
    - make CROSS_COMPILE=arm-none-eabi- all
  artifacts:
    paths:
      - build/*.bin
      - build/*.elf

4.3 Jenkins Pipeline

groovy 复制代码
pipeline {
    agent {
        docker {
            image 'yourregistry/arm-gcc:latest'
        }
    }
    stages {
        stage('Build') {
            steps {
                sh 'make CROSS_COMPILE=arm-none-eabi- all'
            }
        }
        stage('Flash') {
            steps {
                sh 'openocd -f board/stm32f4discovery.cfg -c "program build/firmware.elf verify reset exit"'
            }
        }
    }
}

5. 设备端容器化运行

5.1 轻量级运行时选择

运行时 大小 适用场景
Docker (moby) ~100MB+ 资源充足的边缘网关
Podman ~80MB 无 daemon,更安全
containerd ~30MB 轻量级,K8s 生态
CRI-O ~25MB Kubernetes 专用
LXC/LXD ~10MB 系统级容器
runc ~5MB 最小化容器运行时

5.2 balenaOS (原 resinOS)

专为嵌入式/IoT 设计的容器化操作系统:

bash 复制代码
# balena 设备上的应用结构
# 项目根目录
├── docker-compose.yml
├── Dockerfile.template
└── src/
    └── main.py
yaml 复制代码
# docker-compose.yml (balena)
version: '2'
services:
  sensor-reader:
    build: .
    privileged: true
    restart: always
    labels:
      io.balena.features.kernel-modules: '1'

  data-processor:
    image: myregistry/processor:armv7hf
    depends_on:
      - sensor-reader

5.3 Torizon (NXP 平台)

bash 复制代码
# TorizonCore 上部署应用
torizoncore-builder bundle docker-compose.yml

# 推送到设备
torizoncore-builder images unpack torizon-core-docker-apalis-imx6-Tezi_*.tar
torizoncore-builder deploy --remote-host 192.168.1.100 --remote-username torizon

5.4 设备端 Compose 示例

yaml 复制代码
# docker-compose.yml (边缘网关)
version: '3.8'

services:
  # MQTT Broker
  mosquitto:
    image: eclipse-mosquitto:2-openssl
    ports:
      - "1883:1883"
    volumes:
      - ./mosquitto/config:/mosquitto/config
      - mosquitto-data:/mosquitto/data
    restart: always

  # 数据采集
  collector:
    build: ./collector
    privileged: true
    devices:
      - /dev/i2c-1:/dev/i2c-1
      - /dev/spidev0.0:/dev/spidev0.0
    environment:
      - MQTT_BROKER=mosquitto
    depends_on:
      - mosquitto
    restart: always

  # 边缘推理
  inference:
    image: myregistry/edge-ai:arm64
    devices:
      - /dev/accelerator:/dev/accelerator  # NPU/AI 加速器
    volumes:
      - ./models:/models:ro
    restart: always

  # Web 管理界面
  webui:
    image: nginx:alpine
    ports:
      - "80:80"
    volumes:
      - ./webui:/usr/share/nginx/html:ro
    restart: always

volumes:
  mosquitto-data:

6. 模拟与测试

6.1 QEMU 模拟嵌入式设备

bash 复制代码
# 运行 ARM 虚拟机
docker run --rm -it \
    --platform linux/arm64 \
    multiarch/ubuntu-core:arm64-focal \
    bash

# 在容器内模拟 RISC-V
docker run --rm -it riscv64/ubuntu:22.04 bash

6.2 硬件在环测试 (HIL)

dockerfile 复制代码
# Dockerfile.hil-test
FROM python:3.11-slim

RUN pip install \
    pyserial \
    pytest \
    pytest-timeout \
    pyocd

COPY tests/ /tests/
WORKDIR /tests

CMD ["pytest", "--tb=short", "-v"]
bash 复制代码
# 需要访问 USB 设备(调试器)
docker run --rm \
    --privileged \
    -v /dev/bus/usb:/dev/bus/usb \
    -v $(pwd):/tests \
    hil-test

6.3 网络模拟

yaml 复制代码
# docker-compose.network-sim.yml
version: '3.8'

services:
  device-1:
    build: ./device
    networks:
      iot-net:
        ipv4_address: 10.0.0.10

  device-2:
    build: ./device
    networks:
      iot-net:
        ipv4_address: 10.0.0.11

  # 模拟网关
  gateway:
    build: ./gateway
    ports:
      - "8080:8080"
    networks:
      iot-net:
        ipv4_address: 10.0.0.1
      wan-net:

  # 模拟云端
  cloud:
    image: nginx:alpine
    networks:
      wan-net:

networks:
  iot-net:
    driver: bridge
    ipam:
      config:
        - subnet: 10.0.0.0/24
  wan-net:
    driver: bridge

7. 常用工具与镜像

7.1 官方 & 社区镜像

镜像 用途
arm32v7/ubuntu ARM 32位 Ubuntu
arm64v8/debian ARM 64位 Debian
balenalib/%%BALENA_MACHINE_NAME%%-ubuntu balena 设备基础镜像
torizon/debian Torizon 基础镜像
eclipse-mosquitto MQTT Broker
homeassistant/home-assistant 智能家居
nodered/node-red Node-RED 可视化编程

7.2 开发工具镜像

bash 复制代码
# GDB 调试
docker run --rm -it \
    --privileged \
    -v /dev/bus/usb:/dev/bus/usb \
    arm-gcc \
    gdb-multiarch -ex "target remote :3333" build/firmware.elf

# Wireshark 抓包分析
docker run --rm -it \
    --net=host \
    --privileged \
    linuxserver/wireshark

# Serial 串口调试
docker run --rm -it \
    --device=/dev/ttyUSB0 \
    alpine \
    picocom -b 115200 /dev/ttyUSB0

8. 实战示例

8.1 完整的 STM32 项目

dockerfile 复制代码
# Dockerfile.stm32
FROM ubuntu:22.04

ENV DEBIAN_FRONTEND=noninteractive

RUN apt-get update && apt-get install -y \
    gcc-arm-none-eabi \
    libnewlib-arm-none-eabi \
    cmake \
    make \
    git \
    python3 \
    python3-pip \
    stlink-tools \
    openocd \
    && rm -rf /var/lib/apt/lists/*

# 安装 STM32CubeMX CLI (可选)
# RUN pip3 install stm32cubemx

WORKDIR /project

# 构建 & 烧录脚本
COPY scripts/build.sh scripts/flash.sh /usr/local/bin/
RUN chmod +x /usr/local/bin/build.sh /usr/local/bin/flash.sh

ENTRYPOINT ["/bin/bash"]
bash 复制代码
# build.sh
#!/bin/bash
set -e
mkdir -p build && cd build
cmake -DCMAKE_TOOLCHAIN_FILE=../cmake/arm-none-eabi.cmake ..
make -j$(nproc)
arm-none-eabi-objcopy -O binary firmware.elf firmware.bin
echo "Build complete: build/firmware.bin ($(wc -c < firmware.bin) bytes)"
bash 复制代码
# 使用
docker run --rm -v $(pwd):/project stm32-dev build.sh

# 烧录(需要 USB 设备访问)
docker run --rm \
    --privileged \
    -v /dev/bus/usb:/dev/bus/usb \
    -v $(pwd):/project \
    stm32-dev flash.sh

8.2 Raspberry Pi 应用开发

dockerfile 复制代码
# Dockerfile.rpi
FROM arm64v8/python:3.11-slim

RUN apt-get update && apt-get install -y \
    libgpiod2 \
    i2c-tools \
    && rm -rf /var/lib/apt/lists/*

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

COPY . .

CMD ["python3", "main.py"]
bash 复制代码
# 在 x86 主机上构建 ARM 镜像
docker buildx build --platform linux/arm64 -t my-rpi-app -f Dockerfile.rpi .

# 推送到树莓派
docker save my-rpi-app | ssh pi@raspberrypi 'docker load'

# 树莓派上运行
docker run --rm \
    --device /dev/i2c-1 \
    --device /dev/gpiochip0 \
    my-rpi-app

8.3 多服务 IoT 系统

yaml 复制代码
# docker-compose.iot.yml
version: '3.8'

services:
  # 数据采集
  sensor-hub:
    build: ./sensor-hub
    privileged: true
    environment:
      - SENSOR_INTERVAL=5
    restart: always

  # 本地数据库
  influxdb:
    image: influxdb:1.8
    volumes:
      - influxdb-data:/var/lib/influxdb
    ports:
      - "8086:8086"
    restart: always

  # 数据可视化
  grafana:
    image: grafana/grafana:latest
    ports:
      - "3000:3000"
    volumes:
      - grafana-data:/var/lib/grafana
    environment:
      - GF_SECURITY_ADMIN_PASSWORD=admin
    depends_on:
      - influxdb
    restart: always

  # 规则引擎
  nodered:
    image: nodered/node-red:latest
    ports:
      - "1880:1880"
    volumes:
      - nodered-data:/data
    restart: always

  # 反向代理
  nginx:
    image: nginx:alpine
    ports:
      - "80:80"
    volumes:
      - ./nginx/nginx.conf:/etc/nginx/nginx.conf:ro
    depends_on:
      - grafana
      - nodered
    restart: always

volumes:
  influxdb-data:
  grafana-data:
  nodered-data:

9. 注意事项与最佳实践

9.1 资源受限设备的优化

dockerfile 复制代码
# 使用 Alpine 减小体积
FROM arm64v8/alpine:3.18

# 多阶段构建
FROM arm64v8/gcc:12 AS builder
COPY . /src
WORKDIR /src
RUN gcc -O2 -o app main.c

FROM arm64v8/alpine:3.18
COPY --from=builder /src/app /usr/local/bin/
CMD ["app"]

# 最终镜像可能只有 10MB

9.2 安全注意事项

yaml 复制代码
# 安全最佳实践
services:
  secure-app:
    image: my-app
    # 1. 不要使用 privileged,尽量指定具体设备
    devices:
      - /dev/i2c-1:/dev/i2c-1
    # 2. 只读文件系统
    read_only: true
    tmpfs:
      - /tmp
    # 3. 非 root 用户
    user: "1000:1000"
    # 4. 限制资源
    deploy:
      resources:
        limits:
          memory: 256M
          cpus: '0.5'
    # 5. Drop 不必要的 capabilities
    cap_drop:
      - ALL
    cap_add:
      - SYS_RAWIO  # 仅在需要时

9.3 存储优化

bash 复制代码
# 使用 tmpfs 避免写入闪存(延长存储寿命)
docker run --rm \
    --tmpfs /tmp:rw,noexec,nosuid,size=64m \
    --tmpfs /var/log:rw,noexec,nosuid,size=32m \
    my-app

# 使用 overlay2 存储驱动
# /etc/docker/daemon.json
{
  "storage-driver": "overlay2",
  "storage-opts": [
    "overlay2.override_kernel_check=true"
  ]
}

9.4 网络配置

bash 复制代码
# 使用 host 网络模式(减少开销)
docker run --rm --net=host my-iot-app

# 或使用 macvlan(设备看起来像独立主机)
docker network create -d macvlan \
    --subnet=192.168.1.0/24 \
    --gateway=192.168.1.1 \
    -o parent=eth0 \
    macvlan-net

docker run --rm --net=macvlan-net --ip=192.168.1.100 my-app

9.5 常见陷阱

陷阱 解决方案
镜像太大 多阶段构建 + Alpine 基础镜像
编译太慢 使用 BuildKit 缓存 + ccache
USB 设备访问 --privileged--device
GPIO 访问 --privileged + --device /dev/gpiochip0
时钟不同步 --cap-add SYS_TIME 或挂载宿主机时钟
日志占满磁盘 配置日志轮转 --log-opt max-size=10m
跨架构构建慢 使用 docker buildx + 远程 builder

附录:快速命令参考

bash 复制代码
# 构建 ARM 镜像
docker buildx build --platform linux/arm64 -t my-app:arm64 .

# 导出镜像到离线设备
docker save my-app:arm64 | gzip > my-app-arm64.tar.gz
ssh target "gunzip -c my-app-arm64.tar.gz | docker load"

# 查看容器资源使用
docker stats

# 清理无用资源
docker system prune -a

# 导出容器文件系统
docker export my-container > rootfs.tar

# 导入为镜像
cat rootfs.tar | docker import - my-image:latest
相关推荐
ylscode1 小时前
CVE-2026-31694 漏洞深度分析:Linux 内核 FUSE 组件存在本地提权风险,普通用户可直取 root 权限
linux·运维·服务器
智脑API平台2 小时前
Codex CLI 怎么用 .env 配置 API Key?本地项目和自动化脚本接入教程
运维·自动化
weixin_307779133 小时前
Linux下Docker Compose里运行的MySQL数据库故障诊断Shell脚本
linux·运维·mysql·docker·自动化
h3guang Official4 小时前
K8s安全实战:从漏洞靶场到红蓝对抗
安全·容器·kubernetes
流浪0014 小时前
Git篇(三):吃透远程协作:SSH 密钥、分支、标签全流程实战指南
运维·git·ssh
FoldWinCard4 小时前
D4 Linux 进程 内存 等命令
linux·运维·服务器
nuo5342024 小时前
基础 3 —— Docker 的常用命令
docker·容器
番茄育学园5 小时前
2026表格自动化工具实测:四个不同方案,适配不同办公场景
运维·自动化
j7~5 小时前
【Linux 系统】二十.进程间通信(共享内存、消息队列、信号量)
linux·运维·消息队列·共享内存·信号量·system v