文章目录
- [一、Docker 到底解决了什么问题?](#一、Docker 到底解决了什么问题?)
-
- [1. 传统部署的问题](#1. 传统部署的问题)
- [2. Docker 的核心思想](#2. Docker 的核心思想)
- [3. Docker 的核心价值](#3. Docker 的核心价值)
- [二、Docker 和虚拟机的区别](#二、Docker 和虚拟机的区别)
-
- [1. 虚拟机架构](#1. 虚拟机架构)
- [2. Docker 容器架构](#2. Docker 容器架构)
- [3. Docker 和虚拟机对比](#3. Docker 和虚拟机对比)
- [4. 面试回答模板](#4. 面试回答模板)
- [三、Docker 架构与核心组件](#三、Docker 架构与核心组件)
-
- [1. Docker 整体架构](#1. Docker 整体架构)
- [2. Docker Client](#2. Docker Client)
- [3. Docker Daemon](#3. Docker Daemon)
- [4. Docker Image](#4. Docker Image)
- [5. Docker Container](#5. Docker Container)
- [6. Docker Registry](#6. Docker Registry)
- 四、镜像、容器、仓库的关系
-
- [1. 三者关系](#1. 三者关系)
- [2. 面试回答模板](#2. 面试回答模板)
- [五、Docker 常用命令](#五、Docker 常用命令)
- [六、Docker 容器生命周期](#六、Docker 容器生命周期)
-
- [1. 容器状态变化](#1. 容器状态变化)
- [2. 容器为什么会自动退出?](#2. 容器为什么会自动退出?)
- [3. 常见自动退出原因](#3. 常见自动退出原因)
- [七、Docker 底层核心原理](#七、Docker 底层核心原理)
-
- [1. Namespace:资源隔离](#1. Namespace:资源隔离)
- [2. Cgroup:资源限制](#2. Cgroup:资源限制)
- [3. Namespace 和 Cgroup 的区别](#3. Namespace 和 Cgroup 的区别)
- [4. UnionFS / OverlayFS:镜像分层](#4. UnionFS / OverlayFS:镜像分层)
- [5. 容器可写层](#5. 容器可写层)
- [八、Docker 镜像详解](#八、Docker 镜像详解)
-
- [1. 镜像是什么?](#1. 镜像是什么?)
- [2. 镜像 tag 是什么?](#2. 镜像 tag 是什么?)
- [3. docker commit 是什么?](#3. docker commit 是什么?)
- [4. docker commit 的问题](#4. docker commit 的问题)
- [九、Dockerfile 详解](#九、Dockerfile 详解)
- [十、Dockerfile 镜像优化](#十、Dockerfile 镜像优化)
-
- [1. 为什么要优化镜像?](#1. 为什么要优化镜像?)
- [2. 镜像优化方法](#2. 镜像优化方法)
-
- 1)选择更小的基础镜像
- 2)减少镜像层数
- 3)利用构建缓存
- [4)使用 .dockerignore](#4)使用 .dockerignore)
- 5)多阶段构建
- [十一、Docker 数据卷](#十一、Docker 数据卷)
- [十二、Docker 网络](#十二、Docker 网络)
-
- [1. Docker 为什么需要网络?](#1. Docker 为什么需要网络?)
- [2. 常见网络命令](#2. 常见网络命令)
- [3. Docker 常见网络模式](#3. Docker 常见网络模式)
- [4. bridge 网络原理](#4. bridge 网络原理)
- [5. 为什么推荐自定义网络?](#5. 为什么推荐自定义网络?)
- [6. host 网络模式](#6. host 网络模式)
- [7. none 网络模式](#7. none 网络模式)
- [8. container 网络模式](#8. container 网络模式)
- [9. 面试回答模板](#9. 面试回答模板)
- 十三、端口映射
-
- [1. 为什么需要端口映射?](#1. 为什么需要端口映射?)
- [2. 多端口映射](#2. 多端口映射)
- [3. 随机端口映射](#3. 随机端口映射)
- [十四、Docker Compose](#十四、Docker Compose)
-
- [1. Docker Compose 是什么?](#1. Docker Compose 是什么?)
- [2. 为什么需要 Compose?](#2. 为什么需要 Compose?)
- [3. Compose 核心概念](#3. Compose 核心概念)
- [4. Compose 示例:Spring Boot + MySQL + Redis](#4. Compose 示例:Spring Boot + MySQL + Redis)
- [5. Compose 常用命令](#5. Compose 常用命令)
- [6. depends_on 的注意点](#6. depends_on 的注意点)
- [7. 面试回答模板](#7. 面试回答模板)
- [十五、Docker 部署常见中间件](#十五、Docker 部署常见中间件)
-
- [1. Nginx](#1. Nginx)
- [2. MySQL](#2. MySQL)
- [3. Redis](#3. Redis)
- [十六、Docker Registry 和镜像发布](#十六、Docker Registry 和镜像发布)
-
- [1. 镜像发布流程](#1. 镜像发布流程)
- [2. 打 tag](#2. 打 tag)
- [3. 推送镜像](#3. 推送镜像)
- [4. 拉取镜像](#4. 拉取镜像)
- [5. 为什么公司需要私有镜像仓库?](#5. 为什么公司需要私有镜像仓库?)
- [十七、Docker 微服务部署流程](#十七、Docker 微服务部署流程)
-
- [1. 单个 Java 服务 Docker 化流程](#1. 单个 Java 服务 Docker 化流程)
- [2. 多服务部署流程](#2. 多服务部署流程)
- [3. 项目回答模板](#3. 项目回答模板)
- [十八、Docker 监控与可视化](#十八、Docker 监控与可视化)
-
- [1. Docker 原生命令](#1. Docker 原生命令)
- [2. Portainer](#2. Portainer)
- [3. CAdvisor + InfluxDB + Grafana](#3. CAdvisor + InfluxDB + Grafana)
- [十九、Docker 安全](#十九、Docker 安全)
-
- [1. Docker 常见安全风险](#1. Docker 常见安全风险)
- [2. 安全最佳实践](#2. 安全最佳实践)
-
- [1)尽量不用 root 用户运行](#1)尽量不用 root 用户运行)
- [2)避免使用 privileged](#2)避免使用 privileged)
- 3)限制资源
- 4)使用可信镜像
- 5)不要把敏感文件打进镜像
- [6)不要随便挂载 Docker socket](#6)不要随便挂载 Docker socket)
- [3. 面试回答模板](#3. 面试回答模板)
- [二十、Docker 常见排错](#二十、Docker 常见排错)
-
- [1. 容器启动后立刻退出](#1. 容器启动后立刻退出)
- [2. 端口访问不通](#2. 端口访问不通)
- [3. 容器之间访问不通](#3. 容器之间访问不通)
- [4. MySQL 数据丢失](#4. MySQL 数据丢失)
- [5. Docker 占满磁盘](#5. Docker 占满磁盘)
- [二十一、Docker 高频面试题汇总](#二十一、Docker 高频面试题汇总)
-
- [1. Docker 是什么?](#1. Docker 是什么?)
- [2. Docker 和虚拟机有什么区别?](#2. Docker 和虚拟机有什么区别?)
- [3. 镜像和容器有什么区别?](#3. 镜像和容器有什么区别?)
- [4. Docker 为什么启动快?](#4. Docker 为什么启动快?)
- [5. Docker 底层怎么实现隔离?](#5. Docker 底层怎么实现隔离?)
- [6. Namespace 和 Cgroup 的区别?](#6. Namespace 和 Cgroup 的区别?)
- [7. Docker 镜像为什么是分层的?](#7. Docker 镜像为什么是分层的?)
- [8. docker run 做了什么?](#8. docker run 做了什么?)
- [9. docker exec 和 docker attach 区别?](#9. docker exec 和 docker attach 区别?)
- [10. COPY 和 ADD 区别?](#10. COPY 和 ADD 区别?)
- [11. RUN、CMD、ENTRYPOINT 区别?](#11. RUN、CMD、ENTRYPOINT 区别?)
- [12. EXPOSE 和 -p 的区别?](#12. EXPOSE 和 -p 的区别?)
- [13. Docker 数据卷有什么用?](#13. Docker 数据卷有什么用?)
- [14. Volume 和 Bind Mount 区别?](#14. Volume 和 Bind Mount 区别?)
- [15. Docker 网络有哪些模式?](#15. Docker 网络有哪些模式?)
- [16. 容器之间为什么不要写死 IP?](#16. 容器之间为什么不要写死 IP?)
- [17. Docker Compose 有什么作用?](#17. Docker Compose 有什么作用?)
- [18. depends_on 能保证 MySQL 完全启动好吗?](#18. depends_on 能保证 MySQL 完全启动好吗?)
- [19. docker commit 和 Dockerfile 哪个更适合生产?](#19. docker commit 和 Dockerfile 哪个更适合生产?)
- [20. 如何减小 Docker 镜像体积?](#20. 如何减小 Docker 镜像体积?)
- [21. 什么是虚悬镜像?](#21. 什么是虚悬镜像?)
- [22. Docker 容器为什么会自动退出?](#22. Docker 容器为什么会自动退出?)
- [23. Docker 怎么限制容器资源?](#23. Docker 怎么限制容器资源?)
- [24. Docker 容器日志怎么看?](#24. Docker 容器日志怎么看?)
- [25. Docker 生产环境有哪些注意点?](#25. Docker 生产环境有哪些注意点?)
- [二十二、后端校招 Docker 考点覆盖检查](#二十二、后端校招 Docker 考点覆盖检查)
- 二十三、简历项目表达模板
-
- [1. 简历上写 Docker,面试官可能怎么问?](#1. 简历上写 Docker,面试官可能怎么问?)
- [2. 项目回答模板](#2. 项目回答模板)
- [3. 面试官追问:为什么不用直接在服务器上部署?](#3. 面试官追问:为什么不用直接在服务器上部署?)
- [4. 面试官追问:Docker 和 Kubernetes 是什么关系?](#4. 面试官追问:Docker 和 Kubernetes 是什么关系?)
- [二十四、Docker 面试复习路线](#二十四、Docker 面试复习路线)
- [二十五、一句话总结 Docker](#二十五、一句话总结 Docker)
适用场景:后端开发校招、实习面试、Docker 快速复习
复习目标:每次面试前根据本讲义快速回顾 Docker 的核心概念、底层原理、常用命令、Dockerfile、网络、数据卷、Compose、部署排错和项目表达。
一、Docker 到底解决了什么问题?
1. 传统部署的问题
传统后端项目部署通常是:
text
写代码
↓
打包 jar / war / 二进制文件
↓
上传服务器
↓
安装运行环境
↓
修改配置
↓
启动服务
这种方式容易出现几个问题:
-
环境不一致
- 开发环境能跑,测试环境不能跑。
- 测试环境能跑,生产环境又报错。
- JDK、MySQL、Redis、Nginx、系统依赖版本不一致。
-
部署过程复杂
- 每台服务器都要手动安装依赖。
- 配置文件、端口、目录权限都可能出错。
-
迁移成本高
- 换一台机器要重新搭建环境。
- 很难保证新机器和旧机器完全一致。
-
扩容麻烦
- 想多启动几个实例,需要手动复制配置、改端口、启动进程。
2. Docker 的核心思想
Docker 的核心思想是:
text
把应用程序 + 运行环境 + 依赖库 + 配置文件 打包成镜像,
然后在任意安装了 Docker 的机器上运行这个镜像。
也就是常说的:
text
Build once, run anywhere.
一次构建,到处运行。
3. Docker 的核心价值
Docker 主要解决以下问题:
-
环境一致性
- 镜像中包含应用运行所需环境。
- 本地、测试、生产可以使用同一个镜像。
-
快速部署
- 不需要在宿主机上手动安装一堆依赖。
- 一个
docker run就可以启动服务。
-
应用隔离
- 不同容器之间有独立的进程空间、网络空间和文件系统视图。
- 一个服务崩溃一般不会直接影响另一个服务。
-
方便扩缩容
- 同一个镜像可以启动多个容器实例。
- 配合 Docker Compose 或 Kubernetes 可以批量编排。
-
适合 CI/CD
- 代码提交后自动构建镜像。
- 镜像推送到仓库。
- 服务器拉取指定版本镜像并启动容器。
二、Docker 和虚拟机的区别
1. 虚拟机架构
虚拟机大致结构:
text
物理机
└── Host OS
└── Hypervisor
├── Guest OS + App1
├── Guest OS + App2
└── Guest OS + App3
每个虚拟机都有完整的 Guest OS,因此资源占用较大,启动较慢,但是隔离性更强。
2. Docker 容器架构
Docker 容器大致结构:
text
物理机
└── Host OS
└── Docker Engine
├── Container1: App1 + 依赖
├── Container2: App2 + 依赖
└── Container3: App3 + 依赖
容器不是完整虚拟机,它共享宿主机内核,本质上是被隔离和限制的一组进程。
3. Docker 和虚拟机对比
| 对比项 | 虚拟机 | Docker 容器 |
|---|---|---|
| 虚拟化层级 | 硬件级虚拟化 | 操作系统级虚拟化 |
| 操作系统 | 每个虚拟机有完整 Guest OS | 共享宿主机内核 |
| 启动速度 | 较慢 | 较快,通常秒级 |
| 资源占用 | 较高 | 较低 |
| 镜像大小 | 通常较大 | 通常较小 |
| 隔离性 | 更强 | 相对较弱 |
| 适用场景 | 强隔离、多系统环境 | 微服务、快速部署、CI/CD |
4. 面试回答模板
面试官问:Docker 和虚拟机有什么区别?
可以这样回答:
Docker 是操作系统级虚拟化,虚拟机是硬件级虚拟化。虚拟机通过 Hypervisor 模拟硬件,每个虚拟机都有完整的 Guest OS,因此隔离性强,但是资源占用大、启动慢。Docker 容器共享宿主机内核,通过 Namespace 做资源隔离,通过 Cgroup 做资源限制,通过镜像提供独立的文件系统环境,所以 Docker 更轻量、启动更快、部署更方便,适合微服务和 CI/CD 场景。
三、Docker 架构与核心组件
1. Docker 整体架构
Docker 是典型的 Client-Server 架构:
text
Docker Client
|
| docker run / docker pull / docker build
v
Docker Daemon
|
| 管理镜像、容器、网络、数据卷
v
Container Runtime
|
v
Containers
2. Docker Client
Docker Client 就是我们平时使用的 docker 命令行工具。
例如:
bash
docker run nginx
docker ps
docker images
docker build -t myapp:v1 .
这些命令本身不直接创建容器,而是把请求发送给 Docker Daemon。
3. Docker Daemon
Docker Daemon 通常叫 dockerd,是 Docker 后台服务。
它负责:
- 管理镜像。
- 创建容器。
- 启动和停止容器。
- 管理网络。
- 管理数据卷。
- 和镜像仓库交互。
4. Docker Image
镜像是一个只读模板,包含:
- 应用程序。
- 运行时环境。
- 依赖库。
- 配置文件。
- 文件系统快照。
例如:
bash
nginx:latest
mysql:5.7
redis:6.0.8
openjdk:17
5. Docker Container
容器是镜像运行起来后的实例。
可以类比为:
text
镜像 = 类
容器 = 对象
镜像 = 程序安装包
容器 = 正在运行的程序实例
一个镜像可以启动多个容器。
6. Docker Registry
镜像仓库用于存储和分发镜像。
常见镜像仓库:
- Docker Hub。
- 阿里云镜像仓库。
- 腾讯云镜像仓库。
- Harbor。
- Docker Registry 私有仓库。
四、镜像、容器、仓库的关系
1. 三者关系
text
Dockerfile
↓ docker build
Image 镜像
↓ docker run
Container 容器
↓ docker push
Registry 镜像仓库
2. 面试回答模板
面试官问:镜像和容器有什么区别?
可以这样回答:
镜像是一个只读模板,包含应用运行所需的文件系统、依赖和配置;容器是镜像运行后的实例。镜像本身不会运行,容器才是真正运行的进程。一个镜像可以启动多个容器,每个容器都会在镜像只读层之上增加自己的可写层,因此不同容器之间的修改互不影响。
五、Docker 常用命令
1. Docker 服务命令
bash
# 启动 Docker
systemctl start docker
# 停止 Docker
systemctl stop docker
# 重启 Docker
systemctl restart docker
# 查看 Docker 状态
systemctl status docker
# 设置开机自启
systemctl enable docker
# 查看 Docker 版本
docker version
# 查看 Docker 详细信息
docker info
# 查看帮助
docker --help
2. 镜像命令
查看本地镜像
bash
docker images
常见字段:
text
REPOSITORY TAG IMAGE ID CREATED SIZE
nginx latest xxxxxxxx 2 weeks ago 190MB
字段含义:
| 字段 | 含义 |
|---|---|
| REPOSITORY | 镜像名 |
| TAG | 标签 |
| IMAGE ID | 镜像 ID |
| CREATED | 创建时间 |
| SIZE | 镜像大小 |
搜索镜像
bash
docker search nginx
拉取镜像
bash
docker pull nginx
docker pull mysql:5.7
docker pull redis:6.0.8
如果不指定 tag,默认拉取 latest。
删除镜像
bash
docker rmi 镜像ID
docker rmi nginx:latest
docker rmi -f 镜像ID
查看 Docker 磁盘占用
bash
docker system df
清理无用资源
bash
docker system prune
谨慎使用:
bash
docker system prune -a
它会清理没有被容器使用的镜像,生产环境慎用。
3. 容器命令
启动容器
bash
docker run nginx
后台启动容器
bash
docker run -d nginx
指定容器名
bash
docker run -d --name mynginx nginx
端口映射
bash
docker run -d --name mynginx -p 8080:80 nginx
含义:
text
宿主机 8080 端口 -> 容器 80 端口
访问宿主机的 8080 端口,实际访问容器内的 80 端口。
交互式启动容器
bash
docker run -it ubuntu /bin/bash
参数说明:
| 参数 | 含义 |
|---|---|
| -i | 保持标准输入打开 |
| -t | 分配伪终端 |
| -d | 后台运行 |
| --name | 指定容器名 |
| -p | 端口映射 |
| -v | 挂载数据卷 |
| --network | 指定网络 |
查看正在运行的容器
bash
docker ps
查看所有容器
bash
docker ps -a
停止容器
bash
docker stop 容器ID或容器名
docker stop 会先发送 SIGTERM,让进程优雅退出,如果超时再强制结束。
强制杀死容器
bash
docker kill 容器ID或容器名
docker kill 默认直接发送 SIGKILL,适合进程卡死时使用。
启动已经停止的容器
bash
docker start 容器ID或容器名
重启容器
bash
docker restart 容器ID或容器名
删除容器
bash
docker rm 容器ID或容器名
docker rm -f 容器ID或容器名
查看容器日志
bash
docker logs 容器名
docker logs -f 容器名
docker logs --tail=100 容器名
进入容器
bash
docker exec -it 容器名 /bin/bash
如果容器没有 bash,可以使用:
bash
docker exec -it 容器名 /bin/sh
推荐使用 docker exec,因为它是在正在运行的容器里开启一个新进程。
宿主机和容器之间拷贝文件
从容器拷贝到宿主机:
bash
docker cp 容器名:/容器路径 /宿主机路径
从宿主机拷贝到容器:
bash
docker cp /宿主机路径 容器名:/容器路径
六、Docker 容器生命周期
1. 容器状态变化
text
镜像
↓ docker run
Created
↓ docker start
Running
↓ docker stop
Exited
↓ docker rm
Deleted
2. 容器为什么会自动退出?
容器的生命周期和容器中的 PID 1 主进程绑定。
如果主进程结束,容器就会退出。
例如:
bash
docker run ubuntu
这个容器会立刻退出。
原因是 Ubuntu 镜像默认没有一个持续运行的前台进程。
正确写法:
bash
docker run -it ubuntu /bin/bash
或者:
bash
docker run -d ubuntu sleep 3600
3. 常见自动退出原因
- 主进程执行完成。
- 启动命令写错。
- 应用启动失败。
- 配置文件错误。
- 端口冲突。
- 权限不足。
- 内存不足被 OOM Kill。
七、Docker 底层核心原理
Docker 容器主要依赖 Linux 内核的三个核心能力:
text
Namespace:资源隔离
Cgroup:资源限制
UnionFS / OverlayFS:镜像分层
1. Namespace:资源隔离
Namespace 的作用是让容器"看起来像自己独占一套系统资源"。
常见 Namespace:
| Namespace | 隔离内容 |
|---|---|
| PID Namespace | 进程号隔离 |
| NET Namespace | 网络设备、IP、端口、路由表隔离 |
| MNT Namespace | 挂载点和文件系统视图隔离 |
| IPC Namespace | 进程间通信隔离 |
| UTS Namespace | 主机名、域名隔离 |
| USER Namespace | 用户和用户组隔离 |
例如,在容器中执行:
bash
ps aux
只能看到容器内部的进程,看不到宿主机上的全部进程,这就是 PID Namespace 的效果。
2. Cgroup:资源限制
Cgroup 用来限制和统计容器使用的资源。
可以限制:
- CPU。
- 内存。
- 磁盘 IO。
- 进程数量。
- 网络资源等。
限制内存:
bash
docker run -d --name app --memory=512m nginx
限制 CPU:
bash
docker run -d --name app --cpus=1.5 nginx
3. Namespace 和 Cgroup 的区别
面试回答:
Namespace 解决的是"看不见"的问题,也就是隔离资源;Cgroup 解决的是"用多少"的问题,也就是限制资源。Docker 通过 Namespace 让容器拥有独立的进程、网络、文件系统视图,通过 Cgroup 限制容器最多能使用多少 CPU、内存、IO 等资源。
4. UnionFS / OverlayFS:镜像分层
Docker 镜像不是一个单独的大文件,而是由多层只读层组成。
例如 Dockerfile:
dockerfile
FROM ubuntu
RUN apt-get update
RUN apt-get install -y vim
COPY app.jar /app/app.jar
每一条指令都可能形成一层镜像层。
镜像分层的好处:
-
复用
- 多个镜像可以共享相同基础层。
-
节省空间
- 相同层只保存一份。
-
加速构建
- 前面的层没有变化时,可以使用缓存。
-
加快分发
- 拉取镜像时只拉本地没有的层。
5. 容器可写层
镜像层是只读的,容器启动后会在镜像只读层之上增加一个可写层。
text
容器可写层
镜像层 N
镜像层 N-1
...
基础镜像层
当容器修改文件时,修改发生在容器可写层,不会改变原始镜像。
所以可以总结为:
text
镜像 = 多层只读层
容器 = 镜像只读层 + 容器可写层
八、Docker 镜像详解
1. 镜像是什么?
镜像是一个只读文件系统模板,它包含应用运行所需要的一切内容。
例如 Java 项目镜像可能包含:
- Linux 基础文件系统。
- JDK/JRE。
- 应用 jar 包。
- 配置文件。
- 启动命令。
2. 镜像 tag 是什么?
例如:
bash
mysql:5.7
mysql:8.0
redis:6.0.8
nginx:latest
: 后面的就是 tag。
注意:
text
latest 不一定代表最新稳定版本,它只是一个普通标签。
生产环境不建议使用:
bash
nginx:latest
mysql:latest
推荐使用明确版本:
bash
nginx:1.25
mysql:5.7
redis:6.0.8
这样可以避免镜像版本变化导致环境不可控。
3. docker commit 是什么?
docker commit 可以把一个容器当前状态提交成新的镜像。
示例:
bash
docker run -it ubuntu /bin/bash
# 容器内安装 vim
apt-get update
apt-get install -y vim
# 宿主机提交镜像
docker commit 容器ID myubuntu:vim
4. docker commit 的问题
docker commit 不适合生产标准化构建。
原因:
- 操作过程不可追溯。
- 不知道镜像里具体改了什么。
- 不利于团队协作。
- 不利于 CI/CD。
- 镜像容易越来越臃肿。
生产中更推荐使用 Dockerfile。
面试回答:
docker commit适合临时保存容器状态或教学演示,但生产环境应该使用 Dockerfile。因为 Dockerfile 是声明式的、可版本管理、可重复构建、可审计,更适合团队协作和 CI/CD。
九、Dockerfile 详解
1. Dockerfile 是什么?
Dockerfile 是用来构建 Docker 镜像的文本文件。
它通过一组指令描述:
text
从哪个基础镜像开始
安装哪些依赖
复制哪些文件
暴露哪些端口
容器启动时执行什么命令
构建命令:
bash
docker build -t myapp:v1 .
2. Dockerfile 常用指令
FROM
指定基础镜像。
dockerfile
FROM openjdk:17-jdk-slim
每个 Dockerfile 通常都要从 FROM 开始。
WORKDIR
指定工作目录。
dockerfile
WORKDIR /app
后续的 COPY、RUN、CMD 等默认都在这个目录下执行。
COPY
把宿主机构建上下文中的文件复制到镜像里。
dockerfile
COPY target/app.jar /app/app.jar
ADD
和 COPY 类似,但额外支持:
- 自动解压本地 tar 包。
- 从 URL 添加文件。
一般建议优先使用 COPY,因为 COPY 语义更明确。
面试回答:
COPY 只负责复制文件,行为简单可控;ADD 功能更多,可以自动解压本地 tar 包,也可以从 URL 添加文件,但容易产生不透明行为。所以生产中除非需要自动解压 tar 包,否则优先使用 COPY。
RUN
在镜像构建阶段执行命令。
dockerfile
RUN apt-get update && apt-get install -y vim
注意:
text
RUN 是构建镜像时执行,不是容器启动时执行。
CMD
指定容器启动时的默认命令,可以被 docker run 后面的命令覆盖。
dockerfile
CMD ["java", "-jar", "app.jar"]
ENTRYPOINT
指定容器启动时固定执行的入口命令。
dockerfile
ENTRYPOINT ["java", "-jar", "app.jar"]
ENTRYPOINT 更适合作为容器固定入口。
EXPOSE
声明容器内部服务监听端口。
dockerfile
EXPOSE 8080
注意:
text
EXPOSE 只是声明,不会自动完成宿主机端口映射。
真正映射端口需要:
bash
docker run -p 8080:8080 myapp:v1
ENV
设置环境变量。
dockerfile
ENV JAVA_OPTS="-Xms512m -Xmx512m"
VOLUME
声明挂载点。
dockerfile
VOLUME ["/data"]
USER
指定容器运行用户。
dockerfile
USER appuser
生产中不建议容器长期以 root 用户运行。
3. RUN、CMD、ENTRYPOINT 区别
| 指令 | 执行时机 | 作用 | 是否容易被覆盖 |
|---|---|---|---|
| RUN | 镜像构建时 | 安装依赖、执行构建命令 | 构建后不可变 |
| CMD | 容器启动时 | 默认启动命令或默认参数 | 容易被 docker run 覆盖 |
| ENTRYPOINT | 容器启动时 | 固定入口命令 | 需要 --entrypoint 覆盖 |
面试回答:
RUN 是构建镜像时执行的,用来安装依赖、创建目录、编译文件等,会形成新的镜像层。CMD 和 ENTRYPOINT 是容器启动时执行的。CMD 更像默认命令或默认参数,容易被
docker run后面的命令覆盖;ENTRYPOINT 更像固定入口,适合定义容器一定要执行的主程序。实际生产中常用ENTRYPOINT + CMD组合,ENTRYPOINT 指定主程序,CMD 提供默认参数。
示例:
dockerfile
ENTRYPOINT ["java", "-jar", "app.jar"]
CMD ["--spring.profiles.active=prod"]
4. Java 微服务 Dockerfile 示例
dockerfile
FROM openjdk:17-jdk-slim
WORKDIR /app
COPY target/demo.jar /app/demo.jar
EXPOSE 8080
ENTRYPOINT ["java", "-jar", "/app/demo.jar"]
构建镜像:
bash
docker build -t demo:v1 .
运行容器:
bash
docker run -d --name demo -p 8080:8080 demo:v1
十、Dockerfile 镜像优化
1. 为什么要优化镜像?
镜像太大会导致:
- 拉取慢。
- 发布慢。
- 占用磁盘。
- 安全风险变大。
- CI/CD 构建时间增加。
2. 镜像优化方法
1)选择更小的基础镜像
例如:
dockerfile
FROM openjdk:17-jdk-slim
比完整 Linux 发行版更小。
也可以使用 Alpine:
dockerfile
FROM alpine
但是 Alpine 使用 musl libc,部分 Java/native 依赖可能存在兼容性问题,不能无脑使用。
2)减少镜像层数
不推荐:
dockerfile
RUN apt-get update
RUN apt-get install -y vim
RUN apt-get install -y curl
推荐:
dockerfile
RUN apt-get update \
&& apt-get install -y vim curl \
&& rm -rf /var/lib/apt/lists/*
3)利用构建缓存
Dockerfile 中不经常变化的内容放前面,经常变化的内容放后面。
例如:
dockerfile
COPY pom.xml .
RUN mvn dependency:go-offline
COPY src ./src
RUN mvn package
这样依赖没变时,可以复用缓存。
4)使用 .dockerignore
.dockerignore 用来排除不需要进入构建上下文的文件。
示例:
text
.git
target
*.log
.idea
.vscode
node_modules
好处:
- 减小构建上下文。
- 加快构建速度。
- 避免敏感文件被打进镜像。
5)多阶段构建
Java 项目示例:
dockerfile
FROM maven:3.9-eclipse-temurin-17 AS builder
WORKDIR /app
COPY pom.xml .
COPY src ./src
RUN mvn clean package -DskipTests
FROM eclipse-temurin:17-jre
WORKDIR /app
COPY --from=builder /app/target/demo.jar /app/demo.jar
EXPOSE 8080
ENTRYPOINT ["java", "-jar", "/app/demo.jar"]
优点:
- 最终镜像不包含 Maven。
- 最终镜像不包含源码。
- 最终镜像不包含构建缓存。
- 镜像更小。
- 安全风险更低。
十一、Docker 数据卷
1. 为什么需要数据卷?
容器默认是临时的。
如果容器被删除,容器可写层中的数据也会丢失。
例如 MySQL 容器:
text
MySQL 数据写在容器内部
↓
容器删除
↓
数据库数据丢失
所以数据库、配置文件、日志等需要持久化。
2. 数据卷解决什么问题?
数据卷主要解决:
- 数据持久化。
- 宿主机和容器共享文件。
- 容器之间共享数据。
- 容器删除后数据仍然保留。
3. 三种挂载方式
Volume
Docker 管理的数据卷。
bash
docker volume create mydata
docker run -d \
--name mysql \
-v mydata:/var/lib/mysql \
mysql:5.7
特点:
- Docker 管理。
- 默认在
/var/lib/docker/volumes/下。 - 适合生产持久化。
- 可移植性比 bind mount 好。
Bind Mount
把宿主机指定目录挂载到容器内。
bash
docker run -d \
--name nginx \
-v /host/nginx/conf:/etc/nginx/conf.d \
nginx
特点:
- 灵活。
- 依赖宿主机路径。
- 适合开发调试、配置文件挂载。
- 宿主机路径变化会影响容器。
tmpfs
数据存在宿主机内存中,不落盘。
bash
docker run --tmpfs /tmp nginx
特点:
- 容器停止后数据消失。
- 适合临时敏感数据。
- 不适合持久化。
4. -v 和 --mount 区别
-v 写法简洁:
bash
docker run -v /host/data:/container/data nginx
--mount 语义更清楚:
bash
docker run \
--mount type=bind,source=/host/data,target=/container/data \
nginx
生产中更推荐 --mount,因为语义明确,不容易写错。
5. 只读挂载
bash
docker run -d \
-v /host/conf:/etc/nginx/conf.d:ro \
nginx
:ro 表示容器只能读,不能改。
适合挂载配置文件,防止容器内误修改宿主机文件。
6. 面试回答模板
面试官问:Docker 数据卷有什么作用?
可以这样回答:
容器本身是临时的,容器删除后可写层数据会丢失。数据卷用于把数据保存到容器生命周期之外,实现持久化和共享。Docker 常见挂载方式有 volume、bind mount、tmpfs。生产环境更推荐 volume 保存业务数据,bind mount 常用于开发调试和配置文件挂载,tmpfs 适合临时敏感数据。
十二、Docker 网络
1. Docker 为什么需要网络?
容器是隔离的,但是服务之间需要通信。
例如:
text
Java 服务容器 需要访问 MySQL 容器
Java 服务容器 需要访问 Redis 容器
Nginx 容器 需要转发请求到 Java 容器
所以 Docker 需要提供容器网络能力。
2. 常见网络命令
bash
# 查看网络
docker network ls
# 查看网络详情
docker network inspect bridge
# 创建网络
docker network create mynet
# 删除网络
docker network rm mynet
3. Docker 常见网络模式
| 网络模式 | 说明 | 使用场景 |
|---|---|---|
| bridge | 默认模式,容器连接到 docker0 或自定义 bridge | 单机容器通信 |
| host | 容器直接使用宿主机网络 | 高性能、少隔离场景 |
| none | 容器没有外部网络 | 安全隔离场景 |
| container | 和另一个容器共享网络命名空间 | sidecar 模式 |
| 自定义 bridge | 用户创建的 bridge 网络 | 推荐用于服务名通信 |
4. bridge 网络原理
默认 bridge 模式大致是:
text
容器 eth0
↓
veth pair
↓
docker0 bridge
↓
宿主机网卡
↓
外部网络
容器访问外网时,通常通过宿主机 NAT 转发。
外部访问容器时,需要端口映射:
bash
docker run -p 8080:80 nginx
5. 为什么推荐自定义网络?
默认 bridge 网络下,容器之间如果通过 IP 通信不稳定,因为容器重建后 IP 可能变化。
创建自定义网络:
bash
docker network create mynet
启动容器:
bash
docker run -d --name mysql --network mynet mysql:5.7
docker run -d --name app --network mynet myapp:v1
此时 app 容器可以直接通过容器名访问 MySQL:
text
mysql:3306
因为自定义网络内置 DNS 解析。
6. host 网络模式
bash
docker run --network host nginx
host 模式下,容器不再拥有独立网络命名空间,而是直接使用宿主机网络。
特点:
- 网络性能好。
- 没有端口映射。
- 端口冲突风险高。
- 隔离性较差。
7. none 网络模式
bash
docker run --network none nginx
特点:
- 容器只有 loopback。
- 不能访问外部网络。
- 适合极端隔离或手动配置网络。
8. container 网络模式
bash
docker run --network container:容器名 镜像名
表示新容器和指定容器共享同一个网络命名空间。
适合 sidecar 场景。
9. 面试回答模板
面试官问:Docker 容器之间怎么通信?
可以这样回答:
同一台机器上的 Docker 容器可以通过 Docker 网络通信。默认 bridge 网络可以通过容器 IP 通信,但 IP 会随着容器重建变化,所以生产或开发编排中更推荐创建自定义 bridge 网络。自定义网络内置 DNS,容器可以通过容器名或服务名互相访问。跨主机容器通信通常交给 Kubernetes、Overlay 网络或服务发现系统处理。
十三、端口映射
1. 为什么需要端口映射?
容器内部服务监听的是容器内端口,宿主机外部无法直接访问。
需要用 -p 映射端口:
bash
docker run -d -p 8080:80 nginx
含义:
text
宿主机端口 8080 -> 容器端口 80
访问:
text
http://宿主机IP:8080
实际进入容器内的 80 端口。
2. 多端口映射
bash
docker run -d \
-p 8080:8080 \
-p 5005:5005 \
myapp:v1
3. 随机端口映射
bash
docker run -d -P nginx
-P 会把 Dockerfile 中 EXPOSE 声明的端口随机映射到宿主机端口。
十四、Docker Compose
1. Docker Compose 是什么?
Docker Compose 是用来定义和运行多容器应用的工具。
它通过一个 compose.yaml 或 docker-compose.yml 文件描述:
- 服务。
- 镜像。
- 构建方式。
- 端口。
- 环境变量。
- 数据卷。
- 网络。
- 依赖关系。
然后一条命令启动多个容器:
bash
docker compose up -d
老版本命令是:
bash
docker-compose up -d
2. 为什么需要 Compose?
不用 Compose 时,启动一个后端项目可能要执行:
bash
docker network create mynet
docker run -d --name mysql --network mynet -p 3306:3306 mysql:5.7
docker run -d --name redis --network mynet -p 6379:6379 redis:6
docker run -d --name app --network mynet -p 8080:8080 myapp:v1
问题:
- 命令太长。
- 容器多了容易乱。
- 启动顺序不好管理。
- 环境变量不好维护。
- 不利于团队协作。
Compose 把这些配置写入 YAML 文件,方便版本管理和一键启动。
3. Compose 核心概念
service
一个 service 表示一类容器。
例如:
yaml
services:
mysql:
image: mysql:5.7
mysql 就是一个服务。
project
一个 Compose 文件启动的一组服务属于同一个 project。
默认 project 名通常取当前目录名。
4. Compose 示例:Spring Boot + MySQL + Redis
yaml
services:
mysql:
image: mysql:5.7
container_name: mysql
environment:
MYSQL_ROOT_PASSWORD: root
MYSQL_DATABASE: demo
ports:
- "3306:3306"
volumes:
- mysql_data:/var/lib/mysql
networks:
- app_net
redis:
image: redis:6.0.8
container_name: redis
ports:
- "6379:6379"
networks:
- app_net
app:
image: myapp:v1
container_name: app
ports:
- "8080:8080"
environment:
SPRING_DATASOURCE_URL: jdbc:mysql://mysql:3306/demo?useSSL=false
SPRING_REDIS_HOST: redis
depends_on:
- mysql
- redis
networks:
- app_net
volumes:
mysql_data:
networks:
app_net:
注意:
在 Compose 网络中,服务之间可以通过服务名访问。
例如:
text
mysql:3306
redis:6379
5. Compose 常用命令
bash
# 启动
docker compose up -d
# 停止并删除容器、网络
docker compose down
# 查看服务
docker compose ps
# 查看日志
docker compose logs -f
# 重新构建并启动
docker compose up -d --build
# 停止服务
docker compose stop
# 启动服务
docker compose start
# 进入服务容器
docker compose exec app /bin/bash
6. depends_on 的注意点
depends_on 只保证容器启动顺序,不保证服务已经完全可用。
例如:
yaml
depends_on:
- mysql
只能说明 MySQL 容器先启动,但不代表 MySQL 已经初始化完成并可以连接。
生产中更可靠的方式:
- 应用自身实现连接重试。
- 使用 healthcheck。
- 使用脚本等待依赖服务 ready。
7. 面试回答模板
面试官问:Docker Compose 有什么作用?
可以这样回答:
Docker Compose 用来编排单机多容器应用。它通过 YAML 文件声明多个服务的镜像、端口、环境变量、网络和数据卷,一条
docker compose up -d就能启动整套环境。对于后端开发来说,Compose 很适合本地开发、测试环境和小规模部署,比如同时启动 Spring Boot、MySQL、Redis、Nginx。它解决了多个docker run命令难维护、容器 IP 变化、启动配置分散的问题。
十五、Docker 部署常见中间件
1. Nginx
bash
docker run -d \
--name nginx \
-p 80:80 \
-v /host/nginx/conf:/etc/nginx/conf.d \
-v /host/nginx/html:/usr/share/nginx/html \
nginx
常见用途:
- 静态资源服务。
- 反向代理。
- 负载均衡。
- HTTPS 入口。
2. MySQL
bash
docker run -d \
--name mysql57 \
-p 3306:3306 \
-e MYSQL_ROOT_PASSWORD=root \
-v mysql_data:/var/lib/mysql \
mysql:5.7
注意:
- MySQL 数据目录必须挂载。
- 生产不要使用简单密码。
- 字符集和时区最好显式配置。
- 生产数据库通常不建议单容器裸跑,需要考虑备份、主从、高可用和监控。
3. Redis
bash
docker run -d \
--name redis \
-p 6379:6379 \
-v /host/redis/redis.conf:/etc/redis/redis.conf \
redis:6.0.8 \
redis-server /etc/redis/redis.conf
注意:
- Redis 配置文件可以挂载。
- 需要持久化时配置 AOF/RDB。
- 生产环境不要暴露无密码 Redis 到公网。
十六、Docker Registry 和镜像发布
1. 镜像发布流程
text
编写 Dockerfile
↓
docker build 构建镜像
↓
docker tag 打标签
↓
docker login 登录仓库
↓
docker push 推送镜像
↓
服务器 docker pull 拉取镜像
↓
docker run 启动容器
2. 打 tag
bash
docker tag myapp:v1 registry.example.com/myapp:v1
3. 推送镜像
bash
docker push registry.example.com/myapp:v1
4. 拉取镜像
bash
docker pull registry.example.com/myapp:v1
5. 为什么公司需要私有镜像仓库?
面试回答:
公司内部镜像通常包含业务代码和内部依赖,不适合推送到公共 Docker Hub。私有仓库可以统一管理镜像版本、权限、安全扫描和发布流程。CI/CD 构建镜像后推送到私有仓库,测试或生产服务器再从仓库拉取指定版本镜像部署,保证发布过程可追溯、可回滚。
十七、Docker 微服务部署流程
1. 单个 Java 服务 Docker 化流程
text
编写代码
↓
Maven/Gradle 打包 jar
↓
编写 Dockerfile
↓
docker build 构建镜像
↓
docker run 启动容器
↓
暴露端口访问服务
示例:
bash
mvn clean package -DskipTests
docker build -t order-service:v1 .
docker run -d \
--name order-service \
-p 8080:8080 \
order-service:v1
2. 多服务部署流程
text
Nginx
↓
Gateway
↓
业务服务
↓
MySQL / Redis / MQ
可以使用 Compose 编排:
bash
docker compose up -d
3. 项目回答模板
面试官问:你项目中怎么使用 Docker?
可以这样回答:
我在项目中主要用 Docker 做环境标准化和服务部署。开发阶段,我使用 Docker Compose 启动 MySQL、Redis、Nginx 等依赖服务,避免每个同学手动安装环境。服务打包后,我会编写 Dockerfile,把 Spring Boot jar 包构建成镜像,并通过环境变量区分开发、测试和生产配置。部署时通过镜像仓库分发镜像,服务器拉取指定 tag 后启动容器。这样可以保证环境一致、部署可重复,也方便后续扩容和回滚。
十八、Docker 监控与可视化
1. Docker 原生命令
查看容器资源使用:
bash
docker stats
可以看到:
- CPU 使用率。
- 内存使用。
- 网络 IO。
- 磁盘 IO。
- PIDs。
查看容器日志:
bash
docker logs -f 容器名
查看容器内部信息:
bash
docker inspect 容器名
2. Portainer
Portainer 是 Docker 图形化管理工具。
可以管理:
- 容器。
- 镜像。
- 网络。
- 数据卷。
- Compose Stack。
启动示例:
bash
docker run -d \
-p 9000:9000 \
--name portainer \
--restart=always \
-v /var/run/docker.sock:/var/run/docker.sock \
-v portainer_data:/data \
portainer/portainer-ce
访问:
text
http://服务器IP:9000
注意:
挂载 /var/run/docker.sock 权限非常高,因为这相当于允许 Portainer 控制 Docker Daemon。
3. CAdvisor + InfluxDB + Grafana
经典组合:
text
CAdvisor:采集容器指标
InfluxDB:存储时序数据
Grafana:展示监控图表
作用:
- 监控容器 CPU。
- 监控容器内存。
- 监控网络 IO。
- 监控磁盘 IO。
- 可视化展示趋势。
十九、Docker 安全
1. Docker 常见安全风险
- 容器以 root 用户运行。
- 镜像来源不可信。
- 镜像里有漏洞依赖。
- 容器挂载了敏感宿主机目录。
- Docker socket 被暴露。
- 容器使用
--privileged。 - 容器端口直接暴露公网。
- 容器资源不限制导致宿主机被打满。
2. 安全最佳实践
1)尽量不用 root 用户运行
Dockerfile 中创建普通用户:
dockerfile
RUN useradd -m appuser
USER appuser
2)避免使用 privileged
危险命令:
bash
docker run --privileged nginx
--privileged 会给容器很高权限,容易突破隔离。
3)限制资源
bash
docker run -d \
--memory=512m \
--cpus=1.0 \
nginx
4)使用可信镜像
优先使用官方镜像或公司内部审核过的镜像。
5)不要把敏感文件打进镜像
例如:
text
.env
id_rsa
数据库密码文件
云厂商 AK/SK
使用 .dockerignore 排除。
6)不要随便挂载 Docker socket
危险挂载:
bash
-v /var/run/docker.sock:/var/run/docker.sock
容器一旦拿到 Docker socket,基本可以控制宿主机上的 Docker。
3. 面试回答模板
面试官问:Docker 容器安全吗?
可以这样回答:
Docker 提供的是进程级隔离,不等同于虚拟机级强隔离。它通过 Namespace 隔离进程、网络、挂载点等资源,通过 Cgroup 限制 CPU、内存等资源,再配合 capabilities、seccomp、AppArmor/SELinux 等机制提升安全性。但如果容器以 root 运行、使用 privileged、挂载宿主机敏感目录或暴露 Docker socket,就可能带来较大风险。因此生产中要使用可信镜像、最小权限、资源限制、非 root 用户和镜像扫描。
二十、Docker 常见排错
1. 容器启动后立刻退出
排查命令:
bash
docker ps -a
docker logs 容器名
docker inspect 容器名
常见原因:
- 主进程执行完退出。
- 启动命令写错。
- 配置文件错误。
- 端口冲突。
- 缺少环境变量。
- 权限不足。
- OOM。
2. 端口访问不通
排查命令:
bash
docker ps
docker port 容器名
docker logs 容器名
检查点:
- 容器是否运行。
- 是否使用
-p做端口映射。 - 应用是否监听
0.0.0.0,而不是只监听127.0.0.1。 - 宿主机防火墙是否放行。
- 云服务器安全组是否放行。
- 容器内服务是否真正启动。
3. 容器之间访问不通
排查命令:
bash
docker network ls
docker network inspect 网络名
docker exec -it app /bin/sh
ping mysql
检查点:
- 是否在同一个 Docker 网络。
- 是否使用服务名访问。
- 是否写死了容器 IP。
- 目标服务端口是否正确。
- 服务是否启动完成。
4. MySQL 数据丢失
常见原因:
- 没有挂载
/var/lib/mysql。 - 删除容器时数据在容器可写层里一起被删。
- 错误执行了
docker compose down -v。
解决:
bash
-v mysql_data:/var/lib/mysql
注意:
bash
docker compose down -v
会删除 volume,慎用。
5. Docker 占满磁盘
查看:
bash
docker system df
清理:
bash
docker container prune
docker image prune
docker volume prune
docker network prune
docker system prune
谨慎清理所有未使用镜像:
bash
docker system prune -a
二十一、Docker 高频面试题汇总
1. Docker 是什么?
Docker 是一个容器化平台,用来把应用程序及其依赖打包成镜像,并以容器的方式运行。它解决了环境一致性、部署复杂、扩缩容困难等问题,非常适合微服务部署和 CI/CD。
2. Docker 和虚拟机有什么区别?
Docker 是操作系统级虚拟化,共享宿主机内核;虚拟机是硬件级虚拟化,每个虚拟机有完整 Guest OS。Docker 更轻量、启动更快、资源占用更少,但隔离性弱于虚拟机。
3. 镜像和容器有什么区别?
镜像是只读模板,容器是镜像运行后的实例。镜像不运行,容器才是真正运行的进程。一个镜像可以启动多个容器,每个容器有自己的可写层。
4. Docker 为什么启动快?
因为容器不需要启动完整操作系统,它共享宿主机内核,本质上是启动一个被隔离和限制的进程,所以通常可以秒级启动。
5. Docker 底层怎么实现隔离?
主要依靠 Linux Namespace 实现进程、网络、挂载点、主机名等隔离,依靠 Cgroup 实现 CPU、内存、IO 等资源限制,再通过 UnionFS/OverlayFS 实现镜像分层和容器可写层。
6. Namespace 和 Cgroup 的区别?
Namespace 负责资源隔离,让容器看不到其他容器和宿主机的资源;Cgroup 负责资源限制和统计,控制容器最多能用多少 CPU、内存、IO 等。
7. Docker 镜像为什么是分层的?
分层可以复用基础层、节省存储、加快构建、加快镜像分发。Dockerfile 中的指令会形成镜像层,相同的层可以被多个镜像共享。
8. docker run 做了什么?
大致流程:
text
检查本地是否存在镜像
↓
没有则从仓库 pull
↓
基于镜像创建容器可写层
↓
创建 Namespace 和 Cgroup
↓
配置网络、挂载数据卷
↓
启动容器主进程
9. docker exec 和 docker attach 区别?
docker exec 是在运行中的容器里启动一个新的进程,常用于进入容器排查问题。
docker attach 是连接到容器主进程的标准输入输出,退出时可能影响主进程,不如 exec 常用。
10. COPY 和 ADD 区别?
COPY 只复制文件或目录,语义简单明确;ADD 除了复制,还支持自动解压本地 tar 包和从 URL 添加文件。生产中优先使用 COPY,除非确实需要 ADD 的自动解压能力。
11. RUN、CMD、ENTRYPOINT 区别?
RUN 在镜像构建时执行,用于安装依赖和构建文件。CMD 和 ENTRYPOINT 在容器启动时执行。CMD 是默认命令或参数,容易被覆盖;ENTRYPOINT 是固定入口,适合作为容器主程序。
12. EXPOSE 和 -p 的区别?
EXPOSE 只是 Dockerfile 中的端口声明,不会真正发布端口。-p 才会把容器端口映射到宿主机端口。
13. Docker 数据卷有什么用?
数据卷用于数据持久化和宿主机/容器之间共享数据。因为容器删除后可写层数据会丢失,所以 MySQL、Redis、日志、配置文件等通常需要挂载数据卷。
14. Volume 和 Bind Mount 区别?
Volume 由 Docker 管理,适合生产数据持久化;Bind Mount 直接挂载宿主机指定路径,灵活但依赖宿主机目录结构,更适合开发调试和配置文件挂载。
15. Docker 网络有哪些模式?
常见有 bridge、host、none、container、自定义 bridge。默认是 bridge。开发和单机部署更推荐自定义 bridge,因为容器之间可以通过容器名或服务名通信。
16. 容器之间为什么不要写死 IP?
容器删除重建后 IP 可能变化。应该使用自定义 Docker 网络,让容器通过容器名或 Compose 服务名访问。
17. Docker Compose 有什么作用?
Docker Compose 用 YAML 文件编排多容器应用,可以定义服务、镜像、端口、环境变量、网络和数据卷,一条命令启动整套环境。适合本地开发、测试环境和小规模部署。
18. depends_on 能保证 MySQL 完全启动好吗?
不能。depends_on 只能保证容器启动顺序,不保证服务已经 ready。应用需要实现重试机制,或者配合 healthcheck。
19. docker commit 和 Dockerfile 哪个更适合生产?
Dockerfile 更适合生产。因为 Dockerfile 可读、可维护、可版本控制、可重复构建。docker commit 只能保存某个容器的当前状态,不利于审计和协作。
20. 如何减小 Docker 镜像体积?
常见方法:
- 使用更小的基础镜像。
- 使用多阶段构建。
- 合并 RUN 指令并清理缓存。
- 使用
.dockerignore。 - 不把源码、日志、临时文件、密钥打进镜像。
- 只安装运行时必要依赖。
21. 什么是虚悬镜像?
虚悬镜像是 repository 和 tag 都是 <none> 的镜像,通常是反复构建新镜像后旧镜像失去标签产生的。
查看:
bash
docker images -f dangling=true
清理:
bash
docker image prune
22. Docker 容器为什么会自动退出?
因为容器的生命周期依赖主进程。主进程结束,容器就退出。常见原因是启动命令执行完了、应用启动失败、配置错误或依赖服务不可用。
23. Docker 怎么限制容器资源?
可以通过参数限制:
bash
docker run --memory=512m --cpus=1.0 nginx
底层依靠 Cgroup 控制 CPU、内存等资源。
24. Docker 容器日志怎么看?
bash
docker logs 容器名
docker logs -f 容器名
docker logs --tail=100 容器名
生产中通常会把日志采集到 ELK、Loki、Prometheus/Grafana 等系统。
25. Docker 生产环境有哪些注意点?
- 不使用 latest 镜像标签。
- 不以 root 用户运行容器。
- 不随便使用 privileged。
- 不暴露 Docker socket。
- 使用数据卷持久化数据。
- 限制 CPU 和内存。
- 使用私有镜像仓库。
- 做日志和监控。
- 对镜像进行漏洞扫描。
- 配置健康检查和重启策略。
二十二、后端校招 Docker 考点覆盖检查
| 校招常见考点 | 本讲义是否覆盖 | 对应章节 |
|---|---|---|
| Docker 是什么 | 已覆盖 | 第一章、二十一章 |
| Docker 解决什么问题 | 已覆盖 | 第一章 |
| Docker 和虚拟机区别 | 已覆盖 | 第二章 |
| 镜像、容器、仓库关系 | 已覆盖 | 第三章、第四章 |
| Docker 常用命令 | 已覆盖 | 第五章 |
| 容器生命周期 | 已覆盖 | 第六章 |
| Namespace / Cgroup | 已覆盖 | 第七章 |
| 镜像分层原理 | 已覆盖 | 第七章、第八章 |
| Dockerfile | 已覆盖 | 第九章 |
| RUN/CMD/ENTRYPOINT | 已覆盖 | 第九章 |
| COPY/ADD | 已覆盖 | 第九章 |
| 镜像优化 | 已覆盖 | 第十章 |
| 数据卷 | 已覆盖 | 第十一章 |
| Docker 网络模式 | 已覆盖 | 第十二章 |
| 端口映射 | 已覆盖 | 第十三章 |
| Docker Compose | 已覆盖 | 第十四章 |
| MySQL/Redis/Nginx 容器部署 | 已覆盖 | 第十五章 |
| 私有镜像仓库 | 已覆盖 | 第十六章 |
| 微服务部署 | 已覆盖 | 第十七章 |
| 监控与可视化 | 已覆盖 | 第十八章 |
| 安全实践 | 已覆盖 | 第十九章 |
| 排错思路 | 已覆盖 | 第二十章 |
| 项目表达 | 已覆盖 | 第十七章、二十三章 |
结论:
text
这份讲义已经基本覆盖后端校招 Docker 高频面试题。
如果目标是大厂后端面试,还需要重点掌握:
1. Namespace 和 Cgroup 的底层作用;
2. 镜像分层和 OverlayFS;
3. Dockerfile 优化;
4. Docker 网络通信;
5. Docker Compose 编排;
6. 项目中如何使用 Docker;
7. 容器启动失败、端口不通、数据丢失等排错思路。
二十三、简历项目表达模板
1. 简历上写 Docker,面试官可能怎么问?
常见追问:
- 你项目中为什么使用 Docker?
- 你写过 Dockerfile 吗?
- 你怎么部署 Spring Boot 服务?
- 你怎么处理 MySQL/Redis 数据持久化?
- 你用过 Docker Compose 吗?
- 容器之间怎么通信?
- 容器启动失败怎么排查?
- Docker 镜像怎么优化?
- Docker 和 Kubernetes 有什么关系?
- 生产中使用 Docker 要注意什么?
2. 项目回答模板
可以这样回答:
我在项目中主要使用 Docker 做环境隔离和部署标准化。比如本地开发时,我用 Docker Compose 启动 MySQL、Redis、Nginx 等依赖服务,所有同学使用同一份 Compose 文件,避免环境不一致。后端服务打包成 jar 后,我编写 Dockerfile 构建镜像,通过环境变量注入数据库地址、Redis 地址和运行环境。部署时使用指定版本 tag 的镜像,避免 latest 带来的不可控问题。对于数据库和 Redis,我会通过 volume 挂载数据目录,保证容器重建后数据不丢失。排查问题时主要使用
docker ps、docker logs、docker inspect、docker exec和docker stats。
3. 面试官追问:为什么不用直接在服务器上部署?
可以这样回答:
直接在服务器上部署需要手动安装 JDK、MySQL、Redis、Nginx 等依赖,环境容易不一致,而且迁移和回滚成本高。Docker 把应用和依赖封装成镜像,可以做到一次构建、多环境运行。部署时只需要拉取指定镜像版本并启动容器,环境更可控,也更适合后续 CI/CD 和弹性扩容。
4. 面试官追问:Docker 和 Kubernetes 是什么关系?
可以这样回答:
Docker 主要解决单机容器构建、运行和管理问题,而 Kubernetes 主要解决集群级容器编排问题。Docker 可以把应用打包成镜像并启动容器;Kubernetes 则负责在多台机器上调度这些容器,提供服务发现、负载均衡、自动扩缩容、滚动更新、故障自愈等能力。简单说,Docker 负责容器化,Kubernetes 负责容器编排。
二十四、Docker 面试复习路线
第一层:必须会
- Docker 是什么。
- Docker 和虚拟机区别。
- 镜像、容器、仓库关系。
- 常用命令。
- Dockerfile 基本写法。
- 数据卷。
- 端口映射。
- Docker Compose 基础。
第二层:面试加分
- Namespace 和 Cgroup。
- 镜像分层和 OverlayFS。
- RUN/CMD/ENTRYPOINT 区别。
- COPY/ADD 区别。
- 自定义网络和服务名通信。
- 多阶段构建。
- 镜像优化。
- 容器排错。
第三层:项目拔高
- Docker 在项目中的使用方式。
- Docker Compose 编排开发环境。
- 私有镜像仓库和 CI/CD。
- 日志、监控、健康检查。
- 安全实践。
- 和 Kubernetes 的衔接。
二十五、一句话总结 Docker
Docker 的本质不是一台轻量虚拟机,而是一个被 Linux Namespace 隔离、被 Cgroup 限制资源、基于镜像分层文件系统运行的特殊进程。
它通过镜像把应用和依赖标准化,通过容器把应用快速运行起来,通过网络和数据卷解决通信与持久化问题,通过 Compose 进一步解决多容器应用的一键编排问题。