了解DevSecOps的清單

在DevSecOps,您的清單充當構成軟體系統的所有構建塊的關鍵集中清單。 對於您在 IBM Cloud上安全開發、部署及維護應用程式所需的一切,它會變成單一資訊點。 以下是此庫存通常包含的內容:

  • IBM Cloud 資源: 這包括傳遞虛擬伺服器、無伺服器功能、Cloudant 資料庫,以及您在 IBM Cloud 環境中佈建的任何其他服務。

  • 基礎架構即程式碼 (IaC) 詳細資料: 這包括 Terraform 配置、儲存在 IBM® Cloud Foundry Artifactory中的環境變數,以及指定如何配置及保護 IBM Cloud 資源的其他設定。

  • 應用程式相依關係: 這些是雲端應用程式用來正常運作的協力廠商服務、API 及微服務。

  • IBM® Key Protect for IBM Cloud® Secrets: 這是指系統作業所需的機密性資訊,例如密碼、API 金鑰及加密金鑰。 這些需要在 IBM Key Protect內嚴格控制及安全儲存體。

庫存的結構和內容

庫存模型會追蹤構件的下列項目:

  • 構件的名稱
  • 它將部署至的環境或區域。
  • 構件的建置位置 (管線執行、確定 sha)
  • 建置構件的簽章

在構件建置和部署期間追蹤證明。 這也有助於「變更管理」和「法規遵循」審核。

分支

庫存在 Git 儲存庫中實作。 Git 在追蹤及審核變更方面是自給自足的。

分支用作環境。 持續整合管線會寫入及更新主要分支 (master)。 使用促銷活動從主要分支更新其他環境。 如需促銷的相關資訊,請參閱 促銷 一節。

庫存內容

庫存包含參與部署的每個構件的庫存項目。 一個庫存項目指向單一構件。 庫存項目是可以在資料夾中結構化並使用項目名稱來命名的 JSON 檔案。

庫存資料夾是項目名稱的一部分。

範例

從下列項目名稱清單中選擇一個名稱作為服務名稱:

  • 授權/服務
  • auth/db
  • ui/服務
  • 主服務
  • helm-charts/main_service

使用下列結構在庫存中實作內容:

/
├── auth
│   ├── service
│   └── db
├── ui
│   └── service
├── helm-charts
│   └── main_service
└ main_service

庫存項目格式

雖然 Entry 類型使用 typescript 語法代表庫存項目的綱目,但您可以將它轉換為使用 JSON 綱目。

interface Entry {
  repository_url: string;
  artifact: string;
  build_number: number;
  commit_sha: string;
  name: string;
  pipeline_run_id: string;
  version: string;

  app_artifacts: {
    signature: string;
    provenance: string;

    [key: string]: any;
  }

  type: string;
  sha256: string;
  provenance: string;
  signature: string;
}

範例

資產影像類型的庫存項目

