Docker镜像中有什么?

栏目: 编程工具 · 发布时间: 6年前

内容简介:你可能会熟悉Dockerfile,这是让Docker为你构建映像的说明。这里有一个简单的例子。每一行都是Docker关于如何构建镜像的说明。它将ubuntu:15.04用作基础,然后复制python脚本。CMD指令是让容器执行操作指令(将映像转换为正在运行的进程)。让我们运行docker build .并检查输出。

你可能会熟悉Dockerfile,这是让 Docker 为你构建映像的说明。这里有一个简单的例子。

FROM ubuntu:15.04
COPY app.py /app
CMD python /app/app.py

每一行都是Docker关于如何构建镜像的说明。它将ubuntu:15.04用作基础,然后复制 python 脚本。CMD指令是让容器执行操作指令(将映像转换为正在运行的进程)。

让我们运行docker build .并检查输出。

$ docker build -t my_test_image .
Sending build context to Docker daemon  364.2MB
Step 1/3 : FROM ubuntu:15.04
 ---> d1b55fd07600
Step 2/3 : COPY app.py /app/
 ---> 44ab3f1d4cd6
Step 3/3 : CMD python /app/app.py
 ---> Running in c037c981012e
Removing intermediate container c037c981012e
 ---> 174b1e992617
Successfully built 174b1e992617
Successfully tagged my_test_image:latest

看看最后两行,我们已经成功构建了一个Docker镜像,我们可以通过标识符174b1e992617来引用它(这个值是图像内容的SHA256哈希摘要)。

我们有最了终的图像,但各个步骤的ID如d1b55fd07600和44ab3f1d4cd6是什么意思?他们也是镜像吗?,是的。

想象一下,如果我们从Dockerfile中删除第二步:COPY app.py /app   Docker仍然会成功地将其构建为一个镜像,因此,在镜像构建过程的每一步,我们都有一个镜像。

这告诉我们镜像可以建立在彼此之上!当您认为Dockerfile中的FROM 指令的意思是:指定要在其上构建哪个镜像,这是有道理的。

导出镜像并解压缩

为了便于使用,可以将图像导出到单个文件中,使我们可以轻松查看内部。

docker save my_test_image > my_test_image

导出的文件是..:

$ file my_test_image

my_test_image: POSIX tar archive

一个tarball!压缩文件或目录。我们打开它。

$ mkdir unpacked_image
$ tar -xvf my_test_image -C unpacked_image
x 174b1e9926177b5dfd22981ddfab78629a9ce2f05412ccb1a4fa72f0db21197b.json
x 28441336175b9374d04ee75fdb974539e9b8cad8fec5bf0ff8cea6f8571d0114/
x 28441336175b9374d04ee75fdb974539e9b8cad8fec5bf0ff8cea6f8571d0114/VERSION
x 28441336175b9374d04ee75fdb974539e9b8cad8fec5bf0ff8cea6f8571d0114/json
x 28441336175b9374d04ee75fdb974539e9b8cad8fec5bf0ff8cea6f8571d0114/layer.tar
x 4631663ba627c9724cd701eff98381cb500d2c09ec78a8c58213f3225877198e/
x 4631663ba627c9724cd701eff98381cb500d2c09ec78a8c58213f3225877198e/VERSION
x 4631663ba627c9724cd701eff98381cb500d2c09ec78a8c58213f3225877198e/json
x 4631663ba627c9724cd701eff98381cb500d2c09ec78a8c58213f3225877198e/layer.tar
x 6c91b695f2ed98362f511f2490c16dae0dcf8119bcfe2fe9af50305e2173f373/
x 6c91b695f2ed98362f511f2490c16dae0dcf8119bcfe2fe9af50305e2173f373/VERSION
x 6c91b695f2ed98362f511f2490c16dae0dcf8119bcfe2fe9af50305e2173f373/json
x 6c91b695f2ed98362f511f2490c16dae0dcf8119bcfe2fe9af50305e2173f373/layer.tar
x c4f8838502da6456ebfcb3f755f8600d79552d1e30beea0ccc62c13a2556da9c/
x c4f8838502da6456ebfcb3f755f8600d79552d1e30beea0ccc62c13a2556da9c/VERSION
x c4f8838502da6456ebfcb3f755f8600d79552d1e30beea0ccc62c13a2556da9c/json
x c4f8838502da6456ebfcb3f755f8600d79552d1e30beea0ccc62c13a2556da9c/layer.tar
x cac0b96b79417d5163fbd402369f74e3fe4ff8223b655e0b603a8b570bcc76eb/
x cac0b96b79417d5163fbd402369f74e3fe4ff8223b655e0b603a8b570bcc76eb/VERSION
x cac0b96b79417d5163fbd402369f74e3fe4ff8223b655e0b603a8b570bcc76eb/json
x cac0b96b79417d5163fbd402369f74e3fe4ff8223b655e0b603a8b570bcc76eb/layer.tar
x manifest.json
x repositories

