欢迎光临
我们一直在努力

Docker到底是什么?一文读懂镜像、容器、仓库三大核心概念

Docker到底是什么?一文读懂镜像、容器、仓库三大核心概念

引言:从一个真实场景说起

你有没有遇到过这样的场景?

周五下午,同事把代码提交到 Git,留下一句“我本地跑得好好的”,然后潇洒下班。周一你拉下代码,npm install 报错、Python 版本不对、MySQL 连不上、Redis 没启动……折腾一整天,项目还是跑不起来。

这个经典的“环境不一致”问题,几乎每个开发者都经历过。而 Docker 正是为了解决它而生的:把应用和它依赖的环境一起打包,做到“一次构建,到处运行”。

但很多初学者在刚接触 Docker 时,会被“镜像、容器、仓库”这三个词绕晕:它们到底有什么区别?为什么拉取的是镜像,运行的却是容器?这篇文章就带你一次性把它们彻底搞清楚。


一、先建立整体认知:Docker 是什么

Docker 是一个开源的容器化平台,它允许开发者将应用及其依赖打包到一个轻量级、可移植的“容器”中,然后发布到任何支持 Docker 的机器上运行。

如果用一句话概括 Docker 的核心价值:

Docker = 标准化的打包 + 标准化的分发 + 标准化的运行

而支撑这套体系的三块基石,就是我们今天要讲的主角:

概念英文类比作用
镜像 Image 类 / 安装包 / 菜谱 只读模板,定义应用和运行环境
容器 Container 对象 / 运行中的程序 / 菜肴 镜像的运行实例,可读写、可启停
仓库 Registry/Repository GitHub / 应用商店 存放和分发镜像的地方

我们先通过一张关系图建立整体印象:

Dockerfile –build–> 镜像(Image) –run–> 容器(Container)
|
push/pull
|
仓库(Registry)

接下来逐一拆解。


二、镜像(Image):容器的“模板”与“菜谱”

2.1 什么是镜像

镜像是一个只读的、包含应用及其运行环境的模板。它由一层一层的文件系统叠加而成(UnionFS),每一层代表一次构建操作。

你可以把镜像理解成“做菜的菜谱”:菜谱本身不能吃,但按照它就能做出一道菜。同样,镜像本身不能运行,但按照它就能启动一个容器。

一个镜像通常包含:

  • 基础操作系统(如 ubuntu:22.04、alpine:3.19)
  • 运行时环境(如 JDK、Python、Node.js)
  • 应用代码或二进制文件
  • 启动配置(如 CMD、ENTRYPOINT、环境变量)

2.2 镜像的命名规范

镜像名遵循如下格式:

[registry地址/][命名空间/]仓库名[:标签]

部分是否必填说明示例
registry地址 否 默认 Docker Hub docker.io、harbor.example.com
命名空间 否 默认 library library、bitnami
仓库名 是 镜像名称 nginx、mysql
标签 否 默认 latest 1.25、8.0、alpine

例如:

nginx # 等价于 docker.io/library/nginx:latest
mysql:8.0 # Docker Hub 官方 MySQL 8.0
bitnami/redis:7.2 # bitnami 命名空间下的 Redis
harbor.example.com/app/api:v1.0.0 # 私有仓库镜像

注意:生产环境绝对不要使用 latest 标签,因为它会随时间变化,导致“同样的命令拉到的镜像却不一样”,难以复现问题。

2.3 镜像常用命令

(1)拉取镜像:docker pull

docker pull nginx:1.25

预期输出:

1.25: Pulling from library/nginx
a2abf6c4d29d: Pull complete
…
Digest: sha256:xxxxxxxx
Status: Downloaded newer image for nginx:1.25
docker.io/library/nginx:1.25

(2)查看本地镜像:docker images

docker images

预期输出:

REPOSITORY TAG IMAGE ID CREATED SIZE
nginx 1.25 a8758716bb6a 2 weeks ago 187MB
mysql 8.0 b1c2d3e4f5a6 3 weeks ago 586MB