{
  "repository_url": "https://github.com/test-org/compliance-app-20201211", # source code repository url of the image artifact
  "artifact": "us.icr.io/namespace/hello-compliance-app:20201217081811-master-b85e3d472e9cc35b429c39e8c3f9eb282738c20a@sha256:da36831d5154307ac9ca4b8d900df2da0c6c14754977c32479dc62994b5722d0", # image artifact name, should be in a format like <static_name>:<version>@sha256:<sha256> OR <static_name>@sha256:<sha256>
  "build_number": 21, # pipeline build number
  "commit_sha": "b85e3d472e9cc35b429c39e8c3f9eb282738c20a",  # should be a proper git commit sha
  "name": "hello-compliance-app", # name of the inventory entry file name
  "pipeline_run_id": "f21321bf-9054-4bf3-80a8-4fb34743b7d9", # pipeline run id where artifact was built
  "version": "v1",  # version of the artifact
  "app_artifacts": {}, # any additional information can be stored here
  "type": "image",
  "sha256": "sha256:da36831d5154307ac9ca4b8d900df2da0c6c14754977c32479dc62994b5722d0",
  "provenance": "us.icr.io/namespace/hello-compliance-app:20201217081811-master-b85e3d472e9cc35b429c39e8c3f9eb282738c20a@sha256:da36831d5154307ac9ca4b8d900df2da0c6c14754977c32479dc62994b5722d0", # The fully qualified URL where artifact is stored, in case of image artifact, it is same as the artifact field
  "signature": "owFNUX1IE3EY3vwAEy0VyoSJdkVWtu0+d3eTjNIIi0jETCSU3353cz+83V23myi2BlFgYiUFqSmZLciUzCJkRYJp9IEWFWXB0hAyrBQxEjIIuhFSf70vL8/7vM/zPi3JsaYEc+TnbqG8qrnTPDZ8w2WqiqwtacCghnQEgYQ5GzAkiLKO9PpoLyiwRtSsmugWNVGGIubE/D4bgpoNKXag60gCdo8oSYoVKl5VQsDAWIGqOkmcxAmSYHGO4AjC6gU+3eBxcYxICTRLijyEFOOiSR5SvMhBys2LLpIjWYqDJA6wwHYMeUG1+J8GL5CRW/TpVgFVG8VQ4vMAknE4BUA5OIoQGIKhKZwFkIeAdnECj+OCmxQADh2QoFmG5lkWUiTN8gJ0kDxPuxiWJAU8ekyvV6PegK54EcyGiqwDJItatg9Vy0D3a2IUpKg6UuS/T4KaaIC1fzuMDbfhmMGEvIY64FUxJ+Ew3PMU5SADgdNmKs5kTjBlrtsQ99iyY49nns8o6jsXWgkjPiYahClxVcrK5Flngumz2j7YdmD0ecutytsvxxNPHflKlRV2RyqD69NjC6Smji9XuukFyZ+lvM18gUr7F4NXge9YyZPik411tc1n9bKO/hDz7oGaX/ur94OnNHiwOG6n5ZsrsCsvtyvmkH4zOWlrRWuL7fzgj9zlcCTAhtu2LbeGLNfv3Ju0Jc9PZPk7Pm3Ov2CW+2xj8ReX6ooGamZ610yqRM/+8Qo2XnOeyZzbVKgs+f2+9unpJOve4Tmla+PdqftDWkHPm9Vq3nsyZ2DUkmP/nTb7qNw5l2SfWtiSemJx9nLaYOpx6amlOT18aeIwJQhHg1XOjJF9r18NXQvPuL9rH5tSQqbGhyN/AA=="  # The signature of the artifact
}

資產的非映像檔類型 (例如部署、helm 圖表) 的庫存項目

{
  "repository_url": "https://github.com/test-org/compliance-app-20201211",  # source code repository url of the artifact
  "artifact": "deployment.yaml", # artifact name, must be contant every build
  "build_number": 21, # pipeline build number
  "commit_sha": "b85e3d472e9cc35b429c39e8c3f9eb282738c20a", # must be a proper git commit sha
  "name": "hello-compliance-app-deployment", # name of the inventory entry file name
  "pipeline_run_id": "f21321bf-9054-4bf3-80a8-4fb34743b7d9", # pipeline run id where artifact was built
  "version": "v1", # version of the artifact
  "app_artifacts": {}, # any additional information can be stored here
  "type": "deployment", # type of the artifact
  "sha256": "sha256:da36831d5154307ac9ca4b8d900df2da0c6c14754977c32479dc62994b5722d0",  # sha256 of the artifact
  "provenance": "https://raw.github.ibm.com/org/my-app/commit-1/deployment.yaml", # The fully qualified URL where artifact is stored
  "signature": "owFNUX1IE3EY3vwAEy0VyoSJdkVWtu0+d3eTjNIIi0jETCSU3353cz+83V23myi2BlFgYiUFqSmZLciUzCJkRYJp9IEWFWXB0hAyrBQxEjIIuhFSf70vL8/7vM/zPi3JsaYEc+TnbqG8qrnTPDZ8w2WqiqwtacCghnQEgYQ5GzAkiLKO9PpoLyiwRtSsmugWNVGGIubE/D4bgpoNKXag60gCdo8oSYoVKl5VQsDAWIGqOkmcxAmSYHGO4AjC6gU+3eBxcYxICTRLijyEFOOiSR5SvMhBys2LLpIjWYqDJA6wwHYMeUG1+J8GL5CRW/TpVgFVG8VQ4vMAknE4BUA5OIoQGIKhKZwFkIeAdnECj+OCmxQADh2QoFmG5lkWUiTN8gJ0kDxPuxiWJAU8ekyvV6PegK54EcyGiqwDJItatg9Vy0D3a2IUpKg6UuS/T4KaaIC1fzuMDbfhmMGEvIY64FUxJ+Ew3PMU5SADgdNmKs5kTjBlrtsQ99iyY49nns8o6jsXWgkjPiYahClxVcrK5Flngumz2j7YdmD0ecutytsvxxNPHflKlRV2RyqD69NjC6Smji9XuukFyZ+lvM18gUr7F4NXge9YyZPik411tc1n9bKO/hDz7oGaX/ur94OnNHiwOG6n5ZsrsCsvtyvmkH4zOWlrRWuL7fzgj9zlcCTAhtu2LbeGLNfv3Ju0Jc9PZPk7Pm3Ov2CW+2xj8ReX6ooGamZ610yqRM/+8Qo2XnOeyZzbVKgs+f2+9unpJOve4Tmla+PdqftDWkHPm9Vq3nsyZ2DUkmP/nTb7qNw5l2SfWtiSemJx9nLaYOpx6amlOT18aeIwJQhHg1XOjJF9r18NXQvPuL9rH5tSQqbGhyN/AA=="  # The signature of the artifact
}

