Files
zhuangang/topics/devops/docker/interview.md
T

80 lines
6.3 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
---
id: devops.docker.interview
title: Docker 面试题
type: interview
domain: devops
stack: [docker]
tags: [Docker, 容器, Compose, 面试]
level: basic
related: [devops.docker.image-dockerfile, devops.docker.compose-network-volume]
week: [3, 14, 16]
---
# Docker 面试题
## 基础
### 【基础】容器和虚拟机有什么区别?
**参考答案**:虚拟机虚拟化硬件,每个虚拟机有自己完整的操作系统内核,隔离性强但启动慢、占用大;容器共享宿主机内核,靠 Linux 的 namespace 做隔离(进程、网络、文件系统等)、cgroups 限制资源,启动快、占用小,但隔离性比虚拟机弱。
**追问**:容器的隔离靠什么实现?→ Linux 的 namespace(隔离进程、网络、文件系统等视图)和 cgroups(限制 CPU、内存等资源);容器共享宿主机内核,隔离程度弱于虚拟机。
### 【基础】镜像和容器是什么关系?镜像为什么要分层?
**参考答案**:镜像是只读模板,容器是镜像运行起来的实例,在镜像之上加了一个可写层。分层让多个镜像可以共享相同的底层(节省磁盘和下载),构建时没有变化的层可以复用缓存。
**追问**:修改 Dockerfile 中间的一行,为什么后面的层都要重新构建?→ 每一层的缓存依赖它之前的所有层,前面的层变了,后面的缓存全部失效。所以把变化少的步骤(安装依赖)放在前面。
### 【基础】`CMD` 和 `ENTRYPOINT` 的区别?`COPY` 和 `ADD` 呢?
**参考答案**:`ENTRYPOINT` 指定容器启动时固定执行的程序;`CMD` 提供默认参数或默认命令,`docker run` 后面的参数会覆盖 `CMD`。两者都推荐使用 exec 形式(JSON 数组),程序作为 PID 1 才能收到信号。`COPY` 只复制本地文件;`ADD` 还能自动解压 tar 和下载 URL,行为不直观,一般用 `COPY`。
**追问**:`ENTRYPOINT` 用 shell 形式会有什么问题?→ shell 形式由 `/bin/sh -c` 启动,进程不是 PID 1,收不到 SIGTERM,无法优雅退出;应该使用 exec 形式(JSON 数组)。
### 【基础】容器里的数据怎么持久化?
**参考答案**:容器可写层的数据随容器删除而消失。持久化用数据卷(Docker 管理的命名卷,适合数据库)或绑定挂载(把宿主机目录挂进容器,适合开发时挂源码、挂配置文件)。
**追问**:命名卷和绑定挂载怎么选?→ 数据库数据用命名卷(由 Docker 管理,可移植);开发时挂载源代码用绑定挂载。
## 进阶
### 【进阶】怎么减小镜像体积、加快构建?
**参考答案**:多阶段构建,运行阶段只保留产物;选小的基础镜像(slim、alpine、distroless);把变化少的步骤(安装依赖)放前面,充分利用层缓存;合并 RUN 并在同一层清理缓存(如 `pip install --no-cache-dir`);用 `.dockerignore` 排除无关文件。
**追问**:多阶段构建为什么能减小体积?→ 编译工具和中间产物留在构建阶段,最终镜像只复制编译结果,基础镜像也可以用更小的 distroless / alpine。
### 【进阶】`docker stop` 时发生了什么?怎么做优雅退出?
**参考答案**:Docker 向容器的 PID 1 发送 SIGTERM,等待超时(默认 10 秒)后发送 SIGKILL。程序要监听 SIGTERM,停止接受新请求、处理完进行中的请求、关闭连接后退出。前提是程序本身是 PID 1(exec 形式启动),否则信号被 shell 吞掉。
**追问**:程序没有处理 SIGTERM 会怎样?→ 等待超时(默认 10 秒)后被 SIGKILL 强制杀掉,正在处理的请求会中断。
### 【进阶】Compose 中服务之间怎么通信?`depends_on` 能保证依赖服务已就绪吗?
**参考答案**:同一个 Compose 项目的服务在同一个网络里,用服务名作为主机名访问。`depends_on` 默认只保证启动顺序,不保证就绪;要配合 `healthcheck` 和 `condition: service_healthy`,应用本身也要有连接重试。
**追问**:怎么让应用等数据库真正就绪?→ 数据库配置 `healthcheck`,应用的 `depends_on` 使用 `condition: service_healthy`;应用本身也要有连接重试。
## 场景题
### 【场景题】容器启动后马上退出,怎么排查?
**参考答案**:`docker ps -a` 看退出码,`docker logs <容器>` 看输出。常见原因:主进程是后台进程(容器以前台进程为生命周期)、配置或环境变量缺失导致程序报错退出、二进制和基础镜像不兼容(CGO 程序放进 scratch)、权限问题(非 root 用户无权读文件)。可以用 `docker run -it --entrypoint sh 镜像` 进入容器手动执行排查。
**追问**:容器反复重启,日志一闪而过怎么办?→ 已退出的容器日志仍然可以用 `docker logs 容器ID`(`docker ps -a` 找到 ID)或 `docker compose logs 服务名` 查看;`docker inspect` 看退出码;也可以临时把启动命令改成 `sleep infinity` 进入容器手动启动程序排查。
### 【场景题】应用容器连不上数据库容器,怎么排查?
**参考答案**:确认连接地址用的是服务名而不是 localhost;两个容器是否在同一网络(`docker network inspect`);数据库是否真的就绪(健康检查、日志);端口是否是容器内端口而不是宿主机映射端口;用户名密码和数据库名是否和初始化时一致(数据卷已存在时环境变量不会重新生效)。
**追问**:容器里的 `localhost` 指的是什么?→ 指容器自己,不是宿主机也不是其他容器;连接其他容器要用服务名,连接宿主机的方式随平台不同(如 `host.docker.internal`)。
### 【场景题】怎么避免把密钥打包进镜像?
**参考答案**:不在 Dockerfile 里写密钥,不把 `.env` 等文件复制进镜像(`.dockerignore` 排除);运行时通过环境变量或密钥管理系统(Kubernetes Secret、云厂商的密钥服务)注入;构建时确实需要的密钥(如私有依赖仓库的 token)用 BuildKit 的 `--secret` 挂载,不会留在镜像层里;镜像推送前用扫描工具检查。
**追问**:构建时需要私有仓库的令牌怎么办?→ 使用 BuildKit 的 `--secret` 挂载,令牌只在构建步骤中可见,不会写入镜像层。