關於容器映像檔摘要


本頁說明映像檔摘要,包括映像檔摘要的用途、如何尋找映像檔摘要,以及如何在 Kubernetes 叢集中強制使用映像檔摘要。本頁面適用於建構及部署容器映像的開發人員和作業人員。

容器映像檔摘要可不重複且不可變更地識別容器映像檔。使用摘要部署映像檔可避免使用映像檔標記部署映像檔的缺點。

本頁面的指令假設您可存取 Linux 或 macOS 的 Shell 環境,且已安裝 Google Cloud CLI、Docker、cURL、jqpack 等工具。或者,您可以使用已預先安裝這些工具的 Cloud Shell

容器映像檔和映像檔標記

使用容器映像檔時,您需要參照所用映像檔的方法。映像檔標記 是參照不同映像檔修訂版本的常見方式。常見的做法是在建構時為映像檔加上版本 ID。舉例來說,v1.0.1 可能是指您呼叫的 1.0.1 版本。

標記可讓使用者透過易讀字串輕鬆查閱圖片修訂版本。不過,標記是可變更的參照,因此標記參照的映像檔可能會變更,如下圖所示:

指向過時圖片的圖片標記。

如上圖所示,如果您使用與現有映像檔相同的標記發布新映像檔,該標記會停止指向現有映像檔,並開始指向新映像檔。

圖片代碼的缺點

由於標記可變動,因此使用標記部署映像檔時,會有下列缺點:

  • 在 Kubernetes 中,透過標記部署可能會導致非預期的結果。舉例來說,假設您有現有的 Deployment 資源,會依標記 v1.0.1 參照容器映像檔。如要修正錯誤或進行小幅變更,建構程序會建立具有相同標記 v1.0.1 的新映像檔。即使您未變更 Deployment 資源規格,從 Deployment 資源建立的新 Pod 仍可能使用舊版或新版映像檔。這個問題也適用於其他 Kubernetes 資源,例如 StatefulSet、DaemonSet、ReplicaSet 和工作。

  • 如果您使用工具掃描或分析圖片,這些工具的結果只適用於掃描的圖片。為確保部署的是掃描的映像檔,您不能依賴標記,因為標記參照的映像檔可能已變更。

  • 如果您搭配使用二進位授權Google Kubernetes Engine (GKE),系統會禁止以標記為基礎的部署作業,因為在建立 Pod 時,無法判斷使用的確切映像檔。

部署映像檔時,您可以使用映像檔摘要,避免使用標記的缺點。你還是可以視需要為圖片加上標記, 但不必這麼做。

圖片結構

映像檔包含下列元件:

下圖說明這些元件:

圖片的元件,顯示圖片資訊清單、設定物件、檔案系統層和圖片索引的詳細資料。

上圖顯示圖片元件的額外詳細資料:

  • 圖片資訊清單是 JSON 文件,內含設定物件、檔案系統層和選用中繼資料的參照。
  • 映像檔資訊清單會使用 digest 屬性,參照設定物件和每個檔案系統層。digest 屬性的值是摘要所指內容的密碼編譯雜湊,通常是使用 SHA-256 演算法計算。
  • 摘要值用於建構物件的不變位址。這個程序稱為「可定址內容的儲存空間」,代表您可以根據摘要擷取映像檔資訊清單、映像檔索引、設定物件和層。
  • 映像檔摘要是映像檔索引或映像檔資訊清單 JSON 文件的雜湊值。
  • 設定物件是 JSON 文件,可定義映像檔的屬性,例如 CPU 架構、進入點、公開的連接埠和環境變數。
  • 檔案系統層陣列會定義容器執行階段用來堆疊層的順序。這些層會以 tar 檔案的形式發布,通常會使用 gzip 公用程式壓縮。
  • 選用的映像檔索引 (有時稱為資訊清單表) 會參照一或多個映像檔資訊清單。參考資料是映像檔資訊清單的摘要。如果您為不同平台 (例如 amd64arm64 架構) 製作多個相關圖片,映像檔索引就非常實用。

詳情請參閱「探索映像檔資訊清單、摘要和標記」一節。

尋找映像檔摘要

