Docker镜像中有什么?

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

内容简介:你可能会熟悉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)。


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

查看所有标签

猜你喜欢:

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

Programming PHP

Programming PHP

Rasmus Lerdorf、Kevin Tatroe、Peter MacIntyre / O'Reilly Media / 2006-5-5 / USD 39.99

Programming PHP, 2nd Edition, is the authoritative guide to PHP 5 and is filled with the unique knowledge of the creator of PHP (Rasmus Lerdorf) and other PHP experts. When it comes to creating websit......一起来看看 《Programming PHP》 这本书的介绍吧!

图片转BASE64编码
图片转BASE64编码

在线图片转Base64编码工具

MD5 加密
MD5 加密

MD5 加密工具

正则表达式在线测试
正则表达式在线测试

正则表达式在线测试