强曰为道

与天地相似,故不违。知周乎万物,而道济天下,故不过。旁行而不流,乐天知命,故不忧.
文档目录

15 - Docker 中的 QEMU

15 - Docker 中的 QEMU

掌握在 Docker 中使用 QEMU 实现多架构构建,包括 Buildx 配置与 QEMU 用户模式集成。


15.1 Docker 与 QEMU 的结合

Docker 与 QEMU 的结合主要体现在多架构容器镜像构建上。通过 QEMU 的用户模式仿真,可以在 x86_64 主机上构建和运行 ARM64、RISC-V 等架构的容器镜像。

多架构构建架构:
  ┌─────────────────────────────────────────────────────┐
  │                 x86_64 主机                          │
  │  ┌──────────────────────────────────────────────┐  │
  │  │           Docker Buildx                       │  │
  │  │  ┌────────────────┐  ┌────────────────┐     │  │
  │  │  │ amd64 builder  │  │ arm64 builder  │     │  │
  │  │  │ (原生)         │  │ (QEMU 仿真)    │     │  │
  │  │  └────────┬───────┘  └────────┬───────┘     │  │
  │  └───────────┼───────────────────┼──────────────┘  │
  │              │                   │                  │
  │  ┌───────────┴───────────────────┴──────────────┐  │
  │  │              Docker Engine                     │  │
  │  │  ┌──────────────┐  ┌──────────────────────┐  │  │
  │  │  │ amd64 容器    │  │ arm64 容器 (QEMU)    │  │  │
  │  │  └──────────────┘  └──────────────────────┘  │  │
  │  └──────────────────────────────────────────────┘  │
  └─────────────────────────────────────────────────────┘

15.2 安装 QEMU 用户模式支持

使用 Docker 官方推荐方式

# 使用 tonistiigi/binfmt 设置多架构支持
docker run --privileged --rm tonistiigi/binfmt --install all

# 验证安装
ls /proc/sys/fs/binfmt_misc/qemu-*
# qemu-aarch64  qemu-arm  qemu-mips64el  qemu-ppc64le  qemu-riscv64  qemu-s390x ...

手动安装

# Debian/Ubuntu
sudo apt install -y qemu-user-static

# 注册 binfmt
sudo update-binfmts --enable qemu-aarch64
sudo update-binfmts --enable qemu-arm

# 验证
docker run --rm --platform linux/arm64 arm64v8/ubuntu:22.04 uname -m
# aarch64

15.3 Docker Buildx 配置

安装 Buildx

# Docker Desktop 已包含 Buildx
# 单独安装:
# 1. 下载 buildx 二进制
mkdir -p ~/.docker/cli-plugins/
wget -O ~/.docker/cli-plugins/docker-buildx \
  https://github.com/docker/buildx/releases/download/v0.12.1/buildx-v0.12.1.linux-amd64
chmod +x ~/.docker/cli-plugins/docker-buildx

# 2. 创建新的 buildx builder(支持多架构)
docker buildx create --name multiarch --driver docker-container --use
docker buildx inspect --bootstrap

创建多架构 Builder

# 查看当前 builder
docker buildx ls

# 创建支持多架构的 builder
docker buildx create --name mybuilder --driver docker-container --use

# 启动 builder
docker buildx inspect mybuilder --bootstrap

# 查看支持的平台
docker buildx inspect mybuilder | grep Platforms
# Platforms: linux/amd64, linux/arm64, linux/riscv64, linux/ppc64le, linux/s390x, ...

15.4 多架构镜像构建

基本多架构构建

# 构建并推送到 Docker Hub
docker buildx build \
  --platform linux/amd64,linux/arm64,linux/arm/v7 \
  -t username/myapp:latest \
  --push .

# 仅构建(不推送)
docker buildx build \
  --platform linux/amd64,linux/arm64 \
  -t myapp:latest \
  --load .
# 注意: --load 只支持单架构

# 导出为本地 tar 文件
docker buildx build \
  --platform linux/amd64,linux/arm64 \
  -t myapp:latest \
  --output type=local,dest=./output .