常用参数:

参数说明
-a 显示所有镜像(含中间层)
-q 只显示 IMAGE ID
–filter 过滤,如 –filter "dangling=true"
–format 自定义输出格式
(3)查看镜像详情:docker inspect

docker inspect nginx:1.25

输出为 JSON,包含镜像的架构、环境变量、暴露端口、层信息等。可以用 –format 提取关键信息:

docker inspect -f '{{.Architecture}} {{.Os}}' nginx:1.25
# 输出:amd64 linux

(4)删除镜像:docker rmi

docker rmi nginx:1.25

如果镜像正在被容器使用,需要先删除容器,或使用 -f 强制删除:

docker rmi -f nginx:1.25

(5)构建镜像:docker build

docker build -t myapp:v1.0 .

其中 . 表示构建上下文为当前目录,Docker 会读取当前目录下的 Dockerfile。

一个最简单的 Dockerfile:

FROM node:20-alpine
WORKDIR /app
COPY package*.json ./
RUN npm install –production
COPY . .
EXPOSE 3000
CMD ["node", "server.js"]

2.4 镜像的分层机制(重点)

镜像由多个只读层叠加而成,每一层对应 Dockerfile 中的一条指令。例如:

FROM ubuntu:22.04 # 第 1 层
RUN apt-get update # 第 2 层
RUN apt-get install -y curl # 第 3 层
COPY app.py /app/ # 第 4 层

用 docker history 查看:

docker history myapp:v1.0

