Docker 入门(4)镜像与容器

1. 镜像与容器

1.1 镜像

Docker镜像类似于未运行的exe应用程序,或者停止运行的VM。当使用docker run命令基于镜像启动容器时,容器应用便能为外部提供服务。

镜像实际上就是这个用来为容器进程提供隔离后执行环境的文件系统。我们也称之为根文件系统(Rootfs)。(注意,rootfs 只是一个操作系统所包含的文件、配置和目录,并不包括操作系统内核。同一台机器上的所有容器,都共享宿主机操作系统的内核。)

由于 rootfs 里封装的不仅仅是应用,还包括它运行所需要的所有依赖。这就赋予了容器的强一致性:无论在本地、云端,还是其他任何地方,用户只需要解压打包好的容器镜像,这个应用运行所需要的完整的执行环境就被重现出来了。

我们可以将 Docker 镜像理解为包含应用程序以及其相关依赖的一个基础文件系统,在 Docker 容器启动的过程中,它以只读的方式被用于创建容器的运行环境。

1.1.1 镜像分层

但还有一个不容忽视的问题,例如如果我需要一个在CentOS环境中跑的apache应用,我可以将它打包成一个apache镜像;如果我还需要一个在CentOS环境中跑的mysql应用,我又将它打包成一个mysql镜像……

这几个镜像中都有全部的CentOS的全部环境,将造成大量空间占用问题及碎片化问题。

Docker的解决方法是: 在镜像的设计中,引入了层(layer)的概念。即: 用户制作镜像的每一步操作,都生成一个层,也就是一个增量 rootfs。

Docker 镜像其实是由基于 UnionFS 文件系统的一组镜像层依次挂载而得,而每个镜像层包含的其实是对上一镜像层的修改,这些修改其实是发生在容器运行的过程中的。所以,我们也可以反过来理解,镜像是对容器运行环境进行持久化存储的结果。

1.1.1 镜像的实现

1.1.1.1 Docker 是如何构建并且存储镜像的

Docker 中的每一个镜像都是由一系列只读的层组成的,Dockerfile 中的每一个命令都会在已有的只读层上创建一个新的层,容器中的每一层都只对当前容器进行了非常小的修改

当镜像被 docker run 命令创建时就会在镜像的最上层添加一个可写的层,也就是容器层,所有对于运行时容器的修改其实都是对这个容器读写层的修改

image.png

上面的这张图片非常好的展示了组装的过程,每一个镜像层都是建立在另一个镜像层之上的,同时所有的镜像层都是只读的,只有每个容器最顶层的容器层才可以被用户直接读写,所有的容器都建立在一些底层服务(Kernel)上,包括命名空间、控制组、rootfs 等等,这种容器的组装方式提供了非常大的灵活性,只读的镜像层通过共享也能够减少磁盘的占用。

1.1.1.2镜像概述

所有的 Docker 镜像都是按照 Docker 所设定的逻辑打包的,也是受到 Docker Engine 所控制的

我们常见的虚拟机镜像,通常是由热心的提供者以他们自己熟悉的方式打包成镜像文件,被我们从网上下载或是其他方式获得后,恢复到虚拟机中的文件系统里的。而 Docker 的镜像我们必须通过 Docker 来打包,也必须通过 Docker 下载或导入后使用,不能单独直接恢复成容器中的文件系统。

虽然这么做失去了很多灵活性,但固定的格式意味着我们可以很轻松的在不同的服务器间传递 Docker 镜像,配合 Docker 自身对镜像的管理功能,让我们在不同的机器中传递和共享 Docker 变得非常方便。

对于每一个记录文件系统修改的镜像层来说,Docker 都会根据它们的信息生成了一个 Hash 码,这是一个 64 长度的字符串,足以保证全球唯一性。

由于镜像层都有唯一的编码,我们就能够区分不同的镜像层并能保证它们的内容与编码是一致的,这带来了另一项好处,就是允许我们在镜像之间共享镜像层

image.png

举一个实际的例子,由 Docker 官方提供的两个镜像 elasticsearch 镜像和 jenkins 镜像都是在 openjdk 镜像之上修改而得,那么在我们实际使用的时候,这两个镜像是可以共用 openjdk 镜像内部的镜像层的。

1.1.2 查看镜像