我们将开始研究 manifest.json:

[
  {
    <font>"Config"</font><font>: </font><font>"174b1e9926177b5dfd22981ddfab78629a9ce2f05412ccb1a4fa72f0db21197b.json"</font><font>,
    </font><font>"RepoTags"</font><font>: [
      </font><font>"my_test_image:latest"</font><font>
    ],
    </font><font>"Layers"</font><font>: [
      </font><font>"cac0b96b79417d5163fbd402369f74e3fe4ff8223b655e0b603a8b570bcc76eb/layer.tar"</font><font>,
      </font><font>"28441336175b9374d04ee75fdb974539e9b8cad8fec5bf0ff8cea6f8571d0114/layer.tar"</font><font>,
      </font><font>"4631663ba627c9724cd701eff98381cb500d2c09ec78a8c58213f3225877198e/layer.tar"</font><font>,
      </font><font>"c4f8838502da6456ebfcb3f755f8600d79552d1e30beea0ccc62c13a2556da9c/layer.tar"</font><font>,
      </font><font>"6c91b695f2ed98362f511f2490c16dae0dcf8119bcfe2fe9af50305e2173f373/layer.tar"</font><font>
    ]
  }
]
</font>

清单文件是一段元数据,它准确描述了这个镜像中的内容。我们可以看到镜像有一个标签my_test_image,它有一个叫做Layers的东西,另一个叫做Config。

配置JSON文件的前12个字符与我们从docker build中看到的镜像ID相同,巧合 - 我想不是!

$ cat 174b1e9926177b5dfd22981ddfab78629a9ce2f05412ccb1a4fa72f0db21197b.json