如要使用映像檔摘要進行部署,必須先找出摘要。接著,您可以在部署指令中使用摘要,或將摘要納入 Kubernetes 資訊清單。

視目前情況而定,您可以透過各種方式取得圖片摘要。以下各節包含不同產品和工具的範例。

在下列各節中,請在 Cloud Shell 或殼層環境中執行指令,並安裝 gcloud CLI、Docker、cURL 和 jq 等工具。

Artifact Registry

Container Registry

  • 如要取得儲存在 Container Registry 中的映像檔摘要,請提供名稱和標記,然後使用 gcloud container images describe 指令。使用 --format 旗標,只顯示摘要:

    gcloud container images describe \
        gcr.io/google-containers/pause-amd64:3.2 \
        --format 'value(image_summary.digest)'
    

    輸出內容大致如下,但摘要值可能有所不同:

    sha256:4a1c4b21597c1b4415bdbecb28a3296c6b5e23ca4f9feeb599860a1dac6a0108
    

Cloud Build

如要取得使用 Cloud Build 建構的映像檔摘要,請使用 gcloud builds describe 指令搭配 --format 標記。無論您使用哪個登錄檔發布映像檔,這種做法都適用。

  • 如要為已完成的建構作業執行這項操作,請按照下列步驟操作:

    1. 取得專案的建構版本清單:

      gcloud builds list
      

      請記下 BUILD_ID

    2. 取得映像檔摘要:

      gcloud builds describe BUILD_ID \
          --format 'value(results.images[0].digest)'
      

      BUILD_ID 替換為 Cloud Build 指派給建構作業的專屬 ID。

  • 從目前專案的 Cloud Build 取得最新版本的映像檔名稱和摘要:

    gcloud builds describe \
        $(gcloud builds list --limit 1 --format 'value(id)') \
        --format 'value[separator="@"](results.images[0].name,results.images[0].digest)'
    
  • 如果建構作業產生多個映像檔,請篩選輸出內容,並取得其中一個映像檔的摘要:

    gcloud builds describe BUILD_ID --format json \
        | jq -r '.results.images[] | select(.name=="YOUR_IMAGE_NAME") | .digest'
    

    YOUR_IMAGE_NAME 換成 cloudbuild.yaml 檔案中的其中一個圖片名稱

  • 如果您使用 gcloud builds submit 指令將建構作業提交至 Cloud Build,可以從輸出內容擷取映像檔摘要,並儲存在環境變數中:

    IMAGE_DIGEST=$(gcloud builds submit \
        --format 'value(results.images[0].digest)' | tail -n1)
    

Cloud Native Buildpacks

  • 如果您使用 Cloud Native Buildpacks 和 Google Cloud 建構子建構及發布映像檔,可以搭配 pack 指令使用 --quiet 旗標擷取映像檔名稱和摘要:

    pack build --builder gcr.io/buildpacks/builder:v1 --publish --quiet \
        LOCATION-docker.pkg.dev/PROJECT_ID/REPOSITORY/IMAGE \
        > image-with-digest.txt
    

    更改下列內容:

    • LOCATION:存放區的區域或多區域位置
    • PROJECT_ID:您的 Google Cloud 專案 ID
    • REPOSITORY:存放區名稱
    • IMAGE:圖片名稱

    檔案 image-with-digest.txt 包含圖片名稱和摘要。

    如要為圖片新增標記,請使用 --tag 旗標。

