Docker鏡像的存儲機制

Docker鏡像中的關鍵概念

  • registry:registry 用以保存 docker 鏡像,其中還包括鏡像層次結構和關於鏡像的元數據。
  • repository:registry 是 repository 的集合,repository 是鏡像的集合。(docker inspect中repoDigest的由來,在這個repo中的簽名)
  • manifest:manifest(描述文件)主要存在於 registry 中作爲 docker 鏡像的元數據文件,在 pull、push、save 和 load 過程中作爲鏡像結構和基礎信息的描述文件。
  • image:是用來存儲一組鏡像相關的元數據信息,主要包括鏡像的架構(如 amd64)、鏡像默認配置信息、構建鏡像的容器配置信息、包含所有鏡像層信息的 rootfs。docker 利用 rootfs 中的 diff_id 計算出內容尋址的索引(chainID) 來獲取 layer 相關信息,進而獲取每一個鏡像層的文件內容。
  • layer:docker 鏡像管理中的 layer 主要存放了鏡像層的 diff_id、size、cache-id 和 parent 等內容,實際的文件內容則是由存儲驅動來管理,並可以通過 cache-id 在本地索引到。

Docker pull流程

  1. docker發送image的名稱+tag(或者digest)給registry服務器,服務器根據收到的image的名稱+tag(或者digest),找到相應image的manifest,然後將manifest返回給docker
  2. docker得到manifest後,讀取裏面image配置文件的digest(sha256),這個sha256碼就是image的ID
{
  "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"
  }
}

layers.digest就是鏡像的digest,不是layer的diff_id也不是chainId,如果打包格式爲tar時,digest等於diff_id
3. 根據ID在本地找有沒有存在同樣ID的image,有的話就不用繼續下載了
4. 如果沒有,那麼會給registry服務器發請求(裏面包含配置文件的sha256和media type),拿到image的配置文件(Image Config)

{
    "created": "2015-10-31T22:22:56.015925234Z",
    "author": "Alyssa P. Hacker <[email protected]>",
    "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
      }
    ]
}
  1. 根據配置文件中的diff_ids(每個diffid對應一個layer tar包的sha256,tar包相當於layer的原始格式),在本地找對應的layer是否存在
  2. 如果layer不存在,則根據manifest裏面layer的sha256和media type去服務器拿相應的layer(相當去拿壓縮格式的包)(media type: header.Set(“Accept”, “application/vnd.docker.distribution.manifest.v2+json”))
  3. 拿到後進行解壓,並檢查解壓後tar包的sha256能否和配置文件(Image Config)中的diff_id對的上,對不上說明有問題,下載失敗
  4. 根據docker所用的後臺文件系統類型,解壓tar包並放到指定的目錄
  5. 等所有的layer都下載完成後,整個image下載完成,就可以使用了

什麼是鏡像

  • docker 鏡像是一個只讀的 docker 容器模板,含有啓動 docker 容器所需的文件系統結構及其內容,因此是啓動一個 docker 容器的基礎。docker 鏡像的文件內容以及一些運行 docker 容器的配置文件組成了 docker 容器的靜態文件系統運行環境:rootfs。
    docker daemon、docker鏡像以及docker容器三者的關係
  • rootfs: docker 容器在啓動時內部進程可見的文件系統,即 docker 容器的根目錄。rootfs 通常包含一個操作系統運行所需的文件系統,例如可能包含典型的類 Unix 操作系統中的目錄系統,如 /dev、/proc、/bin、/etc、/lib、/usr、/tmp 及運行 docker 容器所需的配置文件、工具等。
  • 當 docker daemon 爲 docker 容器掛載 rootfs 時,沿用了 Linux 內核啓動時的做法,即將 rootfs 設爲只讀模式。在掛載完畢之後,利用聯合掛載(union mount)技術在已有的只讀 rootfs 上再掛載一個讀寫層。這樣,可讀寫的層處於 docker 容器文件系統的最頂層,其下可能聯合掛載了多個只讀的層,只有在 docker 容器運行過程中文件系統發生變化時,纔會把變化的文件內容寫到可讀寫層,並隱藏只讀層中的舊版本文件。

Docker鏡像的主要特點

  • 分層:分層達到了在不的容器同鏡像之間共享鏡像層的效果。docker commit在這個鏡像層之上新加了一層鏡像
  • 寫時複製:所有容器共享同一份數據,只有在 docker 容器運行過程中文件系統發生變化時,纔會把變化的文件內容寫到可讀寫層,並隱藏只讀層中的老版本文件。寫時複製配合分層機制減少了鏡像對磁盤空間的佔用和容器啓動時間。
  • 內容尋址:新模型對鏡像層的內容計算校驗和,生成一個內容哈希值,並以此哈希值代替之前的 UUID 作爲鏡像層的唯一標識。該機制主要提高了鏡像的安全性。(cache-id)
  • 聯合掛載:聯合掛載技術可以在一個掛載點同時掛載多個文件系統,將掛載點的原目錄與被掛載內容進行整合,使得最終可見的文件系統將會包含整合之後的各層的文件和目錄。聯合掛載是用於將多個鏡像層的文件系統掛載到一個掛載點來實現一個統一文件系統視圖的途徑,是下層存儲驅動(aufs、overlay等) 實現分層合併的方式。