{
  <font>"architecture"</font><font>: </font><font>"amd64"</font><font>,
  </font><font>"config"</font><font>: {
    </font><font>"Hostname"</font><font>: </font><font>"d2d404286fc4"</font><font>,
    </font><font>"Domainname"</font><font>: </font><font>""</font><font>,
    </font><font>"User"</font><font>: </font><font>""</font><font>,
    </font><font>"AttachStdin"</font><font>: false,
    </font><font>"AttachStdout"</font><font>: false,
    </font><font>"AttachStderr"</font><font>: false,
    </font><font>"Tty"</font><font>: false,
    </font><font>"OpenStdin"</font><font>: false,
    </font><font>"StdinOnce"</font><font>: false,
    </font><font>"Env"</font><font>: [
      </font><font>"PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"</font><font>
    ],
    </font><font>"Cmd"</font><font>: [
      </font><font>"/bin/sh"</font><font>,
      </font><font>"-c"</font><font>,
      </font><font>"python /app/app.py"</font><font>
    ],
    </font><font>"ArgsEscaped"</font><font>: <b>true</b>,
    </font><font>"Image"</font><font>: </font><font>"sha256:44ab3f1d4cd69d84c9c67187b378b1d1322b5fddf4068c11e8b11856ced7efc0"</font><font>,
    </font><font>"Volumes"</font><font>: <b>null</b>,
    </font><font>"WorkingDir"</font><font>: </font><font>""</font><font>,
    </font><font>"Entrypoint"</font><font>: <b>null</b>,
    </font><font>"OnBuild"</font><font>: <b>null</b>,
    </font><font>"Labels"</font><font>: <b>null</b>
  },
  </font><font>"container"</font><font>: </font><font>"c037c981012e8f03ac5466fcdda8f78a14fb9bb5ee517028c66915624a5616fa"</font><font>,
  </font><font>"container_config"</font><font>: {
    </font><font>"Hostname"</font><font>: </font><font>"d2d404286fc4"</font><font>,
    </font><font>"Domainname"</font><font>: </font><font>""</font><font>,
    </font><font>"User"</font><font>: </font><font>""</font><font>,
    </font><font>"AttachStdin"</font><font>: false,
    </font><font>"AttachStdout"</font><font>: false,
    </font><font>"AttachStderr"</font><font>: false,
    </font><font>"Tty"</font><font>: false,
    </font><font>"OpenStdin"</font><font>: false,
    </font><font>"StdinOnce"</font><font>: false,
    </font><font>"Env"</font><font>: [
      </font><font>"PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"</font><font>
    ],
    </font><font>"Cmd"</font><font>: [
      </font><font>"/bin/sh"</font><font>,
      </font><font>"-c"</font><font>,
      </font><font>"#(nop) "</font><font>,
      </font><font>"CMD [\"/bin/sh\" \"-c\" \"python /app/app.py\"]"</font><font>
    ],
    </font><font>"ArgsEscaped"</font><font>: <b>true</b>,
    </font><font>"Image"</font><font>: </font><font>"sha256:44ab3f1d4cd69d84c9c67187b378b1d1322b5fddf4068c11e8b11856ced7efc0"</font><font>,
    </font><font>"Volumes"</font><font>: <b>null</b>,
    </font><font>"WorkingDir"</font><font>: </font><font>""</font><font>,
    </font><font>"Entrypoint"</font><font>: <b>null</b>,
    </font><font>"OnBuild"</font><font>: <b>null</b>,
    </font><font>"Labels"</font><font>: {}
  },
  </font><font>"created"</font><font>: </font><font>"2018-11-01T03:19:16.8517953Z"</font><font>,
  </font><font>"docker_version"</font><font>: </font><font>"18.09.0-ce-beta1"</font><font>,
  </font><font>"history"</font><font>: [
    {
      </font><font>"created"</font><font>: </font><font>"2016-01-26T17:48:17.324409116Z"</font><font>,
      </font><font>"created_by"</font><font>: </font><font>"/bin/sh -c #(nop) ADD file:3f4708cf445dc1b537b8e9f400cb02bef84660811ecdb7c98930f68fee876ec4 in /"</font><font>
    },
    {
      </font><font>"created"</font><font>: </font><font>"2016-01-26T17:48:31.377192721Z"</font><font>,
      </font><font>"created_by"</font><font>: </font><font>"/bin/sh -c echo '#!/bin/sh' > /usr/sbin/policy-rc.d \t&& echo 'exit 101' >> /usr/sbin/policy-rc.d \t&& chmod +x /usr/sbin/policy-rc.d \t\t&& dpkg-divert --local --rename --add /sbin/initctl \t&& cp -a /usr/sbin/policy-rc.d /sbin/initctl \t&& sed -i 's/^exit.*/exit 0/' /sbin/initctl \t\t&& echo 'force-unsafe-io' > /etc/dpkg/dpkg.cfg.d/docker-apt-speedup \t\t&& echo 'DPkg::Post-Invoke { \"rm -f /var/cache/apt/archives/*.deb /var/cache/apt/archives/partial/*.deb /var/cache/apt/*.bin || true\" };' > /etc/apt/apt.conf.d/docker-clean \t&& echo 'APT::Update::Post-Invoke { \"rm -f /var/cache/apt/archives/*.deb /var/cache/apt/archives/partial/*.deb /var/cache/apt/*.bin || true\" };' >> /etc/apt/apt.conf.d/docker-clean \t&& echo 'Dir::Cache::pkgcache \"\" Dir::Cache::srcpkgcache \"\"' >> /etc/apt/apt.conf.d/docker-clean \t\t&& echo 'Acquire::Languages \"none\"' > /etc/apt/apt.conf.d/docker-no-languages \t\t&& echo 'Acquire::GzipIndexes \"true\" Acquire::CompressionTypes::Order:: \"gz\"' > /etc/apt/apt.conf.d/docker-gzip-indexes"</font><font>
    },
    {
      </font><font>"created"</font><font>: </font><font>"2016-01-26T17:48:33.59869621Z"</font><font>,
      </font><font>"created_by"</font><font>: </font><font>"/bin/sh -c sed -i 's/^#\\s*\\(deb.*universe\\)$/\\1/g' /etc/apt/sources.list"</font><font>
    },
    {
      </font><font>"created"</font><font>: </font><font>"2016-01-26T17:48:34.465253028Z"</font><font>,
      </font><font>"created_by"</font><font>: </font><font>"/bin/sh -c #(nop) CMD [\"/bin/bash\"]"</font><font>
    },
    {
      </font><font>"created"</font><font>: </font><font>"2018-11-01T03:19:16.4562755Z"</font><font>,
      </font><font>"created_by"</font><font>: </font><font>"/bin/sh -c #(nop) COPY file:8069dbb6bfc301562a8581e7bbe2b7675c2f96108903c0889d258cd1e11a12f6 in /app/ "</font><font>
    },
    {
      </font><font>"created"</font><font>: </font><font>"2018-11-01T03:19:16.8517953Z"</font><font>,
      </font><font>"created_by"</font><font>: </font><font>"/bin/sh -c #(nop)  CMD [\"/bin/sh\" \"-c\" \"python /app/app.py\"]"</font><font>,
      </font><font>"empty_layer"</font><font>: <b>true</b>
    }
  ],
  </font><font>"os"</font><font>: </font><font>"linux"</font><font>,
  </font><font>"rootfs"</font><font>: {
    </font><font>"type"</font><font>: </font><font>"layers"</font><font>,
    </font><font>"diff_ids"</font><font>: [
      </font><font>"sha256:3cbe18655eb617bf6a146dbd75a63f33c191bf8c7761bd6a8d68d53549af334b"</font><font>,
      </font><font>"sha256:84cc3d400b0d610447fbdea63436bad60fb8361493a32db380bd5c5a79f92ef4"</font><font>,
      </font><font>"sha256:ed58a6b8d8d6a4e2ecb4da7d1bf17ae8006dac65917c6a050109ef0a5d7199e6"</font><font>,
      </font><font>"sha256:5f70bf18a086007016e948b04aed3b82103a36bea41755b6cddfaf10ace3c6ef"</font><font>,
      </font><font>"sha256:9720cebfd814895bf5dc4c1c55d54146719e2aaa06a458fece786bf590cea9d4"</font><font>
    ]
  }
}
</font>

