走進docker(02):image(鏡像)是什麼?

上一篇介紹了hello-world的大概流程,那麼hello-world的image裏面到底包含了些什麼呢?裏面的格式是怎麼樣的呢?html

image所包含的內容以及格式都是有標準的,由Open Containers Initiative(OCI)負責維護,地址爲image-spec,本文將對該標準作一個簡單的解釋。linux

image包含的內容

一個image由manifestimage index (可選)、filesystem layersconfiguration四部分組成。git

關係圖

先來看看構成image的四部分的關係圖:程序員

+-----------------------+
                    | Image Index(optional) |
                    +-----------------------+
                               |
                               | 1..*
                               ↓
                    +----------------------+
                    |    Image Manifest    |
                    +----------------------+
                               |
                     1..1      |     1..*
               +---------------+--------------+
               |                              |
               ↓                              ↓
       +--------------+             +-------------------+
       | Image Config |             | Filesystem Layers |
       +--------------+             +-------------------+
  • Image Index和Manifest的關係是"1..*",表示它們是一對多的關係github

  • Image Manifest和Config的關係是"1..1",表示它們是一對一的關係web

  • Image Manifest和Filesystem Layers是一對多的關係docker

下面分別介紹它們各自都包含了哪些內容。json

Filesystem Layers

Filesystem Layer包含了文件系統的信息,即該image包含了哪些文件/目錄,以及它們的屬性和數據。ubuntu

包含的內容

每一個filesystem layer都包含了在上一個layer上的改動狀況,主要包含三方面的內容:服務器

  • 變化類型:是增長、修改仍是刪除了文件

  • 文件類型:每一個變化發生在哪一種文件類型上

  • 文件屬性:文件的修改時間、用戶ID、組ID、RWX權限等

好比在某一層增長了一個文件,那麼這一層所包含的內容就是增長的這個文件的數據以及它的屬性,具體的細節請參考標準文檔

打包格式

最終每一個layer都會打包成一個文件,這個文件的格式能夠是tar和tar+gzip兩種中的一種。

對於這兩種不一樣類型的文件格式,標準定義了兩個新的media types,分別是application/vnd.oci.image.layer.v1.tar和application/vnd.oci.image.layer.v1.tar+gzip。

同時標準還定義了application/vnd.oci.image.layer.nondistributable.v1.tar和application/vnd.oci.image.layer.nondistributable.v1.tar+gzip這兩種對應於nondistributable的格式,其實這兩種格式和前兩種格式包含的內容是同樣的,只是用不一樣的類型名稱來區分它們的用途,對於名稱中有nondistributable的layer,標準要求這種類型的layer不能上傳,只能下載。

作過web開發的程序員對media type應該比較熟悉,簡單點說,就是當客戶端用http協議下載一個文件的時候,須要在http的首部帶上Accept字段,告訴服務器端它支持哪些類型的文件,服務器返回文件的時候,須要在http的首部帶上Content-Type字段,告訴客戶端返回文件的類型,如Accept: text/html,application/xmlContent-Type: text/html

Image Config

image config就是一個json文件,它的media type是application/vnd.oci.image.config.v1+json,這個json文件包含了對這個image的描述。先看看官方網站給的例子:

{
    "created": "2015-10-31T22:22:56.015925234Z",
    "author": "Alyssa P. Hacker <alyspdev@example.com>",
    "architecture": "amd64",
    "os": "linux",
    "config": {
        "User": "alice",
        "ExposedPorts": {
            "8080/tcp": {}
        },
        "Env": [
            "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
            "FOO=oci_is_a",
            "BAR=well_written_spec"
        ],
        "Entrypoint": [
            "/bin/my-app-binary"
        ],
        "Cmd": [
            "--foreground",
            "--config",
            "/etc/my-app.d/default.cfg"
        ],
        "Volumes": {
            "/var/job-result-data": {},
            "/var/log/my-app-logs": {}
        },
        "WorkingDir": "/home/alice",
        "Labels": {
            "com.example.project.git.url": "https://example.com/project.git",
            "com.example.project.git.commit": "45a939b2999782a3f005621a8d0f29aa387e1d6b"
        }
    },
    "rootfs": {
      "diff_ids": [
        "sha256:c6f988f4874bb0add23a778f753c65efe992244e148a1d2ec2a8b664fb66bbd1",
        "sha256:5f70bf18a086007016e948b04aed3b82103a36bea41755b6cddfaf10ace3c6ef"
      ],
      "type": "layers"
    },
    "history": [
      {
        "created": "2015-10-31T22:22:54.690851953Z",
        "created_by": "/bin/sh -c #(nop) ADD file:a3bc1e842b69636f9df5256c49c5374fb4eef1e281fe3f282c65fb853ee171c5 in /"
      },
      {
        "created": "2015-10-31T22:22:55.613815829Z",
        "created_by": "/bin/sh -c #(nop) CMD [\"sh\"]",
        "empty_layer": true
      }
    ]
}

這裏只介紹幾個比較重要的屬性,其它的請參考標準文檔

  • architecture:CPU架構類型,如今大部分都是amd64,不過arm64估計會慢慢多起來

  • os:操做系統,本人只用過linux

  • config:當根據這個image啓動container時,config裏面的配置就是運行container時的默認參數,在後續介紹runtime的時候再仔細介紹每一項的意義

  • rootfs:指定了image所包含的filesystem layers,type的值必須是layers,diff_ids包含了layer的列表(順序排列),每個sha256就是每層layer對應tar包的sha256碼

manifest

manifest也是一個json文件,media type爲application/vnd.oci.image.manifest.v1+json,這個文件包含了對前面filesystem layers和image config的描述,一看官方網站給出的示例就明白了:

manifest文件中config的sha256就是image的ID,即上面image config文件的sha256值,這裏是b5b2b2c507a0944348e0303114d8d93aaaa081732b86451d9bce1f432a537bc7

{
  "schemaVersion": 2,
  "config": {
    "mediaType": "application/vnd.oci.image.config.v1+json",
    "size": 7023,
    "digest": "sha256:b5b2b2c507a0944348e0303114d8d93aaaa081732b86451d9bce1f432a537bc7"
  },
  "layers": [
    {
      "mediaType": "application/vnd.oci.image.layer.v1.tar+gzip",
      "size": 32654,
      "digest": "sha256:e692418e4cbaf90ca69d05a66403747baa33ee08806650b51fab815ad7fc331f"
    },
    {
      "mediaType": "application/vnd.oci.image.layer.v1.tar+gzip",
      "size": 16724,
      "digest": "sha256:3c3a4604a545cdc127456d94e421cd355bca5b528f4a9c1905b15da2eb4a4c6b"
    },
    {
      "mediaType": "application/vnd.oci.image.layer.v1.tar+gzip",
      "size": 73109,
      "digest": "sha256:ec4b8955958665577945c89419d1af06b5f7636b4ac3da7f12184802ad867736"
    }
  ],
  "annotations": {
    "com.example.key1": "value1",
    "com.example.key2": "value2"
  }
}
  • config裏面包含了對image config文件的描述,有media type,文件大小,以及sha256碼

  • layers包含了對每個layer的描述,和對config文件的描述同樣,也包含了media type,文件大小,以及sha256碼

這裏layer的sha256和image config文件中的diff_ids有可能不同,好比這裏的layer文件格式是tar+gzip,那麼這裏的sha256就是tar+gzip包的sha256碼,而diff_ids是tar+gzip解壓後tar文件的sha256碼

Image Index(可選)

image index也是個json文件,media type是application/vnd.oci.image.index.v1+json

其實到manifest爲止,已經有了整個image的完整描述,爲何還須要image index這個文件呢?主要緣由是manifest描述的image只能支持一個平臺,也無法支持多個tag,加上index文件的目的就是讓這個image能支持多個平臺和多tag。

image index是v1.0.0-rc5才加進來的一個文件,還不穩定,後面可能還會修改,而且docker如今也不支持該文件,這裏看看官方給的示例,先了解一下:

{
  "schemaVersion": 2,
  "manifests": [
    {
      "mediaType": "application/vnd.oci.image.manifest.v1+json",
      "size": 7143,
      "digest": "sha256:e692418e4cbaf90ca69d05a66403747baa33ee08806650b51fab815ad7fc331f",
      "platform": {
        "architecture": "ppc64le",
        "os": "linux"
      }
    },
    {
      "mediaType": "application/vnd.oci.image.manifest.v1+json",
      "size": 7682,
      "digest": "sha256:5b0bcabd1ed22e9fb1310cf6c2dec7cdef19f0ad69efa1f392e94a4333501270",
      "platform": {
        "architecture": "amd64",
        "os": "linux",
        "os.features": [
          "sse4"
        ]
      }
    }
  ],
  "annotations": {
    "com.example.key1": "value1",
    "com.example.key2": "value2"
  }
}

index文件包含了對image中全部manifest的描述,至關於一個manifest列表,包括每一個manifest的media type,文件大小,sha256碼,支持的平臺以及平臺特殊的配置。

好比ubuntu想讓它的image支持amd64和arm64平臺,因而它在兩個平臺上都編譯好相應的包,而後將兩個平臺的layer都放到這個image的filesystem layers裏面,而後寫兩個config文件和兩個manifest文件,再加上這樣一個描述不一樣平臺manifest的index文件,就可讓這個image支持兩個平臺了,兩個平臺的用戶可使用一樣的命令獲得本身平臺想要的那些layer。

image index最新的標準裏面並無涉及到tag,不過估計後續會加上。

image layout

上面介紹了image所包含的內容,在開始介紹layout以前,先來回顧一下上一篇介紹hello-world時提到的從register服務器拉image的過程:

  • 首先獲取image的manifests

  • 根據manifests文件中config的sha256碼,獲得image config文件

  • 遍歷manifests裏面的全部layer,根據其sha256碼在本地找,若是找到對應的layer,則跳過,不然從服務器取相應layer的壓縮包

  • 等上面的全部步驟完成後,就會拼出完整的image

從上面的過程當中能夠看出,咱們從服務器上取image的時候不須要知道image manifests和config文件的名字,也不須要知道layer壓縮包的名字。

那麼image從服務器拉下來後,在本地應該怎麼存儲呢?文件名稱和目錄結構應該是怎樣的呢?OCI也有相應的標準,名字叫image layout,有了這樣的標準以後,咱們就能夠將整個image打成一個包,方便的在不一樣機器,不一樣容器平臺之間導入導出。

不過遺憾的是,OCI的這個標準還在變化中,根據github上所看到的,v1.0.0-rc5在v1.0.0-rc4上就有較大的修改,而且如今docker也不支持該標準。

docker對OCI image layout的支持還在開發中,相關動態請關注:Support OCI image layout in docker save/load

這裏咱們看看v1.0.0-rc4的格式,下面是hello-world image的目錄結構,瞭解一下:

dev@debian:~/images/hello-world$ tree
.
├── blobs
│   └── sha256
│       ├── 636fcf0bc8246e08d2df4771dc764d35ea50428b8dfaa904773b0707cb4f6303
│       ├── 7520415ce76232cdd62ecc345cea5ea44f5b6b144dc62351f2cd2b08382532a3
│       └── 9b8e3ce88f3a2aaa478cfe613632f38d27be5eddaa002a719fa1bfa9ff4f7f63
├── oci-layout
└── refs
    └── latest

oci-layout

包含image標準的版本信息

dev@debian:~/images/hello-world$ cat ./oci-layout| jq .
{
  "imageLayoutVersion": "1.0.0"
}

refs

裏面的每一個文件就是一個tag(hello-world的image中只有一個latest tag),每一個tag都是一個單獨的image,至關於一個image的layout包裏面能夠包含多個有關係的image,文件的內容以下:

dev@debian:~/images/hello-world$ cat ./refs/latest | jq .
{
  "mediaType": "application/vnd.oci.image.manifest.v1+json",
  "digest": "sha256:9b8e3ce88f3a2aaa478cfe613632f38d27be5eddaa002a719fa1bfa9ff4f7f63",
  "size": 347
}

其實就是對manifest文件的描述,根據sha256就能夠在blobs的目錄裏面找到相應的manifest文件

blobs

裏面包含了具體文件的內容,每一個文件名都是其內容的sha256碼,根據上面refs文件裏面的sha256,就能在這裏找到對應的manifest文件的內容,而後根據manifest文件的內容,就能一步一步的往下找到image config文件和filesystem layers文件。

dev@debian:~/images/hello-world$ file ./blobs/sha256/*
./blobs/sha256/636fcf0bc8246e08d2df4771dc764d35ea50428b8dfaa904773b0707cb4f6303: ASCII text, with very long lines, with no line terminators
./blobs/sha256/7520415ce76232cdd62ecc345cea5ea44f5b6b144dc62351f2cd2b08382532a3: gzip compressed data
./blobs/sha256/9b8e3ce88f3a2aaa478cfe613632f38d27be5eddaa002a719fa1bfa9ff4f7f63: ASCII text, with very long lines, with no line terminators

dev@debian:~/images/hello-world$ sha256sum ./blobs/sha256/*
636fcf0bc8246e08d2df4771dc764d35ea50428b8dfaa904773b0707cb4f6303  ./blobs/sha256/636fcf0bc8246e08d2df4771dc764d35ea50428b8dfaa904773b0707cb4f6303
7520415ce76232cdd62ecc345cea5ea44f5b6b144dc62351f2cd2b08382532a3  ./blobs/sha256/7520415ce76232cdd62ecc345cea5ea44f5b6b144dc62351f2cd2b08382532a3
9b8e3ce88f3a2aaa478cfe613632f38d27be5eddaa002a719fa1bfa9ff4f7f63  ./blobs/sha256/9b8e3ce88f3a2aaa478cfe613632f38d27be5eddaa002a719fa1bfa9ff4f7f63

下載image

上面介紹image layout時用到了hello-world的image,它是從哪裏來的呢?

爲了快速的構建container的rootfs,docker在本地有它本身的一套image管理方式,有本身的layout,而且目前docker save命令也不支持導出OCI格式的image,只能導出docker本身的格式,因此咱們只能藉助其它的工具獲得OCI格式的image。

這裏咱們用skopeo來演示一下從docker hub上拉取hello-world的image,並把它在本地存成OCI的layout。

這裏生成的layout和最新版本的標準有點差異,和v1.0.0-rc4的標準一致,若是你用一樣的命令獲得不同的layout,說明skopeo有更新,支持了更新的標準。

#這裏的全部命令在debian 8.6的環節上運行經過
#編譯安裝skopeo
$ git clone https://github.com/projectatomic/skopeo $GOPATH/src/github.com/projectatomic/skopeo
$ sudo apt-get install libgpgme11-dev libdevmapper-dev btrfs-tools go-md2man
$ cd $GOPATH/src/github.com/projectatomic/skopeo 
$ make binary-local
$ sudo make install

#下載hello-world的image
$ skopeo copy docker://hello-world oci:hello-world
$ tree hello-world/
hello-world/
├── blobs
│   └── sha256
│       ├── 636fcf0bc8246e08d2df4771dc764d35ea50428b8dfaa904773b0707cb4f6303
│       ├── 7520415ce76232cdd62ecc345cea5ea44f5b6b144dc62351f2cd2b08382532a3
│       └── 9b8e3ce88f3a2aaa478cfe613632f38d27be5eddaa002a719fa1bfa9ff4f7f63
├── oci-layout
└── refs
    └── latest

結束語

雖然OCI iamge的標準還處於rc階段,沒有正式release,而且docker如今也不支持OCI image的layout,不過相信過不了多久,OCI image的標準就會被普遍支持,先了解一下仍是頗有必要的。

參考

相關文章
相關標籤/搜索