リソースグループ

GitLab 12.7から導入されました

デフォルトでは、GitLab CI/CDのパイプラインは同時に実行されます。同時実行はマージリクエストのフィードバックループを改善するために重要な要素ですが、デプロイジョブの同時実行を制限して1つずつ実行したい状況もあるでしょう。リソースグループを使用して、ジョブの同時実行を戦略的に制御し、継続的デプロイのワークフローを安全に最適化します。

リソースグループの追加

以下のパイプライン設定(.gitlab-ci.yml ファイルがリポジトリにあること)があることが前提です:

build:
  stage: build
  script: echo "Your build script"

deploy:
  stage: deploy
  script: echo "Your deployment script"
  environment: production

ブランチに新しいコミットをプッシュするたびに、builddeploy の 2 つのジョブを持つ新しいパイプラインが実行されます。しかし、短い間隔で複数のコミットをプッシュすると、たとえば複数のパイプラインが同時に動き始めます:

  • 最初のパイプラインはbuild -> のジョブを実行します。deploy
  • 2番目のパイプラインはジョブを実行しますbuild ->deploy

この場合、異なるパイプラインのdeploy ジョブがproduction 環境に対して同時に実行される可能性があります。同じインフラストラクチャに対して複数のデプロイスクリプトを実行すると、インスタンスに悪影響/混乱が生じ、最悪の場合、インスタンスが破損した状態になる可能性があります。

deploy ジョブが一度に一度だけ実行されるようにするには、resource_group キーワード を同時実行に敏感なジョブに指定します:

deploy:
  ...
  resource_group: production

この設定により、デプロイの安全性が保証される一方で、パイプラインの効率を最大化するためにbuild ジョブを同時に実行することができます。

前提条件

制限事項

リソースグループにアタッチできるリソースは1つだけです。

プロセスモード

デプロイの好みに合わせて、戦略的にジョブの同時実行を制御するプロセスモードを選択することができます。以下のモードがサポートされています:

  • 順序なし:実行中のジョブの同時実行を制限するデフォルトのプロセスモードです。ジョブの実行順序を気にしない場合に最も使いやすいオプションです。ジョブの実行準備が整った時点で、ジョブの処理を開始します。
  • 最も古いジョブを優先します:このプロセスモードはジョブの同時実行を制限します。リソースに空きがある場合、パイプラインIDの昇順でソートされた今後のジョブリスト (created,scheduled, またはwaiting_for_resource 状態) から最初のジョブを選択します。

    このモードは、ジョブが最も古いパイプラインから実行されるようにしたい場合に効率的です。パイプラインの効率という点ではunordered モードより劣りますが、継続的なデプロイでは安全です。

  • 新しいもの優先:このプロセスモードはジョブの同時実行を制限します。リソースに空きがある場合、パイプラインIDの降順でソートされた今後のジョブリスト(createdscheduledwaiting_for_resource 状態)から最初のジョブを選択します。

    このモードは、ジョブが最新のパイプラインから実行されることを保証し、古いデプロイジョブを防止する機能ですべての古いデプロイジョブを防止したい場合に効率的です。パイプラインの効率という点では最も効率的なオプションですが、各デプロイジョブが冪等であることを確認する必要があります。

プロセスモードの変更

リソースグループのプロセスモードを変更するには、APIを使用して、process_mode を指定して既存のリソースグループの編集リクエストを送信する必要があります:

  • unordered
  • oldest_first
  • newest_first

プロセスモードの違いの例

次の.gitlab-ci.yml を考えてみましょう。2つのジョブbuild があり、deploy それぞれがそれぞれのステージで実行されていて、 deployジョブのリソースグループはproduction に設定されています:

build:
  stage: build
  script: echo "Your build script"

deploy:
  stage: deploy
  script: echo "Your deployment script"
  environment: production
  resource_group: production

短い間隔で3つのコミットがプロジェクトにプッシュされると、3つのパイプラインがほぼ同時に実行されることになります:

  • 最初のパイプラインはbuild ->deploy のジョブを実行します。このデプロイジョブをdeploy-1 とします。
  • 2番目のパイプラインはジョブbuild ->deploy を実行します。このデプロイジョブをdeploy-2 と呼びましょう。
  • 3番目のパイプラインはジョブbuild ->deploy を実行します。このデプロイジョブをdeploy-3 と呼びましょう。

リソースグループのプロセスモードによって異なります:

  • プロセスモードがunordered に設定されている場合:
    • deploy-1の場合、deploy-2deploy-3 は同時に実行されません。
    • ジョブの実行順序は保証されていません。例えば、deploy-3 が実行される前または後にdeploy-1 が実行される可能性があります。
  • プロセスモードがoldest_first の場合:
    • deploy-1の場合、deploy-2deploy-3 は同時に実行されません。
    • deploy-1 が最初に実行され、deploy-2 が2番目に実行され、deploy-3 が最後に実行されます。
  • プロセスモードがnewest_first の場合:
    • deploy-1の場合、deploy-2deploy-3 は同時に実行されません。
    • deploy-3 が最初に実行され、deploy-2 が2番目に実行され、deploy-1 が最後に実行されます。

クロスプロジェクト/親子パイプラインによるパイプラインレベルの同時実行制御

