1. Docker 基础概念:镜像、容器、仓库、Docker 架构详解
1.1 先聊聊我的 Docker 入门故事
我记得第一次接触 Docker 是在一个微服务改造项目里。
那时候团队还在用虚拟机部署,每次环境不一致都让人抓狂。
“我的机器上跑得好好的啊”——这句话我听了不下十遍。
后来我把服务全部容器化,环境问题基本绝迹了。
我个人习惯把 Docker 比作一个“轻量级集装箱系统”:
- 镜像就是集装箱的模板——造好以后能重复用。
- 容器就是真正装货的箱子——启动后就能跑。
- 仓库就是码头仓库——放着各种模板等你拿。
嗯,这么理解基本不跑偏。
1.2 镜像(Image)—— 只读的“运行环境快照”
镜像说白了就是一个分层构建的只读文件系统。
里面包含了运行应用所需的一切:代码、运行时、系统工具、库、配置。
我在项目中遇到过一个问题:基础镜像选 ubuntu:latest,结果半年后镜像更新把 glibc 版本升了,导致旧二进制无法运行。
所以我建议:
- 生产环境用
镜像名:具体版本,别用:latest。 - 镜像层数不是越少越好,但太多层也会影响拉取和构建速度。
docker history 镜像名,每层都对应一个 Dockerfile 指令。
1.3 容器(Container)—— 镜像的运行实例
镜像是静态的,容器是动态的。
你拉下一个 nginx 镜像,然后 docker run,它就变成一个可读写的容器层加在镜像只读层上面。
你以为容器里能随便改文件?能改,但一旦容器删除,修改就丢了——除非挂载了卷。
我曾经犯过一个低级错误:在生产容器里直接改了配置文件,没做卷映射。第二天重启容器,配置全没了……
-v 或 --mount 挂载数据卷。
1.4 仓库(Registry / Repository)—— 镜像的分发中心
Docker Hub 是最知名的公共仓库,但你也能搭建私有仓库(比如 Harbor、AWS ECR)。
镜像的完整名称格式是:仓库地址/命名空间/镜像名:标签。
# 从 Docker Hub 拉取官方 nginx
docker pull nginx:1.25
# 推送到私有仓库
docker tag myapp:1.0 registry.example.com/dev/myapp:1.0
docker push registry.example.com/dev/myapp:1.0
国内团队最好用阿里云或腾讯云的容器镜像服务,拉取速度快很多。
1.5 Docker 架构—— 客户端 / 守护进程 / 运行时
谈到架构,你脑海里有这么一张图就行:
- Docker Client ( CLI ) —— 你敲命令的地方
- Docker Daemon ( dockerd ) —— 管理镜像、容器、网络的主力
- containerd —— 真正负责容器生命周期的守护进程
- runc —— 最底层,按 OCI 标准生成容器进程
- 镜像仓库 —— 远程存储你需要的镜像
如果你只是用 docker run,其实背后发生了一系列调用:
- 客户端发 REST 请求给 dockerd
- dockerd 通过 gRPC 调用 containerd
- containerd 调用 runc 启动容器
为什么会这样?因为 Docker 公司后来把容器运行时捐给了 CNCF,变成了 containerd + runc 的组合。
1.6 镜像、容器、仓库核心区别
| 概念 | 本质 | 是否可写 | 生命周期 | 典型操作 |
|---|---|---|---|---|
| 镜像 | 只读模板 | 否(构建时写,运行只读) | 持久存储 | pull、build、tag、push |
| 容器 | 镜像的运行实例 | 是(可写层+数据卷) | 临时(可 stop/start/rm) | run、exec、logs、stop |
| 仓库 | 镜像的存储和分发中心 | — | 持久 | login、pull、push |
1.7 避坑指南 —— 我踩过的几个坑
- 镜像太大导致拉取慢: 曾经我直接用 python:3.10-slim 当基础镜像,结果项目依赖里带了 opencv,不得不换用更精简的 slim-buster。记住原则——用最小化 base 镜像。
- 容器时区问题: 默认 UTC。我建议在 Dockerfile 里加一行
ENV TZ=Asia/Shanghai,并安装 tzdata。 - 容器里跑 PID 1: 直接在 entrypoint 运行脚本而不处理 SIGTERM,会导致容器 stop 时无法优雅退出。用
exec前缀或 tini 作为 init。
1.8 一个简单的“理论变实践”场景
你想快速验证新环境?试试这个命令(不要在生产用):
# 拉一个最小的 Alpine Linux 镜像
docker pull alpine:3.19
# 启动一个交互式容器,自动删除
docker run --rm -it alpine:3.19 /bin/sh
# 在容器里查看 /etc/os-release
/ # cat /etc/os-release
如果你看到 NAME="Alpine Linux",说明镜像、容器、仓库三个概念在你的机器上已经串通了。嗯,就这么简单。
1.9 最后的个人经验小结
学 Docker 别跳概念。镜像、容器、仓库是地基;架构里的 dockerd、containerd、runc 是承重墙。
我见过太多人只会 docker run 就觉得自己会了。结果容器挂掉不会排查,镜像层叠冲突搞不清楚。
你把这一章的概念真正消化了,后续的构建、优化、调试才会顺。