Docker(二):Docker image & Docker Container

本文将介绍 Docker 映像和容器以及 docker 文件之间的差异与联系,本文还将解释如何以及何时使用它们。

什么是 Dockerfile?

它是一个简单的文本文件,包含命令或过程的集合。我们运行的这些命令和准则作用于配置为创建新的 Docker 镜像的基本镜像。Dockerfile 是 Docker 镜像的源代码。Dockerfile 是包含各种指令和配置的文本文件。Dockerfile 中的 FROM 命令标识要从中构建的基础镜像。运行 Docker run 命令时,Docker 将使用此文件生成镜像本身。Dockerfile 包含映像的创建说明。与仅保留二进制镜像相比,使用 Dockerfile 的好处是自动构建可确保您始终拥有最新版本。这在安全性方面是有利的,因为您不想安装任何不安全的应用程序。(这里只做简单介绍,详细简述会篇幅过长的)

什么是 Docker Image & Docker Container ?

Docker镜像是静态的、只读的模板,包含了运行应用所需的一切。Docker容器是基于这些镜像创建的运行实例,它们是动态的、可修改的。镜像用于分发应用,而容器用于运行应用。理解这两者的关系对于有效使用Docker至关重要。

关系类比:

  • Docker Image:想象Docker镜像就像一个蛋糕的食谱。它包含了所有制作蛋糕所需的原料和步骤,但它本身不是蛋糕。
  • Docker Container:如果把Docker镜像比作蛋糕的食谱,那么Docker容器就是根据这个食谱实际烘焙出来的蛋糕。

1. Docker Image

Docker镜像是一个轻量级、可执行的独立软件包,包含运行某个软件所需的所有内容。它包括代码、运行时环境、系统工具、系统库和设置。

  • 定义:Docker 镜像是一个只读的模板,包含了运行某个应用程序所需的所有文件、环境变量、配置等。镜像是静态的,不会改变。
  • 构建:镜像通常是通过一个名为 Dockerfile 的文件构建的。Dockerfile 包含了一系列指令,描述了如何构建这个镜像,包括从哪个基础镜像开始、需要安装哪些软件包、需要复制哪些文件等。
  • 存储和分发:镜像可以存储在 Docker 仓库中(如 Docker Hub),便于分发和共享。用户可以从公共或私有仓库中拉取镜像,用于创建容器。
  • 可共享:不同镜像可以共享基础层,节省空间
  • 可移植:可以在任何支持Docker的环境中运行

2. Docker Container

Docker容器是镜像的运行实例。它是从镜像创建的可运行进程。

  • 定义:Docker 容器是镜像的一个运行实例。容器是动态的,可以启动、停止、移动和删除。每个容器都是独立的,有自己的文件系统、网络接口和进程空间。
  • 启动和运行:容器是从镜像启动的。当你运行一个容器时,Docker 会从指定的镜像创建一个可写层,并在这个可写层上运行应用程序。
  • 可变性:容器是可变的,运行时的数据和状态会存储在容器的可写层中。如果需要保存容器的数据,可以将其提交为一个新的镜像,或者使用数据卷(volumes)。
  • 轻量级:启动快速,资源占用少
  • 可移植:可以在任何支持Docker的环境中启动

3. Docker Image & Docker Container 的关系

docker run: 命令会将 docker image作为容器运行。

a) 镜像是容器的基础:容器是基于镜像创建的运行实例。

b) 一对多关系:一个镜像可以创建多个容器。

c) 镜像是静态的,容器是动态的:镜像是只读的模板,而容器在运行时可以被修改。

d) 容器可以创建新镜像:通过对容器的修改,我们可以创建新的镜像。

生命周期

  • 镜像的生命周期:构建 -> 分发 -> 运行 -> 更新
  • 容器的生命周期:创建 -> 运行 -> 暂停 -> 恢复 -> 停止 -> 删除

使用场景

  • 镜像:用于分发和部署应用程序。开发人员创建镜像,然后将其推送到镜像仓库。
  • 容器:用于运行应用程序。运维人员从镜像仓库拉取镜像,然后启动容器来运行应用。

主要命令

  • 镜像相关:docker build, docker push, docker pull, docker images
  • 容器相关:docker run, docker start, docker stop, docker rm, docker ps

数据持久化

  • 镜像本身不存储数据。
  • 容器可以写入数据,但默认情况下,当容器被删除时,数据也会丢失。
  • 为了持久化数据,我们使用Docker卷(Volumes)或绑定挂载(Bind Mounts)。

Docker Image实现原理

1. 联合文件系统(UnionFS)

联合文件系统是一种分层、轻量级且高性能的文件系统,它支持对文件系统的修改作为一次提交来一层层的叠加,同时可以将不同目录挂载到同一个虚拟文件系统下。

想象一下,你有几张透明的塑料片,每张上面画了不同的图案。当你把这些塑料片叠在一起时,你会看到一个完整的图像,这就是联合文件系统的基本原理。

2. Docker镜像加载原理

