类比
镜像(Image) ≈ 类(模板、静态的)
容器(Container) ≈ 对象(实例、运行中的)
镜像是静态的文件,包含运行程序所需的一切:代码、运行时、库、环境变量、配置文件。
容器是镜像的运行实例——一个被隔离的进程。
镜像的结构
分层(Layer)
Docker 镜像由多个只读层叠加而成。
每一层对应 Dockerfile 中的一条指令(如 RUN、COPY)。
优势:
一、共享与复用
多个镜像如果共享相同的层(如相同的基础镜像),这些层只存储一份。
这大幅节省了磁盘空间和镜像拉取的带宽。
二、构建缓存
如果某条指令及其上下文没变,Docker 可以复用之前构建的层,跳过重新构建。
实践含义:Dockerfile 中,把变化频率低的指令放前面(如安装依赖),变化频率高的放后面(如复制源码)——这样能最大化利用缓存。
联合文件系统(Union FS)
多层通过联合文件系统(如 overlay2)合并成一个统一的文件视图。
上层的文件会"遮蔽"下层同路径的文件。
容器的可写层
容器启动时,Docker 在镜像的只读层之上添加一个可写层(容器层)。
写时复制(Copy-on-Write):
- 读取文件:从下面的只读层读;
- 修改文件:先把文件复制到可写层,再修改;
- 删除文件:在可写层标记一个"whiteout",遮蔽下层文件。
关键含义:
容器内的文件修改不会写入镜像。 多个容器可以共享同一个镜像,各自的可写层独立。
容器删除后,可写层也删除了——因此容器内的数据默认不持久。
持久化:Volume
要在容器重启后保留数据,需要卷(Volume):
一、命名卷(Named Volume)
由 Docker 管理的存储位置,与容器生命周期解耦。
docker run -v mydata:/var/lib/mysql mysql二、绑定挂载(Bind Mount)
把宿主机的某个目录挂载到容器中。
docker run -v /host/path:/container/path myapp开发时常用(挂载源码目录,实现热重载)。
三、tmpfs 挂载:只存在于内存中。
常用命令
# 构建镜像
docker build -t myapp:1.0 .
# 查看镜像
docker images
# 运行容器
docker run -d -p 8080:80 --name mycontainer myapp:1.0
# 查看运行中的容器
docker ps
# 停止/启动/删除容器
docker stop mycontainer
docker start mycontainer
docker rm mycontainer
# 删除镜像
docker rmi myapp:1.0
# 清理未使用的资源
docker system prune几个实践要点
一、镜像要小
- 使用精简基础镜像(如 Alpine、slim 版本、distroless);
- 多阶段构建(Multi-stage Build):在一个阶段编译,在另一个阶段只复制产物——避免把编译工具链打包进最终镜像;
- 清理包管理器缓存。
二、一个容器一个进程(推荐)
虽然技术上可以在一个容器里跑多个进程,但这违背了容器的设计初衷(不利于隔离、扩缩容、日志处理)。
三、注意层缓存失效
COPY . . 会把整个上下文复制进去,任何文件变化都会导致后续层的缓存失效。用 .dockerignore 排除无关文件。
四、不要在容器里保存状态
容器应该是无状态、可随时销毁重建的。状态放在卷或外部服务中。
说明:本文为容器技术的知识整理。