DevSecOpsのインベントリを理解する

DevSecOps, では、インベントリはソフトウェアシステムを構成するすべてのビルディングブロックの重要な集中リストとして機能します。 これは、 IBM Cloud上でアプリケーションを安全に開発、デプロイ、および保守するために必要なすべてのものの単一情報ポイントになります。 通常、このインベントリーに含まれるものを以下に示します。

  • IBM Cloud リソース: これには、 IBM Cloud 環境全体でプロビジョンした仮想サーバー、サーバーレス機能、 Cloudant データベース、およびその他のサービスが含まれます。

  • Infrastructure as Code (IaC) の詳細: これには、Terraform 構成、 IBM® Cloud Foundry Artifactoryに保管されている環境変数、および IBM Cloud リソースの構成および保護方法を指示するその他の設定が含まれます。

  • アプリケーション依存関係: これらは、クラウド・アプリケーションが完璧に機能するために依存しているサード・パーティーのサービス、API、およびマイクロサービスです。

  • IBM® Key Protect for IBM Cloud® シークレット: これは、システム操作に不可欠な機密情報 (パスワード、API キー、暗号鍵など) を参照します。 これらには、 IBM Key Protect内の詳細な制御とセキュア・ストレージが必要です。

在庫の構造と内容

インベントリー・モデルは、成果物の以下の項目を追跡します。

  • 成果物の名前
  • デプロイ先となる環境または地域。
  • 成果物のビルド・ロケーション (パイプライン実行、コミット社)
  • ビルドされた成果物のシグニチャー

成果物のビルドおよびデプロイメント中に証拠を追跡します。 これは、変更管理およびコンプライアンスの監査にも役立ちます。

ブランチ

インベントリーは Git リポジトリーに実装されます。 Git は、変更の追跡と監査において自己完結型です。

ブランチは環境として使用されます。 メインのブランチ (master) の作成と更新は、継続的統合パイプラインによって行われます。 その他の環境は、プロモーションを使用してマスター・ブランチから更新されます。 プロモーションについて詳しくは、プロモーションのセクションを参照してください。

インベントリー・コンテンツ

インベントリーには、デプロイメントに組み込まれるすべての成果物それぞれのインベントリー・エントリーが含まれています。 1 つのインベントリー・エントリーは 1 つの成果物を表します。 インベントリー・エントリーは、エントリー名を名前として持つ JSON ファイルであり、フォルダーで構造化されていることもあります。

インベントリー・フォルダーはエントリー名の一部になります。

サービス名として、以下の項目名のリストから 1 つの名前を選択します。

  • auth/service
  • auth/db
  • ui/service
  • main_service
  • 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 コミット履歴を線形にする必要があります。

リリース・スクリプトをカスタマイズすることにより、インベントリーへの書き込みをスキップできます。 詳しくは、 Release to inventory を参照してください。

継続的インテグレーションのインベントリへの書き込み
継続的インテグレーションのインベントリへの書き込み

プロモーション

プル要求は、ターゲット・ブランチへのプロモートが行われると作成されます。 プル要求の内容から、変更要求のフィールドにデータが取り込まれます。 プル要求は、レビューされた後、マージできます。

PRを使ってターゲットブランチをプロモートする
PRを使ってターゲットブランチをプロモートする

デルタとデプロイメント

プロモーションのプル要求がマージされたら、デプロイメント・パイプラインを開始できます。 デプロイメント・デルタは、完了した最後のデプロイメントの内容と、現行のデプロイメントの内容との差分です。 デプロイメント・デルタは、デプロイされるインベントリー項目のリストです。

デプロイメントの差分の詳細を示すデプロイメント・パイプライン・フロー
図 3. デプロイメント差分の詳細を示すデプロイメント・パイプライン・フロー

在庫の結論

デプロイメントが完了したら、latest タグを先へ移動させることができます。

開発モードのトリガー中は、タグは拡張されません。 開発モード・トリガーの目的は、CD パイプラインのテストのみです。

配備完了
配備完了

他の環境へのプロモート

あるブランチから他のブランチへプロモートしてデプロイすることが可能です。

ステージングからプロッドブランチへのPRを使用したプロモーション
ステージングからプロッドブランチへのPRを使用したプロモーション

インベントリーの全体像

デプロイが済んだ後の現行の状態には、環境にデプロイされた内容が含まれます。 ターゲットブランチで昇格したコミットには、関連するパイプラインの実行IDと変更リクエストIDがタグとして含まれています。 一部のコミットには複数のタグがある場合があります。例えば、失敗したデプロイメントを再試行した場合や、デプロイメントをやり直した場合などがこれに該当します。 インベントリーには、デプロイメントをやり直すためのすべての情報があります。

タグ付きのデプロイメント・フロー・ダイアグラム
タグ付きのデプロイメント・フロー・ダイアグラム

タグの使用

次の表は、利用可能なインベントリータグを示しています。

在庫タグ
タグ 説明
latest ブランチの、デプロイが正常に行われて完了した後のインベントリーの現行の状態に対して付けられるタグです。
pipeline run id ブランチの現行のインベントリーの状態に対して付けられるタグです。これには、実際のデプロイメントのパイプライン実行 ID またはビルド番号が含められます。 並行でデプロイメントがトリガーされた時のインベントリーの内容のオーバーラップを避けるには、このタグを使用して、ブランチ履歴内の実際のインベントリー・ポイントのハッシュを参照します。
change request id (オプション) 履歴表示でインベントリー内の変更要求 ID を追跡するために、変更要求 ID の現行の状態に対して付けられるタグです。

複数のリージョンがある単一のターゲットのためのセットアップ

複数の継続的デプロイメント・パイプラインが同じターゲット上で異なるタイプのユースケースに対応できるように、1つのターゲット環境に対して複数の latest タグが導入されている。 例えば、実動のターゲット環境およびインベントリー・ブランチ内で、複数のリージョン (us-southeu-de など) のために同一のターゲット環境を使用できます。

us-south-prodeu-de-prod のように、 region プロパティを使用して地域ごとに異なるブランチを設定し、プロモーションを冗長に実行する必要はありません。 代わりに、同じインベントリー・ブランチにこれらの追加のターゲットを指定してから、それらを Git タグとして使用します。

この設定では、prodブランチには、 us-south_prod_latesteu-de_prod_latest など、同じブランチに複数の latest タグがあります。 各リージョンを担当する継続的デプロイメントパイプラインは、これらのタグを使ってデプロイできる。

地域ごとに複数の最新タグを持つプロッドブランチ
地域ごとに複数の最新タグを持つプロッドブランチ

例えば、あらゆる場所にデプロイする予定の一連の変更は、まず単一のリージョンにリリースし、その後、継続的デプロイメントパイプラインを使用して、それらのリージョンをターゲットとして、徐々に他のリージョンにデプロイすることができる。

インベントリーの操作

インベントリーには、CLI を使用して、または pure Git と GitHub CLI を使用して実行されるいくつかの基本操作が含まれます。

CLI コマンド

  1. master から staging 内のターゲット・ブランチへのプロモーションのプル要求を作成します。

    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. master から staging 内のターゲット・ブランチへのプロモーションのプル要求を作成します。

    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 を使用して、staging を以前の状態に戻します。

    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 である場合は、環境変数 inventory-ignore-file に値 custominventoryignore を追加します。

以下は、 .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/ and !sample_file: 無視ルールを無効にし、 ** が存在しても、これらの特定のファイルまたはディレクトリを保持する。