Docker 常见面试问题

Docker 常见面试问题 IT策士 10余年一线大厂经验专注 IT 思维、架构、职场进阶。我会在各个平台持续发布最新文章助你少走弯路。Docker 早已不是“加分项”而是现代软件开发、测试、部署的必备技能。面试官不会只问你“Docker 是什么”而是会顺着一条条命令、一层层镜像、一个个网络策略深挖下去直到触到你对内核隔离机制的理解。下面我以高频率、高区分度的真实面试题为线索配以可运行的例子和深度解读帮你构建完整的 Docker 知识地图。一、基础概念你究竟在“装”什么Q1Docker 容器和虚拟机到底区别在哪请从技术层面说明别只说“轻量”。很多候选人脱口而出“共享内核所以轻量”但当被追问“共享内核到底共享了什么”就卡住了。回答这个问题需要落到 Linux 内核特性上。例子 解读启动一个 Ubuntu 容器并查看它的“内核版本”dockerrun-itubuntu:22.04uname-r你会发现打印出的内核版本与宿主机完全相同——容器根本没有自己的内核它只是宿主机上一个被 namespace 隔离的进程组并通过 cgroups 限制资源。而虚拟机里的uname -r会是虚拟机自带的 Guest OS 内核由 Hypervisor 模拟完整硬件栈启动。更深入一点容器的“文件系统独立”靠的是联合文件系统OverlayFS进程隔离靠 PID namespace网络隔离靠 net namespace。你可以直接验证 namespacedockerrun-d--nameweb nginx# 获取容器内 nginx 主进程在宿主机的 PIDPID$(dockerinspect web--format{{.State.Pid}})# 查看该进程所在的 namespace 链接sudols-l/proc/$PID/ns/你会看到pid,net,mnt,uts,ipc,cgroup等 namespace 的符号链接它们指向与宿主机不同的 namespace 实例。这就是隔离的“原材料”。面试中能把namespace 做隔离、cgroup 做限制、UnionFS 做分层镜像这三个内核支柱讲清楚并给出验证命令基本直接通过基础关。二、镜像与分层你的镜像为什么这么大Q2写 Dockerfile 时为什么推荐把COPY或RUN apt-get update这类命令放在一起这考察你对镜像分层与缓存的理解。Docker 镜像由多个只读层堆叠而成每条 Dockerfile 指令RUN/COPY/ADD会生成一个新层。例子糟糕写法FROM ubuntu:22.04 RUNapt-getupdate RUNapt-getinstall-ynginx RUNapt-getinstall-ycurl优化写法FROM ubuntu:22.04 RUNapt-getupdate\apt-getinstall-ynginxcurl\rm-rf/var/lib/apt/lists/*为什么后者好层数减少三个 RUN 变一个减少元数据操作加速推拉镜像。缓存失效控制若分开写当你增加第四个RUN apt-get install -y vim时前面的apt-get update层缓存可能早已过期构建时会用旧缓存导致安装失败。合并后只要这条命令不变缓存一直有效。镜像体积在同一个 RUN 里清理apt缓存才能真正删除因为上一层产生的大文件即使在下层删除也只是用“白障文件”标记隐藏总镜像体积并不会变小。你也可以用docker history image直观看到每一层的大小。如果一个层是 120MB下一层“删除”了一个 100MB 文件镜像总大小并不会减少 100MB。这种“层叠文件系统”的会计方式是面试官最爱问的陷阱之一。三、Dockerfile 指令的深度较量Q3CMD和ENTRYPOINT的区别不要背定义给我一个场景必须用ENTRYPOINT。当面试官要场景你就给他一个封装 CLI 工具的例子。例子你想把自己的 Python 脚本打包成“命令行工具”让用户像这样使用dockerrun my-tool--inputdata.csv--outputresult.jsonDockerfile 应该这么写FROM python:3.11-slim COPY tool.py /usr/local/bin/tool RUNchmodx /usr/local/bin/tool ENTRYPOINT[/usr/local/bin/tool]CMD[--help]ENTRYPOINT定义了容器启动时必须执行的主程序。CMD提供默认参数可被docker run末尾的参数覆盖。如果只用CMD [/usr/local/bin/tool, --help]用户执行docker run my-tool --input data.csv时整个 CMD 会被替换成--input data.csvshell 找不到可执行文件直接报错。ENTRYPOINT保证了工具本体不变只追加或覆盖参数。更深一层ENTRYPOINT有两种形式exec 形式JSON 数组和 shell 形式字符串。exec 形式不会启动 shell所以无法使用环境变量替换但能正确接收 Unix 信号shell 形式会在/bin/sh -c下运行能展开变量但 PID 1 是 shell 而不是你的程序导致docker stop信号无法被程序优雅处理。生产环境必须用 exec 形式。四、网络容器间怎么“说话”Q4两个独立的自定义 bridge 网络中的容器如何通信为什么默认不能通Docker 的 bridge 网络默认通过 iptables 做了隔离。不同 bridge 网络之间不转发流量这是有意为之的安全边界。验证例子# 创建两个网络dockernetwork create net1dockernetwork create net2# 分别在两个网络中启动容器dockerrun-d--namec1--networknet1 alpinesleep3600dockerrun-d--namec2--networknet2 alpinesleep3600# 进入 c1 ping c2 的 IP先用 docker inspect 查 c2 IPdockerexecc1pingc2_ipPing 不通。因为宿主机的 iptables FORWARD 链默认丢弃了net1与net2之间的包。打通方案将一个容器同时接入两个网络dockernetwork connect net2 c1此时再 pingc2通了。因为c1在net2网桥上也获得了一个 IP路由可达。解读面试官要的不是“能通/不能通”而是你对Docker 网络模型的理解。Docker 使用 Linux bridge 和 iptables 构建网络虚拟化。每个自定义 bridge 网络对应宿主机一个 bridge 接口有独立的子网Docker daemon 会自动添加 iptables 规则只允许同网桥内的转发。你甚至可以用iptables -L -n在宿主机看到DOCKER-ISOLATION-STAGE-2链的存在。讲出这一步足以证明你不仅会用命令还知道它背后是标准的 Linux 网络栈。五、存储与数据持久化Volume 还是 Bind MountQ5数据库容器必须持久化数据该用 Volume 还是 Bind Mount为什么答案首选 Volume。例子 对比Bind mountdockerrun-v/home/user/mysql-data:/var/lib/mysql mysql:8Volumedockervolume create mysql-datadockerrun-vmysql-data:/var/lib/mysql mysql:8解读可移植性Volume 由 Docker 管理/var/lib/docker/volumes/不依赖宿主机特定路径跨平台迁移一致。Bind mount 绑定宿主机绝对路径换一台机器目录结构不同就挂。权限管理Volume 初始化时Docker 会将容器内目标目录的内容复制到空 Volume 中而 Bind mount 直接遮蔽可能导致容器找不到初始化的数据库文件从而启动失败或权限错误。性能Volume 绕过联合文件系统直接读写宿主机文件系统性能与 Bind mount 相同但 Volume 驱动可更换如 NFS、cloud 存储扩展性更强。面试中要特别提一点数据备份。Volume 的数据可以直接用临时容器打包dockerrun--rm-vmysql-data:/data-v$(pwd):/backup alpinetarczf /backup/mysql-backup.tar.gz-C/data.这种“用临时容器操作数据卷”的模式在自动化运维中非常优雅能体现你的工程思维。六、Docker Compose 与多服务编排Q6docker-compose.yml 中depends_on真的能保证服务 A 先于服务 B 完全就绪吗不能的话怎么解决depends_on只控制容器启动顺序不检查服务内部是否 ready。比如数据库容器启动了但 MySQL 进程还在做数据恢复应用容器已经尝试连接导致 crash loop。解决方案——带重试和健康检查version:3.8services: db: image: mysql:8 environment: MYSQL_ROOT_PASSWORD: root healthcheck: test:[CMD,mysqladmin,ping,-h,localhost]interval: 5s retries:10start_period: 30s app: build:.depends_on: db: condition: service_healthy现在depends_on增加了condition: service_healthy应用必须等到数据库 healthcheck 连续返回成功才会启动。如果没有 Compose v3.4 支持的条件等待还可以在应用启动脚本里使用wait-for-it.sh或在代码里实现带指数退避的重连逻辑。解读这个问题挖的是你对分布式启动依赖的工程理解。容器编排的启动顺序是经典陷阱面试官希望听到你如何将“容器启动”与“服务就绪”解耦——健康检查、重试机制、外挂初始化脚本这些才是生产级的答案。七、安全与资源限制Q7如何防止一个容器拖垮整台宿主机具体怎么限制容器共享内核若不限制资源一个 fork 炸弹或内存泄漏就能导致宿主机 OOM。必须使用 cgroups 的 Docker 封装进行限制。例子dockerrun-d--namerisky-app\--memory512m\--memory-swap1g\--cpus1.5\--pids-limit100\--ulimitnofile1024:2048\nginx--memory硬限制物理内存 512MB超过会触发 OOM。--memory-swap内存Swap 总量限制 1GB设成与--memory相同值则禁用 swap。--cpus限制只能使用 1.5 个 CPU 核心的计算时间。--pids-limit容器内最大进程数防止 fork 炸弹。--ulimit限制文件描述符数量防止耗尽宿主机 fd。解读不要只说“用--memory就行”。深入讲Docker 背后通过 cgroup v1 或 v2 写入对应的限制文件例如--memory最终体现为/sys/fs/cgroup/memory/docker/container-id/memory.limit_in_bytes的值。你可以进入宿主机的 cgroup 文件系统直接观察。还要提到 OOM 的调整--oom-score-adj可以在宿主机 OOM 时优先 kill 这个容器。这些细节会让面试官认为你是真正在生产中排过雷的人。八、镜像优化与多阶段构建Q8一个 Go 程序的 Docker 镜像从 900MB 降到 15MB你做了什么这是展示多阶段构建的经典题目。原始写法FROM golang:1.21 WORKDIR /app COPY..RUN go build-omyapp CMD[./myapp]Go 编译需要完整的工具链但运行只需要一个静态二进制文件和系统最小依赖。多阶段构建# 阶段一编译FROM golang:1.21-alpine AS builder WORKDIR /app COPY..RUNCGO_ENABLED0GOOSlinux go build-a-installsuffixcgo-omyapp.# 阶段二运行FROM scratch COPY--frombuilder /app/myapp /myapp EXPOSE8080ENTRYPOINT[/myapp]scratch是一个空镜像里面没有任何系统文件连 shell 都没有。你复制进去的静态二进制成为唯一文件。最终镜像体积只等于你的二进制大小~10-15MB且攻击面极小。解读多阶段构建让你只在需要时引入重型依赖编译器、SDK最终只拷贝必要的产物到干净镜像。面试中要强调每个FROM开始一个新阶段你可以给阶段命名AS builder后续用COPY --frombuilder引用。这正是解决“构建时依赖”和“运行时依赖”解耦的最佳实践。你也可以举前端例子Node.js 构建后只拷贝dist到 Nginx 镜像。思路一致。九、Docker Daemon 与调试Q9docker run后容器立刻退出你如何定位原因面试官在考你容器生命周期和调试三板斧。场景模拟dockerrun--rmmy-app# 无输出直接退出状态 Exited (0) 或 (1)解读 步骤查看退出日志docker logs container。如果是 Exited (1) 多半有错误输出。覆盖入口点调试如果应用本身没有输出你可以覆盖 ENTRYPOINT/CMD 直接启动 shell 进去排查dockerrun-it--entrypoint/bin/sh my-app在 shell 里手动执行启动命令看报错。检查退出码0 是正常退出1 是应用错误137 是被kill -9通常是 OOM 或docker stop超时强行杀掉139 是 segfault143 是优雅终止收到 SIGTERM。不删除容器不要用--rm保留 Exited 容器用docker inspect查看其状态、挂载、网络等详细信息。信号处理问题如果容器docker stop超过 10 秒才退出说明应用没正确处理 SIGTERM。这是 Dockerfile 中 CMD 用了 shell 形式导致的常见病改为 exec 形式即可解决。十、Docker 在 CI/CD 与生产中的模式Q10如何实现零停机部署描述一个基于 Docker 的滚动更新策略。这考察的是你如何在生产中使用容器。以 Docker Swarm 或 Kubernetes 为例即便你只提概念也能加分核心点健康检查新容器启动后必须经过healthcheck判定为 healthy 才能接入流量。滚动更新参数--update-parallelism 2 --update-delay 10s控制一次更新几个容器及批次间隔。反向代理配合 Nginx/Traefik 动态 reload先 weight 引流再摘除旧容器。回滚docker service rollback或者保留前一版本的镜像标签确保可秒级回退。简单编排示例Docker Swarm 模式dockerservicecreate\--nameweb\--replicas3\--update-parallelism1\--update-delay 10s\--update-order start-first\nginx:1.25更新时dockerserviceupdate--imagenginx:1.26 webDocker 会逐个替换副本新副本 healthy 后再替换下一个实现零停机。解读生产环境的部署不是“停旧启新”而是流量切换。你必须把应用设计成无状态、可并行运行配置外挂到环境变量或配置中心日志输出到 stdout/stderr 由 Docker 日志驱动收集这样容器才能像“牲畜”一样被随时替换。这种云原生的设计哲学是面试官判断候选人是否具备“大厂思维”的关键。十一、更多深度问题Q11Docker 的几种网络模式分别用在什么场景例子 解读bridge默认容器连接到docker0网桥通过 NAT 访问外网适合单机容器通信。host容器直接使用宿主机网络栈性能最高但端口冲突风险大适合高性能网络应用。none无网络用于极度安全要求的批处理任务。container共享另一个容器的网络命名空间常用于 sidecar 模式如网络代理。overlay跨主机容器通信Swarm/K8s 基础。创建 container 模式的例子dockerrun-d--nameweb nginxdockerrun-it--networkcontainer:web alpinesh这时 alpine 容器看到的网卡、IP 与 nginx 完全一样它们可以通过localhost通信。Q12自定义 bridge 网络和默认 bridge 网络的区别例子 解读默认 bridge (docker0) 不支持 DNS 自动解析容器间必须用 IP 通信自定义 bridge 网络内嵌 DNS 服务可以直接用容器名互访。测试dockernetwork create mynetdockerrun-d--nameweb--networkmynet nginxdockerrun--rm--networkmynet alpinepingweb# 成功换成默认 bridge 则ping web失败。面试中要强调 DNS 解析用的是内置的127.0.0.11内部 nameserver跨网络无法解析。Q13Overlay 网络如何实现跨主机容器通信例子 解读Overlay 网络在多个 Docker 宿主机之间创建一个二层网络利用 VXLAN 隧道封装数据包。初始化 Swarm 集群后创建 overlaydockernetwork create-doverlay--attachablemy-overlay容器发往另一台主机的包被 VXLAN 封装经宿主机物理网络传输对端拆封装后送入目标容器。这是 Docker Swarm 和 Kubernetes 的底座。面试时可提一下 VXLAN、control plane 和数据面的分离展示网络功底。Q14Docker 的日志驱动有哪些如何把容器日志发送到 ELK例子 解读默认日志驱动是json-file存储在/var/lib/docker/containers/id/id-json.log。若要集中采集可改用syslog、fluentd、journald、awslogs等。dockerrun --log-driverfluentd --log-opt fluentd-addresslocalhost:24224 nginx或全局配置/etc/docker/daemon.json{log-driver:fluentd,log-opts:{fluentd-address:localhost:24224}}搭配 Filebeat 或 Fluentd 可无侵入收集。但要注意切换日志驱动后docker logs命令可能无法查看历史日志。Q15Docker 存储驱动 Overlay2 的原理和优势是什么解读Overlay2 利用 Linux OverlayFS将多个目录层叠成一个视图。镜像层为lowerdir容器层为upperdir合并后为容器文件系统。相比 AUFSOverlay2 已是主线内核特性性能更好inode 消耗少。使用docker info | grep Storage Driver查看当前驱动。避免使用 devicemapper 等老驱动。Q16什么是 Docker Content Trust (DCT)有什么用例子 解读DCT 使用数字签名确保镜像来源和完整性。启用后docker pull只会拉取经过签名的镜像exportDOCKER_CONTENT_TRUST1dockerpull alpine:latest如果镜像未签名会拒绝拉取并报错。这防止镜像被中间人篡改。企业级安全必备。Q17如何扫描 Docker 镜像安全漏洞例子 解读使用docker scan基于 Snyk或第三方工具如 Trivydockerscan myapp:latest# 或trivy image myapp:latest扫描镜像内软件包版本对比 CVE 数据库输出漏洞等级。面试中应提到基础镜像选择 distroless 或 Alpine 可大幅减少攻击面CI 中集成扫描可做卡点。Q18除了多阶段构建还有哪些缩小镜像体积的方法例子 解读使用更小的基础镜像如alpine甚至scratch。合并 RUN 指令并清理临时文件。安装包时用--no-install-recommendsapt或--no-cacheapk。用.dockerignore排除.git、node_modules等。使用docker-slim等工具进行镜像瘦身。将多个镜像层 squash 成一层的实验性功能docker build --squash。Q19构建上下文过大导致docker build很慢怎么排查和优化解读构建前 Docker 会把整个上下文目录通常为项目根目录打包发给 Daemon。若目录中包含大量文件如node_modules、日志、数据会导致传输慢、镜像层膨胀。使用.dockerignore排除无关文件或调整 Dockerfile 路径dockerbuild-fdocker/Dockerfile-tapp.并确保上下文路径最小化。Q20docker stop有时要等 10 秒为什么怎么改例子 解读docker stop发送 SIGTERM等待应用优雅退出默认 10 秒超时后发 SIGKILL 强杀。若应用没正确处理 SIGTERM如 CMD 用 shell 形式就会持续到超时。修改等待时间dockerstop-t5container更根本的解决确保 Dockerfile 中CMD/ENTRYPOINT使用 exec 形式让应用直接成为 PID 1 接收信号。Q21如何安全地传递敏感信息给容器例子 解读不要用环境变量传密码会留在docker inspect和日志中。Swarm 模式使用secretsechodbpass|dockersecret create db_pass -dockerservicecreate--secretdb_pass myapp非 Swarm 可用 Docker Compose 的secrets定义或挂载只读文件Volume/Bind Mount 权限 400。运行时也可用 Vault 等外部密钥管理。Q22Docker Compose 中extends和profiles怎么用解读extends可复用其他 Compose 文件的服务定义已在新版本中用include替代。profiles则允许按需启动服务组services: debug: image: busybox profiles:[debug]正常docker compose up不会启动debug需显式指定dockercompose--profiledebug up这在开发、调试场景中隔离不同服务集合很有用。Q23--privileged和--device的区别什么时候避免用特权模式例子 解读--privileged赋予容器所有内核能力并解除设备限制等于把容器 root 几乎等同于宿主机 root极度危险。--device仅映射特定设备文件dockerrun--device/dev/video0:/dev/video0 mycam在生产中禁止--privileged改用细粒度的--cap-add添加所需内核能力遵循最小权限原则。Q24如何远程安全访问 Docker Daemon解读开启 TCP 端口监听dockerd-Htcp://0.0.0.0:2376-Hunix:///var/run/docker.sock\--tlsverify--tlscacertca.pem--tlscertserver-cert.pem--tlskeyserver-key.pem必须配置 TLS 双向认证避免暴露无保护的 API被植入挖矿等。客户端使用对应证书连接。Q25一个容器需要运行多个进程怎么办例子 解读容器设计哲学是单进程但现实中需要辅助进程如 cron、agent。可以使用tini作为 init 进程--init参数处理僵尸进程或用 supervisord 管理FROM ubuntu RUNapt-getupdateapt-getinstall-ysupervisor COPY supervisord.conf /etc/supervisor/conf.d/ CMD[/usr/bin/supervisord]更好的做法是拆分成多个容器通过共享卷或网络通信。Q26如何备份和恢复 Docker Volume 数据例子 解读备份用一个临时容器挂载 volume 和宿主机目录执行打包dockerrun--rm-vdb_data:/data-v$(pwd):/backup alpinetarczf /backup/backup.tar.gz-C/data.恢复dockerrun--rm-vdb_data:/data-v$(pwd):/backup alpinetarxzf /backup/backup.tar.gz-C/dataQ27容器内时间和宿主机不同步怎么办解读容器通常共享宿主机时钟但若没权限修改系统时间需要SYS_TIME能力。可挂载宿主机/etc/localtime解决时区dockerrun-v/etc/localtime:/etc/localtime:ro...或设置环境变量TZAsia/Shanghai依赖于镜像支持。但要注意这并不改变内核时钟仅影响用户态库。Q28排查 “no space left on device” 错误磁盘满了的路径解读可能原因容器日志过大、未清理的悬空镜像和卷。清理docker system prune -a --volumes查看磁盘占用docker system df日志限制在 daemon.json 中配置每个容器日志最大大小和轮转{log-driver:json-file,log-opts:{max-size:10m,max-file:3}}检查 overlay2 层是否堆积。Q29怎样构建多平台镜像amd64, arm64例子 解读使用docker buildxdockerbuildx create--namemybuilder--usedockerbuildx build--platformlinux/amd64,linux/arm64-tmyapp:multi-arch--push.buildx 利用 QEMU 模拟或交叉编译一次打出多架构镜像适合边缘计算和树莓派等场景。Q30Docker、containerd 和 runc 之间的关系是什么解读这是经典的容器运行时分层runcOCI 规范的低层运行时负责创建和运行容器基于 libcontainer。containerd管理容器的生命周期、镜像传输、存储为上层提供 gRPC 接口。docker守护进程通过 containerd 启动容器。Docker面向用户的高层平台包括 CLI、API、镜像构建、编排等。可以用命令docker info | grep -i runtime查看。Kubernetes 也可以跳过 Docker 直接通过 CRI 接 containerd这就是“弃用 Docker shim”的原因。这些问题覆盖了 Docker 面试的绝大部分核心点从基础概念、镜像优化、网络存储、安全限制到生产实践、问题排查和架构理解。每一个答案都包含可验证的示例和背后的原理帮你在面试中不仅有话说更说得深、说得准。想了解更多还可以去各个平台搜索「IT策士」一起升级 IT 思维