如果要查看当前连接的 docker daemon 中存放和管理了哪些镜像,我们可以使用 docker images 这个命令 ( Linux、macOS 还是 Windows 上都是一致的 )。

在 docker images 命令的结果中,我们可以看到镜像的 ID ( IMAGE ID)、构建时间 ( CREATED )、占用空间 ( SIZE ) 等数据。

image.png

1.1.3 镜像命名

镜像层的 ID 既可以识别每个镜像层,也可以用来直接识别镜像 ( 因为根据最上层镜像能够找出所有依赖的下层镜像,所以最上层进行的镜像层 ID 就能表示镜像的 ID ),但是使用这种无意义的超长哈希码显然是违背人性的,通过镜像名我们能够更容易的识别镜像。

准确的来说,镜像的命名我们可以分成三个部分:username、repository 和 tag

  • username: 主要用于识别上传镜像的不同用户,与 GitHub 中的用户空间类似。

对于 username 来说,在上面我们展示的 docker images 结果中,有的镜像有 username 这个部分,而有的镜像是没有的。没有 username 这个部分的镜像,表示镜像是由 Docker 官方所维护和提供的,所以就不单独标记用户了。

  • repository:主要用于识别进行的内容,形成对镜像的表意描述。
    Docker 中镜像的 repository 部分通常采用的是软件名。我们推崇一个容器运行一个程序的做法,那么自然容器的镜像也会仅包含程序以及与它运行有关的一些依赖包,所以我们使用程序的名字直接套用在镜像之上,既祛除了镜像取名的麻烦,又能直接表达镜像中的内容。

  • tag:主要用户表示镜像的版本,方便区分进行内容的不同细节
    在镜像命名中,还有一个非常重要的部分,也就是镜像的标签 ( tag )。镜像的标签是对同一种镜像进行更细层次区分的方法,也是最终识别镜像的关键部分。

通常来说,镜像的标签主要是为了区分同类镜像不同构建过程所产生的不同结果的。由于时间、空间等因素的不同,Docker 每次构建镜像的内容也就有所不同,具体体现就是镜像层以及它们的 ID 都会产生变化。而标签就是在镜像命名这个层面上区分这些镜像的方法。

与镜像的 repository 类似,镜像 tag 的命名方法也通常参考镜像所关联的应用程序。更确切的来说,我们通常会采用镜像内应用程序的版本号以及一些环境、构建方式等信息来作为镜像的 tag。

1.2 容器

容器就是将软件打包成标准化单元,以用于开发、交付和部署。

  • 容器镜像是轻量的、可执行的独立软件包 ,包含软件运行所需的所有内容:代码、运行时环境、系统工具、系统库和设置。
  • 容器化软件适用于基于Linux和Windows的应用,在任何环境中都能够始终如一地运行。
  • 容器赋予了软件独立性,使其免受外在环境差异(例如,开发和预演环境的差异)的影响,从而有助于减少团队间在相同基础设施上运行不同软件时的冲突。

image.png

1.2.1 容器的生命周期

由于 Docker 揽下了大部分对容器管理的活,只提供给我们非常简单的操作接口,这就意味着 Docker 里对容器的一些运行细节会被更加严格的定义,这其中就包括了容器的生命周期。

这里有一张容器运行的状态流转图:

image.png

1.2.2 主进程

当我们启动容器时,Docker 其实会按照镜像中的定义,启动对应的程序,并将这个程序的主进程作为容器的主进程 ( 也就是 PID 为 1 的进程 )。而当我们控制容器停止时,Docker 会向主进程发送结束信号,通知程序退出。

而当容器中的主进程主动关闭时 ( 正常结束或出错停止 ),也会让容器随之停止。

1.2.3 写时复制

Docker 的写时复制与编程中的相类似,也就是在通过镜像运行容器时,并不是马上就把镜像里的所有内容拷贝到容器所运行的沙盒文件系统中,而是利用 UnionFS 将镜像以只读的方式挂载到沙盒文件系统中。只有在容器中发生对文件的修改时,修改才会体现到沙盒环境上。

也就是说,容器在创建和启动的过程中,不需要进行任何的文件系统复制操作,也不需要为容器单独开辟大量的硬盘空间,与其他虚拟化方式对这个过程的操作进行对比,Docker 启动的速度可见一斑。

