03.镜像、容器、仓库:三个概念打通 Docker 任督二脉
03. 镜像、容器、仓库:三个概念打通 Docker 任督二脉
上一篇你已经装好了 Docker,敲下
docker version看到了那行帅气的 "Server: Docker Engine"。但紧接着问题来了——打开文档,满屏都是 Image、Container、Registry,这三个词翻来覆去地出现,到底是什么意思?它们之间又是什么关系?别慌,这篇文章用大白话把这三个概念一次性讲透,讲完你就能自己动手跑起第一个容器。

▲ 镜像是"光盘"、容器是"跑起来的游戏"、仓库是"应用商店"——记住这三个比喻,Docker 的世界你就懂了一半。
一、先搞懂关系:一个比喻打通全局
在深入技术细节之前,先用一个生活场景建立直觉:
| Docker 概念 | 生活类比 | 一句话解释 |
|---|---|---|
| 镜像(Image) | 📀 一张游戏光盘 | 只读的"安装包",里面打包好了运行所需的一切 |
| 容器(Container) | 🎮 用光盘启动的游戏 | 镜像跑起来后的"活"实例,可以读写、可以操作 |
| 仓库(Registry) | 🏪 Steam 游戏平台 | 存放和分发镜像的"应用商店" |
先看一张图,把三者的关系印在脑子里:

▲ 整条链路就两步:从仓库 docker pull 拉镜像,再用镜像 docker run 起容器。
再看一张"教科书版"的关系图:
┌──────────────┐ │ 仓库 Registry │ ← 镜像的"应用商店"(Docker Hub) └──────┬───────┘ │ docker pull(下载镜像) ▼ ┌──────────────┐ │ 镜像 Image │ ← 只读模板(像一张光盘) └──────┬───────┘ │ docker run(启动容器) ▼ ┌──────────────┐ │ 容器 Container │ ← 运行中的实例(像光盘跑起来的游戏) └──────────────┘
核心关系:仓库提供镜像,镜像启动容器。 就这么简单,别想复杂了。
二、镜像(Image):你的程序被"冻"住了
2.1 镜像到底是什么?
镜像就是一个只读的打包模板。它把运行一个程序所需要的所有东西——代码、运行环境、系统库、配置文件——全部冻结成一个"快照"。
用面向对象的话说:镜像就是"类"(Class),它是一个模板,本身不会运行,但你可以用它创建无数个"实例"。
一句话:镜像不是"程序本身",而是"程序的完整说明书 + 全套原料",随时能烤出一模一样的成品。
2.2 镜像不是一坨,而是"千层饼"
Docker 镜像并不是一整个大文件,它由多个只读层叠加而成,这就是所谓的联合文件系统(UnionFS):

