Skip to content

Docker 面试题精选 ​

生成时间:2026-06-06 20:54 | 共 10 题


1. 请解释 Docker 的整体架构,包括 Client、Daemon 和 Registry 的职责与交互方式 ​

难度: ⭐

标准答案:

Docker 采用 C/S(Client/Server)架构,由三个核心组件组成:

  • Docker Client:用户与 Docker 交互的入口,通过 CLI(如 docker run)或 SDK 发送请求给 Docker Daemon。Client 与 Daemon 可以在同一主机,也可通过网络远程连接。
  • Docker Daemon(dockerd):后台服务进程,负责接收 Client 请求并执行实际的容器管理操作——镜像构建、容器创建/启动/停止、网络和存储管理等。Daemon 是 Docker 的"大脑"。
  • Docker Registry:镜像的存储与分发服务。Docker Hub 是默认的公共 Registry;企业可搭建私有 Registry(如 Harbor)。Daemon 通过 docker pull 从 Registry 拉取镜像,通过 docker push 推送镜像。

交互流程:Client → REST API → Daemon → Registry。Client 不直接与 Registry 通信,所有操作由 Daemon 代理完成。

延伸思考: Docker Desktop 的架构与传统 Linux Docker 有何区别?提示:Docker Desktop 在 macOS/Windows 上通过虚拟机(Hyper-V / Apple Silicon 上的 QEMU)运行 Linux 内核,Client 在宿主机,Daemon 在 VM 内。


2. Dockerfile 中 COPY 和 ADD 指令有什么区别?为什么推荐优先使用 COPY? ​

难度: ⭐

标准答案:

特性COPYADD
基本复制✅✅
从 URL 复制❌✅
自动解压 tar 归档❌✅
多阶段构建中引用✅ --from=✅ --from=

推荐 COPY 的原因:

  1. 语义明确:COPY 只做一件事——将文件从构建上下文复制到镜像中,行为可预测。
  2. ADD 的副作用:ADD 会自动解压 tar 文件,如果你的本意是复制一个 .tar.gz 文件而不是解压它,就会产生意外行为。
  3. ADD 从 URL 下载是不可缓存且不可预测的,应使用 RUN curl 替代,这样可以更好地控制缓存层。
  4. Docker 官方最佳实践文档明确推荐:除非需要自动解压功能,否则一律使用 COPY。

延伸思考: 如果需要在构建时从远程下载文件,ADD <url> 和 RUN curl + COPY 哪个更好?为什么?提示:后者可以更好地利用 Docker 缓存层和校验文件完整性。


3. 什么是多阶段构建(Multi-stage Build)?它解决了什么问题?请给出一个典型示例 ​

难度: ⭐⭐

标准答案:

多阶段构建允许在一个 Dockerfile 中定义多个 FROM 指令,每个 FROM 开始一个新的构建阶段。关键是可以用 COPY --from=<stage> 从前一个阶段复制只需要的产物到最终镜像,丢弃构建依赖。

解决的问题:

  • 构建环境(编译器、SDK、源码)不需要留在运行时镜像中
  • 显著减小最终镜像体积(从数 GB 降至数十 MB)
  • 无需维护单独的 Dockerfile 或编写复杂的构建脚本

典型示例(Go 应用):

dockerfile
# 阶段1:构建
FROM golang:1.22 AS builder
WORKDIR /app
COPY go.mod go.sum ./
RUN go mod download
COPY . .
RUN CGO_ENABLED=0 go build -o myapp

# 阶段2:运行
FROM alpine:3.19
RUN apk --no-cache add ca-certificates
COPY --from=builder /app/myapp /usr/local/bin/myapp
ENTRYPOINT ["myapp"]

最终镜像仅包含 alpine 基础 + 二进制文件,约 20MB,而非 golang 镜像的 1GB+。

延伸思考: 多阶段构建中,如何给阶段命名以提升可读性?COPY --from=builder 和 COPY --from=0 各有什么优劣?提示:命名阶段语义更清晰,数字索引在阶段顺序调整时容易出错。


4. Docker 的三种主要网络模式(bridge、host、overlay)分别适用于什么场景? ​

难度: ⭐⭐

标准答案:

网络模式原理适用场景注意事项
bridge默认模式。每个容器分配独立网络命名空间,通过虚拟网桥(docker0)互联,通过 NAT 访问外部单机多容器通信,最常用端口需 -p 映射才能从宿主机访问
host容器直接使用宿主机网络栈,无网络隔离需要极低网络延迟的场景(如高性能计算、网络监控工具)端口冲突风险高,安全性低,失去了网络隔离
overlay跨主机的 VXLAN 隧道网络,由 Docker Swarm 或独立使用多主机集群中容器间通信需要键值存储(如 Consul)或 Swarm 模式;性能有 VXLAN 开销

补充:

  • none:完全禁用网络,适用于安全敏感的批处理任务。
  • macvlan:容器拥有独立 MAC 地址,直接出现在物理网络中,适用于遗留系统对接。

延伸思考: 在 Kubernetes 中为什么不使用 Docker 的 overlay 网络?提示:K8s 使用 CNI 插件(如 Calico、Cilium)实现更灵活的网络策略和跨节点通信。


5. Docker Volume 和 Bind Mount 有什么区别?生产环境应该如何选择? ​

难度: ⭐⭐

标准答案:

特性VolumeBind Mount
存储位置/var/lib/docker/volumes/(Docker 管理)宿主机任意路径
创建方式docker volume create 或 -v vol_name:/path-v /host/path:/container/path
可移植性✅ 跨平台,不依赖宿主机目录结构❌ 依赖宿主机特定路径
性能Linux 下与 Bind Mount 相当macOS/Windows 下可能较慢(文件系统转译)
备份/迁移docker run --rm -v vol:/data busybox tar czf ...直接操作宿主机文件
安全性Docker 管理,非 root 难以直接访问容器可修改宿主机敏感文件
生命周期独立于容器,docker volume prune 清理随宿主机目录

生产环境建议:

  1. 优先使用 Named Volume:数据持久化、数据库存储、配置文件等——可移植、易备份。
  2. Bind Mount 适用于:开发环境代码热更新、需要精确控制宿主机路径的场景。
  3. 避免匿名 Volume(-v /path 不指定名称):难以管理和清理。
  4. tmpfs:敏感临时数据(如密钥),不写入磁盘。

延伸思考: 在 Kubernetes 中,PV/PVC 对应 Docker 的哪种挂载方式?K8s 的 StorageClass 如何动态供给存储?提示:PV 可以基于 Volume(NFS、云盘等),也可以基于 HostPath(类似 Bind Mount)。


6. 如何使用 Docker Compose 编排一个包含 Web 服务、数据库和缓存的微服务应用? ​

难度: ⭐⭐

标准答案:

yaml
# docker-compose.yml
version: "3.9"

services:
  web:
    build: ./web
    ports:
      - "8080:8080"
    environment:
      - DB_HOST=db
      - REDIS_HOST=cache
    depends_on:
      db:
        condition: service_healthy
      cache:
        condition: service_started
    restart: unless-stopped
    networks:
      - frontend
      - backend

  db:
    image: postgres:16-alpine
    environment:
      POSTGRES_DB: myapp
      POSTGRES_PASSWORD: ${DB_PASSWORD}
    volumes:
      - pgdata:/var/lib/postgresql/data
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U postgres"]
      interval: 5s
      timeout: 5s
      retries: 5
    restart: unless-stopped
    networks:
      - backend

  cache:
    image: redis:7-alpine
    command: redis-server --appendonly yes
    volumes:
      - redisdata:/data
    restart: unless-stopped
    networks:
      - backend

volumes:
  pgdata:
  redisdata:

networks:
  frontend:
  backend:
    internal: true  # 后端网络不对外暴露

关键点:

  1. depends_on + healthcheck 确保启动顺序和依赖就绪
  2. 环境变量通过 ${} 引用 .env 文件,避免硬编码密码
  3. internal: true 网络限制后端服务不被外部访问
  4. Named Volume 持久化数据
  5. restart: unless-stopped 保证服务自愈

延伸思考: depends_on 能保证服务"可用"吗?数据库启动了但还没完成初始化怎么办?healthcheck 和 wait-for-it.sh 脚本各有什么优劣?


7. Docker 容器的安全加固有哪些关键措施? ​

难度: ⭐⭐⭐

标准答案:

容器安全是纵深防御,需从多个层面入手:

1. 镜像安全

  • 使用可信基础镜像(官方镜像 > 社区镜像),优先选择 alpine/distroless 等最小化镜像
  • 定期扫描镜像漏洞:docker scout、Trivy、Clair
  • 固定镜像版本标签(python:3.12.4-slim 而非 python:latest)
  • 使用多阶段构建,最终镜像不含编译工具

