Docker鏡像構建原理解析(不裝docker也能構建鏡像)

image

在devops流程裏面 構建鏡像是一個非常重要的過程,一般構建鏡像是寫dockerfile文件然後通過docker client來構建的image。

docker client 會先檢查本地有沒有image,如果沒有幫你 從鏡像倉庫 pull 下來
然後解析你寫的dockerfile構建新的image。

本文帶你了解

  • pull 命令 背後是怎麼做的?
  • build 命令 背後是怎麼做的?

下篇文章帶你解析:

  • 如果我不用docker 我如何構建一個鏡像?

我們以微軟的aspnet2.2為基礎構建一個aspnetcore項目的鏡像為例子

mcr.microsoft.com/dotnet/core/aspnet:2.2

根據基礎鏡像REGISTRY去獲取mainfest信息

image

//mcr.microsoft.com/v2/dotnet/core/aspnet/manifests/2.2
Accept:
application/vnd.oci.image.manifest.v1+json,application/vnd.docker.distribution.manifest.v2+json,application/vnd.docker.distribution.manifest.v1+json

獲取到的內容如下:

{
    "schemaVersion": 2,
    "mediaType": "application/vnd.docker.distribution.manifest.v2+json",
    "config": {
        "mediaType": "application/vnd.docker.container.image.v1+json",
        "size": 4039,
        "digest": "sha256:e7e3b238011ce0f2b9350153535fe273caa01f0e7188d0b91f965b3802ddc600"
    },
    "layers": [
        {
            "mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip",
            "size": 22524609,
            "digest": "sha256:804555ee037604c40de144f9f8da0d826d38db82f15d74cded32790fe279a8f6"
        },
        {
            "mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip",
            "size": 17692725,
            "digest": "sha256:970251047358aea56ba6db6975b14ff12470b75de0c2477f4445240ddd727fd4"
        },
        {
            "mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip",
            "size": 2978257,
            "digest": "sha256:f3d4c41a4fd13f35c0b46f19a4e27845f4695163cc7174d908ff84836bbc2f5a"
        },
        {
            "mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip",
            "size": 62145592,
            "digest": "sha256:bd391c46585f9f8d84992bbaa9087189148c1601968eaaf097d5b3ed60840e5e"
        }
    ]
}

mainfest文件裏面都是摘要(digest)記錄

  • config信息摘要
  • 每個layer的摘要 (上面的例子有4個)

根據上面的config信息摘要獲取config詳情

image

GET://mcr.microsoft.com/v2/dotnet/core/aspnet/blobs/sha256:e7e3b238011ce0f2b9350153535fe273caa01f0e7188d0b91f965b3802ddc600