这是一个非常大的JSON文件,但通过它你可以看到有很多不同的元数据。特别是,有关于如何将此镜像转换为正在运行的容器的元数据 - 要运行的命令和要添加的环境变量。

镜像就像洋葱

它们都有层次。但是什么代表一层?

$ ls cac0b96b79417d5163fbd402369f74e3fe4ff8223b655e0b603a8b570bcc76eb
VERSION   json      layer.tar

这是另一个tarfile,让我们打开压缩包看一看。

$ tree -L 1
.
├── bin
├── boot
├── dev
├── etc
├── home
├── lib
├── lib64
├── media
├── mnt
├── opt
├── proc
├── root
├── run
├── sbin
├── srv
├── sys
├── tmp
├── usr
└── <b>var</b>

这是Docker镜像的大秘密,它由文件系统组成不同的视图!几乎所有你在标准的Ubuntu文件系统中看到的这里面都有。

那么每个图层究竟包含什么?那么它将有助于知道哪些层来自基本镜像,以及哪些层是由我们添加的。

使用我们之前做过的相同过程,但在ubuntu:15.04我可以看到这些层:

cac0b96b79417d5163fbd402369f74e3fe4ff8223b655e0b603a8b570bcc76eb
28441336175b9374d04ee75fdb974539e9b8cad8fec5bf0ff8cea6f8571d0114
4631663ba627c9724cd701eff98381cb500d2c09ec78a8c58213f3225877198e
c4f8838502da6456ebfcb3f755f8600d79552d1e30beea0ccc62c13a2556da9c