使用 cocoa inventory add 指令來寫入庫存。

庫存工作流程

庫存包含 master 分支以外的數個分支。 這些分支可以代表部署階段、環境或區域,或兩者的混合。 這些分支的結構取決於設定和用法。

CI 寫入庫存

從連續整合建置移入 master 分支。 目標中的前次確定 (在本例中名為 staging) 包含一個標籤,顯示它是前次完成的部署。

如果庫存的預設分支已切換至不同的分支,則您必須將確定從前一個預設分支重新基線到新的預設分支,以讓 Git 確定歷程線性。

您可以透過自訂發行 Script 來跳過寫入庫存。 如需相關資訊,請參閱 發貨至庫存

持續整合寫入庫存
持續整合寫入庫存

促銷

當您升級至目標分支時,會建立取回要求。 取回要求內容會移入變更要求欄位。 檢閱取回要求之後,您可以合併它。

使用 PR 提升目標分支
使用 PR
提升目標分支

差異和部署

合併促銷活動取回要求之後,部署管線即可啟動。 「部署差異」是前次完成部署與現行部署的內容之間的差異。 部署差異會列出正在部署的庫存項目。

顯示部署差異詳細資料的部署管線流程
圖 3. 顯示部署差異詳細資料的部署管線流程

庫存結論

部署完成時,您可以將 latest 標籤往前移動。

在 dev 模式觸發程式期間,將不會進階標籤。 dev 模式觸發程式僅用於測試 CD 管線。

部署完成
部署完成

升級至進一步環境

您可以從任何分支調升及部署至另一個分支。

使用 PR 從 staging 升級到 prod 分支'
使用 PR 從 staging 升級到 prod 分支

庫存趨勢

現行已部署狀態包含要部署至環境的內容。 目標分支中每一個升級的確定都包含相關「管線執行 ID」及「變更要求 ID」作為標籤。 部分確定可能有多個標籤,例如,當您重試失敗的部署或重新部署時。 「庫存」會保留每一條資訊,以重播部署。

帶有標籤
的部署流程圖 帶有標籤
部署流程圖

使用標籤

下表顯示可用的庫存標籤。

庫存標籤
標籤 說明
latest 標記分支上庫存的現行、已順利部署及已結束狀態。
pipeline run id 以實際部署的管線執行 ID 或建置號碼來標記分支中的現行庫存狀態。 若要在觸發平行部署時避免庫存內容重疊,請使用此標籤來參照分支歷程中的實際庫存點雜湊。
change request id(選用) 以歷程表示法標記變更要求 ID 的現行狀態,以追蹤庫存中的變更要求 ID。

針對具有多個地區的單一目標進行設定

針對單一目標環境引進多個 latest 標籤,以便針對不同類型的使用案例,多個連續部署管線可以在相同目標上運作。 例如,您可以針對正式作業目標環境及庫存分支中的多個區域使用相同的目標環境 (例如 us-southeu-de)。

您不需要使用 region 內容 (例如 us-south-prodeu-de-prod),為每一個地區設定不同的分支,並以冗餘方式執行促銷。 相反地,請針對相同的庫存分支指定這些其他目標,然後使用它們作為 Git 標籤。

在此設定中,正式作業分支在相同分支上具有多個 latest 標籤,例如 us-south_prod_latesteu-de_prod_latest。 負責每一個地區的每一個連續部署管線都可以使用那些標籤來部署。

每個區域有多個最新標籤的產品分支
每個區域有多個最新標籤的產品分支

例如,您計劃隨處部署的一組變更可能先發行至單一地區,然後使用連續部署管線將這些地區設為目標,逐漸部署至其他地區。

庫存作業

庫存包含一些使用 CLI 或使用純 Git 及 GitHub CLI 來執行的基本作業。