Dockerfile 多架构优化

# 多架构 Dockerfile 示例
FROM --platform=$BUILDPLATFORM golang:1.21-alpine AS builder

ARG TARGETPLATFORM
ARG TARGETOS
ARG TARGETARCH

WORKDIR /app
COPY go.mod go.sum ./
RUN go mod download

COPY . .
RUN CGO_ENABLED=0 GOOS=${TARGETOS} GOARCH=${TARGETARCH} \
    go build -o /app/server .

FROM --platform=$TARGETPLATFORM alpine:3.19

RUN apk --no-cache add ca-certificates tzdata

COPY --from=builder /app/server /usr/local/bin/server

EXPOSE 8080
CMD ["server"]

使用 ARG 处理架构差异

# 处理不同架构的依赖差异
FROM ubuntu:22.04

ARG TARGETARCH

# 根据架构安装不同包
RUN case "${TARGETARCH}" in \
      "amd64") \
        apt-get update && apt-get install -y intel-microcode ;; \
      "arm64") \
        apt-get update && apt-get install -y linux-firmware ;; \
    esac && \
    rm -rf /var/lib/apt/lists/*

# 架构特定的下载链接
ARG NODE_VERSION=20
RUN ARCH=$(dpkg --print-architecture) && \
    wget "https://nodejs.org/dist/v${NODE_VERSION}/node-v${NODE_VERSION}-linux-${ARCH}.tar.xz" && \
    tar -xf "node-v${NODE_VERSION}-linux-${ARCH}.tar.xz" -C /usr/local --strip-components=1

15.5 实战:构建多架构 Go 应用

# 项目结构
myapp/
├── Dockerfile
├── go.mod
├── go.sum
└── main.go
// main.go
package main

import (
    "fmt"
    "net/http"
    "runtime"
)

func main() {
    http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
        fmt.Fprintf(w, "Hello from %s/%s!\n", runtime.GOOS, runtime.GOARCH)
    })
    fmt.Printf("Server running on %s/%s\n", runtime.GOOS, runtime.GOARCH)
    http.ListenAndServe(":8080", nil)
}
# Dockerfile
FROM --platform=$BUILDPLATFORM golang:1.21-alpine AS builder

WORKDIR /app
COPY go.mod go.sum ./
RUN go mod download

COPY . .

ARG TARGETOS TARGETARCH
RUN CGO_ENABLED=0 GOOS=${TARGETOS} GOARCH=${TARGETARCH} \
    go build -ldflags="-s -w" -o /server .

FROM scratch
COPY --from=builder /server /server
EXPOSE 8080
CMD ["/server"]
# 构建多架构镜像
docker buildx build \
  --platform linux/amd64,linux/arm64,linux/arm/v7,linux/ppc64le,linux/s390x \
  -t username/myapp:latest \
  --push .

15.6 实战:构建多架构 Python 应用

# Python 多架构 Dockerfile
FROM python:3.11-slim

WORKDIR /app

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

COPY . .

EXPOSE 5000
CMD ["python", "app.py"]
# 构建
docker buildx build \
  --platform linux/amd64,linux/arm64 \
  -t username/python-app:latest \
  --push .

15.7 多架构镜像管理

查看多架构镜像 manifest

# 查看镜像支持的架构
docker buildx imagetools inspect username/myapp:latest

# 输出示例:
# Name:      docker.io/username/myapp:latest
# MediaType: application/vnd.oci.image.index.v1+json
# Digest:    sha256:...
# Manifests:
#   Name:      docker.io/username/myapp:latest@sha256:...
#   MediaType: application/vnd.oci.image.manifest.v1+json
#   Platform:  linux/amd64
#
#   Name:      docker.io/username/myapp:latest@sha256:...
#   MediaType: application/vnd.oci.image.manifest.v1+json
#   Platform:  linux/arm64

手动创建 manifest 列表

# 分别构建各架构
docker buildx build --platform linux/amd64 -t username/myapp:amd64 --push .
docker buildx build --platform linux/arm64 -t username/myapp:arm64 --push .

# 创建 manifest 列表
docker manifest create username/myapp:latest \
  username/myapp:amd64 \
  username/myapp:arm64

# 推送 manifest
docker manifest push username/myapp:latest

15.8 QEMU 全系统模式在 Docker 中

某些场景下需要在 Docker 中运行完整的虚拟机:

# 在 Docker 中运行 QEMU 全系统仿真
FROM ubuntu:22.04

RUN apt-get update && apt-get install -y \
    qemu-system-x86 \
    qemu-utils \
    && rm -rf /var/lib/lib/lists/*

COPY vm-disk.qcow2 /images/vm.qcow2

# 注意: 需要 --privileged 或 KVM 设备
CMD ["qemu-system-x86_64", \
     "-m", "2G", \
     "-drive", "file=/images/vm.qcow2,format=qcow2", \
     "-nographic", \
     "-net", "user,hostfwd=tcp::2222-:22"]
# 运行(需要特权模式)
docker run --privileged -d -p 2222:2222 qemu-vm

# 或者使用 KVM 加速
docker run --device=/dev/kvm -d -p 2222:2222 qemu-vm

15.9 CI/CD 多架构构建

GitHub Actions

name: Build Multi-Arch Image

on:
  push:
    tags: ['v*']

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      
      - name: Set up QEMU
        uses: docker/setup-qemu-action@v3
      
      - name: Set up Docker Buildx
        uses: docker/setup-buildx-action@v3
      
      - name: Login to Docker Hub
        uses: docker/login-action@v3
        with:
          username: ${{ secrets.DOCKER_USERNAME }}
          password: ${{ secrets.DOCKER_TOKEN }}
      
      - name: Build and push
        uses: docker/build-push-action@v5
        with:
          context: .
          platforms: linux/amd64,linux/arm64,linux/arm/v7
          push: true
          tags: |
            username/myapp:${{ github.ref_name }}
            username/myapp:latest
          cache-from: type=gha
          cache-to: type=gha,mode=max

GitLab CI

build-multi-arch:
  stage: build
  image: docker:24
  services:
    - docker:24-dind
  before_script:
    - docker run --privileged --rm tonistiigi/binfmt --install all
    - docker buildx create --use
  script:
    - echo "$CI_REGISTRY_PASSWORD" | docker login -u "$CI_REGISTRY_USER" --password-stdin $CI_REGISTRY
    - docker buildx build
        --platform linux/amd64,linux/arm64
        -t $CI_REGISTRY_IMAGE:$CI_COMMIT_TAG
        --push .

15.10 性能优化

构建缓存

# 使用 registry 缓存
docker buildx build \
  --platform linux/amd64,linux/arm64 \
  -t myapp:latest \
  --cache-from type=registry,ref=myapp:buildcache \
  --cache-to type=registry,ref=myapp:buildcache,mode=max \
  --push .

减少 QEMU 仿真开销

# 1. 使用多阶段构建,只在最后阶段使用目标架构
# 2. 在 BUILDPLATFORM 上编译,避免 QEMU 编译
# 3. 使用缓存,减少重复构建

# 示例: Go 应用只在最后 COPY 二进制
FROM scratch
COPY --from=builder /app/server /server

要点回顾

要点核心内容
binfmt_misc通过 tonistiigi/binfmt 安装多架构支持
BuildxDocker 多架构构建的标准工具
多平台构建--platform linux/amd64,linux/arm64
最佳实践在 BUILDPLATFORM 编译,避免 QEMU 编译开销
CI/CDdocker/setup-qemu-action + docker/setup-buildx-action

注意事项

构建速度: QEMU 仿真构建比原生构建慢 5-20 倍。建议在 BUILDPLATFORM 上进行编译,仅在最后阶段使用目标架构。

测试: 多架构镜像应进行跨架构测试,确保各架构都能正常运行。

基础镜像: 确保基础镜像支持目标架构。可以使用 docker manifest inspect 查看镜像支持的架构。


扩展阅读


下一步

16 - 最佳实践:学习 QEMU 性能调优、安全加固与生产部署的最佳实践。