Docker的容器是一个多层的结构。如果对镜像做history操作,我们可以看见他里面每一次dockerfile的命令都会创建一个新的层次。

[root@ip-172-16-1-4 ec2-user]# docker image history nginx
IMAGE               CREATED             CREATED BY                                      SIZE                COMMENT
8cf1bfb43ff5        6 days ago          /bin/sh -c #(nop)  CMD ["nginx" "-g" "daemon…   0B
<missing>           6 days ago          /bin/sh -c #(nop)  STOPSIGNAL SIGTERM           0B
<missing>           6 days ago          /bin/sh -c #(nop)  EXPOSE 80                    0B
<missing>           6 days ago          /bin/sh -c #(nop)  ENTRYPOINT ["/docker-entr…   0B
<missing>           6 days ago          /bin/sh -c #(nop) COPY file:0fd5fca330dcd6a7…   1.04kB
<missing>           6 days ago          /bin/sh -c #(nop) COPY file:1d0a4127e78a26c1…   1.96kB
<missing>           6 days ago          /bin/sh -c #(nop) COPY file:e7e183879c35719c…   1.2kB
<missing>           6 days ago          /bin/sh -c set -x     && addgroup --system -…   63.3MB
<missing>           6 days ago          /bin/sh -c #(nop)  ENV PKG_RELEASE=1~buster     0B
<missing>           6 days ago          /bin/sh -c #(nop)  ENV NJS_VERSION=0.4.2        0B
<missing>           6 days ago          /bin/sh -c #(nop)  ENV NGINX_VERSION=1.19.1     0B
<missing>           6 days ago          /bin/sh -c #(nop)  LABEL maintainer=NGINX Do…   0B
<missing>           6 days ago          /bin/sh -c #(nop)  CMD ["bash"]                 0B
<missing>           6 days ago          /bin/sh -c #(nop) ADD file:6ccb3bbcc69b0d44c…   69.2MB

Docker里面有一个重要的概念叫做 Storage driver,他可以帮助我们实现对容器的分层和读写。目前,docker的默认storage driver 是overlay2。所有的容器相关的文件都保存在/var/lib/docker这个目录下。我们可以看见在overlay2里面有很多不同的文件

[root@ip-172-16-1-4 ec2-user]# ls /var/lib/docker/overlay2/
12597d435b78d470bed7cf3a4cc7d60691432e74f12c00fd44def7ecf6ab659f       6945f4530a5212bc3a8aa598dc88839d39b763799ccdfbd18a5f04180e3b676e       d1f1bcc388595272f11f4ace02f9e6976dc96fd80cce8216d3626484ba114aad
145e76991ae57691ed94de5dd2ea950ff7b50dc26729605e711cd0f35f275e84       7c4d732c22b84e0df8c0d317df825623958d4eff59055827e6b2c76cbe8b0350       d1f1bcc388595272f11f4ace02f9e6976dc96fd80cce8216d3626484ba114aad-init
16da856b3acb71eea396f529a80cf728d664a4bf3eb042f828cb9651d82e90bf       9b6749a9a76ad9f76d7795ebbf8e47595dada7a06b9126f5d9c6e1084f1d0c02       f089b3fd763c560e1c398c9b432100cea56ad4dae3a6b760de42b45e856ce693
16da856b3acb71eea396f529a80cf728d664a4bf3eb042f828cb9651d82e90bf-init  a60b187ea615a59402ad2fe6687a4dc87f1c037eafea6880167795c6e1b7f900       f089b3fd763c560e1c398c9b432100cea56ad4dae3a6b760de42b45e856ce693-init
658b1eab364b4523a8c7274e9b8a2bdc55095e9bd56dcb697f6456b4064abe66       a60b187ea615a59402ad2fe6687a4dc87f1c037eafea6880167795c6e1b7f900-init  l
658b1eab364b4523a8c7274e9b8a2bdc55095e9bd56dcb697f6456b4064abe66-init  backingFsBlockDev