Docker镜像加载原理: Docker镜像是由多个只读层叠加而成的。在拉取镜像时,Docker会从Docker Registry中下载这些层,并将它们缓存在本地。当运行容器时,Docker会将这些只读层组合成一个可写的容器层。这种分层设计使得镜像更加高效和灵活,因为不同的层可以被共享和复用。

Docker镜像是基于联合文件系统的概念构建的。每个Docker镜像由一系列层(layers)组成,这些层是只读的。当我们运行一个容器时,Docker会加载这些层,并在最顶部添加一个可写层(容器层)。

让我们用一个例子来解释这个过程:

bootfs (boot file system)

bootfs是启动文件系统的缩写。它包含了bootloader和kernel(内核)。这是系统启动时最先加载的部分。

主要特点:

  • bootfs负责引导系统启动
  • 在系统启动完成后,整个bootfs会被卸载,以释放内存
  • 所有的Docker镜像都共享同一个bootfs

想象bootfs就像电脑的BIOS和操作系统引导程序。它的工作是启动系统,然后就"功成身退"了。

rootfs (root file system)

rootfs是根文件系统的缩写。它包含了典型Linux系统中的/dev, /proc, /bin, /etc等目录和文件。

主要特点:

  • rootfs位于bootfs之上
  • 不同的Linux发行版,如Ubuntu, CentOS等,有不同的rootfs
  • 在Docker中,不同的镜像可能有不同的rootfs

将rootfs想象成你在安装完操作系统后看到的文件系统结构。它包含了操作系统运行所需的所有基本目录和文件。

3. 理解docker于虚拟机的优势

知道了这些概念就可以理解,为什么Docker容器比传统虚拟机启动更快、占用资源更少

  1. bootfs通常非常小,仅包含bootloader和kernel。
  2. 当容器启动时,bootfs被加载到内存中,然后kernel启动。
  3. Docker容器的rootfs默认是只读的。当创建一个容器时,会在rootfs上添加一个读写层。
  4. 镜像的分层结构:当Docker加载镜像时,它会从底层开始,逐层向上加载。每一层都只包含与上一层的差异部分。这种方法大大减少了存储空间的使用和镜像的传输时间。
  • 基础镜像(如Ubuntu)包含bootfs和该发行版特定的rootfs。
  • 在此基础上,每安装一个程序,就在上面添加一个新层。
  • 这种分层结构使得Docker可以:
    • 快速构建镜像(只需加载变化的部分)

    • 节省存储空间(相同的层可以被多个镜像共享)

    • 快速传输镜像(只需传输变化的层)

    • 示例:

      bash 复制代码
      当你运行docker run命令时,Docker会执行以下步骤:
      
      a) 检查本地是否有该镜像。如果没有,就从Docker Hub或指定的registry下载。
      
      b) 创建一个新的容器。
      
      c) 为容器分配一个文件系统,并在镜像的最顶层添加一个可写层。
      
      d) 分配网络/桥接接口。
      
      e) 设置IP地址。
      
      f) 执行你在docker run命令中指定的程序。
      
      g) 捕获并返回应用程序的输出。

这种结构带来了几个重要优势:

  • 共享资源:多个容器可以共享同一个bootfs,节省了内存。
  • 快速启动:由于bootfs很小且共享,容器可以非常快速地启动。
  • 层级管理:每一层的变化都可以被单独管理,便于版本控制和回滚。

通过这种方式,Docker实现了高效的镜像管理和快速的容器启动。


分层理解(镜像层 & 容器层)

1. 容器读写层(Container read-write layer)

后续就叫容器层了方便理解。当你基于一个镜像启动一个容器时,Docker会在镜像的最顶层添加一个可写的容器层。

2. 镜像层 (Image Layers)

镜像层是构建Docker镜像的基础。每个镜像由多个只读层组成,这些层堆叠在一起形成最终的镜像。

3. 镜像层和容器层的关系

  • 镜像层提供了容器的基础文件系统。
  • 容器层允许对运行中的容器进行修改,而不影响底层的镜像。
  • 当需要修改一个文件时,Docker使用写时复制(Copy-on-Write)策略:
    1. 首先,Docker从镜像层复制文件到容器层。
    2. 然后,在容器层中进行修改。
    3. 后续的读取操作会从容器层获取修改后的文件。

4. Copy-on-Write 策略

这是Docker用来优化存储空间和提高性能的一种重要机制。

定义

Copy-on-Write是一种延迟复制的优化策略。当多个进程共享同一块数据时,如果某个进程需要进行修改,系统只有在真正写入时才会复制一份数据

5. 工作原理

  • 读取文件:如果文件存在于容器层,直接读取;如果不存在,则从镜像层读取。
  • 写入文件:如果文件不在容器层,Docker会将文件复制到容器层然后修改。如果文件已在容器层,直接在容器层修改。这就是"写时复制"的由来。
  • 删除文件:Docker在容器层创建一个特殊的删除标记,遮蔽下层的同名文件。

6. 优势

  • 空间效率:多个容器可以共享相同的底层镜像层,只存储差异部分。
  • 快速启动:创建新容器时,无需复制整个文件系统。启动新容器只需添加一个新的容器层,非常快速。
  • 性能优化:读操作性能良好,因为大多数文件不会被修改。
  • 增量更新:更新镜像只需要添加或替换特定的层。