鏡像關鍵點介紹

repository

  • 介紹:docker.elastic.co/elasticsearch/elasticsearch中的 elasticsearch/elasticsearch 就是repository
  • 位置: /var/lib/docker/image/<graph_driver>/repositories.json
    repository情況
  • 當前 docker 默認採用 SHA256 算法根據鏡像元數據配置文件計算出鏡像 ID。上圖中的兩條記錄本質上是一樣的,第二條記錄和第一條記錄指向同一個鏡像 ID。其中sha256:c8c275751219dadad8fa56b3ac41ca6cb22219ff117ca98fe82b42f24e1ba64e 被稱爲鏡像的摘要
  • 鏡像摘要Digest:SHA256對鏡像manifest內容計算
  • 對於本地生成的鏡像來說,由於沒有上傳到registry上去,所以沒有digest,因爲鏡像的manifest由registry生成

manifest

  • 介紹:manifest也是一個json文件,media type爲這個文件包含了對前面layers和image config的描述
  • 位置:無
{
  "schemaVersion": 2,
  "config": {
    "mediaType": "application/vnd.oci.image.config.v1+json",
    "size": 7023,
    "digest": "sha256:b5b2b2c507a0944348e0303114d8d93aaaa081732b86451d9bce1f432a537bc7"  //imageId
  },
  "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" //大概可以理解爲diffId
    },
    {
      "mediaType": "application/vnd.oci.image.layer.v1.tar+gzip",
      "size": 73109,
      "digest": "sha256:ec4b8955958665577945c89419d1af06b5f7636b4ac3da7f12184802ad867736"
    }
  ],
  "annotations": {
    "com.example.key1": "value1",
    "com.example.key2": "value2"
  }
}
  • 這個文件其實是registry的一種描述,鏡像下載完成後會自動刪除

Image Config

  • 介紹:json文件包含了對這個image的描述
  • 位置:/var/lib/docker/image/<graph_driver>/imagedb/content/sha256/<image_id>
    image config
    在這裏插入圖片描述
  • diffId和chainId有換算公式的
  • sha256 /var/lib/docker/image/<graph_driver>/imagedb/content/sha256/<image_id> image_id

layer

layer元數據
  • 位置:/var/lib/docker/image/<graph_driver>/layerdb/sha256//
  • layer包含了上一個layer上的改動情況,主要包含三方面的內容:
    1)變化類型:是增加、修改還是刪除了文件
    2)文件類型:每個變化發生在哪種文件類型上
    3)文件屬性:文件的修改時間、用戶ID、組ID、RWX權限等
  • chainID的計算方法
    1)如果該鏡像層是最底層(沒有父鏡像層),該層的 diffID 便是 chainID。
    2)該鏡像層的 chainID 計算公式爲 chainID(n)=SHA256(chain(n-1) diffID(n)),也就是根據父鏡像層的 chainID 加上一個空格和當前層的 diffID,再計算 SHA256 校驗碼。
 /var/lib/docker/image/aufs/layerdb/sha256/67b5e1df1b671d4751794767b20f6973d77e91528ab475ee1118ce8dab796193# ls
cache-id  diff  parent  size  tar-split.json.gz
  • chche-id: 本機隨機生成的uuid用來標識在本機器上唯一
  • diff: diffId
  • parent: 父layer的chainId
  • size: 這個layer的大小
layer的diffId和digest的對應關係
  • digest可以再docker pull的時候查看到
tree -d /var/lib/docker/image/aufs/distribution/
/var/lib/docker/image/aufs/distribution/
├── diffid-by-digest
│   └── sha256
└── v2metadata-by-diffid
    └── sha256

① diffid-by-digest: 存放digest到diffid的對應關係
② v2metadata-by-diffid: 存放diffid到digest的對應關係

layer數據

位置:/var/lib/docker/<graph_driver>

結束語

本篇介紹了image在本地的存儲方式,包括了/var/lib/docker/image和/var/lib/docker/aufs這兩個目錄,但/var/lib/docker/image下面有兩個目錄沒有涉及:

  • /var/lib/docker/image/aufs/imagedb/metadata:裏面存放的是本地image的一些信息,從服務器上pull下來的image不會存數據到這個目錄,

  • /var/lib/docker/image/aufs/layerdb/mounts: 創建container時,docker會爲每個container在image的基礎上創建一層新的layer,裏面主要包含/etc/hosts、/etc/hostname、/etc/resolv.conf等文件,創建的這一層layer信息就放在這裏。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章