{
    "architecture": "amd64",
    "config": {
        "Hostname": "",
        "Domainname": "",
        "User": "",
        "AttachStdin": false,
        "AttachStdout": false,
        "AttachStderr": false,
        "Tty": false,
        "OpenStdin": false,
        "StdinOnce": false,
        "Env": [
            "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
            "ASPNETCORE_URLS=//+:80",
            "DOTNET_RUNNING_IN_CONTAINER=true",
            "ASPNETCORE_VERSION=2.2.8"
        ],
        "Cmd": [
            "bash"
        ],
        "ArgsEscaped": true,
        "Image": "sha256:5ecfe4016ac8e911a94aa601a675f7204e9ccab00cbb08e7067c184ad40f34e9",
        "Volumes": null,
        "WorkingDir": "",
        "Entrypoint": null,
        "OnBuild": null,
        "Labels": null
    },
    "container": "14196c2f9c327d41e26682d32c7c89c4e7c78aa32f8b7501a23192035a9f4844",
    "container_config": {
        "Hostname": "",
        "Domainname": "",
        "User": "",
        "AttachStdin": false,
        "AttachStdout": false,
        "AttachStderr": false,
        "Tty": false,
        "OpenStdin": false,
        "StdinOnce": false,
        "Env": [
            "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
            "ASPNETCORE_URLS=//+:80",
            "DOTNET_RUNNING_IN_CONTAINER=true",
            "ASPNETCORE_VERSION=2.2.8"
        ],
        "Cmd": [
            "/bin/sh",
            "-c",
            "curl -SL --output aspnetcore.tar.gz //dotnetcli.azureedge.net/dotnet/aspnetcore/Runtime/$ASPNETCORE_VERSION/aspnetcore-runtime-$ASPNETCORE_VERSION-linux-x64.tar.gz     && aspnetcore_sha512='954072376698be69acb7e277df2c243f931e10529def21dcbf9ce277609b30d462126bf8b8b3cab36476bec3d63a927b8e44e59e4d4cade23eef45956fba1ffd'     && echo \"$aspnetcore_sha512  aspnetcore.tar.gz\" | sha512sum -c -     && mkdir -p /usr/share/dotnet     && tar -zxf aspnetcore.tar.gz -C /usr/share/dotnet     && rm aspnetcore.tar.gz     && ln -s /usr/share/dotnet/dotnet /usr/bin/dotnet"
        ],
        "Image": "sha256:5ecfe4016ac8e911a94aa601a675f7204e9ccab00cbb08e7067c184ad40f34e9",
        "Volumes": null,
        "WorkingDir": "",
        "Entrypoint": null,
        "OnBuild": null,
        "Labels": null
    },
    "created": "2019-12-28T08:12:05.676492579Z",
    "docker_version": "3.0.8",
    "history": [
        {
            "created": "2019-12-28T04:23:47.4966447Z",
            "created_by": "/bin/sh -c #(nop) ADD file:90a2c81769a336bed3f731f44a385f2a65b0916f517a0b77c06c224579bf9a9a in / "
        },
        {
            "created": "2019-12-28T04:23:47.719507596Z",
            "created_by": "/bin/sh -c #(nop)  CMD [\"bash\"]",
            "empty_layer": true
        },
        {
            "created": "2019-12-28T08:11:05.607009582Z",
            "created_by": "/bin/sh -c apt-get update     && apt-get install -y --no-install-recommends         ca-certificates                 libc6         libgcc1         libgssapi-krb5-2         libicu57         liblttng-ust0         libssl1.0.2         libstdc++6         zlib1g     && rm -rf /var/lib/apt/lists/*"
        },
        {
            "created": "2019-12-28T08:11:07.64336022Z",
            "created_by": "/bin/sh -c #(nop)  ENV ASPNETCORE_URLS=//+:80 DOTNET_RUNNING_IN_CONTAINER=true",
            "empty_layer": true
        },
        {
            "created": "2019-12-28T08:11:16.475068844Z",
            "created_by": "/bin/sh -c apt-get update     && apt-get install -y --no-install-recommends         curl     && rm -rf /var/lib/apt/lists/*"
        },
        {
            "created": "2019-12-28T08:11:43.814078508Z",
            "created_by": "/bin/sh -c #(nop)  ENV ASPNETCORE_VERSION=2.2.8",
            "empty_layer": true
        },
        {
            "created": "2019-12-28T08:12:05.676492579Z",
            "created_by": "/bin/sh -c curl -SL --output aspnetcore.tar.gz //dotnetcli.azureedge.net/dotnet/aspnetcore/Runtime/$ASPNETCORE_VERSION/aspnetcore-runtime-$ASPNETCORE_VERSION-linux-x64.tar.gz     && aspnetcore_sha512='954072376698be69acb7e277df2c243f931e10529def21dcbf9ce277609b30d462126bf8b8b3cab36476bec3d63a927b8e44e59e4d4cade23eef45956fba1ffd'     && echo \"$aspnetcore_sha512  aspnetcore.tar.gz\" | sha512sum -c -     && mkdir -p /usr/share/dotnet     && tar -zxf aspnetcore.tar.gz -C /usr/share/dotnet     && rm aspnetcore.tar.gz     && ln -s /usr/share/dotnet/dotnet /usr/bin/dotnet"
        }
    ],
    "os": "linux",
    "rootfs": {
        "type": "layers",
        "diff_ids": [
            "sha256:814c70fdae62bc26c603bfae861f00fb1c77fc0b1ee8d565717846f4df24ae5d",
            "sha256:0cf75cb98eb2e0a82631d4aff71b40ba79ff7f83e0361f696875e592a1a4cefc",
            "sha256:15e45d99c92686fb1fd61a41431d8400d7a0e8381595d09d666b0809c4f5d993",
            "sha256:579a8f1d6a123f98095c0b1a1395079f7504391fd2a8bc529dede305a2072a36"
        ]
    }
}

根據diff_ids裏面去下載對應layers

下載完後對比摘要一致,確保鏡像文件合法性

路徑規則:
//mcr.microsoft.com/v2/dotnet/core/aspnet/blobs/sha256:XXXXXX

image

image

image

image

構建我們的鏡像

在基礎鏡像的配置基礎上加入我們的自定義配置

  • Entrypoint
  • Cmd
  • Ports
  • Environment
  • ImageWorkingDirectory
  • Volumes
  • Labels

在基礎鏡像的所有的layers把我們要打包到鏡像也做成一個layer

生成的鏡像tar包解壓出來

image

多了一個 tar.gz文件,解壓之後 就是我們打包放進去的文件

image

  • 原來基礎鏡像有4個layer 加上我們的 共5個
  • config.json
  • manifest.json
manifest.json對比與基礎鏡像

image

config.json對比與基礎鏡像

image

我們來複習下構建鏡像的過程

    1. 根據鏡像名稱拉取mainfest
    1. 根據mainfest拉取config
    1. 根據config拉取layers
    1. 下載各個layer
    1. 修改到基礎鏡像的配置(config.json和mainfest.json)
    1. 加入我們要加入的文件layer

知道原理後我們可以自己寫一個工具來實現整個過程

我開源的docker鏡像構建,讓你不用裝docker也能快速構建容器鏡像

支持以下鏡像倉庫作為基礎鏡像構建

  • docker hub
  • aliyun
  • 騰訊雲