Docker 面试题精选 10 道
生成时间:2026-06-06 21:38 | 难度范围:⭐ ~ ⭐⭐⭐
1. 请描述 Docker 的整体架构,以及 Client、Daemon、Registry 三者的协作关系
难度: ⭐
标准答案:
Docker 采用 C/S 架构,核心由三部分组成:
- Docker Client:用户与 Docker 交互的入口(
docker命令行、Docker Desktop GUI 等),负责发送请求到 Daemon。 - Docker Daemon(dockerd):后台守护进程,监听 Docker API 请求,负责镜像构建、容器生命周期管理、网络和存储等核心操作。Client 与 Daemon 可以在同一主机,也可通过 TCP/Unix Socket 远程通信。
- Docker Registry:镜像仓库,存储和分发 Docker 镜像。Docker Hub 是默认公共仓库,企业可搭建私有 Registry(Harbor、自建 registry 容器等)。
协作流程:Client 发送 docker pull nginx → Daemon 接收请求 → Daemon 从 Registry 拉取镜像层 → 存储到本地 /var/lib/docker → Client 收到成功响应。
延伸思考: 当 Daemon 与 Client 分离部署时,如何保证通信安全?(提示:TLS 双向认证、配置 DOCKER_HOST 与 DOCKER_TLS_VERIFY)
2. 编写 Dockerfile 时有哪些最佳实践?请至少列举 5 条
难度: ⭐⭐
标准答案:
- 使用多阶段构建:编译阶段与运行阶段分离,最终镜像只包含运行时必要文件,大幅减小体积。
- 合理利用构建缓存:将变化频率低的指令(如
COPY requirements.txt+RUN pip install)放前面,变化频繁的(如COPY . .)放后面。 - 选择精简基础镜像:优先使用
alpine、slim变体而非完整版(如python:3.12-slim代替python:3.12)。 - 减少镜像层数:合并多条
RUN指令(RUN apt-get update && apt-get install -y ... && rm -rf /var/lib/apt/lists/*),并在同一层清理缓存。 - 使用
.dockerignore:排除.git、node_modules、__pycache__等,避免无关文件进入构建上下文。 - 避免以 root 运行:使用
USER指定非 root 用户运行应用。 - 固定基础镜像版本:使用
python:3.12.3-slim而非python:latest,确保构建可复现。
延伸思考: ADD 和 COPY 有什么区别?什么时候该用 ADD?(提示:ADD 支持远程 URL 和自动解压 tar,但语义不明确时优先用 COPY)
3. 什么是多阶段构建(Multi-stage Build)?它解决了什么问题?请举例说明
难度: ⭐⭐
标准答案:
多阶段构建允许在一个 Dockerfile 中使用多个 FROM 语句,每个 FROM 开始一个新阶段,可从前一阶段选择性拷贝产物。
解决的问题:
- 镜像体积膨胀:传统方式需在同一镜像中安装编译工具链(如 Go 编译器、Node 构建依赖),导致最终镜像包含大量无用文件。
- 安全性风险:编译工具和源码留在生产镜像中,增加攻击面。
示例(Go 应用):
# 阶段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 add --no-cache ca-certificates
COPY --from=builder /app/myapp /usr/local/bin/myapp
ENTRYPOINT ["myapp"]最终镜像从 ~1GB(含 Go 工具链)缩减到 ~10MB(仅 alpine + 二进制)。
延伸思考: 多阶段构建中如何调试中间阶段的产物?(提示:docker build --target builder -t debug-image . 可构建到指定阶段)
4. Docker 的网络模式有哪些?bridge、host、overlay 分别适用于什么场景?
难度: ⭐⭐
标准答案:
| 模式 | 原理 | 适用场景 |
|---|---|---|
| bridge | 默认模式。容器通过虚拟网桥(docker0)连接,每个容器分配独立 IP,通过 NAT 访问外部网络 | 单机多容器通信、需要端口映射的场景 |
| host | 容器直接使用宿主机网络栈,无网络隔离 | 对网络性能要求极高的场景(如高并发网关),或需要直接访问宿主机网络接口时 |
| overlay | 跨主机的虚拟网络,基于 VXLAN 隧道实现,需配合 Swarm 或独立 etcd/Consul | Docker Swarm 集群中跨主机容器通信、多主机微服务互联 |
其他常用模式:
- none:无网络,适用于安全敏感的离线计算任务
- macvlan:容器拥有独立 MAC 地址,直接接入物理网络,适用于需要被外部网络直接访问的场景
延伸思考: bridge 模式下容器间通信的性能瓶颈在哪?如何优化?(提示:docker0 的 NAT 转发、iptables 规则匹配开销,可考虑 macvlan 绕过网桥)
5. Docker 数据卷(Volume)与绑定挂载(Bind Mount)有什么区别?生产环境应如何选择?
难度: ⭐⭐
标准答案:
| 特性 | Volume | Bind Mount |
|---|---|---|
| 存储位置 | Docker 管理(/var/lib/docker/volumes/) | 宿主机任意路径 |
| 创建方式 | docker volume create 或 Dockerfile VOLUME | -v /host/path:/container/path |
| 生命周期 | 独立于容器,需显式删除 | 依赖宿主机目录 |
| 可移植性 | 高,跨平台一致 | 低,依赖宿主机目录结构 |
| 权限管理 | Docker 自动处理 | 可能遇到 UID/GID 映射问题 |
| 备份 | docker run --rm -v vol:/data busybox tar czf /backup/data.tar.gz /data | 直接备份宿主机目录 |
生产建议:
- 优先使用 Volume:数据库持久化、配置文件、日志等场景,由 Docker 统一管理更安全可靠
- Bind Mount 适用于:开发环境代码热更新(挂载源码目录)、需要精确控制宿主机路径的场景
- tmpfs Mount:敏感临时数据(如密码、session),不落盘,容器停止即消失
延伸思考: 容器集群(Swarm/K8s)中如何处理数据卷的跨节点共享?(提示:分布式存储如 NFS、Ceph、GlusterFS,或使用 CSI 插件)
6. 如何使用 Docker Compose 编排一个包含 Web + Redis + PostgreSQL 的多层应用?请写出核心配置并说明关键指令
难度: ⭐⭐
标准答案:
version: "3.8"
services:
web:
build: .
ports:
- "8000:8000"
environment:
- DATABASE_URL=postgresql://app:secret@db:5432/appdb
- REDIS_URL=redis://cache:6379/0
depends_on:
db:
condition: service_healthy
cache:
condition: service_started
restart: unless-stopped
db:
image: postgres:16-alpine
environment:
POSTGRES_USER: app
POSTGRES_PASSWORD: secret
POSTGRES_DB: appdb
volumes:
- pgdata:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U app"]
interval: 5s
timeout: 5s
retries: 5
cache:
image: redis:7-alpine
command: redis-server --appendonly yes
volumes:
- redisdata:/data
volumes:
pgdata:
redisdata:关键点:
depends_on+healthcheck:确保数据库就绪后再启动 Web,避免连接失败- 命名 Volume 持久化数据库和缓存数据
- 服务间通过服务名(
db、cache)互相访问,Docker 内置 DNS 解析
延伸思考: depends_on 只保证启动顺序,如何确保应用在依赖服务真正可用后才开始连接?(提示:应用层重试机制 + healthcheck + condition: service_healthy)
7. Docker 容器安全有哪些关键实践?如何防止容器逃逸?
难度: ⭐⭐⭐
标准答案:
镜像安全:
- 使用可信基础镜像(官方镜像、Docker Scout 扫描漏洞)
- 定期更新镜像,修复已知 CVE
- 不在镜像中硬编码密钥(使用 Docker Secrets 或外部密钥管理)
运行时安全:
- 禁止以 root 运行:
USER nobody或指定非零 UID - 只读文件系统:
docker run --read-only,配合--tmpfs挂载临时目录 - 限制能力:
--cap-drop ALL --cap-add NET_BIND_SERVICE,仅授予必要 Linux 能力 - 禁用特权模式:避免
--privileged,这是容器逃逸的主要入口 - 资源限制:
--memory、--cpus、--pids-limit防止 DoS - AppArmor/SELinux:启用强制访问控制策略
- Seccomp:限制容器可用的系统调用(Docker 默认启用 Seccomp Profile)
防止容器逃逸核心措施:
- 不挂载 Docker Socket(
/var/run/docker.sock) - 不挂载宿主机敏感目录(
/、/etc、/proc、/sys) - 不使用
--privileged和--pid=host - 使用 User Namespace 隔离(
--userns-remap) - 保持 Docker 版本更新,关注 runc 等运行时安全补丁
延伸思考: 什么是 User Namespace?它如何映射容器内 root 到宿主机非特权用户?(提示:/etc/subuid、/etc/subgid 配置映射范围)
8. 如何对 Docker 容器进行性能调优?请从 CPU、内存、I/O、网络四个维度说明
难度: ⭐⭐⭐
标准答案:
CPU 调优:
--cpus=2.5:限制容器最多使用 2.5 个 CPU 核心--cpuset-cpus=0-3:绑定容器到指定 CPU 核心,减少上下文切换--cpu-shares=512:设置 CPU 时间分配权重(相对值,非绝对限制)
内存调优:
--memory=2g --memory-swap=2g:限制内存和 swap(swap=memory 表示禁用 swap)--memory-reservation=1g:软限制,内存紧张时才生效--oom-kill-disable:禁止 OOM Killer 杀容器(⚠️ 慎用,可能导致宿主机不可用)- 使用
tmpfs替代磁盘 I/O 临时数据:--tmpfs /tmp:size=100m
I/O 调优:
--device-write-bps /dev/sda:10mb:限制块设备写入速率- 使用 Volume 而非 Bind Mount(Volume 使用 Docker 优化的存储驱动)
- 选择合适的 Storage Driver(overlay2 > devicemapper)
- 数据库类容器考虑直接挂载物理磁盘(
--device)
网络调优:
- host 模式消除 NAT 开销(适合高并发网关)
- 调整
net.core.somaxconn等内核参数 - 使用
--dns指定高性能 DNS,减少解析延迟
监控工具: docker stats、cAdvisor、Prometheus + Grafana
延伸思考: --cpu-shares 和 --cpus 的本质区别是什么?为什么 --cpu-shares 在宿主机空闲时不起作用?(提示:shares 是相对权重,只在资源竞争时生效;cpus 是绝对上限)
9. 如何有效减小 Docker 镜像体积?请给出具体策略和工具
难度: ⭐⭐
标准答案:
策略层面:
- 多阶段构建:构建阶段与运行阶段分离,只拷贝最终产物(效果最显著)
- 选择精简基础镜像:
alpine(~5MB)>slim(~70MB)> 默认(~900MB) - 合并 RUN 指令:
RUN apt-get update && apt-get install -y pkg && rm -rf /var/lib/apt/lists/* - 清理构建缓存:pip 缓存
--no-cache-dir、npm 缓存npm cache clean --force - 使用
.dockerignore:排除.git、node_modules、测试文件等 - 使用 distroless 镜像:Google distroless 仅包含应用运行时,无 shell 和包管理器
工具层面:
docker image history <image>:查看每层大小,定位臃肿层dive(开源工具):交互式分析镜像层内容,发现浪费空间docker-slim:自动分析容器运行时依赖,生成最小化镜像(可从 400MB 压缩到 20MB)trivy:扫描镜像漏洞的同时也能发现无用包
对比示例:
node:20 → 1.1GB
node:20-slim → 230MB
node:20-alpine → 180MB
多阶段 + alpine → ~50MB
distroless → ~30MB延伸思考: alpine 镜像使用 musl libc 而非 glibc,可能带来什么兼容性问题?(提示:预编译二进制可能不兼容、DNS 解析行为差异、某些 Python C 扩展编译失败)
10. 如何搭建企业级 Docker 私有仓库?请对比主流方案并说明 CI/CD 集成方式
难度: ⭐⭐⭐
标准答案:
方案对比:
| 方案 | 特点 | 适用场景 |
|---|---|---|
| 官方 registry | 轻量,仅提供 push/pull,无 UI | 小团队、简单需求 |
| Harbor | 企业级:RBAC、镜像扫描、镜像签名、复制策略、审计日志 | 中大型团队、合规要求 |
| Nexus Repository | 支持多种包格式(Docker + Maven + npm) | 多语言制品统一管理 |
| 云厂商 ACR/ECR/GCR | 免运维、全球加速、与云生态深度集成 | 云原生部署 |
Harbor 搭建核心步骤:
# 1. 下载 Harbor 安装包
wget https://github.com/goharbor/harbor/releases/download/v2.10.0/harbor-online-installer-v2.10.0.tgz
tar xzf harbor-online-installer-*.tgz
# 2. 配置 harbor.yml
hostname: harbor.company.com
harbor_admin_password: <密码>
data_volume: /data/harbor
# 3. 安装(含 Trivy 镜像扫描)
./install.sh --with-trivy --with-chartmuseumCI/CD 集成流程:
# GitLab CI 示例
build-and-push:
stage: deploy
image: docker:24
services:
- docker:24-dind
script:
- echo "$HARBOR_PASSWORD" | docker login harbor.company.com -u "$HARBOR_USER" --password-stdin
- docker build -t harbor.company.com/project/app:$CI_COMMIT_SHORT_SHA .
- docker push harbor.company.com/project/app:$CI_COMMIT_SHORT_SHA
# 可选:推送 latest 标签
- docker tag harbor.company.com/project/app:$CI_COMMIT_SHORT_SHA harbor.company.com/project/app:latest
- docker push harbor.company.com/project/app:latest
only:
- main关键实践:
- 使用 HTTPS + 证书认证,禁止 HTTP 推送
- 启用镜像漏洞自动扫描(Trivy/Clair),阻止高危漏洞镜像部署
- 配置镜像保留策略,自动清理旧标签
- Robot Account 代替个人凭据用于 CI/CD 推送
- 多 Harbor 实例间配置复制策略实现异地容灾
延伸思考: 如何实现镜像的「不可变标签」策略,防止同一 tag 被覆盖导致生产环境不可复现?(提示:Harbor 支持开启 Immutability Rules,锁定特定 tag 不可被覆盖或删除)
本文档由阿虾 🦐 自动生成,祝面试顺利!