Status | Authors | Coach | DRIs | Owning Stage | Created |
---|---|---|---|---|---|
proposed |
@ajwalker
|
@ayufan
|
@DarrenEastman
@engineering-manager
|
devops
| 2023-03-07 |
GitLab Runner アドミッションコントローラー
GitLabadmission controller
(Kubernetesのアドミッションコントローラのコンセプトにインスパイアされた)は、ジョブが永続化されたり、実行のためにビルドキューに追加されたりする前に、ジョブをインターセプトするための技術的なソリューションの提案です。
アドミッションコントローラはGitLabインスタンスに登録され、作成されるジョブを含むペイロードを受け取ることができます。アドミッションコントローラは、ミューティング、バリデーション、またはその両方を行うことができます。
- _変異させる_場合、変異可能なジョブ情報を修正してGitLabインスタンスに送り返すことができます。ジョブは、組織のポリシーやセキュリティ要件に適合するように変更したり、特定のランナーにルーティングされるようにタグリストを変更したりすることができます。
- _検証の_際、ジョブの実行を拒否することもできます。
動機
職務分掌、組織ポリシー、またはセキュリティ要件に準拠するために、金融サービス、米国連邦政府市場セグメント、またはその他の規制の厳しい業界のお客様は、特定のCIジョブ環境に関連するRunnerを作成者のみが使用できるようにする必要があります。
この文脈では、環境という用語を使用することは、GitLab CI environments and deployments ドキュメントで使用されている環境の定義と同等ではありません。SLSA ガイドの定義を用いると、環境とは “ジョブが実行されるマシン、コンテナ、VM、または同様のもの” です。
ローレンス・リバモア国立研究所のRemote Computing Enablement(RCE) グループからの追加要件です。この例では、ユーザーはCIジョブを実行するためにターゲットとなるRunner CIビルド環境のユーザーIDを持っていなければなりません。ユーザー全体の管理を簡素化するために、RCEはRunnerをGitLabユーザーエンティティに関連付けることができる必要があります。
現在の GitLab CI ジョブ処理メカニズム
先に進む前に、GitLab CIとGitLab Runnerの現在のジョブハンドリングの仕組みをレベル設定しておくと便利です。
- まず、GitLabインスタンスに関連付けられたRunnerは、GitLabインスタンスAPIに継続的にクエリを送り、実行可能な新しいジョブがあるかどうかをチェックします。
-
.gitlab-ci.yml
ファイルが存在する GitLab 上のプロジェクトリポジトリへのプッシュがあるたびに、GitLab インスタンスに存在する CI サービスがそのイベントをキャッチし、新しい CI ジョブをトリガーします。 - CI ジョブは、Runner がインスタンスにジョブをリクエストするまでキューで保留状態になります。
- ランナーが API にジョブを要求すると、データベースがクエリされてジョブのパラメータがランナーのパラメータと一致することが確認されます。言い換えると、RunnerがGitLabインスタンスに実行するジョブをポーリングするとき、指定された条件にマッチすればジョブが割り当てられます。
- もしそのジョブがランナーと一致すれば、GitLabインスタンスはそのジョブをランナーに接続し、ジョブの状態をrunningに変更します。言い換えると、GitLabは
job
オブジェクトとRunner
オブジェクトを接続します。 - Runnerはタグのないジョブを実行するように設定することができます。タグは、顧客が特定の種類のジョブを実行するRunnerをある程度コントロールできるようにするために、今日使われている主なメカニズムです。
- ランナーはインスタンス、グループ、またはプロジェクトにスコープされますが、ユーザーまたはグループ識別子に基づいてランナーへのアクセスを拒否するために簡単に拡張できる、追加のアクセス制御メカニズムは現在ありません。
現在のCIジョブのキューロジックは以下の通りです。 **注 - コードでは、ww はまだ非常に古いbuild
命名構造を使っていますが、 build
製品やドキュメントではjob
にbuild
マイグレーションしています build
。
jobs =
if runner.instance_type?
jobs_for_shared_runner
elsif runner.group_type?
jobs_for_group_runner
else
jobs_for_project_runner
end
# select only jobs that have tags known to the runner
jobs = jobs.matches_tag_ids(runner.tags.ids)
# select builds that have at least one tag if required
unless runner.run_untagged?
jobs = jobs.with_any_tags
end
目標
-
allow
deny
redirect
基本的なジョブの詳細(ユーザー、グループ、プロジェクトメンバーシップなど)に基づいて、特定のランナーエンティティで CI ジョブを実行するための、設定と使用が簡単なメカニズムを提供する初期ソリューションを実装します。
非ゴール
- CIジョブキューメカニズムの再設計は、このブループリントの範囲ではありません。
提案
CI ジョブをインターセプトし、ジョブの変異、検証、またはその両方を行うことを可能にするメカニズム、admission controllers
を実装します。アドミッションコントローラは、CIジョブを変更したり、ポリシーに従ってジョブを拒否したりすることができる、変更可能なWebhookです。WebhookはジョブがCIジョブキューに挿入される前に呼び出されます。
指針
- Webhookペイロードスキーマは公開APIの一部となります。
- Webhook ペイロードを拡張する際には、後方互換性を維持する必要があります。
- コントローラは冪等であるべきです。
入試コントローラはどのように動作しますか?
シナリオ1:あるランナーへのアクセスを拒否したいのですが。
- 特定のプロジェクトからのジョブのみを受け入れるように、承認コントローラを設定します。
- ジョブが作成されると、
project information
(project_id
,job_id
,api_token
) が GitLab に特定の詳細をクエリするために使われます。 -
project information
が許可リストにマッチする場合、ジョブのペイロードは変更されず、ジョブはターゲットの Runner 上で実行されます。 -
project information
が許可リストに一致しない場合、ジョブのペイロードは変更されず、ジョブは破棄されます。 - ジョブタグは変更されません。
- アドミッションコントローラは、オプションで、辞退を決定した理由を任意のテキストで返送することができます。
シナリオ2:共通の設定とタグを使用する大規模なランナーフリート。
各ランナーには、zone_a
、zone_b
のようなタグがあります。このシナリオでは、zone_a
にアクセスできるユーザーもいれば、zone_b
にアクセスできるユーザーもいるため、顧客は特定のジョブがどこで実行できるかを知りません。顧客は、zone_a
で実行されるべきジョブを失敗させたくないのですが、その代わりに、ジョブの実行に正しくタグ付けされていない場合は、ジョブをリダイレクトします。zone_a.
-
user_id
に基づいてジョブを変異させるように、認可コントローラを設定します。 - ジョブが作成されると、
project information
(project_id
,job_id
,api_token
) が GitLab に特定の詳細をクエリするために使われます。 -
user_id
が一致した場合、アドミッションコントローラはジョブタグリストを修正します。zone_a
ジョブをトリガーするユーザーがジョブを IN で実行することをコントローラが検出したため、タグリストに追加さzone_a
れます。
車々間通信
アドミッションコントローラー
- 1つのアドミッションコントローラーはインスタンスレベルでのみ登録可能です。
- アドミッションコントローラーは30秒以内に応答する必要があります。
- アドミッションコントローラは、個々のジョブの配列を受け取ります。これらのジョブは、互いに関連していても、関連していなくてもよい。応答は、リクエストの一部として行われたジョブに対する応答のみを含む必要があります。
ジョブのライフサイクル
-
ジョブのライフサイクルが更新され、新しい
validating
状態が追加されます。stateDiagram-v2 created --> validating state validating { [*] --> accept [*] --> reject } reject --> failed accept --> pending pending --> running: picked by runner running --> executed state executed { [*] --> failed [*] --> success [*] --> canceled } executed --> created: retry - ステートが
validating
、ミューテ ィングWebhookペイロードがアドミッションコントローラに送信されます。 - Webhookがタイムアウトする(30秒)ジョブのために、それらのステータスはあたかもアドミッションが拒否されたかのように設定されるべきです。これは典型的な状況ではまれなことです。
- 拒否されたジョブは再試行できます。再試行されたジョブは、以前に受け取った変異とともにアドミッションコントローラに再送されます。
-
allow_failure
例えば、拒否されたアドミッションで失敗するジョブをサポートするように更新されるべきです:job: script: - echo "I will fail admission" allow_failure: on_denied_admission: true
- UIが更新され、ジョブが失敗した理由が表示されるようにすべきです(提供されている場合)。
- 変異を保存するためにデータベースのテーブルが作成されるべきです。タグのような変更はすべて永続化され、
acts_as_taggable :admission_tags
でci_builds
に添付されるべきです。
ペイロード
- ペイロードは個々のジョブエントリで構成されます:
- ジョブID。
- 定義済み変数
- ジョブタグリスト。
- レスポンスペイロードは、以下のような個々のジョブエントリで構成されます:
- ジョブID。
- 入学状態:
accepted
またはdenied
. - 変異:現在のところ、
tags
のみサポートされています。提供されるタグは、オリジナルのタグリストを置き換えます。 - 理由:コントローラは、アドミッションとミューテーションの理由を指定できます。
リクエスト例
[
{
"id": 123,
"variables": {
# predefined variables: https://docs.gitlab.com/ee/ci/variables/predefined_variables.html
"CI_PROJECT_ID": 123,
"CI_PROJECT_NAME": "something",
"GITLAB_USER_ID": 98123,
...
},
"tags": [ "docker", "windows" ]
},
{
"id": 245,
"variables": {
"CI_PROJECT_ID": 245,
"CI_PROJECT_NAME": "foobar",
"GITLAB_USER_ID": 98123,
...
},
"tags": [ "linux", "eu-west" ]
},
{
"id": 666,
"variables": {
"CI_PROJECT_ID": 666,
"CI_PROJECT_NAME": "do-bad-things",
"GITLAB_USER_ID": 98123,
...
},
"tags": [ "secure-runner" ]
},
]
応答例
[
{
"id": 123,
"admission": "accepted",
"reason": "it's always-allow-day-wednesday"
},
{
"id": 245,
"admission": "accepted",
"mutations": {
"tags": [ "linux", "us-west" ]
},
"reason": "user is US employee: retagged region"
},
{
"id": 666,
"admission": "rejected",
"reason": "you have no power here"
},
]
MVC
- グループやプロジェクトレベルでの複数のアドミッションコントローラ。
- ジョブ定義をコントローラの連鎖(プロジェクトで開始し、定義されたすべてのグループコントローラからインスタンスコントローラまで)を介して渡します。
- 各レベルは、チェーン内の前のコントローラによって変更された定義を取得し、現在の状態に基づいて決定を行います。
- 複数のコントローラからレポーターが報告された場合、変更理由は連結されます。
- そのため、プロジェクト+インスタンス、プロジェクト+グループ+親グループ+インスタンス、プロジェクト+グループ、グループ+インスタンスなどを含むチェーンを持つことができます。
実装の詳細
- アドミッションコントローラのMVCコーディングに必要なステップのプレースホルダ
解決すべき技術的イシュー
イシュー | 解決 |
---|---|
コントローラを変異させることで、タグのAND、OR、NONEの論理定義が可能になるため、タグセットが競合する可能性があります。これはすぐにかなり複雑になります。 | |
キューウェブフックのルール定義 | |
どのデータをアドミッションコントローラに送信しますか?定義済みの変数のサブセットですか、それともすべてですか? | |
queueing web hook は GitLab.com のスケールで実行できますか?GitLab.comでは、毎秒数百万のWebhookをトリガーすることになり、Sidekiqに過負荷がかかったり、システムを悪用されたりすることが懸念されます。 |