Docker 用戶端

  • docker 指令列用戶端的 manifest 子指令可從容器映像檔登錄檔擷取映像檔資訊清單和資訊清單清單。

    從映像檔 registry.k8s.io/pause:3.9 的資訊清單清單中取得摘要,適用於 amd64 CPU 架構和 linux 作業系統:

    docker manifest inspect --verbose registry.k8s.io/pause:3.9 | \
        jq -r 'if type=="object"
            then .Descriptor.digest
            else .[] | select(.Descriptor.platform.architecture=="amd64" and
            .Descriptor.platform.os=="linux") | .Descriptor.digest
            end'
    

    輸出看起來類似以下內容:

    sha256:8d4106c88ec0bd28001e34c975d65175d994072d65341f62a8ab0754b0fafe10
    
  • 如要取得儲存在本機 Docker Daemon 中,且已從映像檔登錄檔提取或推送至該登錄檔的映像檔摘要,請使用 Docker 指令列工具:

    1. 將映像檔拉取至本機 Docker Daemon

      docker pull docker.io/library/debian:bookworm
      
    2. 取得映像檔摘要:

      docker inspect docker.io/library/debian:bookworm \
          | jq -r '.[0].RepoDigests[0]' \
          | cut -d'@' -f2
      

      輸出內容會與以下所示內容類似,但摘要值可能不同:

      sha256:3d868b5eb908155f3784317b3dda2941df87bbbbaa4608f84881de66d9bb297b
      
  • 列出本機 Docker Daemon 中的所有映像檔和摘要:

    docker images --digests
    

    輸出會顯示具有摘要值的圖片摘要。只有從映像檔登錄檔提取或推送至該登錄檔的映像檔,才會有摘要值。

cranegcrane

