3460 字
17 分钟
Docker 八股

基础概念#

Docker 是什么? 容器化平台,将应用及其依赖打包成轻量级、可移植的容器,实现操作系统级虚拟化。

为什么容器被称为”一次构建,到处运行”? 镜像把应用和它的所有依赖(库、运行时、配置)一起打包,容器运行时只依赖宿主机内核,因此只要内核兼容,任何装了 Docker 的机器行为都一致,消除了”环境不一致”问题。

Docker 镜像与容器的关系? 镜像是只读模板(代码+依赖+环境),容器是镜像的运行实例(可读写层)。

Docker 的三大核心是什么? 镜像(Image)、容器(Container)、仓库(Repository)。

Docker 工作原理是什么? Docker 使用客户端-服务器架构。Docker 客户端与 Docker 守护进程通信,后者负责构建、运行和分发 Docker 容器的繁重工作。

Docker 组成部分?

  1. docker client / CLI,客户端/命令行工具,为用户提供一系列可执行命令,用户用这些命令实现跟 docker daemon 交互;
  2. docker daemon,守护进程,在宿主主机后台运行,等待接收来自客户端的请求消息;
  3. docker build,用于构建镜像,使用 BuildKit 作为构建器;
  4. docker container,容器,一个系统级别的服务,拥有自己的 ip 和系统目录结构。

Docker 容器与虚拟机的本质区别? 虚拟机通过 Hypervisor 虚拟化硬件,每个 VM 运行独立 Guest OS(GB 级、分钟级启动);Docker 共享宿主机内核,进程级隔离(MB 级、秒级启动)。

容器是一种轻量级虚拟机吗? 不是。容器本质就是宿主机上被 namespace 隔离、被 cgroup 限流的普通进程,与宿主共享同一个内核,所以:

  • 优势是启动快、开销小、密度高;
  • 代价是隔离性弱于虚拟机,内核漏洞会影响所有容器,且无法运行不同内核版本(如 Linux 上跑 Windows 容器)。

Dockerfile / 镜像构建#

Dockerfile 中常用指令?

  • FROM(基础镜像)
  • COPY/ADD(复制文件)
  • RUN(构建时执行)
  • CMD(默认命令)
  • ENTRYPOINT(入口点)
  • EXPOSE(声明端口,仅文档作用)
  • WORKDIR(工作目录)
  • ENV(环境变量)
  • ARG(构建参数,构建后可丢弃)
  • USER(运行用户)
  • VOLUME(声明匿名卷)
  • LABEL(元数据)
  • HEALTHCHECK(健康检查)
  • ONBUILD(被继承时触发)

CMD 和 ENTRYPOINT 的区别?

  • CMD 可被 docker run 命令覆盖;
  • ENTRYPOINT 不可覆盖,适合固定执行程序。
  • 两者可组合使用:ENTRYPOINT 定可执行文件,CMD 提供默认参数。

ADD 和 COPY 的区别?

  • COPY 只复制本地文件;
  • ADD 支持自动解压 tar 和远程 URL 下载。
  • 官方推荐用 COPY(行为更可预期)。

RUN、CMD、ENTRYPOINT 的区别?

  • RUN 在构建时执行并把结果固化进镜像层;
  • CMD/ENTRYPOINT 在容器启动时执行,写进镜像元数据,只有最后一条生效。

RUN 命令 shell 形式和 exec 形式的区别?

  • RUN apt-get update(shell)等价于 /bin/sh -c "...",能使用变量和管道,但会多一层 shell 进程;
  • RUN ["apt-get","update"](exec)直接执行,PID 1 就是目标进程,能正确接收信号。
  • CMD/ENTRYPOINT 推荐用 exec 形式,否则 docker stop 的 SIGTERM 会被 sh 吞掉,导致容器等 10 秒后被杀(退出码 137)。

ARG 和 ENV 的区别?

  • ARG 只在构建期有效,容器运行时拿不到(可用 --build-arg 传入,不应放敏感信息,docker history 能看到);
  • ENV 会写入镜像元数据,构建期和运行期都可见。