分层的好处:

  • 复用:多个镜像共享相同的基础层,节省磁盘。
  • 缓存:构建时若某层未变,直接复用缓存,加速构建。
  • 增量分发:推送/拉取时只传变化的层。
  • 最佳实践:把不常变化的指令放在前面(如安装依赖),把常变化的放在后面(如 COPY 代码),最大化利用构建缓存。


    三、容器(Container):镜像的“运行实例”

    3.1 什么是容器

    容器是镜像运行时的实例,是一个独立、隔离的进程环境。它拥有自己的文件系统、网络、进程空间,但共享宿主机的内核。

    用前面的比喻:如果镜像是“菜谱”,那么容器就是“按菜谱做出来的菜”。一道菜谱可以做出很多盘菜,一个镜像也可以启动很多个容器。

    容器与镜像的关键区别:

    维度镜像容器
    可读写 只读 可读写(在镜像之上加一层可写层)
    是否运行 否 是(有进程)
    生命周期 长期存在 可创建、启动、停止、删除
    存储 分层只读 镜像层 + 容器可写层

    3.2 容器常用命令

    (1)运行容器:docker run

    docker run -d –name my-nginx -p 8080:80 nginx:1.25

    参数说明:

    参数含义
    -d 后台运行(detach)
    –name 指定容器名
    -p 8080:80 端口映射,宿主机 8080 → 容器 80
    -v 挂载数据卷
    -e 设置环境变量
    –rm 容器退出后自动删除
    -it 交互式终端

    访问验证:

    curl http://localhost:8080

    预期输出(Nginx 欢迎页 HTML 片段):

    <!DOCTYPE html>
    <html>
    <head>
    <title>Welcome to nginx!</title>
    …

    (2)查看容器:docker ps

    docker ps # 查看运行中的容器
    docker ps -a # 查看所有容器(含已停止)

    预期输出:

    CONTAINER ID IMAGE COMMAND STATUS PORTS NAMES
    f3a1b2c4d5e6 nginx:1.25 "/docker-entrypoint.…" Up 2 minutes 0.0.0.0:8080->80/tcp my-nginx

    (3)进入容器:docker exec

    docker exec -it my-nginx /bin/bash

    进入后可以查看容器内部环境:

    root@f3a1b2c4d5e6:/# cat /etc/os-release
    PRETTY_NAME="Debian GNU/Linux 12 (bookworm)"

    (4)查看日志:docker logs

    docker logs -f –tail 100 my-nginx

    参数说明
    -f 持续跟踪输出
    –tail N 只显示最后 N 行
    -t 显示时间戳
    (5)停止与删除容器

    docker stop my-nginx # 停止
    docker start my-nginx # 启动已停止的容器
    docker restart my-nginx # 重启
    docker rm my-nginx # 删除(需先停止)
    docker rm -f my-nginx # 强制删除运行中的容器

    (6)查看容器详情与资源占用

    docker inspect my-nginx
    docker stats my-nginx

    docker stats 预期输出:

    CONTAINER ID NAME CPU % MEM USAGE / LIMIT MEM % NET I/O BLOCK I/O
    f3a1b2c4d5e6 my-nginx 0.00% 3.5MiB / 7.7GiB 0.05% 1.2kB / 0B 0B / 0B

    3.3 容器的生命周期

    created → running → paused → running → stopped → deleted
    ↑ ↓
    └────── restart ──────┘

    对应命令:

    docker create # 创建但不启动
    docker start # 启动
    docker pause # 暂停
    docker unpause # 恢复
    docker stop # 停止
    docker kill # 强制杀死
    docker rm # 删除

    3.4 容器为什么“轻量”

    容器不是虚拟机,它不模拟硬件、不跑独立内核,而是直接复用宿主机内核,通过 Linux 的 namespace 做隔离、cgroups 做资源限制。所以:

    • 启动速度:容器秒级,虚拟机分钟级
    • 资源占用:容器 MB 级,虚拟机 GB 级
    • 隔离性:容器进程级,虚拟机硬件级

    详细原理见后文“知识扩展”部分。


    四、仓库(Registry):镜像的“应用商店”

    4.1 什么是仓库

    仓库(Registry)是集中存放和分发镜像的服务。最常用的是 Docker Hub,此外还有阿里云容器镜像服务、Harbor、GitHub Container Registry 等。

    这里要区分两个概念:

    • Registry(注册中心):存放多个仓库的服务,如 Docker Hub。
    • Repository(仓库):某个镜像的所有版本集合,如 nginx 仓库包含 1.25、1.24、latest 等标签。

    关系如下:

    Docker Hub (Registry)
    ├── library/nginx (Repository)
    │ ├── 1.25 (Tag)
    │ ├── 1.24 (Tag)
    │ └── latest (Tag)
    └── library/mysql (Repository)
    ├── 8.0 (Tag)
    └── 5.7 (Tag)

    4.2 仓库常用操作

    (1)登录仓库:docker login

    docker login
    # 或登录私有仓库
    docker login harbor.example.com

    (2)给镜像打标签:docker tag

    推送前必须将镜像命名为 仓库地址/命名空间/名称:标签 格式:

    docker tag myapp:v1.0 harbor.example.com/dev/myapp:v1.0

    (3)推送镜像:docker push

    docker push harbor.example.com/dev/myapp:v1.0

    预期输出:

    The push refers to repository [harbor.example.com/dev/myapp]
    5f70bf18a086: Pushed
    …
    v1.0: digest: sha256:xxxx size: 1234

    (4)拉取镜像:docker pull

    docker pull harbor.example.com/dev/myapp:v1.0

    (5)搜索镜像:docker search

    docker search nginx –limit 5

    预期输出:

    NAME DESCRIPTION STARS OFFICIAL
    nginx Official build of Nginx. 19000 [OK]
    …

    4.3 搭建私有仓库

    使用官方 registry 镜像,一行命令即可:

    docker run -d -p 5000:5000 –name registry \\
    -v /data/registry:/var/lib/registry \\
    registry:2

    推送镜像到本地仓库:

    docker tag myapp:v1.0 localhost:5000/myapp:v1.0
    docker push localhost:5000/myapp:v1.0

    查询仓库中的镜像:

    curl http://localhost:5000/v2/_catalog
    # 输出:{"repositories":["myapp"]}

    注意:Docker 默认要求 HTTPS,若使用 HTTP 的私有仓库,需要在 daemon.json 中配置 insecure-registries:

    {
    "insecure-registries": ["localhost:5000"]
    }

    修改后重启 Docker 服务。

    4.4 配置镜像加速器

    国内拉取 Docker Hub 镜像较慢,可配置加速器。编辑 /etc/docker/daemon.json:

    {
    "registry-mirrors": [
    "https://docker.m.daocloud.io",
    "https://mirror.ccs.tencentyun.com"
    ]
    }

    重启生效:

    sudo systemctl daemon-reload
    sudo systemctl restart docker
    docker info | grep -A 5 "Registry Mirrors"


    五、三者关系再梳理:一张表看懂

    对比项镜像 Image容器 Container仓库 Registry
    本质 只读模板 运行实例 存储分发服务
    状态 静态 动态 静态存储
    可读写 只读 可读写 只读(对使用者)
    生命周期 长期 临时/可长可短 长期
    类比 类 / 菜谱 对象 / 菜肴 GitHub / 应用商店
    常用命令 pull、build、rmi run、exec、logs login、push、search
    数量关系 1 个镜像 → N 个容器 N 个容器 ← 1 个镜像 1 个仓库 → N 个镜像

    一个完整的开发流程:

    # 1. 编写 Dockerfile 构建镜像
    docker build -t myapp:v1.0 .

    # 2. 本地运行容器验证
    docker run -d -p 8080:8080 –name myapp myapp:v1.0

    # 3. 打标签并推送到仓库
    docker tag myapp:v1.0 registry.example.com/dev/myapp:v1.0
    docker push registry.example.com/dev/myapp:v1.0

    # 4. 在服务器上拉取并运行
    docker pull registry.example.com/dev/myapp:v1.0
    docker run -d -p 80:8080 registry.example.com/dev/myapp:v1.0


    六、知识扩展:容器隔离的底层原理

    容器之所以能做到“看起来像一台独立机器”,靠的是 Linux 内核的两大机制:

    6.1 Namespace:负责“看不见”

    Namespace 让容器内的进程只能看到自己命名空间内的资源。Docker 主要使用了 6 种:

    Namespace隔离内容效果
    PID 进程 ID 容器内 PID 从 1 开始,看不到宿主机进程
    NET 网络 独立网卡、IP、端口
    MNT 挂载点 独立文件系统视图
    UTS 主机名/域名 独立 hostname
    IPC 进程间通信 独立信号量、消息队列
    USER 用户/组 容器内 root 可映射为非 root

    6.2 Cgroups:负责“用多少”

    Control Groups 限制容器能使用的资源:

    docker run -d –cpus="1.5" –memory="512m" nginx:1.25

    上面的命令限制容器最多用 1.5 个 CPU 核、512MB 内存。查看限制:

    docker inspect -f '{{.HostConfig.Memory}}' <container_id>
    # 输出:536870912(单位字节)

    6.3 与虚拟机的本质区别

    维度容器虚拟机
    隔离级别 进程级(namespace) 硬件级(Hypervisor)
    内核 共享宿主机内核 独立 Guest OS 内核
    启动速度 秒级 分钟级
    镜像大小 MB 级 GB 级
    隔离强度 较弱 强
    适用场景 微服务、CI/CD、快速迭代 强隔离、异构 OS

    一句话总结:虚拟机虚拟的是硬件,容器虚拟的是操作系统。


    七、常见错误与最佳实践

    7.1 常见错误

    错误 1:容器退出后数据丢失

    docker run -d mysql:8.0
    docker rm -f <container>
    # 数据库数据全没了!

    原因:数据写在容器可写层,容器删除即丢失。

    解决:使用数据卷。

    docker run -d -v mysql-data:/var/lib/mysql mysql:8.0

    错误 2:镜像越构建越大

    FROM ubuntu:22.04
    RUN apt-get update && apt-get install -y build-essential
    COPY . .
    RUN make
    # 最终镜像包含了编译工具链,几百 MB 浪费

    解决:多阶段构建。

    FROM golang:1.22 AS builder
    WORKDIR /src
    COPY . .
    RUN go build -o app .

    FROM alpine:3.19
    COPY –from=builder /src/app /app
    CMD ["/app"]

    错误 3:在容器里存密钥

    ENV DB_PASSWORD=123456 # 会被 docker history 看到!

    解决:使用 –env-file、Docker Secret 或外部密钥管理。

    错误 4:使用 latest 标签上生产

    解决:始终使用明确的语义化版本标签,如 v1.2.3。

    7.2 最佳实践清单

    • ✅ 一个容器只跑一个主进程
    • ✅ 镜像尽量小(Alpine / Distroless)
    • ✅ 使用非 root 用户运行
    • ✅ 数据持久化用 volume,不用容器层
    • ✅ 敏感信息用 secret,不写进镜像
    • ✅ 标签使用语义化版本,禁用 latest 上生产
    • ✅ 用 .dockerignore 排除无关文件
    • ✅ 定期清理无用镜像和容器

    清理命令:

    docker system prune -a –volumes

    ⚠️ 该命令会删除所有未使用的镜像、容器、网络和卷,生产环境慎用。


    八、总结与练习

    8.1 核心总结

  • 镜像是只读模板,由多层文件系统叠加而成,通过 Dockerfile 构建。
  • 容器是镜像的运行实例,在镜像之上加一层可写层,可启停、可删除。
  • 仓库是镜像的存储分发中心,Docker Hub 是默认公共仓库,也可自建私有仓库。
  • 三者关系:Dockerfile → 镜像 → 容器,镜像通过仓库在机器之间流转。
  • 容器的轻量源于 namespace 隔离 + cgroups 限制 + 共享内核。
  • 8.2 练习题

    练习 1:拉取 redis:7.2-alpine 镜像,后台启动一个名为 my-redis 的容器,映射宿主机 6379 端口,并用 redis-cli 连接验证。

    提示:docker run -d –name my-redis -p 6379:6379 redis:7.2-alpine,然后 docker exec -it my-redis redis-cli ping,预期返回 PONG。

    练习 2:查看 nginx:1.25 镜像的分层结构,并解释为什么 docker history 中有的层大小为 0。

    提示:使用 docker history nginx:1.25,大小为 0 的层通常是元数据指令(如 ENV、CMD、EXPOSE),不产生实际文件变更。

    练习 3:编写一个 Dockerfile,基于 python:3.12-slim 构建一个 Flask 应用镜像,要求:

    • 使用非 root 用户运行
    • 只复制必要文件
    • 使用 CMD 启动

    提示:使用 USER 指令切换用户,COPY requirements.txt 后再 COPY .,利用缓存。

    练习 4:搭建一个本地私有 Registry,将练习 3 构建的镜像推送到该仓库,再从另一台机器(或删除本地镜像后)拉取验证。

    提示:docker run -d -p 5000:5000 registry:2,注意配置 insecure-registries。

    练习 5(思考题):为什么同一个镜像可以启动多个容器而互不影响?请从镜像分层和容器可写层的角度解释。

    提示:镜像层只读且共享,每个容器在镜像之上拥有独立的可写层(Copy-on-Write),修改只发生在自己的可写层中。


    如果本文对你有帮助,欢迎点赞、收藏、关注,你的支持是我持续更新的最大动力!有任何问题也欢迎在评论区留言交流。

    赞(0)
    未经允许不得转载:171主机测评 » Docker到底是什么?一文读懂镜像、容器、仓库三大核心概念
    分享到: 更多 (0)

    评论 抢沙发

    • 昵称 (必填)
    • 邮箱 (必填)
    • 网址