7. 注意事项

  • 写操作可能会带来性能开销,特别是对大文件的首次写入。
  • 需要合理规划镜像层,以平衡层数和文件更新频率。过多的层可能会影响性能,因此在构建镜像时应该适当控制层数。
  • 容器层中的更改在容器被删除后就会消失。如果需要持久化数据,应该使用Docker卷(Volumes)。

杂项补充

Docker Commit

docker commit 命令会从一个运行中(或停止)的容器创建一个新的镜像。这个新镜像包含了原始镜像的内容,以及在容器中所做的所有更改。

bash 复制代码
docker commit [OPTIONS] CONTAINER [REPOSITORY[:TAG]]

常用选项包括:

  • -a, --author: 指定作者
  • -m, --message: 提交信息
  • -p, --pause: 在提交时暂停容器(默认行为)

使用场景

  • 开发过程中保存中间状态
  • 调试和故障排除
  • 创建自定义镜像
  • 将运行时配置保存到新镜像中

最佳实践

  • 优先使用 Dockerfile 来创建镜像
  • 仅在必要时使用 docker commit
  • 在提交前,停止容器并删除不必要的文件
  • 为新创建的镜像添加清晰的标签和描述

虽然 docker commit 是一个有用的工具,但在生产环境中,通常推荐使用 Dockerfile 来创建可重现的镜像构建过程。docker commit 主要用于开发、测试和调试阶段。

Docker Image Tags

使用镜像标签是 Docker 最佳实践的重要部分。它们帮助我们有效地管理镜像版本,便于回滚和部署特定版本的应用。通过合理使用标签,我们可以更好地控制应用的生命周期和版本历史。

标签的类型

  • 版本标签:如 v1.0, 2.1.3
  • 描述性标签:如 latest, stable, dev
  • 日期标签:如 20230701

相关命令

  • docker tag: 为镜像添加新标签
  • docker rmi: 删除镜像(可以指定标签)
  • docker inspect: 查看镜像详细信息
bash 复制代码
docker tag SOURCE_IMAGE[:TAG] TARGET_IMAGE[:TAG]

docker rmi [OPTIONS] IMAGE [IMAGE...]

docker inspect [OPTIONS] NAME|ID [NAME|ID...]

Docker ID

理解和正确使用 Docker ID 对于有效管理 Docker 环境、确保操作的精确性和提高工作流程的自动化程度非常重要。无论是在开发、测试还是生产环境中,熟练运用 Docker ID 都能帮助您更好地控制和管理 Docker 资源。

Docker ID 主要指两个概念:

a) 用户账户标识符:用于 Docker Hub 和其他 Docker 服务的唯一用户标识。

b) 容器或镜像的唯一标识符:用于在本地 Docker 环境中标识特定的容器或镜像。

Docker 用户账户 ID

  • 这是您在 Docker Hub 注册时创建的唯一用户名。
  • 用于登录 Docker Hub、拉取私有镜像、推送镜像等操作。

容器 ID

  • 每个 Docker 容器都有一个唯一的 ID。
  • 通常是一个长的十六进制字符串。
  • 也有短 ID,是完整 ID 的前 12 个字符。

容器管理

bash 复制代码
docker start 1234abcd
docker stop 1234abcd
docker restart 1234abcd
docker rm 1234abcd
docker logs 1234abcd

镜像 ID

  • 每个 Docker 镜像也有一个唯一的 ID。
  • 同样是一个长的十六进制字符串,也有短 ID 版本。

镜像管理

bash 复制代码
删除镜像:docker rmi 5678efgh

基于特定镜像创建容器:docker run -d 5678efgh

参考

https://cto.ai/blog/docker-image-vs-container-vs-dockerfile/

https://docs.docker.com/build/guide/layers/

18、镜像原理之联合文件系统_哔哩哔哩_bilibili

19、镜像原理之分层理解_哔哩哔哩_bilibili

相关推荐
chenbin52028 分钟前
Jenkins 自动构建Job
运维·jenkins
java 凯30 分钟前
Jenkins插件管理切换国内源地址
运维·jenkins
xidianjiapei00132 分钟前
Kubernetes的Ingress 资源是什么?
云原生·容器·kubernetes
AI服务老曹33 分钟前
运用先进的智能算法和优化模型,进行科学合理调度的智慧园区开源了
运维·人工智能·安全·开源·音视频
sszdzq2 小时前
Docker
运维·docker·容器
book01212 小时前
MySql数据库运维学习笔记
运维·数据库·mysql
dmy2 小时前
docker 快速构建开发环境
后端·docker·容器
bugtraq20213 小时前
XiaoMi Mi5(gemini) 刷入Ubuntu Touch 16.04——安卓手机刷入Linux
linux·运维·ubuntu
xmweisi3 小时前
【华为】报文统计的技术NetStream
运维·服务器·网络·华为认证
VVVVWeiYee3 小时前
BGP配置华为——路径优选验证
运维·网络·华为·信息与通信