如何减小 Docker 镜像体积 / 加速镜像构建?

  • .dockerignore 排除非必要文件(.gitnode_modules);
  • 使用轻量级基础镜像(如 alpine/slim、distroless);
  • 编译型语言采用多阶段构建;
  • 合并 RUN 命令减少层数;
  • 清理 apt/pip/npm 缓存(且必须与安装写在同一层,否则删除只是标记,体积不减);
  • 把变化频率低的指令放前面,充分利用层缓存;
  • BuildKit 的 --mount=type=cache 缓存包管理器目录。

多阶段构建的好处? 最终镜像只保留运行时所需文件,不包含编译工具和中间产物,大幅减小镜像体积,同时降低攻击面(工具链不进生产镜像)。

镜像分层存储的好处? 共享和缓存——多个镜像共享同一基础层只存一份;构建时缓存不变层,加快构建速度。写时容器只在其上叠加一个可写层(Copy-on-Write),多个容器可共享同一个只读镜像层。

镜像构建缓存机制? Docker 逐条指令比对缓存,某条指令的命令文本、父层、以及 COPY/ADD 的文件内容校验和任一变化,该层及其后续所有层缓存全部失效,需要重新构建。所以:

  • 先把 package.json 复制进去装依赖,再复制源码,改动代码时不会重装依赖;
  • --no-cache 强制不用缓存,--cache-from 复用 CI 中拉取的镜像缓存。

HEALTHCHECK 的作用? 在容器内定时执行命令检查应用是否健康(docker ps 显示 healthy/unhealthy),仅状态标记,Docker 本身不会自动重启 unhealthy 容器(这正是 K8s 探针要解决的问题)。

网络与存储#

如何让容器随 Docker 服务自动重启? --restart=always--restart=unless-stopped,还有 on-failure[:max-retries]。注意 docker stop 手动停止的容器在 always 下仍会随 daemon 启动。

Docker 数据持久化方式? Volume(docker volume create)、Bind Mount(挂载宿主机目录)、tmpfs(内存中)。

Volume 和 Bind Mount 的区别?

  • Volume:由 Docker 管理,存放在 /var/lib/docker/volumes/,跨平台、可备份/迁移、可用 volume driver 接 NFS;适合生产数据。
  • Bind Mount:直接映射宿主机任意路径,路径依赖宿主机结构,权限与 SELinux 容易出问题;适合开发时挂代码、挂配置文件。
  • 两者用 docker inspect 的 Mounts 字段区分(Type 为 volume 或 bind)。
  • 容器内数据不持久化的场景:不挂载时数据留在容器可写层,容器删除即丢失

容器网络隔离方式? 存在以下网络模式:

  • Bridge 模式:默认模式,容器通过虚拟网桥 docker0 通信,拥有独立的网络命名空间。
  • Host 模式:容器与宿主机共享网络命名空间,直接使用宿主机的 IP 和端口,性能较高但缺乏隔离。
  • None 模式:容器没有网络配置,适用于无需网络的场景。
  • Container 模式:多个容器共享一个网络命名空间,适合需要紧密协作的容器。
  • Overlay / Macvlan:跨主机通信(Swarm)或让容器直接获得物理网段 IP。

容器之间如何互相通信? 默认 bridge 网络下只能用 IP 互访,且没有 DNS 解析。推荐自定义 bridge 网络:docker network create app-net,加入同一网络的容器可直接用容器名 / --network-alias 作为主机名互访(Docker 内置 DNS 127.0.0.11)。

端口映射的原理? -p 8080:80 由 docker-proxy 与 iptables 的 DNAT 规则实现,把宿主机 8080 的流量转发到容器 IP 的 80 端口。-P 随机映射所有 EXPOSE 端口,-p 127.0.0.1:8080:80 只监听本机。

容器#

如何进入容器?

Terminal window
docker exec -it <容器> /bin/sh # 推荐:新开一个进程进入
docker attach <容器> # 进入主进程的标准输入输出(Ctrl+C 会杀掉容器)
docker exec -it -u root <容器> bash # 指定用户