您可以使用開放原始碼的 cranegcrane 指令列工具,取得映像檔的摘要,而不必將映像檔提取至本機 Docker 精靈。

  1. cranegcrane 下載至目前目錄:

    VERSION=$(curl -sL https://api.github.com/repos/google/go-containerregistry/releases/latest | jq -r .tag_name)
    curl -L "https://github.com/google/go-containerregistry/releases/download/${VERSION}/go-containerregistry_$(uname -s)_$(uname -m).tar.gz" | tar -zxf - crane gcrane
    
  2. 取得圖片摘要:

    ./gcrane digest gcr.io/distroless/static-debian11:nonroot
    

    cranegcrane 還有其他功能,不在本頁面的討論範圍內。詳情請參閱 cranegcrane 的說明文件。

在 Kubernetes 部署作業中強制使用映像檔摘要

如要強制對部署至 Kubernetes 叢集的映像檔使用摘要,可以採用 Policy ControllerOpen Policy Agent (OPA) Gatekeeper。Policy Controller 是以 OPA Gatekeeper 開放原始碼專案為基礎建構而成。

Policy Controller 和 OPA Gatekeeper 都是以 OPA 政策引擎為基礎。Policy Controller 和 OPA Gatekeeper 提供 Kubernetes 驗證許可控制器 Webhook,可強制執行政策,並提供自訂資源定義 (CRD),用於限制範本限制

限制範本包含以高階宣告式語言 (稱為 Rego) 表示的政策邏輯。以下是驗證容器、init 容器臨時容器的限制範本,確保 Kubernetes 資源規格中的這些容器使用含有摘要的映像檔:

apiVersion: templates.gatekeeper.sh/v1
kind: ConstraintTemplate
metadata:
  name: k8simagedigests
  annotations:
    metadata.gatekeeper.sh/title: "Image Digests"
    metadata.gatekeeper.sh/version: 1.0.1
    description: >-
      Requires container images to contain a digest.

      https://kubernetes.io/docs/concepts/containers/images/
spec:
  crd:
    spec:
      names:
        kind: K8sImageDigests
      validation:
        openAPIV3Schema:
          type: object
          description: >-
            Requires container images to contain a digest.

            https://kubernetes.io/docs/concepts/containers/images/
          properties:
            exemptImages:
              description: >-
                Any container that uses an image that matches an entry in this list will be excluded
                from enforcement. Prefix-matching can be signified with `*`. For example: `my-image-*`.

                It is recommended that users use the fully-qualified Docker image name (e.g. start with a domain name)
                in order to avoid unexpectedly exempting images from an untrusted repository.
              type: array
              items:
                type: string
  targets:
    - target: admission.k8s.gatekeeper.sh
      rego: |
        package k8simagedigests

        import data.lib.exempt_container.is_exempt

        violation[{"msg": msg}] {
          container := input.review.object.spec.containers[_]
          not is_exempt(container)
          not regex.match("@[a-z0-9]+([+._-][a-z0-9]+)*:[a-zA-Z0-9=_-]+", container.image)
          msg := sprintf("container <%v> uses an image without a digest <%v>", [container.name, container.image])
        }

        violation[{"msg": msg}] {
          container := input.review.object.spec.initContainers[_]
          not is_exempt(container)
          not regex.match("@[a-z0-9]+([+._-][a-z0-9]+)*:[a-zA-Z0-9=_-]+", container.image)
          msg := sprintf("initContainer <%v> uses an image without a digest <%v>", [container.name, container.image])
        }

        violation[{"msg": msg}] {
          container := input.review.object.spec.ephemeralContainers[_]
          not is_exempt(container)
          not regex.match("@[a-z0-9]+([+._-][a-z0-9]+)*:[a-zA-Z0-9=_-]+", container.image)
          msg := sprintf("ephemeralContainer <%v> uses an image without a digest <%v>", [container.name, container.image])
        }
      libs:
        - |
          package lib.exempt_container

          is_exempt(container) {
              exempt_images := object.get(object.get(input, "parameters", {}), "exemptImages", [])
              img := container.image
              exemption := exempt_images[_]
              _matches_exemption(img, exemption)
          }

          _matches_exemption(img, exemption) {
              not endswith(exemption, "*")
              exemption == img
          }

          _matches_exemption(img, exemption) {
              endswith(exemption, "*")
              prefix := trim_suffix(exemption, "*")
              startswith(img, prefix)
          }

上述政策包含規則運算式,做為 re_match 函式的輸入內容。這個規則運算式會比對容器映像檔摘要,並以開放容器倡議映像檔規格中的摘要格式為依據。

限制會比對 kindnamespace 等屬性,將政策套用至 Kubernetes 資源。以下範例限制會將限制範本中的政策套用至 default 命名空間中的所有 Pod 資源。

apiVersion: constraints.gatekeeper.sh/v1beta1
kind: K8sImageDigests
metadata:
  name: container-image-must-have-digest
spec:
  match:
    kinds:
      - apiGroups: [""]
        kinds: ["Pod"]
    namespaces:
      - "default"

建立限制範本和限制後,default 名稱空間中的任何新 Pod 都必須使用映像檔摘要,才能參照容器映像檔。

如需完整範例,請參閱 Gatekeeper 政策程式庫中的imagedigests政策

關於映像檔資訊清單、摘要和標記

在本節中,您將瞭解如何使用 curldocker 等指令列工具,探索登錄檔中的現有映像檔。在 Cloud Shell 中執行指令,或在已安裝 gcloud CLI、Docker、cURL 和 jq 等工具的殼層環境中執行指令。下列指令會使用 Artifact Registry 中的公開映像檔。

  • 使用 cURL 和資訊清單網址取得圖片的資訊清單 gcr.io/google-containers/pause-amd64:3.2

    curl -s https://gcr.io/v2/google-containers/pause-amd64/manifests/3.2
    

    輸出結果會與下列內容相似:

    {
       "schemaVersion": 2,
       "mediaType": "application/vnd.docker.distribution.manifest.v2+json",
       "config": {
          "mediaType": "application/vnd.docker.container.image.v1+json",
          "size": 759,
          "digest": "sha256:80d28bedfe5dec59da9ebf8e6260224ac9008ab5c11dbbe16ee3ba3e4439ac2c"
       },
       "layers": [
          {
             "mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip",
             "size": 296534,
             "digest": "sha256:c74f8866df097496217c9f15efe8f8d3db05d19d678a02d01cc7eaed520bb136"
          }
       ]
    }
    

    config 區段具有摘要屬性,您可以使用這個值擷取設定物件。同樣地,每個層都有 digest 屬性,可用於擷取該層的 tar 檔案。

  • 如果圖片包含選用的圖片索引,使用標記對資訊清單網址發出的 HTTP GET 要求,會傳回圖片索引,而不是圖片資訊清單。

    取得圖片 gcr.io/google-containers/pause:3.2 的圖片索引:

    curl -s https://gcr.io/v2/google-containers/pause/manifests/3.2
    

    輸出結果會與下列內容相似:

    {
       "schemaVersion": 2,
       "mediaType": "application/vnd.docker.distribution.manifest.list.v2+json",
       "manifests": [
          {
             "mediaType": "application/vnd.docker.distribution.manifest.v2+json",
             "size": 526,
             "digest": "sha256:4a1c4b21597c1b4415bdbecb28a3296c6b5e23ca4f9feeb599860a1dac6a0108",
             "platform": {
                "architecture": "amd64",
                "os": "linux"
             }
          },
          {
             "mediaType": "application/vnd.docker.distribution.manifest.v2+json",
             "size": 526,
             "digest": "sha256:bbb7780ca6592cfc98e601f2a5e94bbf748a232f9116518643905aa30fc01642",
             "platform": {
                "architecture": "arm",
                "os": "linux",
                "variant": "v7"
             }
          },
          {
             "mediaType": "application/vnd.docker.distribution.manifest.v2+json",
             "size": 526,
             "digest": "sha256:31d3efd12022ffeffb3146bc10ae8beb890c80ed2f07363515580add7ed47636",
             "platform": {
                "architecture": "arm64",
                "os": "linux"
             }
          },
          {
             "mediaType": "application/vnd.docker.distribution.manifest.v2+json",
             "size": 526,
             "digest": "sha256:7f82fecd72730a6aeb70713476fb6f7545ed1bbf32cadd7414a77d25e235aaca",
             "platform": {
                "architecture": "ppc64le",
                "os": "linux"
             }
          },
          {
             "mediaType": "application/vnd.docker.distribution.manifest.v2+json",
             "size": 526,
             "digest": "sha256:1175fd4d728641115e2802be80abab108b8d9306442ce35425a4e8707ca60521",
             "platform": {
                "architecture": "s390x",
                "os": "linux"
             }
          }
       ]
    }
    
  • 篩選結果,擷取所需平台的圖片摘要。取得 amd64 CPU 架構和 linux 作業系統的映像檔資訊清單摘要:

    curl -s https://gcr.io/v2/google-containers/pause/manifests/3.2 | \
        jq -r '.manifests[] | select(.platform.architecture=="amd64" and .platform.os=="linux") | .digest'
    

    這項指令中的篩選作業會模擬容器執行階段 (例如 containerd) 從映像檔索引選取符合目標平台的映像檔。

    輸出結果會與下列內容相似:

    sha256:4a1c4b21597c1b4415bdbecb28a3296c6b5e23ca4f9feeb599860a1dac6a0108
    

    圖片摘要是將抗衝突雜湊套用至圖片索引或圖片資訊清單的結果,通常是 SHA-256 演算法。

  • 取得圖片的摘要 gcr.io/google-containers/pause-amd64:3.2

    curl -s https://gcr.io/v2/google-containers/pause-amd64/manifests/3.2 \
        | shasum -a 256 \
        | cut -d' ' -f1
    

    輸出結果會與下列內容相似:

    4a1c4b21597c1b4415bdbecb28a3296c6b5e23ca4f9feeb599860a1dac6a0108
    

    您可以使用映像檔摘要值參照這個映像檔,如下所示:

    gcr.io/google-containers/pause-amd64@sha256:4a1c4b21597c1b4415bdbecb28a3296c6b5e23ca4f9feeb599860a1dac6a0108
    
  • 使用內容可定址儲存空間概念,以摘要做為參照,取得映像檔資訊清單:

    curl -s https://gcr.io/v2/google-containers/pause-amd64/manifests/sha256:4a1c4b21597c1b4415bdbecb28a3296c6b5e23ca4f9feeb599860a1dac6a0108
    
  • 許多容器映像檔登錄檔會在回應 HTTP HEAD 要求時,於 Docker-Content-Digest 標頭中傳回資訊清單、映像檔索引、設定物件和檔案系統層的摘要。取得圖片索引的摘要,圖片索引是指圖片的摘要:gcr.io/google-containers/pause-amd64:3.2

    curl -s --head https://gcr.io/v2/google-containers/pause/manifests/3.2 \
        | grep -i Docker-Content-Digest \
        | cut -d' ' -f2
    

    輸出結果會與下列內容相似:

    sha256:927d98197ec1141a368550822d18fa1c60bdae27b78b0c004f705f548c07814f
    

    Docker-Content-Digest 標頭並非 Open Container Initiative Distribution 規格的必要項目,因此這個方法可能不適用於所有容器映像檔登錄檔。您可以搭配 Artifact RegistryContainer Registry 使用。

  • 如要使用映像檔資訊清單中的摘要值擷取映像檔設定物件,請執行下列操作:

    1. 取得設定摘要:

      CONFIG_DIGEST=$(curl -s https://gcr.io/v2/google-containers/pause-amd64/manifests/3.2 \
          | jq -r '.config.digest')
      
    2. 使用設定摘要擷取設定物件,並使用 jq 格式化輸出內容,方便閱讀:

      curl -sL https://gcr.io/v2/google-containers/pause-amd64/blobs/$CONFIG_DIGEST \
          | jq
      

      輸出結果會與下列內容相似:

      {
        "architecture": "amd64",
        "config": {
          "Env": [
            "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
          ],
          "Entrypoint": [
            "/pause"
          ],
          "WorkingDir": "/",
          "OnBuild": null
        },
        "created": "2020-02-14T10:51:50.60182885-08:00",
        "history": [
          {
            "created": "2020-02-14T10:51:50.60182885-08:00",
            "created_by": "ARG ARCH",
            "comment": "buildkit.dockerfile.v0",
            "empty_layer": true
          },
          {
            "created": "2020-02-14T10:51:50.60182885-08:00",
            "created_by": "ADD bin/pause-amd64 /pause # buildkit",
            "comment": "buildkit.dockerfile.v0"
          },
          {
            "created": "2020-02-14T10:51:50.60182885-08:00",
            "created_by": "ENTRYPOINT [\"/pause\"]",
            "comment": "buildkit.dockerfile.v0",
            "empty_layer": true
          }
        ],
        "os": "linux",
        "rootfs": {
          "type": "layers",
          "diff_ids": [
            "sha256:ba0dae6243cc9fa2890df40a625721fdbea5c94ca6da897acdd814d710149770"
          ]
        }
      }
      
  • 如要使用映像檔資訊清單中的摘要值擷取檔案系統層,請按照下列步驟操作:

    1. 取得要擷取圖層的摘要:

      LAYER_DIGEST=$(curl -s https://gcr.io/v2/google-containers/pause-amd64/manifests/3.2 \
          | jq -r '.layers[0].digest')
      
    2. 使用層摘要擷取層 tar 檔案,並列出內容:

      curl -sL https://gcr.io/v2/google-containers/pause-amd64/blobs/$LAYER_DIGEST \
          | tar --list
      

      這個圖層只有一個名為 pause 的檔案。

  • 如要查詢與映像檔摘要相關聯的標記,請按照下列步驟操作:

    1. 定義要查詢的摘要:

      IMAGE_DIGEST=$(curl -s https://gcr.io/v2/google-containers/pause-amd64/manifests/3.2 \
          | shasum -a 256 \
          | cut -d' ' -f1)
      

      IMAGE_DIGEST 環境變數包含標記 3.2 參照的映像檔摘要。

    2. 使用映像檔標記清單端點 /tags/list 列出標記資訊,並擷取摘要值的標記:

      curl -s "https://gcr.io/v2/google-containers/pause-amd64/tags/list?n=1" \
          | jq ".manifest.\"sha256:$IMAGE_DIGEST\".tag"
      

      輸出結果會與下列內容相似:

      [
        "3.2"
      ]
      
  • 如要使用 cURL 從 Artifact Registry 容器映像檔存放區取得映像檔資訊清單,請在 Authorization 要求標頭中加入存取權杖

    curl -s -H "Authorization: Bearer $(gcloud auth print-access-token)" \
        https://LOCATION-docker.pkg.dev/v2/PROJECT_ID/REPOSITORY/IMAGE/manifests/DIGEST
    

    更改下列內容:

    • LOCATION:存放區的區域或多區域位置
    • PROJECT_ID:您的 Google Cloud 專案 ID
    • REPOSITORY:存放區名稱
    • IMAGE:圖片名稱
    • DIGEST:圖片摘要,格式為 sha256:DIGEST_VALUE

後續步驟

  • 如要進一步瞭解映像檔,請參閱 Open Container Initiative 的映像檔格式發布規格。