2. 运行时安全

  • 非 root 运行:Dockerfile 中添加 USER appuser,或 docker run -u 1000:1000
  • 只读文件系统:docker run --read-only,配合 --tmpfs /tmp 挂载临时目录
  • 限制能力:--cap-drop ALL --cap-add NET_BIND_SERVICE,仅保留必要 Linux capability
  • 禁止特权模式:避免 --privileged,它会赋予容器几乎所有宿主机权限
  • 资源限制:--memory 512m --cpus 1,防止单容器耗尽主机资源
  • AppArmor/SELinux 策略:为容器定制强制访问控制策略

3. 网络安全

  • 最小端口暴露,仅映射必要端口
  • 使用 Docker 网络隔离,internal 网络禁止外部访问
  • 启用 TLS 加密 Daemon 通信

4. 宿主机安全

  • 保持 Docker Engine 更新
  • 限制 Docker Socket 权限(/var/run/docker.sock 不要挂载进容器)
  • 启用 Seccomp 配置文件

延伸思考: --privileged 与 --cap-add ALL 有什么区别?为什么 --privileged 更危险?提示:privileged 还会解除设备访问限制、挂载命名空间限制等,远超 capability 范畴。


8. 如何对 Docker 容器进行性能调优?请从 CPU、内存、I/O 和网络四个维度分析 ​

难度: ⭐⭐⭐

标准答案:

CPU 调优:

  • --cpus=2:限制可用 CPU 核心数(CFS 调度器配额)
  • --cpuset-cpus=0,1:绑定到特定 CPU 核心,减少上下文切换,提升缓存命中率
  • --cpu-shares=512:相对权重分配(仅在 CPU 竞争时生效,不保证绝对值)

内存调优:

  • --memory=2g --memory-swap=2g:限制内存 + 禁止 swap(swap=memory 表示无额外 swap)
  • --memory-reservation=1g:软限制,内存紧张时才触发回收
  • --oom-kill-disable=true:禁止 OOM Killer 杀容器(需同时设 --memory,否则无意义)
  • --kernel-memory=128m:限制内核内存(高级场景,如大量 socket 连接)

I/O 调优:

  • 存储驱动选择:overlay2(默认,性能最佳);生产环境避免 devicemapper 的 loop 模式
  • Volume 性能:Named Volume > Bind Mount(macOS/Windows 上差异显著)
  • 日志限制:--log-driver json-file --log-opt max-size=10m --log-opt max-file=3,防止日志撑爆磁盘
  • tmpfs:高频临时 I/O 使用 --tmpfs /tmp:size=1g,直接在内存中

网络调优:

  • host 网络:对网络 I/O 要求极高的场景,绕过 NAT 和网桥
  • DNS 优化:--dns 8.8.8.8 指定高效 DNS;减少容器内 DNS 查询开销
  • 连接跟踪:高并发短连接场景,调整宿主机 net.netfilter.nf_conntrack_max
  • MTU 调整:--mtu 1400 适配底层网络,避免分片

监控工具:

  • docker stats:实时资源概览
  • docker top:容器内进程
  • cAdvisor + Prometheus + Grafana:完整监控栈

延伸思考: 在 Kubernetes 中,Requests 和 Limits 与 Docker 的 --cpu-shares 和 --cpus 有什么对应关系?为什么 K8s 推荐"Requests = Limits"以避免 CPU 抖动?


9. 如何将 Docker 镜像从 1GB 瘦身到 50MB 以下?请给出完整的策略和步骤 ​

难度: ⭐⭐⭐

标准答案:

镜像瘦身七步法:

第1步:选择最小基础镜像

dockerfile
# ❌ 1.2GB
FROM ubuntu:22.04
# ✅ 80MB
FROM python:3.12-slim
# ✅ 50MB
FROM python:3.12-alpine
# ✅ 20MB(无 shell,极难调试但最安全)
FROM gcr.io/distroless/python3

第2步:多阶段构建

dockerfile
FROM node:20 AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build

FROM node:20-alpine
COPY --from=builder /app/dist ./dist
COPY --from=builder /app/node_modules ./node_modules
CMD ["node", "dist/main.js"]

第3步:合并 RUN 层 + 清理缓存