一个典型的场景如下所示,一个镜像文件里面,里面分了多层,最下面的是基础镜像,这个基础镜像不包括内核文件,执行的时候他会直接调用宿主机的内核,因此他的空间并不大。在基础镜像上面,又分了很多层,每一层代表在dockerfile里面执行的一行命令。这整个镜像文件都是只读的。每个容器通过镜像创建自己的容器层,而容器层是可以读写的,修改的内容他们会保存在自己的目录下面。因此每个容器对自己的修改 不会影响到其他容器。

在docker里面,我们通过storage driver来进行所谓的copy on write ( 写时复制)的操作。storage driver有很多种,目前默认的是overlay2

overlay的基本工作原理如下

我们通过镜像创建的容器包括了三层。最下面的是一个只读的镜像层,第二层是容器层,在他上面最上面的容器挂载层。最上层显示的是我们在容器上直接看见的内容,他通过UnionFS,或者说类似软连接的方式,文件的路径指向了容器层或者是镜像层。当我们尝试读取,修改或者创建一个新的文件时,我们总是从上到下进行搜索,如果在容器层找到了,那么就直接打开;如果容器层没有,那就从镜像层打开。如果是一个新建的文档,那么就从镜像层拷贝到容器层,再打开操作。

image.png

参考资料

《开发者必备的 Docker 实践指南》

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/391279.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

python:pytest中的setup和teardown

原文&#xff1a;https://www.cnblogs.com/peiminer/p/9376352.html  之前我写的unittest的setup和teardown&#xff0c;还有setupClass和teardownClass&#xff08;需要配合classmethod装饰器一起使用&#xff09;&#xff0c;接下来就介绍pytest的类似于这类的固件。 &#…

如何开始使用任何类型的数据? - 第1部分

从数据开始 (START WITH DATA) My data science journey began with a student job in the Advanced Analytics department of one of the biggest automotive manufacturers in Germany. I was nave and still doing my masters.我的数据科学之旅从在德国最大的汽车制造商之一…

iHealth基于Docker的DevOps CI/CD实践

本文由1月31日晚iHealth运维技术负责人郭拓在Rancher官方技术交流群内所做分享的内容整理而成&#xff0c;分享了iHealth从最初的服务器端直接部署&#xff0c;到现在实现全自动CI/CD的实践经验。作者简介郭拓&#xff0c;北京爱和健康科技有限公司&#xff08;iHealth)。负责公…

机器学习图像源代码_使用带有代码的机器学习进行快速房地产图像分类

机器学习图像源代码RoomNet is a very lightweight (700 KB) and fast Convolutional Neural Net to classify pictures of different rooms of a house/apartment with 88.9 % validation accuracy over 1839 images. I have written this in python and TensorFlow.RoomNet是…

leetcode 938. 二叉搜索树的范围和

给定二叉搜索树的根结点 root&#xff0c;返回值位于范围 [low, high] 之间的所有结点的值的和。 示例 1&#xff1a; 输入&#xff1a;root [10,5,15,3,7,null,18], low 7, high 15 输出&#xff1a;32 示例 2&#xff1a; 输入&#xff1a;root [10,5,15,3,7,13,18,1,nul…

COVID-19和世界幸福报告数据告诉我们什么?

For many people, the idea of ​​staying home actually sounded good at first. This process was really efficient for Netflix and Amazon. But then sad truths awaited us. What was boring was the number of dead and intubated patients one after the other. We al…

iOS 开发一定要尝试的 Texture(ASDK)

原文链接 - iOS 开发一定要尝试的 Texture(ASDK)(排版正常, 包含视频) 前言 本篇所涉及的性能问题我都将根据滑动的流畅性来评判, 包括掉帧情况和一些实际体验 ASDK 已经改名为 Texture, 我习惯称作 ASDK 编译环境: MacOS 10.13.3, Xcode 9.2 参与测试机型: iPhone 6 10.3.3, i…

lisp语言是最好的语言_Lisp可能不是数据科学的最佳语言,但是我们仍然可以从中学到什么呢?...

lisp语言是最好的语言This article is in response to Emmet Boudreau’s article ‘Should We be Using Lisp for Data-Science’.本文是对 Emmet Boudreau的文章“我们应该将Lisp用于数据科学”的 回应 。 Below, unless otherwise stated, lisp refers to Common Lisp; in …

static、volatile、synchronize