exec 会在容器内新起一个进程(需镜像里有 shell,distroless 镜像没有);调试特权容器还可用 nsenter -t <PID> -a 直接进入其命名空间(宿主机上无需容器内有工具)。

exec 和 attach 的区别?

  • exec 启动一个新的进程,退出不影响主进程;
  • attach 接管容器 PID 1 的标准流,退出(Ctrl+C)可能终止容器,且多个 attach 会共享输出。

如何查看容器日志?

Terminal window
docker logs -f --tail 100 <容器> # 实时跟踪最近 100 行(-f 实时跟踪)
docker logs --since 10m <容器> # 最近 10 分钟
docker logs --timestamps <容器>

日志来自容器主进程的 stdout/stderr,由 json-file 日志驱动落盘到 /var/lib/docker/containers/<id>/*-json.log默认无大小限制,容易把磁盘写满,需配置 --log-opt max-size=10m --log-opt max-file=3(可在 /etc/docker/daemon.json 全局设置)。应用应把日志打到前台而不是文件里,这是容器的最佳实践。

如何查看容器信息与资源占用?

  • docker ps -a(状态与端口)
  • docker inspect(完整 JSON,含 IP、挂载、退出码)
  • docker stats(实时 CPU/内存/网络)
  • docker top(容器内进程)
  • docker diff(可写层的文件变更)

容器退出码代表什么? 0 正常退出;1 应用错误;126/127 命令不可执行/不存在;137 = 128+9 被 SIGKILL(OOM 或 docker kill);143 = 128+15 被 SIGTERM(docker stop 优雅停止)。

镜像仓库#

如何推送镜像到仓库? docker logindocker tag app:1.0 registry.example.com/ns/app:1.0(必须带仓库地址前缀)→ docker push。拉取时同理,私有仓库需先登录或配置凭证。

镜像的命名规则? [registry/][namespace/]repository[:tag][@digest],tag 默认 latest。生产禁止依赖 latest,因为它是可变的,不同节点可能拉到不同版本;用不可变 tag(版本号或 commit id)或 digest 保证一致性。

如何减小镜像拉取时间? 使用更小的基础镜像、减少层数、把 registry 部署在内网、开启镜像加速器、使用 P2P 分发(Dragonfly)或按需拉取(懒加载)。

Docker Compose#

Docker Compose 是什么? 单机多容器编排工具,用一个 docker-compose.yml 声明多个服务、网络、卷和依赖关系,一条 docker compose up -d 全部拉起。适合本地开发与小型部署;跨主机的生产编排应使用 Kubernetes/Swarm。

Compose 常用命令? up -d(后台启动)、down(停止并删除)、pslogs -fexecbuildrestartconfig(校验并渲染最终配置)。

Compose 中 depends_on 能保证依赖服务就绪吗? 不能,它只保证启动顺序。要等待服务真正可用需配合 healthcheck + condition: service_healthy,或在应用内做重试。

常用命令#

Terminal window
# 镜像
docker build -t app:1.0 . # 构建
docker images / docker image prune # 列出 / 清理悬空镜像
docker tag / push / pull # 打标签与仓库交互
# 容器
docker run -d --name web -p 8080:80 -v data:/usr/share/nginx/html nginx
docker ps -a / start / stop / restart / rm
docker cp file.txt web:/tmp/ # 与容器互拷文件
docker commit web web:snapshot # 把容器存成镜像(不推荐,应用 Dockerfile)
# 清理
docker system df # 查看磁盘占用
docker system prune -a --volumes # 清理未使用镜像/网络/卷

docker run 常用参数? -d 后台运行、-it 交互式 TTY、--name 命名、-p 端口映射、-v 挂载、-e 环境变量、--network 网络、--restart 重启策略、--rm 退出即删除、--memory/--cpus 资源限制、--privileged 特权、-w 工作目录、--entrypoint 覆盖入口。

底层原理#

Docker 使用的底层技术? Namespaces(隔离:PID、NET、IPC、UTS、MNT、USER)、Cgroups(资源限制:CPU、内存)、UnionFS(分层存储)。

各个 Namespace 隔离了什么?

  • PID:进程号,容器内 PID 1 是应用进程;
  • NET:网卡、IP、端口、路由表;
  • IPC:信号量、共享内存;
  • UTS:主机名与域名;
  • MNT:挂载点与文件系统视图;
  • USER:用户与 UID 映射;
  • Cgroup:cgroup 视图(较新)。

Cgroups 的作用? 限制、统计和隔离进程组的资源使用(CPU、内存、IO、PID 数量)。CPU 用 quota/period(--cpus)或 shares(--cpu-shares 相对权重);内存超限触发 OOM Killer,容器退出码 137。

Docker 的分层架构(与 containerd / runc / OCI 的关系)? dockerd(接收 CLI 请求、构建、镜像管理)→ containerd(容器生命周期管理,K8s 也直接用它)→ runc(真正调用 Linux namespace/cgroup 创建进程)。镜像格式遵循 OCI Image Spec,运行时遵循 OCI Runtime Spec,因此 Docker 构建的镜像可以被 containerd、CRI-O、Podman 通用。Docker 1.24 之后 K8s 抛弃的是 dockershim 适配层,不是容器本身。

UnionFS 是如何工作的? 采用写时复制(CoW):镜像由多个只读层叠加而成,容器启动时在最上面追加一个可写层;读文件时从上层往下找,修改文件时先把文件复制到可写层再改,因此多个容器共享一份只读镜像层,启动快、占用小。Docker 默认存储驱动为 overlay2。

容器的 1 号进程与信号处理? 容器 PID 1 就是应用进程(exec 形式时),它需要负责回收僵尸进程、处理 SIGTERM 优雅退出。若应用不处理信号或使用 shell 形式启动,Ctrl+C/docker stop 会失效或超时后被 SIGKILL(10 秒 grace period 可用 --stop-timeout 调整)。

安全与排错#

容器为什么不安全?如何加固?

  • 共享内核:内核漏洞可逃逸;应保持内核与 Docker 版本更新。
  • 默认 root 运行:用 USER 指定非 root、--user 覆盖、开启 user namespace。
  • 权限:避免 --privileged,按需 --cap-drop=ALL --cap-add=NET_BIND_SERVICE,使用 --security-opt no-new-privileges、只读根文件系统 --read-only
  • 敏感信息:不要把密钥写进镜像层(docker history 可见),用环境变量、secret 文件或外部密钥管理。
  • 其他:AppArmor/SELinux 配置、限制资源防 DoS、镜像扫描(Trivy)与最小化基础镜像。

容器启动失败怎么排查?

  1. docker ps -a 看状态与退出码,docker logs 看应用日志;
  2. docker inspect 看 State.Error、OOMKilled、挂载与端口;
  3. docker run -it --entrypoint /bin/sh image 进镜像手工验证命令与文件是否存在;
  4. 端口冲突(netstat -tulnp)、磁盘满(docker system df)、权限问题(挂载目录 SELinux 需 :z)。

常见问题:磁盘被容器占满怎么办?docker system df -v 定位,再清理:docker container prunedocker image prune -adocker volume prunedocker builder prune;同时配置日志 max-size、给容器日志与数据盘单独分区。

为什么容器里不能用 systemd / systemctl? 容器只运行一个前台进程,没有 init 系统;应把每个服务拆成独立容器,或用 supervisor 管理多进程(不推荐),确需时用 --privileged + systemd 镜像(脏且不安全)。

Docker 与 Kubernetes 的关系? Docker 是单机容器引擎,K8s 是集群编排系统;K8s 早期通过 dockershim 调用 Docker,1.24 起改为直接用 CRI 对接 containerd/CRI-O,镜像标准(OCI)保持通用。

Docker 八股
https://blog-l7wd3.pages.dev/posts/interview/docker/
作者
L7WD3-Xiao
发布于
2026-08-20
许可协议
CC BY-NC-SA 4.0

分享文章

生成精美分享图或复制链接,与更多人分享本文。

继续阅读

沿着主题读

基于共同的标签与分类

换条路线

从其他文章中稳定抽取