dockerfile
# ❌ 三层
RUN apt-get update
RUN apt-get install -y curl
RUN rm -rf /var/lib/apt/lists/*

# ✅ 一层
RUN apt-get update && \
    apt-get install -y --no-install-recommends curl && \
    rm -rf /var/lib/apt/lists/*

第4步:利用构建缓存

dockerfile
# 先复制依赖文件,依赖不变则复用缓存
COPY package*.json ./
RUN npm ci
# 再复制源码,源码变不影响依赖层缓存
COPY . .

第5步:.dockerignore 排除无关文件

.git
node_modules
*.md
.env
Dockerfile
docker-compose*.yml

第6步:使用 docker-slim 自动瘦身

bash
docker-slim build --target myapp:fat --tag myapp:slim
# 可减小 30%-70% 体积,通过运行时分析只保留必需文件

第7步:分析镜像层

bash
docker history myapp:latest
dive myapp:latest  # 交互式查看每层内容,发现可优化点

实战效果: 典型 Node.js 应用:node:20 (1.1GB) → 多阶段+alpine → ~120MB → distroless → ~80MB → docker-slim → ~40MB

延伸思考: Alpine 镜像使用 musl libc 而非 glibc,可能导致哪些兼容性问题?提示:Node.js 原生模块(如 sharp、bcrypt)需要编译,musl 环境下可能失败;DNS 解析行为差异等。


10. 如何在 CI/CD 流水线中集成 Docker?请以 GitHub Actions 为例,给出完整的构建、测试、推送流程 ​

难度: ⭐⭐⭐

标准答案:

yaml
# .github/workflows/docker-ci.yml
name: Docker CI/CD

on:
  push:
    branches: [main]
    tags: ['v*']
  pull_request:
    branches: [main]

env:
  REGISTRY: ghcr.io
  IMAGE_NAME: ${{ github.repository }}

jobs:
  build-and-push:
    runs-on: ubuntu-latest
    permissions:
      contents: read
      packages: write

    steps:
      # 1. 检出代码
      - name: Checkout
        uses: actions/checkout@v4

      # 2. 设置 Docker Buildx(支持多平台构建)
      - name: Set up Docker Buildx
        uses: docker/setup-buildx-action@v3

      # 3. 登录容器仓库
      - name: Login to GHCR
        if: github.event_name != 'pull_request'
        uses: docker/login-action@v3
        with:
          registry: ${{ env.REGISTRY }}
          username: ${{ github.actor }}
          password: ${{ secrets.GITHUB_TOKEN }}

      # 4. 提取 Docker 元数据(标签、标注)
      - name: Extract metadata
        id: meta
        uses: docker/metadata-action@v5
        with:
          images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
          tags: |
            type=ref,event=branch
            type=semver,pattern={{version}}
            type=semver,pattern={{major}}.{{minor}}
            type=sha,prefix=

      # 5. 构建并推送
      - name: Build and push
        uses: docker/build-push-action@v5
        with:
          context: .
          push: ${{ github.event_name != 'pull_request' }}
          tags: ${{ steps.meta.outputs.tags }}
          labels: ${{ steps.meta.outputs.labels }}
          cache-from: type=gha
          cache-to: type=gha,mode=max
          platforms: linux/amd64,linux/arm64

      # 6. 安全扫描
      - name: Scan for vulnerabilities
        uses: aquasecurity/trivy-action@master
        with:
          image-ref: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ github.sha }}
          format: 'table'
          exit-code: '1'
          severity: 'CRITICAL,HIGH'

关键最佳实践:

  1. 镜像标签策略:禁止 latest 唯一标签;使用语义版本 + Git SHA 双重标识
  2. 构建缓存:cache-from/cache-to: type=gha 利用 GitHub Actions 缓存,加速重复构建
  3. 多平台构建:Buildx 支持 linux/amd64,linux/arm64,适配云服务器和 ARM 设备
  4. 安全扫描:Trivy 扫描 CRITICAL/HIGH 漏洞,exit-code: 1 阻断发布
  5. 最小权限:GITHUB_TOKEN 仅需 packages:write,不使用 Personal Access Token
  6. PR 仅构建不推送:push: ${{ github.event_name != 'pull_request' }}

延伸思考: 如何实现蓝绿部署或金丝雀发布?提示:结合 Kubernetes 的 Deployment strategy(RollingUpdate 参数调整)或使用 Argo Rollouts / Flagger 实现渐进式发布。


EOF