▲ 镜像是"千层饼":底层是基础系统,往上叠运行时、依赖、代码。底层越通用,被复用得越多。
┌─────────────────────────┐ │ 第 4 层:你的应用代码 │ ← 最上层,最后添加 ├─────────────────────────┤ │ 第 3 层:Python 依赖包 │ ├─────────────────────────┤ │ 第 2 层:Python 运行时 │ ├─────────────────────────┤ │ 第 1 层:Ubuntu 基础系统 │ ← 最底层,最先构建 └─────────────────────────┘
分层的好处是什么?
-
复用:你拉 10 个基于 Ubuntu 的镜像,底层那一份 Ubuntu 只需要下载一次
-
快速构建:改了代码但没改依赖?只有最上面那一层需要重建,底下的全部复用
-
节省空间:多个镜像共享相同的底层
划重点:分层是 Docker 又快又省的"秘密武器",也是后面理解 Dockerfile、多阶段构建的基石。
2.3 动手拉一个镜像
来,实操一下。从 Docker Hub 拉取 Nginx 镜像:
docker pull nginx
输出大致如下:
Using default tag: latest latest: Pulling from library/nginx a2abf6c4d29d: Pull complete a9edb18cadd1: Pull complete 589b7251471a: Pull complete 186b1aaa4aa6: Pull complete b4df32aa5a72: Pull complete a0bcbecc962e: Pull complete Digest: sha256:0d17b565c37bcbd895e9d9231505a320e4b0e3f0c25b3d6e0a8e4d1b6c1a8f Status: Downloaded newer image for nginx:latest docker.io/library/nginx:latest
每一行 Pull complete 就是一层镜像被下载完成。你看到 6 层,说明这个 Nginx 镜像由 6 个只读层组成——正好印证了刚才讲的"千层饼"。
2.4 查看本地镜像
docker images
输出:
REPOSITORY TAG IMAGE ID CREATED SIZE nginx latest 605c77e624dd 2 weeks ago 141MB
| 字段 | 含义 |
|---|---|
| REPOSITORY | 镜像名(可以理解为"软件名") |
| TAG | 标签(可以理解为"版本号") |
| IMAGE ID | 镜像的唯一标识(SHA256 哈希缩写) |
| CREATED | 创建时间 |
| SIZE | 镜像大小 |
2.5 标签(Tag):同一个软件的不同版本
标签是镜像的"版本别名"。同一个软件可以有多个 tag:
# 拉取指定版本 docker pull nginx:1.24 docker pull nginx:1.25-alpine # 再查看 docker images
REPOSITORY TAG IMAGE ID CREATED SIZE nginx 1.25-alpine a8758716bb6a 3 days ago 42.6MB nginx 1.24 92b11f67642b 1 week ago 142MB nginx latest 605c77e624dd 2 weeks ago 141MB
几个关键点:
| 标签 | 含义 |
|---|---|
latest | 默认标签,不代表最新版本,只是"你没指定 tag 时的默认值" |
1.24 | 具体版本号,生产环境推荐用这种 |
alpine | 基于 Alpine Linux 的精简版,体积小但功能可能略少 |
slim | Debian 精简版,比 alpine 兼容性更好 |
血泪教训:生产环境千万不要用
latest!因为你不知道哪天latest指向了哪个版本,出了问题排查起来非常痛苦。一定要写死版本号,比如nginx:1.24。
三、容器(Container):镜像"活"过来了
3.1 容器的本质
如果说镜像是"类",那容器就是"实例对象"。
镜像 + 一个可写层 = 容器。
┌─────────────────────────┐ │ 可写层(容器专属) │ ← 容器运行时产生的文件写在这里 ├─────────────────────────┤ │ 只读层 4:应用代码 │ ├─────────────────────────┤ │ 只读层 3:依赖包 │ ← 这些层和镜像完全一样 ├─────────────────────────┤ │ 只读层 2:运行时 │ 多个容器共享这些只读层 ├─────────────────────────┤ │ 只读层 1:基础系统 │ └─────────────────────────┘
每个容器都有自己独立的可写层,你在容器里创建的文件、修改的配置,都写在这一层。容器删除后,这一层也跟着消失(除非你用了数据卷,后面文章会讲)。
一个镜像可以"复制"出无数个互不干扰的容器——就像一张光盘能刻出很多张游戏盘,大家各玩各的。
3.2 用镜像启动容器
docker run -d --name my-nginx -p 8080:80 nginx:1.24
拆解这条命令:
| 参数 | 含义 |
|---|---|
run | 创建并启动一个容器 |
-d | 后台运行(detached 模式),不占终端 |
--name my-nginx | 给容器起个名字叫 my-nginx(不指定会自动生成一个随机名) |
-p 8080:80 | 把宿主机的 8080 端口映射到容器的 80 端口 |
nginx:1.24 | 用哪个镜像启动 |
输出:
a1b2c3d4e5f6g7h8i9j0...(一长串容器 ID)
3.3 验证容器在运行
docker ps
输出:
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES a1b2c3d4e5f6 nginx:1.24 "/docker-entrypoint.…" 5 seconds ago Up 4 seconds 0.0.0.0:8080->80/tcp my-nginx
| 字段 | 含义 |
|---|---|
| CONTAINER ID | 容器唯一标识 |
| IMAGE | 用的哪个镜像 |
| COMMAND | 容器启动时执行的命令 |
| STATUS | 运行状态(Up = 正在跑) |
| PORTS | 端口映射 |
| NAMES | 容器名称 |
3.4 浏览器访问
打开浏览器,访问 http://localhost:8080,你应该能看到 Nginx 的欢迎页面:
Welcome to nginx! If you see this page, the nginx web server is successfully installed and working.
恭喜,你的第一个容器已经跑起来了! 🎉
3.5 docker run vs docker create + docker start
docker run 其实是两步合一:
| 命令 | 做的事 |
|---|---|
docker create | 只创建容器,但不启动 |
docker start | 启动一个已创建的容器 |
docker run | = create + start,一步到位 |
什么时候用分步?当你想在启动前先配置好容器参数的时候:
# 第一步:只创建 docker create --name my-app -p 3000:3000 my-image:latest # 第二步:检查配置 docker inspect my-app # 第三步:确认无误后再启动 docker start my-app
日常使用 99% 的场景直接用
docker run就行,分步操作了解即可,面试可能会考。
3.6 容器的生命周期
create → start → running → stop → exited → rm(删除)
↑ │
└──── start(重启)─────┘
对应命令:
# 停止容器(发送 SIGTERM,优雅关闭) docker stop my-nginx # 强制停止(发送 SIGKILL,立刻杀掉) docker kill my-nginx # 启动已停止的容器 docker start my-nginx # 重启容器 docker restart my-nginx # 删除容器(必须先停止) docker rm my-nginx # 强制删除(不管是否在运行) docker rm -f my-nginx
注意:
docker stop会先发送 SIGTERM 信号,给容器 10 秒的"善后时间",超时才发 SIGKILL。所以一般用stop而不是kill,避免数据丢失。
3.7 查看容器日志
容器跑在后台,出问题了怎么看日志?
# 查看全部日志 docker logs my-nginx # 实时跟踪(类似 tail -f) docker logs -f my-nginx # 只看最近 20 行 docker logs --tail 20 my-nginx
3.8 进入容器内部
# 在容器内开一个交互式终端 docker exec -it my-nginx /bin/bash
进去之后你就像 SSH 到了一台新机器:
root@a1b2c3d4e5f6:/# ls bin boot dev docker-entrypoint.d docker-entrypoint.sh etc home ... root@a1b2c3d4e5f6:/# cat /etc/nginx/nginx.conf user nginx; worker_processes auto; ... root@a1b2c3d4e5f6:/# exit # 退出容器
execvsattach:docker exec是开一个新终端进去,退出不影响容器运行;docker attach是"接管"容器的主进程终端,退出可能导致容器停止。永远用exec。
四、仓库(Registry):镜像的"应用商店"
4.1 Docker Hub:官方仓库
Docker Hub(hub.docker.com)是 Docker 官方运营的公共镜像仓库,绝大多数开源软件的镜像都能在上面找到:
| 镜像 | 描述 | 拉取量 |
|---|---|---|
nginx | Web 服务器 | 10B+ |
mysql | 关系型数据库 | 10B+ |
redis | 缓存数据库 | 10B+ |
python | Python 运行环境 | 1B+ |
node | Node.js 运行环境 | 1B+ |
4.2 搜索镜像
docker search nginx --limit 5
输出:
NAME DESCRIPTION STARS OFFICIAL AUTOMATED nginx Official build of Nginx. 19000+ [OK] jwilder/nginx-proxy Automated Nginx reverse proxy for docker c… 2200+ nginxinc/nginx-unprivileged Unprivileged NGINX Dockerfiles 100+ bitnami/nginx Bitnami nginx Docker Image 150+ rapidfort/nginx RapidFort optimized, hardened image for NGINX 50+
| 字段 | 含义 |
|---|---|
| NAME | 镜像名 |
| DESCRIPTION | 简介 |
| STARS | 点赞数(越高越受欢迎) |
| OFFICIAL | [OK] 表示官方维护的镜像 |
选镜像的原则:优先用 OFFICIAL(官方镜像),其次选 Stars 高的。不知名的镜像可能包含恶意代码或过时依赖。
4.3 拉取指定版本的镜像
# 从 Docker Hub 拉取 docker pull mysql:8.0 docker pull redis:7-alpine docker pull python:3.11-slim
拉取地址的规则:
[仓库地址/]用户名/镜像名:标签
| 写法 | 实际含义 |
|---|---|
nginx:1.24 | 从 Docker Hub 官方库拉取 |
bitnami/nginx:latest | 从 Docker Hub 的 bitnami 用户下拉取 |
registry.cn-hangzhou.aliyuncs.com/xxx/myapp:v1 | 从阿里云私有仓库拉取 |
4.4 推送自己的镜像
你也可以把自己的镜像推到仓库,让全世界(或团队内部)都能用:
# 1. 先登录 Docker Hub docker login # 输入用户名和密码 # 2. 给镜像打上你的用户名前缀 docker tag my-app:latest your-dockerhub-username/my-app:v1.0 # 3. 推送 docker push your-dockerhub-username/my-app:v1.0
4.5 私有仓库
企业场景下不可能把代码推到公网,这时候需要搭建私有仓库:
| 方案 | 特点 |
|---|---|
| Docker Registry | 官方提供的轻量私有仓库,一行命令就能跑起来 |
| Harbor | VMware 开源的企业级镜像仓库,带权限管理、漏洞扫描 |
| 阿里云 ACR | 云厂商托管服务,免运维 |
| AWS ECR | AWS 生态首选 |
后面文章会专门讲 Harbor 的搭建和使用。
五、完整实操:从零跑一个 Nginx 网站
把前面学的串起来,完整走一遍流程:
5.1 拉取镜像
docker pull nginx:1.24
5.2 查看镜像
docker images nginx
REPOSITORY TAG IMAGE ID CREATED SIZE nginx 1.24 92b11f67642b 1 week ago 142MB
5.3 启动容器
docker run -d \ --name my-website \ -p 8080:80 \ nginx:1.24
5.4 确认运行状态
docker ps
CONTAINER ID IMAGE COMMAND STATUS PORTS NAMES f3e8a1b2c3d4 nginx:1.24 "/docker-entrypoint.…" Up 3 seconds 0.0.0.0:8080->80/tcp my-website
5.5 浏览器验证
访问 http://localhost:8080,看到 Nginx 欢迎页 ✅
5.6 查看日志
docker logs my-website
/docker-entrypoint.sh: /docker-entrypoint.d/ is not empty, will attempt to perform configuration /docker-entrypoint.sh: Looking for shell scripts in /docker-entrypoint.d/ ... 2024/01/15 08:30:00 [notice] 1#1: start worker processes 2024/01/15 08:30:00 [notice] 1#1: start worker process 29
5.7 进入容器改一下欢迎页
docker exec -it my-website /bin/bash
在容器内部:
# 修改欢迎页 echo "<h1>Hello from Docker!</h1>" > /usr/share/nginx/html/index.html # 退出容器 exit
刷新浏览器,页面变成了 "Hello from Docker!" ✅
5.8 清理
# 停止容器 docker stop my-website # 删除容器 docker rm my-website # 删除镜像(可选) docker rmi nginx:1.24
删除顺序很重要:先停容器 → 再删容器 → 最后才能删镜像。容器还"活着"的时候镜像是删不掉的。
六、常用命令速查表
| 场景 | 命令 |
|---|---|
| 拉取镜像 | docker pull nginx:1.24 |
| 查看本地镜像 | docker images |
| 搜索仓库镜像 | docker search nginx |
| 删除镜像 | docker rmi nginx:1.24 |
| 创建并启动容器 | docker run -d --name x -p 80:80 nginx |
| 查看运行中容器 | docker ps |
| 查看所有容器(含已停止) | docker ps -a |
| 停止容器 | docker stop my-nginx |
| 启动已停止的容器 | docker start my-nginx |
| 重启容器 | docker restart my-nginx |
| 进入容器 | docker exec -it my-nginx /bin/bash |
| 查看容器日志 | docker logs -f my-nginx |
| 删除容器 | docker rm my-nginx |
| 强制删除容器 | docker rm -f my-nginx |
七、常见踩坑点
| 问题 | 原因 | 解决方案 |
|---|---|---|
docker pull 超时 | 国内网络访问 Docker Hub 慢 | 配置镜像加速器(阿里云、腾讯云都提供) |
| 端口被占用 | 宿主机 8080 已被其他程序使用 | 换个端口:-p 9090:80 |
| 容器启动后立即退出 | 容器内没有前台进程 | 用 -d 时确保程序在前台运行 |
docker exec 报 "not running" | 容器已经停了 | 先 docker start,再 exec |
| 删不掉镜像 | 有容器还在用这个镜像 | 先删容器,再删镜像 |
| 容器内数据丢失 | 容器删除后可写层也被删了 | 用数据卷(Volume)挂载,后面文章会讲 |
八、本文要点回顾
| 概念 | 类比 | 核心要点 |
|---|---|---|
| 镜像 Image | 光盘 / 类 | 只读模板,分层存储,一个镜像可以创建多个容器 |
| 容器 Container | 游戏 / 对象 | 镜像的运行实例,有自己的可写层,删除后可写层消失 |
| 仓库 Registry | 应用商店 | 存储和分发镜像,Docker Hub 是最大的公共仓库 |
三个要点带走:
-
镜像分层存储是 Docker 高效的秘密——多个镜像共享底层,节省空间和带宽
-
生产环境写死 tag,永远不要用
latest -
先停容器再删容器再删镜像,顺序不能乱
金句:镜像负责"描述世界长什么样",容器负责"在世界上真正活着",仓库负责"把世界分享给所有人"。
九、下集预告
你已经能拉镜像、跑容器了。但每次都要敲一长串 docker run 参数,换个环境还得重新记。下一篇讲 Docker 常用命令速查手册——把所有日常操作用一张表整理清楚,从 Hello World 到容器全生命周期管理,用到就查,查了就敲。
更多推荐

所有评论(0)