原子性&#xff08;排他性&#xff09;&#xff1a;不论是多核还是单核&#xff0c;具有原子性的量&#xff0c;同一时刻只能有一个线程来对它进行操作&#xff01;可见性&#xff1a;多个线程对同一份数据操作&#xff0c;thread1改变了某个变量的值&#xff0c;要保证thread2…

1.10-linux三剑客之sed命令详解及用法

内容:1.sed命令介绍2.语法格式,常用功能查询 增加 替换 批量修改文件名第1章 sed是什么字符流编辑器 Stream Editor第2章 sed功能与版本处理出文本文件,日志,配置文件等增加,删除,修改,查询sed --versionsed -i 修改文件内容第3章 语法格式3.1 语法格式sed [选项] [sed指令…

python pca主成分_超越“经典” PCA:功能主成分分析(FPCA)应用于使用Python的时间序列...

python pca主成分FPCA is traditionally implemented with R but the “FDASRSF” package from J. Derek Tucker will achieve similar (and even greater) results in Python.FPCA传统上是使用R实现的&#xff0c;但是J. Derek Tucker的“ FDASRSF ”软件包将在Python中获得相…

初探Golang(2)-常量和命名规范

1 命名规范 1.1 Go是一门区分大小写的语言。 命名规则涉及变量、常量、全局函数、结构、接口、方法等的命名。 Go语言从语法层面进行了以下限定&#xff1a;任何需要对外暴露的名字必须以大写字母开头&#xff0c;不需要对外暴露的则应该以小写字母开头。 当命名&#xff08…

大数据平台构建_如何像产品一样构建数据平台

大数据平台构建重点 (Top highlight)Over the past few years, many companies have embraced data platforms as an effective way to aggregate, handle, and utilize data at scale. Despite the data platform’s rising popularity, however, little literature exists on…

初探Golang(3)-数据类型

Go语言拥有两大数据类型&#xff0c;基本数据类型和复合数据类型。 1. 数值类型 ##有符号整数 int8&#xff08;-128 -> 127&#xff09; int16&#xff08;-32768 -> 32767&#xff09; int32&#xff08;-2,147,483,648 -> 2,147,483,647&#xff09; int64&#x…

时间序列预测 时间因果建模_时间序列建模以预测投资基金的回报

时间序列预测 时间因果建模Time series analysis, discussed ARIMA, auto ARIMA, auto correlation (ACF), partial auto correlation (PACF), stationarity and differencing.时间序列分析&#xff0c;讨论了ARIMA&#xff0c;自动ARIMA&#xff0c;自动相关(ACF)&#xff0c;…

(58)PHP开发

LAMP0、使用include和require命令来包含外部PHP文件。使用include_once命令&#xff0c;但是include和include_once命令相比的不足就是这两个命令并不关心请求的文件是否实际存在&#xff0c;如果不存在&#xff0c;PHP解释器就会直接忽略这个命令并且显示一个错误消息&#xf…

css flexbox模型_如何将Flexbox后备添加到CSS网格

css flexbox模型I shared how to build a calendar with CSS Grid in the previous article. Today, I want to share how to build a Flexbox fallback for the same calendar. 在上一篇文章中&#xff0c;我分享了如何使用CSS Grid构建日历。 今天&#xff0c;我想分享如何为…

贝塞尔修正_贝塞尔修正背后的推理:n-1

贝塞尔修正A standard deviation seems like a simple enough concept. It’s a measure of dispersion of data, and is the root of the summed differences between the mean and its data points, divided by the number of data points…minus one to correct for bias.标…

RESET MASTER和RESET SLAVE使用场景和说明【转】

【前言】在配置主从的时候经常会用到这两个语句&#xff0c;刚开始的时候还不清楚这两个语句的使用特性和使用场景。 经过测试整理了以下文档&#xff0c;希望能对大家有所帮助&#xff1b; 【一】RESET MASTER参数 功能说明&#xff1a;删除所有的binglog日志文件&#xff0c;…

Kubernetes 入门(1)基本概念

1. Kubernetes简介 作为一个目前在生产环境已经广泛使用的开源项目 Kubernetes 被定义成一个用于自动化部署、扩容和管理容器应用的开源系统&#xff1b;它将一个分布式软件的一组容器打包成一个个更容易管理和发现的逻辑单元。 Kubernetes 是希腊语『舵手』的意思&#xff0…