一、 深入灵魂的拷问:Docker 的底层原理是什么?
面试官如果问:“Docker 到底是怎么实现隔离和资源限制的?” 千万别只回答“它是一个轻量级虚拟机”。Docker 的本质其实是运行在宿主机上的一个普通进程,它只是利用了 Linux 内核的三大核心技术来给自己“套上了一层壳”。
1. Namespace(命名空间):实现“隔离”
Namespace 是 Linux 提供的一种内核级别环境隔离的方法。Docker 利用它实现了六项隔离:
- PID Namespace:隔离进程号(所以你在容器里看到的 PID 经常是 1)。
- Mount Namespace:隔离文件系统挂载点。
- Network Namespace:隔离网络设备、IP、端口(每个容器有自己的网卡)。
- UTS Namespace:隔离主机名和域名。
- IPC Namespace:隔离进程间通信。
- User Namespace:隔离用户和用户组。
2. Cgroups(控制组):实现“资源限制”
如果没有 Cgroups,一个容器里的死循环程序可能会耗尽宿主机的所有 CPU 和内存。Cgroups 的作用就是给容器设定配额
- 限制 CPU 的使用率和核数。
- 限制内存的最大使用量(如果超出,容器会被内核的 OOM Killer 强制杀死)
- 限制磁盘 I/O 读写速度。
3. UnionFS(联合文件系统):实现“分层镜像”
为什么 Docker 镜像下载这么快,而且复用率这么高?因为它使用了联合文件系统。
镜像是由一层层只读层(Readonly Layer)叠加而成的。当容器启动时,Docker 会在最顶层挂载一个可读写层(Writable Layer)。你在容器里做的所有修改(比如新建文件、修改配置),都只保存在这个读写层中,底层的镜像文件原封不动。
二、 必考网络模型:容器之间是怎么通信的?
大厂非常看重网络基础。Docker 默认提供了四种网络模式,必须烂熟于心:
| 网络模式 | 核心特点 | 适用场景 |
| Bridge (桥接) | 默认模式。Docker 会创建一个虚拟网桥 docker0,为每个容器分配内网 IP。容器间通过网桥通信,与外网通过 NAT 转换通信。 | 大多数普通的单机容器部署。 |
| Host (主机) | 容器不创建自己的网络隔离,直接使用宿主机的 IP 和端口。性能最高,但容易发生端口冲突。 | 对网络性能要求极高的服务。 |
| None (无网络) | 容器有独立的网络空间,但不进行任何网络配置(只有本地环回网卡 lo)。 | 极度追求安全,完全断网隔离的计算任务。 |
| Container (容器) | 新创建的容器和一个已经存在的容器共享网络 IP 和端口。 | Kubernetes (K8s) 中的 Pod 就是基于这个模式实现的! |
三、 生产级实战:写出大厂级别的 Dockerfile
在企业级 Java 开发中,随便打包一个包含 JDK 和 Spring Boot 完整环境的镜像可能高达 800MB 甚至 1GB。面试官经常会问:“你怎么优化 Docker 镜像的体积和构建过程?”
1. 使用多阶段构建(Multi-stage Builds)
不要在最终镜像里打包 Maven 和源代码!分阶段进行:
Dockerfile
# 第一阶段:编译打包(使用包含 Maven 的重度镜像)
FROM maven:3.8-openjdk-11 AS builder
WORKDIR /app
COPY pom.xml .
COPY src ./src
RUN mvn clean package -DskipTests
# 第二阶段:运行环境(使用极其轻量的 JRE 镜像)
FROM openjdk:11-jre-slim
WORKDIR /app
# 只从上一阶段把编译好的 jar 包拷过来,丢弃所有源码和 Maven 缓存
COPY --from=builder /app/target/my-backend-app.jar ./app.jar
# 启动命令
ENTRYPOINT ["java", "-jar", "app.jar"]
2. 警惕 JVM 在容器中的内存陷阱 (OOM 刺客)
重点防御: 以前老版本的 JDK(Java 8 早期)无法识别 Docker 的 Cgroups 限制,它会读取宿主机的物理内存来设置自己的堆内存(Heap Size)。如果宿主机有 64G 内存,JVM 默认会分配 16G 堆,但这远远超出了 Docker 容器比如限制的 2G 内存,导致容器频繁被宿主机内核 Kill 掉。
解决办法:
在启动参数中明确指定内存限制,或者确保使用较新的 JDK 版本,并开启容器感知参数:
java -XX:+UseContainerSupport -XX:MaxRAMPercentage=75.0 -jar app.jar
四、 常见高频面试“连环炮”
Q1:Docker 和 虚拟机(VMware/VirtualBox)到底有什么本质区别?
- 虚拟机:在宿主机上虚拟出完整的硬件,然后运行一个完整的 Guest OS(客户机操作系统),再在上面跑应用。启动慢(分钟级),占用资源极大。
- Docker:没有虚拟化硬件,也没有自己的操作系统,它直接复用宿主机的 Linux 内核。启动快(秒级),极其轻量。
Q2:如果我把一个容器删除了,里面的数据还在吗?
不在了。容器的顶层可写层会随着容器的删除而消失。所以数据库数据、重要的日志和配置文件,必须使用 Volume(卷)或者 Bind Mounts 挂载到宿主机文件系统上。
Q3:什么是悬空镜像(Dangling Image)?怎么清理?
在构建镜像时,如果新镜像占用了原有的镜像标签(Tag),旧镜像就会变成没有名字、没有标签的 <none>:<none> 镜像。它们会白白占用磁盘空间。
清理命令:docker image prune
Comments NOTHING