内容简介:你可能会熟悉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,主要是 OverlayFS 和 AUFS 。
但这并非全部,容器意味着短暂,容器运行时对文件系统的更改不应在容器停止后保存。一种方法是将整个镜像复制到其他位置,这样更改不会影响原始文件。这不是非常有效,替代方案(和Docker所做的)是在容器中的文件系统的最顶部添加一个瘦读/写层,进行更改。如果您需要对下面某个图层中的文件进行更改,则需要将该文件复制到进行更改的顶层。这称为 Copy-On-Write 。当容器停止运行时,将丢弃最顶层的文件系统层。
在文件系统之后,除了配置一些后续步骤的元数据之外,镜像不会用于其他许多其他操作。为了完整起见,要创建一个正在运行的容器,我们需要使用 命名空间 来控制进程可以看到的内容(文件系统,进程,网络,用户等); 使用 cgroups 来控制进程可以使用的资源(内存,CPU,网络等); 和安全功能来控制进程可以执行的操作(功能,AppArmor,SELinux,Seccomp)。
以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网
猜你喜欢:- KVM镜像制作及挂载镜像文件
- 查看Docker镜像仓库中镜像的所有标签
- Vagrant 使用国内镜像安装插件和 box 镜像
- 不要轻易使用 Alpine 镜像来构建 Docker 镜像,有坑!
- Harbor 1.7 发布,在线 GC、镜像历史、镜像复制
- docker k8s 集群部署tomcat,使用一个镜像,增加镜像的复用性。
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。