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

相关推荐
WolvenSec24 分钟前
网络基础:EIGRP
运维·网络·安全·智能路由器
踩着阴暗的自己向上爬1 小时前
Day05-04-持续集成总结
linux·运维·ci/cd
TiDB_PingCAP2 小时前
国产化新标杆:TiDB 助力广发银行新一代总账系统投产上线
运维·数据库·开源·tidb
qyhua2 小时前
Linux内网端口转公网端口映射
linux·运维·服务器
coisini.cn3 小时前
基于CentOS Stream 9平台搭建MinIO以及开机自启
运维·minio·centos stream 9
Python私教4 小时前
docker部署onlyoffice,开启JWT权限校验Token
运维·docker·容器
Ramboooooooo5 小时前
Nginx Lua Waf 插件一键部署
运维·nginx·lua·waf·lua waf·nginx waf
lendq6 小时前
k8s-第八节-Helm
云原生·容器·kubernetes
掘根6 小时前
【Linux】压缩命令——gzip,bzip2,xz
大数据·linux·运维