Docker镜像的关键概念
我们尝试解压一个镜像的tar包(demo-1.0.tar.gz)
tar文件目录如下
cbd1cdce753fb2defc9ca899742ebf1a16933189d9111ed69a97fcb7ba631593.json
repositories
manifest.json
1a58e6937db044ef6f2e2962a0dc7bef16a6c33fdfc5a0318c39092612a1bd1a
c12f86d2a60fc27a1d93d555944262fda4ed66e3a3172ac45cd861151a0dc6c1
98867178f60349f16652222772d086159a6d087fcd50bc32b9d75c23cd01ed8d
21c47a73cf2af80d1b2321a7be026939f42b5c8734aa51ecfe1fcc6ca669faeb
1. registry
简单想象成类似Git仓库之类的实体
registry由一系列经过命名的repository组成
2. repository
repository 即具有 某个功能的Docker镜像的所有迭代版本构成的 镜像组.
(repository是一个镜像集合,其中包含了多个不同的版本的镜像,使用标签进行版本区分)
repository通过命名规范对用户仓库和顶层仓库进行组织.
用户仓库的命名由用户名和repository名两部分组成,中间以"/"隔开,
即username/repository_name的形式,repository名通常表示镜像所具有的功能
而顶层仓库则只包含repository名的部分.
repositories.json文件内容:
{"cpsdc-platform/middleware-app":
{"master-59":
"21c47a73cf2af80d1b2321a7be026939f42b5c8734aa51ecfe1fcc6ca669faeb"
}
}
3. manifest
manifest 描述文件 主要存在于registry中作为Docker镜像的元数据文件,在pull/push/save/load中
作为镜像结构和基础信息的描述文件.
在镜像被pull/load到Docker宿主机时,manifest被转化为本地的镜像配置文件config.
manifest.json文件内容:
[
{
"Config":"cbd1cdce753fb2defc9ca899742ebf1a16933189d9111ed69a97fcb7ba631593.json",
"RepoTags":[
"cpsdc-platform/middleware-app:master-59"
],
"Layers":[
"1a58e6937db044ef6f2e2962a0dc7bef16a6c33fdfc5a0318c39092612a1bd1a/layer.tar",
"c12f86d2a60fc27a1d93d555944262fda4ed66e3a3172ac45cd861151a0dc6c1/layer.tar",
"98867178f60349f16652222772d086159a6d087fcd50bc32b9d75c23cd01ed8d/layer.tar",
"21c47a73cf2af80d1b2321a7be026939f42b5c8734aa51ecfe1fcc6ca669faeb/layer.tar"
]
}
]
4. image和layer(镜像和镜像层)
Docker 内部的image概念是用来存储一组镜像相关的元数据信息,
主要包括:
-
镜像的架构(如amd64)
-
镜像默认配置信息
-
构建镜像的容器配置信息, 容器ID,创建时间,创建该镜像的Docker版本,构建镜像历史
-
包含所有镜像层信息的rootfs.
Docker利用rootfs中的diff_id计算出内容寻址的索引(chainID)来获取layer相关信息,
进而获取每一个镜像层的文件内容.
image元数据
cbd1cdce753fb2defc9ca899742ebf1a16933189d9111ed69a97fcb7ba631593.json
文件内容如下:
{
"architecture":"amd64",
"config":{
"Hostname":"",
"Domainname":"",
"User":"",
"AttachStdin":false,
"AttachStdout":false,
"AttachStderr":false,
"ExposedPorts":{
"8080/tcp":{
}
},
"Tty":false,
"OpenStdin":false,
"StdinOnce":false,
"Env":[
"PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/lib/jvm/java-1.8-openjdk/jre/bin:/usr/lib/jvm/java-1.8-openjdk/bin",
"LANG=C.UTF-8",
"JAVA_HOME=/usr/lib/jvm/java-1.8-openjdk",
"JAVA_VERSION=8u212",
"JAVA_ALPINE_VERSION=8.212.04-r0",
"JAVA_OPTS="
],
"Cmd":null,
"ArgsEscaped":true,
"Image":"sha256:776867d1b220c1a3759f764bc560686fbf1165b5d4aeff4db23a5791075823de",
"Volumes":{
"/tmp":{
},
"/var/log":{
}
},
"WorkingDir":"",
"Entrypoint":[
"/bin/sh",
"-c",
"exec java $JAVA_OPTS -jar app.jar"
],
"OnBuild":null,
"Labels":null
},
"container":"38a74d3908c829acb5235fafdc39a8cdd586fce6e4215f65de6f70f342e7be98",
"container_config":{
"Hostname":"38a74d3908c8",
"Domainname":"",
"User":"",
"AttachStdin":false,
"AttachStdout":false,
"AttachStderr":false,
"ExposedPorts":{
"8080/tcp":{
}
},
"Tty":false,
"OpenStdin":false,
"StdinOnce":false,
"Env":[
"PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/lib/jvm/java-1.8-openjdk/jre/bin:/usr/lib/jvm/java-1.8-openjdk/bin",
"LANG=C.UTF-8",
"JAVA_HOME=/usr/lib/jvm/java-1.8-openjdk",
"JAVA_VERSION=8u212",
"JAVA_ALPINE_VERSION=8.212.04-r0",
"JAVA_OPTS="
],
"Cmd":[
"/bin/sh",
"-c",
"#(nop) ",
"EXPOSE 8080"
],
"ArgsEscaped":true,
"Image":"sha256:776867d1b220c1a3759f764bc560686fbf1165b5d4aeff4db23a5791075823de",
"Volumes":{
"/tmp":{
},
"/var/log":{
}
},
"WorkingDir":"",
"Entrypoint":[
"/bin/sh",
"-c",
"exec java $JAVA_OPTS -jar app.jar"
],
"OnBuild":null,
"Labels":{
}
},
"created":"2021-07-20T11:23:43.475808513Z",
"docker_version":"18.09.9",
"history":[
{
"created":"2019-05-11T00:07:03.358250803Z",
"created_by":"/bin/sh -c #(nop) ADD file:a86aea1f3a7d68f6ae03397b99ea77f2e9ee901c5c59e59f76f93adbb4035913 in / "
},
{
"created":"2019-05-11T00:07:03.510395965Z",
"created_by":"/bin/sh -c #(nop) CMD [\"/bin/sh\"]",
"empty_layer":true
},
{
"created":"2019-05-11T01:32:11.0323736Z",
"created_by":"/bin/sh -c #(nop) ENV LANG=C.UTF-8",
"empty_layer":true
},
{
"created":"2019-05-11T01:32:12.044405061Z",
"created_by":"/bin/sh -c { \t\techo '#!/bin/sh'; \t\techo 'set -e'; \t\techo; \t\techo 'dirname \"$(dirname \"$(readlink -f \"$(which javac || which java)\")\")\"'; \t} \u003e /usr/local/bin/docker-java-home \t\u0026\u0026 chmod +x /usr/local/bin/docker-java-home"
},
{
"created":"2019-05-11T01:32:12.271831312Z",
"created_by":"/bin/sh -c #(nop) ENV JAVA_HOME=/usr/lib/jvm/java-1.8-openjdk",
"empty_layer":true
},
{
"created":"2019-05-11T01:32:12.506789049Z",
"created_by":"/bin/sh -c #(nop) ENV PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/lib/jvm/java-1.8-openjdk/jre/bin:/usr/lib/jvm/java-1.8-openjdk/bin",
"empty_layer":true
},
{
"created":"2019-05-11T01:32:12.755877628Z",
"created_by":"/bin/sh -c #(nop) ENV JAVA_VERSION=8u212",
"empty_layer":true
},
{
"created":"2019-05-11T01:32:13.051281176Z",
"created_by":"/bin/sh -c #(nop) ENV JAVA_ALPINE_VERSION=8.212.04-r0",
"empty_layer":true
},
{
"created":"2019-05-11T01:32:17.777332452Z",
"created_by":"/bin/sh -c set -x \t\u0026\u0026 apk add --no-cache \t\topenjdk8=\"$JAVA_ALPINE_VERSION\" \t\u0026\u0026 [ \"$JAVA_HOME\" = \"$(docker-java-home)\" ]"
},
{
"created":"2021-07-20T11:23:29.98302564Z",
"created_by":"/bin/sh -c #(nop) VOLUME [/tmp /var/log]",
"empty_layer":true
},
{
"created":"2021-07-20T11:23:33.156968333Z",
"created_by":"/bin/sh -c #(nop) ADD file:13d31caa85b9055e5fceeb2ea1b04861b6f5b162ecb61f53c929a42c77df1fee in app.jar "
},
{
"created":"2021-07-20T11:23:37.765093873Z",
"created_by":"/bin/sh -c #(nop) ENV JAVA_OPTS=",
"empty_layer":true
},
{
"created":"2021-07-20T11:23:40.436505735Z",
"created_by":"/bin/sh -c #(nop) ENTRYPOINT [\"/bin/sh\" \"-c\" \"exec java $JAVA_OPTS -jar app.jar\"]",
"empty_layer":true
},
{
"created":"2021-07-20T11:23:43.475808513Z",
"created_by":"/bin/sh -c #(nop) EXPOSE 8080",
"empty_layer":true
}
],
"os":"linux",
"rootfs":{
"type":"layers",
"diff_ids":[
"sha256:f1b5933fe4b5f49bbe8258745cf396afe07e625bdab3168e364daf7c956b6b81",
"sha256:9b9b7f3d56a01e3d9076874990c62e7a516cc4032f784f421574d06b18ef9aa4",
"sha256:ceaf9e1ebef5f9eaa707a838848a3c13800fcf32d7757be10d4b08fb85f1bc8a",
"sha256:f149ec39d98314f3ad56fcce4505642a19293d8c72b7ad491935eb472c624489"
]
}
}
layer (镜像层) 是一个Docker用来管理镜像层的中间概念, 镜像是由镜像层组成的,而单个镜像层可能被多个镜像共享,
所以Docker将layer和image的概念分离.
Docker镜像管理中layer主要存放了镜像层的diff_id,size,cache-id和parent等内容,
实际文件内容则是由存储驱动来管理,并可以通过cache-id在本地索引找到.
5. Dockerfile
Dockerfile是通过docker build命令构建自己的Docker镜像时需要使用到的定义文件.
Docker存储管理
Docker 镜像在设计上将镜像元数据与镜像文件的存储完全隔离开了.
Docker 在管理镜像层元数据时,采用的也正是从上至下repository,image,layer三个层次.
1.repository元数据
由于Docker以分层的形式存储镜像,所以repository与image这两类元数据并无物理上的镜像文件与之对应,
而layer这种元数据存在物理上的镜像层文件与之对应.
repositories.json文件存储了所有repository的名字
{"cpsdc-platform/middleware-app":
{"master-59":
"21c47a73cf2af80d1b2321a7be026939f42b5c8734aa51ecfe1fcc6ca669faeb"
}
}
2. image元数据
Docker 内部的image概念是用来存储一组镜像相关的元数据信息,
主要包括:
-
镜像的架构(如amd64)
-
镜像默认配置信息
-
构建镜像的容器配置信息, 容器ID,创建时间,创建该镜像的Docker版本,构建镜像历史
-
包含所有镜像层信息的rootfs.
其中构建镜像的历史信息和rootfs组成部分除了具有描述镜像的作用外,
还将镜像和构建该镜像的镜像层关联了起来.
Docker会根据历史信息和rootfs中的diff_ids计算出构建该镜像的镜像层的存储索引chainID.
这也是Docker1.10 镜像存储中基于内容寻址的核心技术.
3. layer元数据
Docker 1.10 之后,镜像层只包含一个具体的镜像层文件包.
在layer的所有属性中,diffId采用SHA256算法,基于镜像层文件包的内容计算得到.
而chainID是基于内容存储的索引,它是根据当前层与所有祖先镜像层diffID计算出来的.
chainID算法如下:
-
如果该镜像层是最底层(没有父镜像层),该层的diffID便是chainID.
-
该镜像层的chainID计算公式为 chainID(n) = SHA(chain(n-1) diffID(n))
即 根据父镜像层的chainID加上一个空格和当前层diffID,再计算出SHA256校验码.