CLI 指令

  1. 在暫置中建立從主要到目標分支的促銷取回要求。

    cocoa inventory promote \
      --source="master" \
      --target="staging" \
      --priority="moderate" \
      --assigned-to="assignee@ibm.com" \
      --description="Change description" \
      --purpose="Change purpose" \
      --impact="Change impact" \
      --backout-plan="Details on backout and rollback")
    
  2. target_latest 標籤移至與 pipeline-run-id 標籤相同的確定,以結束部署。

    cocoa inventory label move \
      --to-label="${PIPELINE_RUN_ID}" \
      "target_latest"
    

Git 和 GitHub CLI

  1. 在暫置中建立從主要到目標分支的促銷取回要求。

    promote() {
    
      if [ -z $1 ] || [ -z $2 ]; then
        echo "Missing source and target"
        exit 1
      fi
    
      local source="$1"
      local target="$2"
    
      if ! git show-ref "refs/remotes/origin/$target"; then
        # Create a new target branch, from the beginning of master
        git checkout master
        git checkout -b "$target" $(git rev-list --max-parents=0 HEAD)
        git_push
      fi
    
      git checkout "$source"
      git pull --rebase
    
      # Create a promotion branch for the PR
      # this can be discarded after the Promotion PR merge
      git checkout -b "promote-$source-to-$target"
      git push --set-upstream origin "promote-$source-to-$target"
    
      # Create PR from promotion branch to target branch
      gh pr create \
        --base "$target" \
        --head "promote-$source-to-$target" \
        --title "Promote $source to $target" \
        --body "" \
        --repo "https://github.com/org/inventory-repository"
    
      # promotion branch can be deleted once the PR was merged
    }
    
    $ promote master staging
    
  2. target-latest 標籤移至與 pipeline-run-id 標籤相同的確定,以結束部署。

    conclude () {
      local target="$1"
      local tag="$2"
    
      latest="$1-latest"
    
      # remove the latest tag
      git push origin ":refs/tags/$latest"
      # find the commit hash of the target tag
      sha=$(git rev-list -n 1 $tag)
    
      # add the latest tag to the same commit of the target tag
      git tag -fa "$latest" -m "" $sha
      git push --tags --force
    }
    
    $ conclude staging pipeline-run-fe33b05c
    
  3. 使用 Git 及 GitHub CLI,將暫置回復為較早的狀態。

    revert () {
      local branch="$1"
      local commit="$2"
    
      # create a revert branch from the target branch
      git checkout "$branch"
      git pull --rebase
      git checkout -b "$branch-revert"
    
      # revert commits since the target commit, then commit and push
      git revert -n $(git rev-list --no-merges HEAD...$commit)
      git commit -m "revert $branch to $commit"
      git push --set-upstream origin "$branch-revert"
    
      # create PR from revert branch to the target branch
      gh pr create \
        --base "$branch" \
        --head "$branch-revert" \
        --title "Revert $branch to $commit" \
        --body "" \
        --repo "$REPO"
    
      # revert branch can be deleted once the PR was merged
    }
    
    $ revert staging ba3b8e5ed3320e6b4981077e1a1627f08de4f511
    

使用 Git 儲存庫的一般使用案例

如需使用 Git 儲存庫的相關資訊,請參閱下列範例實務:

如何排除庫存中的檔案和目錄

One-依預設,Pipeline 會排除庫存中的隱藏式檔案及 .md 檔案。 在庫存儲存庫中建立名為 .inventoryignore 的檔案,以排除任何檔案或目錄。 管線會在儲存庫根目錄中搜尋 .inventoryignore 檔案。

但是,如果您希望清單排除檔案使用不同的名稱,則可以透過將 inventory-ignore-file 鍵設定為管道中的環境屬性來指定它。 請確定此檔案位於庫存儲存庫的根目錄。

例如,如果您的檔案命名為 .custominventoryignore,請新增具有值 custominventoryignore 的環境變數 inventory-ignore-file

以下是 .custominventoryignore 檔案的範例內容,有兩種格式:

.md
sample_file
sample_directory/
# Ignore everything
**

# But keep these artifacts
!sample_directory/
!sample_file

以下說明 .custominventoryignore 檔案範例中各項目目的:

  • .md:排除所有帶有 .md 副檔名的檔案。 不要在開頭加上 *,例如 *.md,因為不支援正規表示式。
  • sample_file:排除整個儲存庫中的特定檔案。
  • sample_directory/:排除整個目錄。 避免在末尾加上 *,請使用 sample_directory/*,因為不支援正規表示式。
  • 沒有條目或空白行的文件將導致所有文件被排除。 請不要在庫存忽略文件中留下空白條目或行。
  • **:忽略儲存庫中的所有內容。
  • !sample_directory/!sample_file: 否定忽略規則,即使 ** 存在,也會保留這些特定的檔案或目錄。