Docker 基础概念:镜像、容器和仓库之间的关系

Docker 基础概念:镜像、容器和仓库之间的关系

应用在开发环境运行正常,部署到测试服务器后却开始报错。这种情况很可能不是代码的问题,而在于环境:JDK 版本不同、依赖没安装等等。

Docker 要解决的正是这个问题。它把应用代码、运行时和依赖文件打包在一起,只要目标机器提供兼容的容器运行环境,应用通常就能以相近的方式运行。

理解 Docker 的工作方式,需要弄清楚三个概念:镜像、容器、镜像仓库。

目录

镜像

镜像是一个只读模板,包含应用运行所需的全部文件。

以一个 Spring Boot 应用为例,镜像中的内容大致是:

text 复制代码
基础系统文件(Ubuntu / Alpine 等用户空间文件)
├── JDK 17
├── application.jar
└── application.yml

镜像中虽然包含操作系统用户空间的文件,但它不是一套完整的虚拟机操作系统。容器运行时,仍然使用宿主机的内核。

镜像的分层结构

Docker 镜像不是一个不可拆分的大文件,而是由多层文件系统叠加而成。

一个简单的 Dockerfile:

dockerfile 复制代码
FROM eclipse-temurin:17-jre

WORKDIR /app

COPY application.jar app.jar

ENTRYPOINT ["java", "-jar", "app.jar"]

构建之后,镜像大致是这样的结构:

Dockerfile 中会修改文件系统的指令,比如 RUNCOPYADD,通常会生成新的镜像层。CMDENTRYPOINT 等指令主要记录镜像的运行配置,不会单独产生文件系统层。

分层的好处是复用和缓存。两个项目如果基于同一个 JDK 17 基础镜像,可以共享这部分层,不用在磁盘上保存两份。重新构建时,前面没有变化的层可以直接使用缓存。

这也是为什么 Dockerfile 的指令顺序会影响构建速度和镜像体积------后面讲 Dockerfile 优化时会展开。

镜像本身不会运行

镜像只是静态文件,类似于安装包和运行环境的组合。Docker 根据镜像创建容器后,应用进程才真正启动。

容器

容器是镜像的运行实例。

执行 docker run 并不是"把镜像运行起来"这么简单。Docker 会先基于镜像创建一个容器,再启动容器中的应用进程。每执行一次 docker run,通常都会创建一个新的容器:

bash 复制代码
# 这会创建三个不同的 nginx 容器
docker run -d nginx
docker run -d nginx
docker run -d nginx

如果容器已经存在但处于停止状态,应当使用 docker start 而不是再次 docker run

容器的可写层

镜像中的文件是只读的。创建容器时,Docker 会在镜像层上方增加一个可写层,容器运行期间产生的修改会记录在这一层中:

同一个镜像可以创建多个容器,它们共享镜像层,但拥有各自独立的运行状态和可写层。修改一个容器中的文件,不会影响其他容器,也不会修改原始镜像。

容器的生命周期与数据

容器停止后,可写层并不会立即消失。docker stop 只是停止容器中的进程,之后仍然可以用 docker start 重新启动,可写层中的数据通常还在。

真正删除容器时(docker rm),可写层才会一起被删除。

因此,更准确的说法是:容器的可写层与容器生命周期绑定。停止容器不会丢失数据,但删除容器时,其中的数据通常也会被删除。数据库文件、用户上传文件等需要长期保存的数据,应该使用 Docker Volume 或绑定挂载保存到容器之外。

常用命令

容器的创建、停止和删除分别对应下面几条命令:

bash 复制代码
# 创建并启动
docker run -d \
  --name my-app \
  -p 8080:8080 \
  my-springboot-app

# 查看运行中的容器
docker ps

# 查看所有容器(包括已停止的)
docker ps -a

# 停止
docker stop my-app

# 重新启动
docker start my-app

# 删除
docker rm my-app

其中 -d 表示后台运行,--name 指定容器名称,-p 8080:8080 将宿主机端口映射到容器端口。