都属于ubuntu基础镜像,来自FROM ubuntu:15.04命令,知道这一点,我预测我们my_test_image镜像的最顶层6c91b695f2ed98362f511f2490c16dae0dcf8119bcfe2fe9af50305e2173f373应该来自命令COPY app.py /app/。

$ tree
.
└── app
    └── app.py

确实,而且内部的所有内容都是我们对文件系统所做的更改,它只是添加了app.py文件。

工具

如果您希望将来分析镜像,可以使用开源工具 Dive

这怎么变成一个正在运行的容器?

现在我们了解了Docker镜像是什么,Docker如何将其转换为正在运行的容器?

每个容器都有自己的文件系统视图,Docker将获取图像中的所有层,并将它们放在彼此的顶部,以呈现文件系统的一个视图。这种技术称为 Union Mounting ,Docker支持 Linux 上的几个Union Mount Filesystems,主要是 OverlayFSAUFS

但这并非全部,容器意味着短暂,容器运行时对文件系统的更改不应在容器停止后保存。一种方法是将整个镜像复制到其他位置,这样更改不会影响原始文件。这不是非常有效,替代方案(和Docker所做的)是在容器中的文件系统的最顶部添加一个瘦读/写层,进行更改。如果您需要对下面某个图层中的文件进行更改,则需要将该文件复制到进行更改的顶层。这称为 Copy-On-Write 。当容器停止运行时,将丢弃最顶层的文件系统层。

在文件系统之后,除了配置一些后续步骤的元数据之外,镜像不会用于其他许多其他操作。为了完整起见,要创建一个正在运行的容器,我们需要使用 命名空间 来控制进程可以看到的内容(文件系统,进程,网络,用户等); 使用 cgroups 来控制进程可以使用的资源(内存,CPU,网络等); 和安全功能来控制进程可以执行的操作(功能,AppArmor,SELinux,Seccomp)。


以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

算法Ⅰ-Ⅳ

算法Ⅰ-Ⅳ

塞奇威克 / 中国电力出版社 / 2003-11 / 70.00元

《算法I-IV(C实现):基础、数据结构、排序和搜索(第3版)(影印版)》实为一个卓越的读本,作为一个普通的程序员,如果在数学分析方面不算熟练,同时又对理论算法很感兴趣,那么这《算法I-IV(C实现):基础、数据结构、排序和搜索(第3版)(影印版)》确定不容错过,由此你将获益匪浅。Sedgewick擅长深入浅出的方式来解释概念,他在这方面确有天分。另外书中使用了一些实践程序,其篇幅仅有一页左右,而......一起来看看 《算法Ⅰ-Ⅳ》 这本书的介绍吧!

HTML 压缩/解压工具
HTML 压缩/解压工具

在线压缩/解压 HTML 代码

CSS 压缩/解压工具
CSS 压缩/解压工具

在线压缩/解压 CSS 代码

Base64 编码/解码
Base64 编码/解码

Base64 编码/解码