揭秘Docker容器执行链路与核心技术
容器原理与执行链路 Docker容器启动链路为:CLI→dockerd→containerd→shim→runc→kernel(Namespace/Cgroups)。containerd-shim解耦容器生命周期与daemon进程,确保dockerd重启不影响容器。runc按OCI规范创建容器进程后退出,父进程由shim接管。 安全隔离机制 UserNamespace实现UID映射,容器内root
【容器原理与架构】
Q1. 请描述 docker run 背后的完整执行链路。 ⭐⭐
预期回答:CLI → dockerd → containerd → shim → runc → kernel (Namespace/Cgroups)
追问1:containerd-shim 的作用是什么?为什么不能让 dockerd 直接管理容器进程?
containerd-shim 的核心作用是 解耦容器生命周期与 daemon。它作为容器进程的父进程(PPID),接管 stdio、信号和退出状态。若 dockerd 崩溃或重启,shim 仍存活,容器不会被 kill。若由 dockerd 直接 fork 容器,daemon 重启会导致容器成为孤儿进程,被 init 回收。
追问2:runc 启动后就退出了,容器进程如何保持运行?其父 PID 是多少?
runc 在调用
exec后即退出。容器主进程由 containerd-shim 接管,因此其父 PID(PPID)是 shim 进程的 PID。可通过ps -o ppid= -p <container-pid>验证。
追问3:如果 dockerd 崩溃重启,正在运行的容器会受影响吗?为什么?
不会。因为容器进程由 containerd-shim 托管,而 containerd 本身也设计为高可用组件。Docker 20.10+ 默认启用
live-restore模式,即使 dockerd 重启,容器网络和进程均不受影响。
Q2. 什么是 OCI 规范?Docker 与它的关系是什么? ⭐⭐
预期回答:OCI = Open Container Initiative,包含 Image Spec / Runtime Spec / Distribution Spec;Docker 是贡献者和兼容实现
追问1:如何验证一个镜像是 OCI 格式而非旧 Docker v2 Schema?
使用
crane manifest <image>或skopeo inspect --raw docker://<image>查看 manifest 的mediaType:
- OCI:
application/vnd.oci.image.manifest.v1+json- Docker v2 Schema 2:
application/vnd.docker.distribution.manifest.v2+json
追问2:BuildKit 默认生成的是 Docker Image Spec 还是 OCI Image Spec?
BuildKit 默认输出 OCI 格式。可通过
--output type=oci,dest=image.tar显式指定,但即使不指定,推送到兼容 Registry(如 Harbor 2.0+)也会自动转为 OCI。
追问3:能否用非 Docker 工具(如 crane)拉取并运行 OCI 镜像?如何操作?
可以。例如:
# 拉取 OCI 镜像 skopeo copy docker://nginx:alpine oci:nginx-oci # 用 runc 运行(需先解压 rootfs) umoci unpack --image nginx-oci:latest bundle runc run mynginx
Q3. runc 是什么?它和 containerd 的职责边界在哪里? ⭐⭐⭐
预期回答:runc 是 OCI Runtime 实现,负责创建容器进程;containerd 负责生命周期管理、镜像、任务调度
追问1:runc 是否常驻内存?它如何接收启动指令?
runc 是 一次性 CLI 工具,不常驻内存。它通过命令行参数接收指令(如
runc run <id>),配置来自当前目录的config.json。
追问2:如何用纯 runc(无 Docker/containerd)运行一个容器?请写出步骤。
mkdir myctr && cd myctr mkdir rootfs docker export $(docker create alpine) | tar -C rootfs -xvf - runc spec # 生成 config.json sudo runc run myctr
追问3:Kubernetes 中 kubelet 如何调用 runc?中间经过哪些组件?
kubelet → CRI(gRPC)→ containerd → containerd-shim → runc。containerd 作为 CRI 实现,将 PodSpec 转为 OCI config,再调用 runc 创建容器。
Q4. 为什么说“容器只是一个受限制的 Linux 进程”? ⭐⭐
预期回答:容器本质是普通进程,通过 Namespace 隔离视图、Cgroups 限制资源、Capabilities 控制权限
追问1:如何在宿主机上找到某个容器的主进程 PID?
docker inspect <container> --format='{{.State.Pid}}' # 或 cat /proc/$(pgrep -f "containerd-shim.*<container>")/children
追问2:该进程的 /proc/<pid>/ns/ 目录下有哪些文件?分别代表什么?
常见文件:
pid,net,mnt,uts,ipc,user,cgroup。每个符号链接指向内核 namespace inode,如net -> net:[4026532345]。
追问3:能否让两个容器共享同一个 PID Namespace?如何实现?
可以。使用
--pid=container:<other-container-id>。Kubernetes 中通过shareProcessNamespace: true实现 Pod 内共享。
【Namespace 隔离机制】
Q5. User Namespace 如何提升容器安全性? ⭐⭐⭐
预期回答:实现 UID/GID 映射,容器内 root ≠ 宿主机 root,防止逃逸提权
追问1:Docker 在 Linux 上默认启用 User Namespace 吗?如何开启?
默认不启用。需在
/etc/docker/daemon.json中配置:{ "userns-remap": "default" }
追问2:启用后,容器挂载宿主机目录(-v)会出现什么问题?如何解决?
权限错乱。因容器内 UID 0 映射为宿主机 UID 165536,无法访问宿主机属主为 root 的文件。
解决方案:
- 使用
idmapped mounts(Linux 5.12+)- 或
docker run --user $(id -u):$(id -g)
追问3:Kubernetes 支持 User Namespace 吗?当前社区进展如何?
尚未 GA。社区通过 KEP-127 推进,目前需手动配置且多租户场景复杂。
Q6. PID Namespace 嵌套有什么实际用途? ⭐⭐
预期回答:用于调试(nsenter)、构建沙箱(BuildKit)、多租户隔离
追问1:如何查看当前进程处于哪一层 PID Namespace?
递归读取
/proc/<pid>/ns/pid的 inode 号,直到与 init 进程(PID 1)相同。
追问2:最大嵌套深度由哪个内核参数控制?
kernel.pid_max控制最大 PID 数,但嵌套深度实际受限于max_user_namespaces(默认 0 表示无限制,部分发行版设为 1024)。
追问3:在 Kubernetes Pod 中,initContainer 和主容器是否共享 PID Namespace?如何配置?
默认不共享。需在 PodSpec 中显式设置
shareProcessNamespace: true才共享。
Q7. Mount Namespace 的核心作用是什么? ⭐⭐
预期回答:隔离文件系统挂载点,使容器拥有独立的
/、/proc、/sys等视图
追问1:为什么容器内的 /proc 是虚拟的?直接挂载宿主机 /proc 有何风险?
Mount Namespace 隔离了
/proc挂载点,容器看到的是 仅包含自身进程的 procfs。若挂载宿主机/proc,可查看所有进程,导致信息泄露。
追问2:如何让容器看到宿主机的真实 /proc?什么场景需要这样做?
docker run -v /proc:/host/proc:ro ...
追问3:--privileged 模式会禁用哪些 Namespace?为什么危险?
不会禁用,而是 加入宿主机的所有 Namespace(等效于
--net=host --pid=host --ipc=host等),并授予所有 Capabilities,极度危险。
【Cgroups 资源控制】
Q8. Cgroup v2 相比 v1 有哪些关键改进? ⭐⭐⭐
预期回答:统一单棵树、memory.low/min/max、PSI 原生支持、接口语义一致
追问1:Docker 如何指定使用 Cgroup v2?需满足哪些条件?
无需指定。只要宿主机启用 Cgroup v2(内核参数
systemd.unified_cgroup_hierarchy=1),Docker 20.10+ 自动适配。
追问2:memory.reservation 对应 Cgroup v2 中的哪个参数?
对应
memory.low(软保护),不是硬限制。
追问3:能否用 Cgroup v2 限制容器对特定磁盘的 IOPS?Docker CLI 支持吗?
内核支持(通过
io.max),但 Docker CLI 不暴露该参数。需手动写入 cgroup 文件或通过 Kubernetes device plugins 实现。
Q9. 如何利用 PSI(Pressure Stall Information)监控容器资源争用? ⭐⭐⭐
预期回答:读取
/sys/fs/cgroup/.../memory.pressure,解析 some/full 指标
追问1:some avg10=20.0 表示什么含义?
过去 10 秒内,至少有一个任务因内存不足而阻塞的时间占比为 20%。
追问2:Prometheus 如何采集 PSI 数据?需要什么 Exporter?
使用
node_exporter(v1.4+)自动暴露node_cgroup_psi指标,无需额外配置。
追问3:应用如何根据 PSI 实现自适应降级?举例说明。
应用定期读取
/sys/fs/cgroup/memory.pressure,若some> 阈值,则关闭缓存、降低并发等。例如 Redis 可动态调整maxmemory。
Q10. 容器 OOM 时,是 kill 整个容器还是单个进程? ⭐⭐
预期回答:kill 整个 cgroup(即整个容器),因为所有进程属于同一 memory cgroup
追问1:如何避免关键进程被误杀?Cgroup v2 提供了什么机制?
Cgroup v2 提供memory.oom.group:设为 1 时,OOM killer 会 kill 整个 cgroup;设为 0 时可单独 kill 进程(需配合 oom_score_adj)。
追问2:memory.oom.group 作用?
控制 OOM killer 粒度:1 = 整组 kill(默认),0 = 允许单进程 kill。
追问3:OOM 日志在哪里查看?如何关联到具体容器?
- 内核日志:
dmesg | grep -i "killed process"- systemd journal:
journalctl -k --grep=oom- 关联容器:通过 PID 或 cgroup 路径反查
【镜像与存储】
Q11. Docker 镜像为什么采用分层设计? ⭐⭐
预期回答:节省存储、加速构建/拉取、支持镜像复用
追问1:两个基于相同 base 镜像的容器,是否共享底层 layer?
是。只读层(lowerdir)由所有基于该镜像的容器共享,节省存储和内存(page cache 复用)。
追问2:删除镜像某一层后,依赖该层的其他镜像会怎样?
Docker 采用 引用计数,只有当所有镜像都不引用该层时才真正删除。否则保留。
追问3:如何查看镜像各层大小?推荐工具?
docker history <image> # 或使用第三方工具 dive <image>
Q12. OverlayFS 如何实现“写时复制”(Copy-on-Write)? ⭐⭐⭐
预期回答:lowerdir(只读)+ upperdir(可写)+ merged(联合视图);首次修改触发 copy-up
追问1:删除大文件后磁盘空间会释放吗?
✅ 答:
不会立即释放。OverlayFS 仅在 upperdir 创建 whiteout 文件(.wh.filename),原始数据仍在 lowerdir。
追问2:whiteout (.wh.*)的作用是什么?
✅ 答:
标记“该文件已被删除”,使 merged 视图中不可见。
追问3:如何避免 因频繁写入导致 upperdir 膨胀?
✅ 答:
- 使用
--tmpfs存放临时数据 - 定期重建镜像(CI 中清理中间层)
- 避免在容器内频繁写入大文件
Q13. EROFS 作为存储驱动有何优势?适用场景? ⭐⭐
预期回答:只读压缩、节省 50% 存储、启动快 40%,适用于 immutable workload
追问1:EROFS 能否作为 OverlayFS 的 upperdir?为什么?
✅ 答:
不能。EROFS 是只读文件系统,只能作 OverlayFS 的 lowerdir。
追问2:哪些云厂商使用 EROFS?
✅ 答:
阿里云 ACK Edge、华为云 CCE Turbo、AWS Bottlerocket OS。
追问3:如何构建 EROFS 镜像层?
✅ 答:
使用 mkfs.erofs 工具打包目录,再作为 OCI layer 推送(需自定义 builder)。
【安全与合规】
Q14. CVE-2019-5736 漏洞原理及修复方案? ⭐⭐⭐
预期回答:通过
/proc/self/exe覆盖 runc 二进制;修复:memfd_create() + User Namespace
追问1:memfd_create() 如何防止文件覆盖?
✅ 答:
将 runc 二进制复制到匿名内存 fd(无文件路径),攻击者无法通过 /proc/self/exe 覆盖。
追问2:Rootless Docker 模式是否免疫此漏洞?为什么?
✅ 答:
是。因以非 root 用户运行,容器内进程无权写宿主机任何文件。
追问3:除了 runc 修复,还有哪些纵深防御措施?
✅ 答:
- 启用 User Namespace
--read-only根文件系统- drop ALL capabilities
- AppArmor/SELinux 策略
Q15. 如何实现容器的最小权限原则? ⭐⭐
预期回答:非 root 用户、drop capabilities、只读根文件系统、seccomp、AppArmor
追问1:--cap-drop=ALL 为何导致 ping 失败?
✅ 答:
ping 需要 CAP_NET_RAW 发送 raw socket。解决:--cap-add=NET_RAW 或改用 HTTP 健康检查。
追问2:如何自定义 seccomp profile?
✅ 答:
# seccomp.json
{
"defaultAction": "SCMP_ACT_ERRNO",
"syscalls": [{"names": ["open", "read", "write"], "action": "SCMP_ACT_ALLOW"}]
}
docker run --security-opt seccomp=./seccomp.json ...
追问3:AppArmor 和 SELinux 能同时启用吗?
✅ 答:
不能。Linux LSM 框架一次只允许一个模块生效(除非使用 stacking,但 Docker 不支持)。
Q16. SBOM 如何防止供应链攻击? ⭐⭐⭐
预期回答:SBOM 列出组件清单,通过 Sigstore 签名,部署前验证完整性
追问1:如何为 Docker 镜像生成 SBOM?推荐工具?
✅ 答:
syft nginx:alpine -o spdx-json > sbom.json
追问2:cosign 如何将 SBOM 附加为 OCI Artifact?
✅ 答:
cosign attach sbom --sbom sbom.json nginx:alpine
追问3:Kubernetes 如何在 admission 阶段验证 SBOM 签名?
✅ 答:
通过 admission controller(如 Kyverno + cosign)在 Pod 创建前验证 OCI Artifact 签名。
【网络与通信】
Q17. Docker 默认 bridge 网络的工作原理? ⭐⭐
预期回答:docker0 网桥 + veth pair + iptables SNAT/DNAT
追问1:容器间通信是否经过 iptables?如何优化性能?
✅ 答:
同一网桥内不经过(走二层),跨网桥或出站需 SNAT/DNAT。
追问2:为什么 --link 已被废弃?替代方案是什么?
✅ 答:
因依赖环境变量注入,破坏不可变性;现用 自定义 bridge 网络 + DNS 解析 替代。
追问3:如何让容器直接使用宿主机网络栈?有何风险?
✅ 答:docker run --network=host。风险:端口冲突、安全边界消失。
Q18. eBPF 如何替代 iptables 实现容器网络策略? ⭐⭐⭐
预期回答:eBPF 程序挂载到 TC 或 XDP 层,实现无 iptables 的 L3/L4 策略(如 Cilium)
追问1:eBPF 性能提升多少?
✅ 答:
Cilium 测试显示:吞吐提升 2–3 倍,延迟降低 50%,CPU 占用减少 40%。
追问2:Docker 原生支持 eBPF 吗?
✅ 答:
不支持。需通过 CNI 插件(如 Cilium、Calico eBPF mode)实现。
追问3:如何用 bpftrace 跟踪容器网络包?
✅ 答:
bpftrace -e 'tracepoint:net:netif_receive_skb { printf("pkt from %s\n", comm); }'
【日志与监控】
Q19. Docker 容器日志默认存储在哪里?如何轮转? ⭐⭐
预期回答:
/var/lib/docker/containers/<id>/<id>-json.log;通过 log-driver 配置轮转
追问1:如何限制 json-file 日志大小?
✅ 答:
{
"log-driver": "json-file",
"log-opts": {
"max-size": "10m",
"max-file": "3"
}
}
追问2:如何发送日志到 Loki?
✅ 答:
使用 loki-docker-driver 或 Fluentd/Vector sidecar。
追问3:shim 如何处理 stdout/stderr?
✅ 答:
shim 将容器进程的 stdout/stderr 重定向到 FIFO 或日志文件,并转发给 containerd。
【排错与调试】
Q20. 容器启动失败,如何快速定位原因? ⭐⭐
预期回答:
docker logs→docker inspect→ 查看 exit code → 检查 cgroup OOM / seccomp deny
追问1:如何进入已退出容器调试?
✅ 答:
# 获取容器 rootfs 路径
docker inspect <container> | grep MergedDir
# chroot 进入(需 root)
chroot /var/lib/docker/overlay2/.../merged /bin/sh
追问2:seccomp 拦截日志在哪?
✅ 答:
需开启 audit:auditctl -a always,exit -F arch=b64 -S ...,日志在 /var/log/audit/audit.log。
追问3:如何用 strace 跟踪容器进程?
✅ 答:
strace -p $(docker inspect -f '{{.State.Pid}}' <container>)
【Build 与 CI/CD】
Q21. BuildKit 相比传统 builder 有哪些优势? ⭐⭐⭐
预期回答:并行构建、远程缓存、secret mount、更小镜像、语法扩展(# syntax=docker/dockerfile:1.4)
追问1:如何启用 BuildKit?
✅ 答:
export DOCKER_BUILDKIT=1
# 或 daemon.json
{ "features": { "buildkit": true } }
追问2:如何配置远程缓存?
✅ 答:
docker build --build-arg BUILDKIT_INLINE_CACHE=1 \
--cache-from type=registry,ref=user/app:cache \
--cache-to type=registry,ref=user/app:cache .
追问3:--mount=type=secret 如何避免 secrets 泄露到镜像层?
✅ 答:
secret 通过 tmpfs 挂载到构建上下文,不会写入任何 layer,构建结束后自动销毁。
Q22. 多阶段构建(multi-stage build)的原理和好处? ⭐⭐
预期回答:在一个 Dockerfile 中定义多个 FROM,仅复制最终产物,减少镜像体积
追问1:如何从 builder 阶段复制文件?
✅ 答:
COPY --from=builder /app/bin/myapp /usr/local/bin/
追问2:能否跨 Dockerfile 共享缓存?
✅ 答:
不能直接共享,但可通过远程缓存(--cache-from)复用。
追问3:Go 多阶段构建最佳实践?
✅ 答:
FROM golang:1.23 AS builder
WORKDIR /app
COPY . .
RUN CGO_ENABLED=0 go build -o myapp .
FROM gcr.io/distroless/static
COPY --from=builder /app/myapp /myapp
ENTRYPOINT ["/myapp"]
追问1:如何从 builder 阶段复制文件?语法?
追问2:能否跨 Dockerfile 共享构建缓存?如何实现?
追问3:Go 应用多阶段构建的最佳实践?
Q23. Docker Compose v2 与 v1 的主要区别? ⭐
预期回答:Compose v2(Go 重写)性能更高、支持 profiles、与 containerd 深度集成;v1(Python)已弃用
追问1:Compose 文件中的 deploy 字段在非 Swarm 模式下生效吗?
✅ 答:不生效。deploy 仅用于 Docker Swarm 或 Kubernetes(通过 Compose on K8s),本地 docker compose up 会忽略该字段。
追问2:如何用 Compose 实现健康检查和服务依赖?
✅ 答:
services:
web:
depends_on:
db:
condition: service_healthy
db:
image: postgres
healthcheck:
test: ["CMD-SHELL", "pg_isready -U postgres"]
interval: 10s
timeout: 5s
retries: 3
追问3:Compose 是否支持 secrets 和 configs?如何管理?
✅ 答:
- 本地模式:通过文件挂载模拟 secrets(
secrets:+file:) - Swarm 模式:原生支持加密 secrets 和 configs
- 示例:
secrets: db_password: file: ./db.pass services: app: secrets: - db_password
Q24. Rootless Docker 的工作原理和限制? ⭐⭐⭐
预期回答:以普通用户运行 dockerd,利用 User Namespace + slirp4netns 实现隔离,无需 root 权限
追问1:Rootless 模式下如何访问 privileged 端口(<1024)?
✅ 答:无法直接绑定。解决方案:
- 使用端口转发(如
socat TCP-LISTEN:80,fork TCP:localhost:8080) - 或配置
net.ipv4.ip_unprivileged_port_start=0(需 root 设置)
追问2:能否在 Rootless 模式下使用 --device?
✅ 答:有限支持。只能访问当前用户有权限的设备(如 /dev/dri),且需手动配置 cgroup delegation。
追问3:Rootless 是否支持 Cgroup v2?如何配置?
✅ 答:支持,且推荐。需启用 cgroup delegation:
systemctl --user edit docker
# 添加:
[Service]
Delegate=cpu io memory pids
Q25. Docker 为何被 Kubernetes 弃用?现在用什么? ⭐⭐
预期回答:Docker 不符合 CRI 标准;K8s 从 v1.24 起移除 dockershim,改用 containerd 或 CRI-O
追问1:dockershim 是什么?何时被移除?
✅ 答:dockershim 是 K8s 中将 CRI 请求转为 Docker API 的适配层;v1.24(2022年5月)正式移除。
追问2:containerd 如何同时支持 Docker CLI 和 CRI?
✅ 答:containerd 内置两个插件:
criplugin → 供 kubelet 调用dockershim → 供 dockerd 调用
两者共用镜像存储和运行时(runc)
追问3:如何将现有 Docker 镜像迁移到 containerd-only 环境?
✅ 答:无需迁移!containerd 可直接拉取并运行 Docker Hub 镜像(OCI 兼容)。验证命令:
crictl pull nginx
crictl runp pod.json
crictl create <pod-id> container.json pod.json
Q26. 如何优化容器启动速度? ⭐⭐
预期回答:使用 Distroless/Alpine 镜像、启用 BuildKit 缓存、预拉镜像、使用 EROFS、减少 init 逻辑
追问1:lazy-pull(按需拉取)技术原理?哪些运行时支持?
✅ 答:
- 原理:仅拉取 metadata 和必要 layer,数据块按需从远程存储(如 S3)读取
- 支持:Nydus(阿里)、Stargz(AWS)、OverlayBD(华为)
- Docker 原生 不支持,需替换 snapshotter
追问2:如何测量容器从 create 到 ready 的时间?
✅ 答:
time docker run --rm alpine echo "started"
# 或监控 events
docker events --since 1m | grep 'start\|die'
追问3:systemd-cgroup vs cgroupfs 哪个性能更好?
✅ 答:无显著差异。systemd-cgroup 更适合 systemd 系统(避免资源竞争),但性能几乎相同。
Q27. 容器内的时间与时区为何重要?如何正确处理? ⭐
预期回答:容器默认 UTC,应用日志/调度依赖时区;应显式设置 TZ 或挂载宿主机时区文件
追问1:为什么不能直接挂载 /etc/localtime?
✅ 答:
- Alpine 镜像无 glibc,
/etc/localtime可能无效 - 更佳方案:设置
TZ=Asia/Shanghai环境变量
追问2:Java 应用如何识别容器时区?
✅ 答:
- JVM 自动读取
/etc/timezone或TZ - 若未设置,默认使用 UTC
- 推荐:
-e TZ=Asia/Shanghai
追问3:Kubernetes 中如何统一设置 Pod 时区?
✅ 答:
- 方案1:initContainer 拷贝时区文件
- 方案2:使用 volume 挂载 hostPath
/usr/share/zoneinfo - 方案3:基础镜像内置时区(推荐)
Q28. 如何实现容器的优雅停机(Graceful Shutdown)? ⭐⭐⭐
预期回答:主进程需处理 SIGTERM,执行清理后退出;使用 exec 格式 ENTRYPOINT,避免 shell 中转
追问1:docker stop 默认等待多久发送 SIGKILL?
✅ 答:10 秒。可通过 --time 参数调整:docker stop --time=30 <container>
追问2:Node.js 应用如何监听 SIGTERM?
✅ 答:
process.on('SIGTERM', () => {
server.close(() => process.exit(0));
});
追问3:Kubernetes 中 terminationGracePeriodSeconds 的作用?
✅ 答:Pod 删除后,kubelet 等待该秒数再发 SIGKILL。默认 30s,应 ≥ 应用 shutdown 时间。
Q29. Docker Volume 与 Bind Mount 的本质区别? ⭐
预期回答:Volume 由 Docker 管理(路径在
/var/lib/docker/volumes/),Bind Mount 是任意宿主机路径
追问1:Volume 是否支持跨主机共享?
✅ 答:原生不支持。需使用网络存储插件(如 NFS、Ceph、AWS EFS driver)
追问2:如何备份一个 named volume?
✅ 答:
docker run --rm -v myvol:/volume -v $(pwd):/backup alpine \
tar czf /backup/backup.tar.gz -C /volume .
追问3:tmpfs mount 的适用场景?
✅ 答:存放临时敏感数据(如 session、cache),重启即消失,不落盘。
Q30. 如何防止 Docker 镜像供应链攻击? ⭐⭐⭐
预期回答:使用 SBOM + Sigstore 签名 + Notary v2 + 镜像扫描(Trivy/Clair)
追问1:cosign 如何实现镜像签名?
✅ 答:
cosign generate-key-pair
cosign sign --key cosign.key user/app:latest
cosign verify --key cosign.pub user/app:latest
追问2:Notary v2 与 Docker Content Trust (DCT) 的关系?
✅ 答:DCT 基于 Notary v1,已弃用;Notary v2 是 OCI 原生签名标准,与 cosign 兼容。
追问3:如何在 CI 中阻断高危漏洞镜像?
✅ 答:
trivy image --exit-code 1 --severity CRITICAL myapp:latest
Q31. 容器内 DNS 解析是如何工作的? ⭐⭐
预期回答:默认使用 Docker 内置 DNS(127.0.0.11),支持服务发现、自定义 nameserver
追问1:如何覆盖容器 DNS 服务器?
✅ 答:
docker run --dns 8.8.8.8 --dns-search example.com alpine
追问2:为什么容器内 ping 通但 curl 失败?
✅ 答:可能因 nsswitch.conf 顺序问题(先查 files 再 dns),或缺少 CA 证书。
追问3:Kubernetes 中 CoreDNS 如何与容器 DNS 协同?
✅ 答:Pod 的 /etc/resolv.conf 由 kubelet 生成,nameserver 指向 CoreDNS Service IP。
Q32. 如何限制容器对宿主机资源的滥用? ⭐⭐
预期回答:通过
--cpus,--memory,--pids-limit,--blkio-weight等参数限制
追问1:--memory-swap 的作用是什么?
✅ 答:控制内存+swap 总量。设为 -1 表示不限 swap;等于 --memory 表示禁用 swap。
追问2:能否限制容器磁盘使用量?
✅ 答:Docker 原生不支持。需依赖存储驱动(如 devicemapper 配额)或 Kubernetes ephemeral-storage limit。
追问3:如何防止 fork bomb?
✅ 答:设置 --pids-limit=100,限制容器内最大进程数。
Q33. Docker Daemon 高可用如何实现? ⭐⭐⭐
预期回答:Docker Engine 本身无 HA;生产环境应:
- 使用 Swarm/K8s 编排
- 将 dockerd 作为 systemd 服务 + auto-restart
- 镜像/卷存储到分布式文件系统(如 Ceph)
追问1:多个 dockerd 能否共享同一 /var/lib/docker?
✅ 答:绝对禁止!会导致元数据损坏。每个 dockerd 必须独占存储目录。
追问2:如何实现 dockerd 故障自动恢复?
✅ 答:
# /etc/systemd/system/docker.service.d/restart.conf
[Service]
Restart=always
RestartSec=5
追问3:Swarm Mode 是否提供 dockerd HA?
✅ 答:不提供。Swarm 管理的是 服务(service)HA,不是 dockerd 进程 HA。
Q34. 容器冷启动 vs 热启动的区别? ⭐
预期回答:冷启动 = 首次拉镜像+创建容器;热启动 = 镜像已存在,仅创建容器
追问1:如何预热容器?
✅ 答:
- CI 中预拉镜像:
docker pull myapp:latest - 使用 initContainer 预加载缓存
追问2:Kubernetes 中如何预拉镜像?
✅ 答:设置 imagePullPolicy: IfNotPresent + 节点预拉,或使用 prePull DaemonSet。
追问3:镜像分层如何影响冷启动时间?
✅ 答:公共 base 层可复用,减少下载量;建议将变动少的层放前面。
Q35. 如何调试容器内网络不通的问题? ⭐⭐
预期回答:检查 iptables 规则、DNS 解析、路由表、安全组、CNI 插件状态
追问1:容器内无法 ping 外网,但宿主机可以,可能原因?
✅ 答:
- iptables FORWARD 链 DROP
- sysctl
net.ipv4.ip_forward=0 - 云平台安全组限制
追问2:如何查看容器网络命名空间内的路由?
✅ 答:
nsenter -t <container-pid> -n ip route
追问3:docker network inspect 能看到什么信息?
✅ 答:网桥名称、子网、网关、连接的容器、IP 分配等。
Q36. 容器内进程为何看不到宿主机的进程? ⭐
预期回答:因 PID Namespace 隔离,容器内
/proc仅显示自身进程
追问1:如何让容器看到宿主机所有进程?
✅ 答:docker run --pid=host ...(危险!破坏隔离)
追问2:top 命令在容器内显示的 CPU% 准确吗?
✅ 答:准确。top 读取 /proc/stat,Cgroups 已虚拟化该视图。
追问3:如何监控宿主机进程 from 容器?
✅ 答:挂载宿主机 proc:-v /proc:/host/proc:ro,然后读取 /host/proc。
Q37. Dockerfile 中 COPY 与 ADD 的区别? ⭐
预期回答:
ADD支持 URL 下载和自动解压 tar;COPY仅本地文件复制,更透明安全
追问1:为什么官方推荐优先使用 COPY?
✅ 答:ADD 的隐式行为(如自动解压)易导致意外,不符合“显式优于隐式”原则。
追问2:ADD 能否下载 HTTPS 文件?
✅ 答:可以,但不推荐(构建不可重现,且 secrets 易泄露)。
追问3:如何安全下载外部依赖?
✅ 答:在 RUN 中使用 curl/wget,并校验 checksum:
dockerfile
编辑
RUN curl -L https://.../bin.tgz -o bin.tgz && \
echo "sha256 expected" | sha256sum -c && \
tar -xzf bin.tgz -C /usr/local/bin
Q38. 容器内文件权限为何经常出错? ⭐⭐
预期回答:因 UID/GID 在容器内外不一致,尤其挂载卷时
追问1:如何解决挂载卷后权限 denied?
✅ 答:
- 方案1:容器内以匹配 UID 运行:
--user $(id -u):$(id -g) - 方案2:构建时创建对应用户
- 方案3:启用 User Namespace(需配置)
追问2:Dockerfile 中 RUN 创建的文件属主是谁?
✅ 答:当前 USER(默认 root)。若后续切换用户,需 chown。
追问3:Kubernetes 中如何动态设置卷权限?
✅ 答:使用 securityContext.fsGroup,Kubelet 会自动 chown 卷目录。
Q39. 如何实现 Docker 镜像的多架构支持(如 ARM64)? ⭐⭐
预期回答:使用 Buildx + QEMU 模拟,构建 multi-platform 镜像并推送到 manifest list
追问1:如何启用 QEMU 模拟?
✅ 答:
bash
编辑
docker run --privileged --rm tonistiigi/binfmt --install all
追问2:如何构建并推送多架构镜像?
✅ 答:
bash
编辑
docker buildx create --use
docker buildx build --platform linux/amd64,linux/arm64 -t user/app:latest --push .
追问3:客户端如何自动拉取匹配架构的镜像?
✅ 答:Docker 19.03+ 自动根据宿主机架构选择 manifest list 中的合适镜像。
Q40. 容器日志丢失的常见原因及解决方案? ⭐⭐
预期回答:日志驱动配置不当、磁盘满、容器崩溃太快、stdout 未 flush
追问1:json-file 日志默认无大小限制,如何避免占满磁盘?
✅ 答:在 daemon.json 中配置:
json
编辑
{
"log-driver": "json-file",
"log-opts": {
"max-size": "100m",
"max-file": "5"
}
}
追问2:应用日志写入文件而非 stdout,如何采集?
✅ 答:
- 方案1:sidecar 容器 tail 日志文件
- 方案2:挂载 volume,宿主机 fluentd 采集
追问3:容器启动即退出,看不到日志怎么办?
✅ 答:
- 使用
docker logs --timestamps <container> - 或
docker run --rm ... || docker cp <container>:/app/log ./
【Dockerfile 专项题】
Q41. 编写一个安全的 Dockerfile 应遵循哪些最佳实践? ⭐⭐⭐
预期回答:使用非 root 用户、最小 base 镜像、合并 RUN、清理缓存、固定版本、只读根
Q41 追问1:为什么 apt-get update 和 install 必须同 RUN?
✅ 答:
避免缓存失效导致安装旧包。若分开,修改 install 列表不会触发 update,可能装错版本。
Q41 追问2:如何避免 .git/.env 打包进镜像?
✅ 答:
使用 .dockerignore 文件排除敏感路径。
Q41 追问3:HEALTHCHECK 最佳实践?
✅ 答:
- 使用轻量命令(如
curl -f http://localhost/health) - 设置合理间隔(30s)、超时(5s)、重试(3 次)
Q42. 如何减小 Docker 镜像体积? ⭐⭐
预期回答:多阶段构建、Alpine/Distroless、清理包缓存、避免 COPY 大目录
Q42 追问1:Alpine 潜在问题?(glibc 兼容性)
✅ 答:
使用 musl libc 而非 glibc,某些 Java/Python 二进制不兼容。
Q42 追问2:分析镜像大小工具?
✅ 答:dive, docker history, syft(SBOM 分析)
Q42 追问3:squash 镜像是否推荐?
✅ 答:
不推荐。破坏分层缓存,CI/CD 中每次构建都需全量推送。
Q43. Dockerfile 中 ARG 和 ENV 的区别? ⭐⭐
预期回答:ARG 用于构建时变量(不出现在镜像中),ENV 用于运行时环境变量
Q43 追问1:能否在 RUN 中使用 ARG?
✅ 答:
可以,但 ARG 在 RUN 结束后失效。
Q43 追问2:如何传递构建参数?
✅ 答:
docker build --build-arg VERSION=1.2.3 .
Q43 追问3:敏感信息能否用 ARG?
✅ 答:
绝对不行!ARG 会记录在镜像历史中,可通过 docker history 查看。
Q44. 如何在 Dockerfile 中安全地处理 secrets? ⭐⭐⭐
预期回答:使用 BuildKit 的
--mount=type=secret,避免写入镜像层
Q44 追问1:传统 COPY secret 为何不安全?
✅ 答:
secret 会永久留在镜像层中,即使后续 RUN 删除,仍可通过历史层恢复。
Q44 追问2:secret 文件存储位置?
✅ 答:
挂载到 /run/secrets/(tmpfs),构建结束后自动销毁。
Q44 追问3:K8s 如何集成?
✅ 答:
通过 Tekton 或 Argo Workflows 的 secret volume 挂载到 BuildKit 构建上下文。
Q45. EXPOSE 指令真的会打开端口吗? ⭐
预期回答:不会!仅作为元数据声明,实际端口由
docker run -p决定
Q45 追问1:不写 EXPOSE 能否 -p 发布端口?
✅ 答:
可以。EXPOSE 仅为文档声明,不影响运行时。
Q45 追问2:对 docker-compose 影响?
✅ 答:
compose 会自动将 EXPOSE 端口映射到随机宿主机端口(除非显式指定 ports)。
Q45 追问3:如何查看镜像声明端口?
✅ 答:docker inspect <image> | jq '.[].Config.ExposedPorts'
Q46. CMD 和 ENTRYPOINT 的区别与组合使用? ⭐⭐
预期回答:CMD 是默认参数,ENTRYPOINT 是主命令;组合实现“可配置命令”
Q46 追问1:exec vs shell 格式区别?
✅ 答:
- exec:
["cmd", "arg"]→ 直接 exec,PID 1 - shell:
"cmd arg"→ 启动/bin/sh -c,PID 1 是 shell
推荐 exec
Q46 追问2:如何覆盖 ENTRYPOINT?
✅ 答:docker run --entrypoint /new/cmd image args
Q46 追问3:常见陷阱:在 ENTRYPOINT 中使用 shell 特性(如管道)
✅ 答:
信号无法传递到子进程(如 trap 失效),且无法优雅停机。
Q47. 如何确保 Dockerfile 构建结果可重现(reproducible)? ⭐⭐⭐
预期回答:固定 base 镜像 digest、设置 SOURCE_DATE_EPOCH、避免随机文件顺序
Q47 追问1:为什么 tag 不可靠?
✅ 答:
tag 可被覆盖(如 latest),digest(SHA256)唯一标识内容。
Q47 追问2:如何验证镜像 hash 相同?
✅ 答:
比较 docker inspect --format='{{.Id}}' 或镜像 manifest digest。
Q47 追问3:BuildKit 如何帮助?
✅ 答:
通过 SOURCE_DATE_EPOCH 和确定性构建(无随机文件顺序)保证可重现。
Q48. WORKDIR 和 RUN cd 的区别? ⭐
预期回答:WORKDIR 创建目录并设为 pwd,持久化到后续指令;RUN cd 仅在当前层有效
Q48 追问1:WORKDIR 能否用变量?
✅ 答:
可以,但变量需在之前定义(ENV 或 ARG)。
Q48 追问2:WORKDIR 是否创建空目录?
✅ 答:
是。即使目录为空,也会出现在镜像中。
Q48 追问3:最佳实践:每个项目应有一个明确的 WORKDIR
✅ 答:
统一使用 /app 或 /opt/service 作为 WORKDIR。
Q49. 如何在 Dockerfile 中处理时区问题? ⭐
预期回答:COPY /etc/localtime 或设置 TZ 环境变量
Q49 追问1:Alpine 如何设置时区?
✅ 答:
RUN apk add --no-cache tzdata && \
cp /usr/share/zoneinfo/Asia/Shanghai /etc/localtime && \
echo "Asia/Shanghai" > /etc/timezone
Q49 追问2:为什么默认 UTC?
✅ 答:
避免时区依赖,符合 12-Factor App 原则。
Q49 追问3:时区文件应打包还是挂载?
✅ 答:
打包。挂载增加部署复杂度,且容器应 self-contained。
Q50. 如何为 Dockerfile 添加健康检查? ⭐⭐
预期回答:使用 HEALTHCHECK 指令,定义测试命令、间隔、超时、重试次数
Q50 追问1:健康状态有哪几种?
✅ 答:
- starting(刚启动)
- healthy(通过检查)
- unhealthy(连续失败)
Q50 追问2:Compose 如何依赖健康状态?
✅ 答:
depends_on:
db:
condition: service_healthy
Q50 追问3:K8s readinessProbe 与 HEALTHCHECK 关系?
✅ 答:
无直接关系。K8s 使用自己的 probe,忽略 Dockerfile 中的 HEALTHCHECK。
Q51. 如何避免 Dockerfile 中的缓存失效问题? ⭐⭐
预期回答:将变化频率低的指令放前面(如 apt-get install),高频放后面(如 COPY .)
Q51 追问1:COPY . 为何导致缓存失效?
✅ 答:
任何上下文文件变动都会使该层及之后所有层缓存失效。
Q51 追问2:如何利用 .dockerignore?
✅ 答:
排除 node_modules/, .git/, *.log, Dockerfile 等无关文件。
Q51 追问3:BuildKit cache mounts 优化?
✅ 答:
RUN --mount=type=cache,target=/root/.npm npm install
依赖缓存在多次构建间复用。
Q52. 如何在 Dockerfile 中正确处理信号(如 SIGTERM)? ⭐⭐⭐
预期回答:使用 exec 格式 ENTRYPOINT,避免 shell 中转;主进程需处理 SIGTERM
Q52 追问1:为什么 shell 格式无法传递信号?
✅ 答:
shell 进程(PID 1)未转发 SIGTERM 到子进程。
Q52 追问2:如何测试优雅停机?
✅ 答:
docker stop <container> # 观察应用是否打印 shutdown log
Q52 追问3:tini 作用?
✅ 答:
作为 init 进程(PID 1),负责回收僵尸进程并转发信号。
📌 附:高频陷阱题(易错点)
-
Q53.
docker stop发送什么信号?等待多久 kill -9?
→ SIGTERM,默认等待 10 秒 -
Q54. 容器内 PID 1 的特殊性?
→ 负责回收僵尸进程;若不处理 SIGTERM,容器无法优雅退出 -
Q55.
--tmpfs和tmpfsmount 的区别?
→ 功能相同,前者是 CLI 语法糖 -
Q56. 卷(volume)和绑定挂载(bind mount)的本质区别?
→ volume 由 Docker 管理,存储在/var/lib/docker/volumes/;bind mount 是任意宿主机路径
更多推荐


所有评论(0)