镜像仓库

镜像构建完成后,需要交付给测试环境、生产服务器或其他开发人员。更常见的做法是把镜像推送到 Registry。

Registry 是存储和分发镜像的服务,中文习惯上也常称为镜像仓库。Docker Hub 和 Harbor 都属于这类服务。一个 Registry 中可以包含多个镜像 Repository,每个 Repository 又可以通过标签区分版本。

Docker Hub 是 Docker 默认使用的公共 Registry,公司内部项目通常使用私有 Registry。Harbor 是常见的私有方案,提供权限控制、镜像复制、漏洞扫描等功能。

拉取和推送

bash 复制代码
# 从 Registry 拉取镜像
docker pull nginx:1.25

# 查看本地镜像
docker images

# 推送到 Registry
docker push my-harbor.com/myteam/my-app:1.0

镜像名称

一个完整的镜像名称由几部分组成:

text 复制代码
仓库地址/命名空间/镜像名称:标签

例如:

text 复制代码
my-harbor.com/backend/order-service:v1.0

拆开来看:

text 复制代码
my-harbor.com    Registry 地址
backend          命名空间
order-service    镜像名称
v1.0             标签

平时执行 docker pull nginx:1.25,省略了前面的地址和命名空间,完整形式接近 docker.io/library/nginx:1.25

如果省略标签:

bash 复制代码
docker pull nginx

Docker 默认使用 latest。但 latest 只是一个普通标签,不能确定它指向哪个版本。生产部署时通常不建议依赖 latest,更稳妥的做法是使用固定版本标签,要求更严格时还可以固定镜像摘要。

容器和虚拟机的区别

容器和虚拟机都能隔离应用,但隔离的方式不同。

虚拟机虚拟的是一套硬件环境,每个虚拟机都运行自己的操作系统内核:

容器隔离的是进程、网络和文件系统等运行环境,多个容器通常共享宿主机内核:

因此容器创建和启动的成本更低,但隔离边界也不同于虚拟机。两者不是简单的替代关系,实际部署中也经常在虚拟机里运行多个容器。

完整部署流程

Docker 的完整部署流程如下:

小结

镜像负责交付环境,容器负责运行应用,Registry 负责保存和分发镜像。日常使用 Docker,基本就是围绕构建、推送、拉取和运行这几步展开。

理解了这三个概念,再去看 docker builddocker pulldocker run,就不会把它们当成孤立的命令,而是能看出每条命令处在整个工作流的哪个环节。

相关推荐
QYR-分析6 小时前
注塑机机械手自动化装备行业发展研判:存量升级催生增量红利,2032 年市场规模逼近 9 亿美元
运维·自动化
牢姐与蒯7 小时前
Linux基本指令及知识点(二)
linux·运维·服务器
ᥬ 小月亮7 小时前
SonarQube使用教程(Docker安装)
运维·docker·容器
去伪存真20257 小时前
数字样机、态势推演与3D汇报场景下的平台选型对比
运维·服务器
自动化测试行业观察8 小时前
从“自动化”到“智能化”:TestMan AI测试平台引领软件测试范式转移
运维·自动化测试·人工智能·测试工具·自动化·app测试·移动应用测试
xiaoxiangsiyan8 小时前
运维之前端反调试学习
运维·前端·学习·状态模式
RisunJan8 小时前
Linux命令-spell(Unix 拼写检查工具 —— 文本校对的基础利器)
linux·运维·unix
深圳市恒星物联科技有限公司9 小时前
破解复杂水务监测难题:投入式压力液位计如何实现“零中断”长效运维?
运维·物联网·智慧城市
自律最差的编程狗9 小时前
从零搭建:VMware + Ubuntu + Docker + MySQL 完整指南
mysql·ubuntu·docker
RPA机器人就用八爪鱼9 小时前
八爪鱼RPA客服质检自动化:会话记录采集与关键词分析方案
运维·自动化·rpa