Appearance
本模块聚焦 Docker 容器化核心知识,从基础概念到镜像构建、网络、存储、安全等实战层面,全面考察候选人对容器技术的理解深度和实践经验。
Q1: Docker是什么?和虚拟机有什么区别? 「🟢 校招/初级」
考察点:考察容器基础概念,筛掉对容器化和虚拟化本质区别理解不清的人。
参考答案:
- Docker 是一个开源的容器化平台,用于将应用及其依赖打包到一个轻量级、可移植的容器中,实现"一次构建,到处运行"。
- 容器 vs 虚拟机的核心区别:
- 架构层面:虚拟机(VM)通过 Hypervisor 模拟完整的操作系统,每个 VM 有独立的内核;容器共享宿主机内核,在用户空间进行隔离。
- 启动速度:容器秒级启动,VM 需要分钟级。
- 资源占用:容器体积小(MB 级),资源利用率高;VM 体积大(GB 级),开销大。
- 隔离性:VM 是操作系统级别的强隔离;容器是进程级别的轻量隔离(Namespace + Cgroup)。
- 性能:容器接近原生性能,VM 有虚拟化层损耗。
架构对比:
VM 模式: App → Bins/Libs → Guest OS → Hypervisor → Host OS → Hardware
容器模式:App → Bins/Libs → Docker Engine → Host OS → Hardware追问延伸:
- Docker 利用了 Linux 的哪些内核特性实现隔离?(Namespace、Cgroup、UnionFS)
- 容器的隔离性是不是绝对安全的?有哪些容器逃逸的风险?
- 什么场景下更适合用 VM 而不是容器?
Q2: Docker的核心概念?镜像、容器、仓库 「🟢 校招/初级」
考察点:考察 Docker 三大核心概念的理解,筛掉对基础概念混淆的人。
参考答案:
镜像(Image):
- 是一个只读的模板,包含运行应用所需的代码、运行时、库、环境变量和配置文件。
- 采用分层存储(UnionFS),每一层都是只读的,通过层的叠加构建完整镜像。
- 类似于面向对象中的"类"。
容器(Container):
- 是镜像的运行实例,是镜像创建的运行态实体。
- 在镜像的只读层之上添加了一层可写层(Container Layer),所有文件修改都发生在可写层。
- 可以被创建、启动、停止、删除,容器删除后可写层数据也随之丢失。
- 类似于面向对象中的"对象/实例"。
仓库(Repository/Registry):
- 用于存放镜像的集中地,类似于代码的 Git 仓库。
- Docker Hub 是官方公共仓库,也可以搭建私有仓库(如 Harbor)。
- 一个仓库可以包含多个标签(Tag)的镜像,如
nginx:latest、nginx:alpine。
三者关系:从仓库拉取镜像 → 基于镜像创建容器 → 容器运行应用 → 容器提交后可生成新镜像 → 推送回仓库。
追问延伸:
- 镜像的分层机制有什么好处?(复用、节省空间、增量分发)
- 容器的可写层数据如何持久化?(数据卷 Volume)
- 什么是 Docker Registry?和 Docker Repository 有什么区别?
Q3: Dockerfile的最佳实践有哪些? 「🟡 中级」
考察点:考察镜像构建能力和工程化意识,筛掉只会写简单 Dockerfile、不关注构建效率和镜像质量的人。
参考答案:
- 多阶段构建(Multi-stage Build):
- 使用
FROM ... AS builder构建阶段编译代码,再用FROM运行阶段只拷贝产物。 - 最终镜像不包含编译工具和源码,大幅减小体积。
dockerfileFROM golang:1.21 AS builder WORKDIR /app COPY . . RUN go build -o myapp . FROM alpine:3.18 COPY --from=builder /app/myapp /usr/local/bin/ CMD ["myapp"] - 使用
- 合理利用构建缓存:
- 将变化少的指令(如依赖安装)放在前面,变化多的指令(如代码拷贝)放在后面。
- 先拷贝
package.json/go.mod再安装依赖,最后拷贝源码,避免每次代码变更都重新下载依赖。
- 减少镜像层数:
- 合并多个
RUN指令为一个,用&&连接,减少中间层。 - 清理缓存和临时文件在同一个
RUN指令中完成。
- 合并多个
- 使用 .dockerignore:
- 排除
.git、node_modules、日志等不需要的文件,加快构建速度、减小上下文体积。
- 排除
- 使用非 root 用户:
- 创建普通用户并切换,遵循最小权限原则,增强安全性。
dockerfileRUN adduser -D appuser USER appuser - 选择合适的基础镜像:
- 优先使用
alpine或distroless等精简镜像。 - 固定镜像版本标签,避免使用
latest导致的不可预期。
- 优先使用
- 设置合理的 WORKDIR、EXPOSE、CMD/ENTRYPOINT:
- 明确工作目录、暴露端口、启动命令,提升可读性和规范性。
追问延伸:
- 多阶段构建有哪些高级用法?(多 FROM 并行构建、COPY --from 跨阶段拷贝)
- 如何调试 Dockerfile 构建失败的问题?(查看中间层、--progress=plain、进入失败层排查)
- CMD 和 ENTRYPOINT 的区别是什么?
Q4: Docker的网络模式有哪些? 「🟡 中级」
考察点:考察容器网络知识,筛掉对网络模式一知半解、只会用默认 bridge 的人。
参考答案:
- bridge 模式(默认):
- Docker 创建一个
docker0虚拟网桥,容器通过 veth pair 连接到网桥上。 - 容器之间可以通过 IP 互相通信,外部通过端口映射访问容器。
- 适用于同一宿主机上的容器间通信,是最常用的模式。
- Docker 创建一个
- host 模式:
- 容器共享宿主机的网络命名空间,直接使用宿主机的 IP 和端口。
- 性能最好(没有 NAT 开销),但端口冲突风险高,隔离性差。
- 适用于对网络性能要求高的场景,如网络插件、监控 Agent。
- none 模式:
- 容器没有网络栈,只有 loopback 接口,完全隔离网络。
- 适用于高安全需求、不需要网络的计算任务。
- container 模式:
- 新创建的容器共享另一个已存在容器的网络命名空间。
- 两个容器之间可以通过
localhost通信。 - Kubernetes 的 Pod 就是基于此模式实现的(Pause 容器共享网络)。
- overlay 模式:
- 用于跨多台宿主机的容器通信,构建在底层网络之上的覆盖网络。
- 使用 VXLAN 技术实现,常见于 Docker Swarm 或 Kubernetes 集群网络。
- 适用于分布式应用、集群环境。
网络模式选择参考:
单机单容器通信 → bridge
极致网络性能 → host
跨主机集群 → overlay
Pod 内共享 → container
完全隔离 → none追问延伸:
- bridge 模式下容器是怎么访问外网的?(iptables NAT + 宿主机转发)
- 容器之间除了 IP 还能怎么通信?(自定义网络 + DNS 服务发现)
- Docker 的自定义网络和默认 bridge 有什么区别?(DNS 自动解析、更好的隔离性)
Q5: Docker的数据卷(Volume)有什么用?有几种类型? 「🟡 中级」
考察点:考察数据持久化方案,筛掉不理解容器数据管理、不知道如何持久化的人。
参考答案:
- 数据卷的作用:
- 数据持久化:容器删除后数据不丢失,解决容器可写层数据随容器销毁的问题。
- 数据共享:多个容器之间可以共享数据。
- 性能更好:直接操作宿主机文件系统,绕过 UnionFS。
- 备份和迁移方便。
- 三种数据卷类型:
- Bind Mount(绑定挂载):
- 将宿主机上的任意文件或目录挂载到容器中。
- 灵活,直接访问宿主机文件系统,但依赖宿主机目录结构,可移植性差。
- 适用于开发环境、配置文件挂载。
bashdocker run -v /host/path:/container/path ... - Volume(命名卷):
- 由 Docker 管理,存储在 Docker 的存储目录中(
/var/lib/docker/volumes)。 - 生命周期独立于容器,可以通过名称引用,便于共享和管理。
- 支持卷驱动(Volume Driver),可对接远程存储。
- 是生产环境推荐的持久化方式。
bashdocker run -v myvolume:/container/path ... - 由 Docker 管理,存储在 Docker 的存储目录中(
- tmpfs Mount(内存文件系统):
- 挂载到宿主机内存中,不写入磁盘,速度极快。
- 容器停止后数据丢失,适用于敏感数据临时存储、高性能缓存。
bashdocker run --tmpfs /run ...
- Bind Mount(绑定挂载):
- 数据共享方案:同一 Volume 挂载到多个容器;使用
--volumes-from继承其他容器的卷。
追问延伸:
- Volume 和 Bind Mount 应该怎么选?各有什么优缺点?
- 如何备份和恢复 Docker Volume?(启动临时容器挂载卷 + tar 备份)
- 什么场景下适合用 tmpfs?有什么限制?
Q6: 如何优化Docker镜像大小? 「🟡 中级」
考察点:考察镜像优化能力,筛掉不关注镜像体积、构建出来的镜像臃肿不堪的人。
参考答案:
- 多阶段构建(Multi-stage Build):
- 构建阶段使用完整开发镜像编译,运行阶段只拷贝二进制或产物。
- 去除编译工具、源码、中间产物,是最有效的优化手段。
- 选择精简的基础镜像:
alpine:基于 Alpine Linux,体积小(5MB 左右),包管理器完善,最常用。distroless:Google 出品,只包含应用和运行时依赖,无 shell 和包管理器,最安全最小。scratch:空镜像,适合静态编译的 Go 等语言。- 避免使用
ubuntu、centos等完整系统镜像作为基础。
- 合并 RUN 指令:
- 多个
RUN会产生多个镜像层,合并为一个RUN并清理缓存可减少层数和体积。
dockerfile# 不好的写法(3层,且缓存未清理) RUN apt-get update RUN apt-get install -y curl RUN apt-get clean # 好的写法(1层,清理缓存) RUN apt-get update && apt-get install -y curl && \ rm -rf /var/lib/apt/lists/* - 多个
- 清理缓存和不必要的文件:
- 包管理器缓存(
apt clean、yum clean、npm cache clean)。 - 删除文档、man 手册、临时文件、调试符号。
- 包管理器缓存(
- 使用 .dockerignore:
- 排除
node_modules、.git、测试文件等,避免不必要的文件进入构建上下文。
- 排除
- COPY 精准拷贝:
- 只拷贝需要的文件,避免
COPY . .把所有文件拷进去。
- 只拷贝需要的文件,避免
- 使用镜像压缩/瘦身工具:
docker-slim:自动分析并精简镜像。- 多架构镜像构建时只拉取需要的架构。
优化效果参考:
未优化 Node.js 镜像 → ~1GB
alpine 基础镜像 → ~300MB
多阶段构建 + alpine → ~100MB
distroless → ~50MB追问延伸:
- alpine 镜像有什么坑吗?(musl libc 兼容性问题、时区问题)
- 镜像层数是不是越少越好?层数有没有限制?(不是越少越好,要平衡缓存利用和体积;存储驱动有层数上限)
- 什么是 distroless 镜像?适合什么场景?
Q7: Docker容器安全有哪些注意事项? 「🔴 高级」
考察点:考察容器安全意识和实践经验,筛掉只关注功能、对安全毫无概念的人。
参考答案:
- 用户权限:
- 容器内避免使用 root 用户运行应用,Dockerfile 中创建普通用户并
USER切换。 - 设置合理的文件权限,避免敏感文件全局可读。
- 容器内避免使用 root 用户运行应用,Dockerfile 中创建普通用户并
- 资源限制(Cgroup):
- 限制 CPU(
--cpus、--cpu-shares)、内存(--memory、--memory-swap)。 - 防止单个容器占用过多资源影响宿主机和其他容器。
bashdocker run --cpus=2 --memory=512m --memory-swap=512m myimage - 限制 CPU(
- 只读文件系统:
- 使用
--read-only将容器根文件系统设为只读,防止恶意篡改。 - 需要写入的目录使用 tmpfs 或 Volume。
bashdocker run --read-only --tmpfs /tmp myimage - 使用
- 禁用特权模式:
- 避免使用
--privileged,该模式赋予容器几乎所有宿主机权限,风险极大。 - 确有需要时使用
--cap-add精确添加所需能力。
- 避免使用
- 镜像安全:
- 使用官方或可信来源的基础镜像,避免来路不明的镜像。
- 镜像扫描工具:Trivy、Clair、Docker Scout,检测 CVE 漏洞。
- 镜像签名验证(Docker Content Trust / Cosign),确保镜像未被篡改。
- 内核安全特性:
- seccomp:限制容器可调用的系统调用,减少攻击面。Docker 默认启用 seccomp 配置。
- AppArmor / SELinux:强制访问控制(MAC),限制容器的文件访问和操作。
- Linux Capabilities:精细化控制 root 权限,按需添加能力而非全给。
- 网络安全:
- 避免使用 host 网络模式,减少网络暴露面。
- 使用自定义网络隔离不同业务的容器。
- Docker 守护进程安全:
- 保护 Docker socket(
/var/run/docker.sock),防止未授权访问。 - 启用 TLS 认证的远程 API 访问。
- 保护 Docker socket(
追问延伸:
- 什么是容器逃逸?常见的容器逃逸方式有哪些?
- 如何在容器中运行需要 root 权限的应用但又保证安全?(cap-add 精确授权)
- Kubernetes 环境下的容器安全有哪些额外的措施?(Pod Security Standards、OPA/Gatekeeper)