GitLab 13.9 で導入されました

同時実行に敏感なダウンストリームパイプラインのためにresource_group を定義することができます。trigger キーワード はダウンストリームパイプラインをトリガーすることができ、resource_group キーワード はそれと共存することができます。resource_group はデプロイパイプラインの同時実行を制御するのに効率的で、他のジョブは同時実行を続けることができます。

次の例では、プロジェクトに 2 つのパイプライン設定があります。パイプラインの実行が開始されると、センシティブでないジョブが最初に実行され、他のパイプラインの同時実行の影響を受けません。しかし、GitLabはデプロイ(子)パイプラインをトリガーする前に、他のデプロイパイプラインが実行されていないことを確認します。他のデプロイパイプラインが実行されている場合、GitLabはそれらのパイプラインが終了するまで待ってから別のパイプラインを実行します。

# .gitlab-ci.yml (parent pipeline)

build:
  stage: build
  script: echo "Building..."

test:
  stage: test
  script: echo "Testing..."

deploy:
  stage: deploy
  trigger:
    include: deploy.gitlab-ci.yml
    strategy: depend
  resource_group: AWS-production
# deploy.gitlab-ci.yml (child pipeline)

stages:
  - provision
  - deploy

provision:
  stage: provision
  script: echo "Provisioning..."

deployment:
  stage: deploy
  script: echo "Deploying..."
  environment: production

trigger キーワードでstrategy: depend を定義する必要があります。これにより、ダウンストリームパイプラインが終了するまでロックがリリースされないようになります。

トラブルシューティング

パイプライン設定のデッドロックを回避

oldest_first process mode はジョブをパイプラインの順番で実行することを強制するため、他のCI機能との相性が悪い場合があります。

例えば、親パイプラインと同じリソースグループを必要とする子パイプラインを実行すると、デッドロックが発生する可能性があります。以下に_悪い_設定の例を示します:

# BAD
test:
  stage: test
  trigger:
    include: child-pipeline-requires-production-resource-group.yml
    strategy: depend

deploy:
  stage: deploy
  script: echo
  resource_group: production
  environment: production

親パイプラインでは、test ジョブを実行し、その後子パイプラインを実行します。strategy: depend オプション は、子パイプラインが終了するまでtest ジョブを待機させます。親パイプラインは次のステージでdeploy ジョブを実行します。このジョブはproduction リソースグループのリソースを必要とします。プロセスモードがoldest_firstの場合、最も古いパイプラインからジョブを実行します。つまり、deploy ジョブが次に実行されます。

しかし、子パイプラインもproduction リソースグループのリソースを必要とします。子パイプラインは親パイプラインよりも新しいため、子パイプラインはdeploy ジョブが終了するまで待機します。

この場合、代わりに親パイプライン設定でresource_group キーワードを指定する必要があります:

# GOOD
test:
  stage: test
  trigger:
    include: child-pipeline.yml
    strategy: depend
  resource_group: production # Specify the resource group in the parent pipeline

deploy:
  stage: deploy
  script: echo
  resource_group: production
  environment: production

ジョブが “Waiting for resource” (リソース待ち) でスタックします。

ジョブがWaiting for resource: <resource_group> というメッセージでハングすることがあります。解決するには、まずリソースグループが正しく動作していることを確認してください:

  1. ジョブの詳細ページにアクセスします。
  2. 現在リソースを使用しているジョブを表示]を選択します。
  3. ジョブのステータスを確認します:
    • ステータスがrunning またはpending の場合、この機能は正常に動作しています。ジョブが終了し、リソースがリリースされるまで待ちます。
    • ステータスがrunning またはpending でない場合、機能が正しく動作していない可能性があります。以下の情報で新しいイシューを作成してください:
      • ジョブID
      • ジョブステータス。
      • 問題の発生頻度
      • 問題の再現手順

GraphQL API からジョブ情報を取得することもできます。プロジェクト間/親子間のパイプラインでパイプラインレベルの同時実行制御を使用する場合は、トリガジョブに UI からアクセスできないため、GraphQL API を使用する必要があります。

GraphQL APIからジョブ情報を取得するには、以下の手順に従います:

  1. パイプラインの詳細ページに移動します。
  2. Jobsタブを選択し、スタックしたジョブのIDを探します。
  3. GraphiQLエクスプローラーに移動します。
  4. 次のクエリを実行します:

    {
      project(fullPath: "<fullpath-to-your-project>") {
        name
        job(id: "gid://gitlab/Ci::Bridge/<job-id>") {
          name
          detailedStatus {
            action {
              path
              buttonTitle
            }
          }
        }
      }
    }
    

    job.detailedStatus.action.path フィールドには、リソースを使用しているジョブ ID が含まれています。

  5. 以下のクエリを実行し、上記の条件に従ってjob.status フィールドをチェックしてください。pipeline.path フィールドからパイプラインページにアクセスすることもできます。

    {
      project(fullPath: "<fullpath-to-your-project>") {
        name
        job(id: "gid://gitlab/Ci::Bridge/<job-id-currently-using-the-resource>") {
          name
          status
          pipeline {
            path
          }
        }
      }
    }
    

    ステータスがrunning またはpendingでない場合は、新しいイシューを作成し、サポートに連絡してください。