TECHSTEP

ITインフラ関連の記事を公開してます。

GitLab roleを整理する

今回はGitLabで利用できるロールを簡単に整理します。

docs.gitlab.com

背景

GitLabユーザーをProject/Groupに追加するとき、ユーザーに対して何らかのRoleを付与します。Roleは、あらかじめ用意されたDefault Roleと、ユーザーが作成したCustomr Roleがあります。Custom RoleはUltimateプランでのみ利用できます。

docs.gitlab.com

Default Roleは以下の5種類です。簡単に言えば Guest が最も権限が弱く、 Owner が最も強い権限を持ちます。

  • Guest
  • Reporter
  • Developer
  • Maintainer
  • Owner

Roleは主にProject / Group / CI/CD設定という3つのリソースに対して割り当て、権限を付与します。Default roleが各リソースにどんな権限を付与するかは、以降で簡単に紹介します。

Projectメンバーの権限

Project内のユーザーに振り当てられるRoleは、Project上の操作権限をコントロールします。Owner roleは全ての権限を付与しますが、Owner roleは以下の場合にのみ付与されます。

  • Administratorの場合
  • Group/ProjectのOwnerの場合

また、Personal namespaceのOwnerは、GitLab 14.9より以前のバージョンの場合、該当のNamespaceではMaintainerと表示されますが、実際はOwner roleを持つユーザーと同じ権限を有します。

各Default roleにどんな権限が付与されるかですが、基本的に Guest / Reporter はView権限のみが付与され、Developer はProject管理関連の機能以外 (policy/ruleなど) はCreate権限も付与されています。Ownerは各種リソースのDelete権限も含め、すべての権限を所持します。

いくつかのリソースに対する権限を載せておきます。

リソース 操作 Guest Reporter Developer Maintainer Owner
Merge request View
Approve
Assign
Create
Delete
Project Download
Export
Rename
Archive
Delete
Container Registry Pull image
Push image
Remove image

全ての権限は以下のページに記載されています。

docs.gitlab.com

Groupメンバーの権限

Group内のユーザーに振り当てられるRoleは、Groupに対する操作権限をコントロールします。そのGroupの最後のOwnerでない限り、全てのユーザーは自身をGroupから削除することができます。

また、Subgroupにユーザーを追加すると、親Groupから権限レベルを受け継ぎます。また、いずれかの親Groupに属していれば、その配下のNested groupにアクセスできます。

いくつかのリソースに対する権限を載せておきます。

リソース 操作 Guest Reporter Developer Maintainer Owner
Group epic View
Create
Edit
Delete
Group View
Delete
Migrate
Subgroup Create

docs.gitlab.com

GitLab CI/CD権限

GitLab CI/CDの権限は、そのパイプラインを実行したユーザーの権限が反映されます。またCI/CDの権限は、いくつかのRoleについては以下の設定から修正できます。

  • Public pipelineに設定した場合、Guest Projectメンバーに一部機能へのアクセス許可を与えます。
  • Pipeline visibilityを Everyone with Access に設定した場合、Project外のメンバーにも一部CI/CDの View権限を付与します。

いくつかのリソースに対する権限を載せておきます。

リソース 操作 Guest Reporter Developer Maintainer Owner
Environment View
Create
Stop
Job View
Retry
Cancel

docs.gitlab.com

その他

Premium/Ultimateプランでは、以下の機能も利用できます。

  • Custom role : 利用者が使用したい権限を組み合わせてカスタマイズしたroleを作成・適用できます。Ultimateプランでのみ利用可能です。
  • Users with Minimal access : SAML SSOを使用する場合、Owner はユーザーに Minimal Access というRoleを付与できます。このRoleはユーザーにGroupのList権限を付与しますが、リソースやProjectなどの詳細は確認できません。

docs.gitlab.com

docs.gitlab.com

about.gitlab.com

GitLab CI/CD Catalogの紹介

今回はGitLab 16.7でベータ版をリリースしたCI/CD Catalogを紹介します。

docs.gitlab.com

背景

GitLab CI/CD Catalogの前に、CI/CD componentsについて理解する必要があります。

CI/CD componentsはGitLab内部で再利用可能なパイプラインのユニットです。CI/CD componentを使うことで、小さな部品を組み合わせるようにパイプラインを設定でき、より大きなパイプラインを設定することも可能となります。

CI/CD componentは 以前取り上げた include と似たような機能であり、CI/CD componentを利用するときは include:component という形で定義します。ただし、CI/CD componentにはいくつかの利点があります。

  • CI/CD Catalogの中でリスト化され、必要なものを見つけやすくなります。
  • componentは特定のバージョンをリリース・使用できます。
  • 同じProject内で複数のcomponentを定義し、バージョン管理できます (最大10個まで) 。

CI/CD Catalogは、上記の通りCI/CD componentsをリスト化したものですが、Project内部で用意したものだけでなく、外部に公開されたものも利用できます。

なお、CI/CD CatalogについてはGitLabのブログでも紹介されています。

about.gitlab.com

検証

ここから実際にCI/CD Catalogを使ってみます。

CI/CD componentsを組織内部で利用

まずはCI/CD Catalogの前にCI/CD Componentsを組織内で利用する例を試します。ここでは cicd-catalog-test-component というProjectにテスト用のテンプレートを配置し、 cicd-catalog-test Projectからそれを呼び出します。

CI/CD componentsのテンプレートを用意する場合、いくつかの条件 (と推奨事項) があります。

  • ディレクトリ構成
    • README.mdリポジトリ中のcomponentsの詳細が記載されていること
    • templates ディレクトリがルートに配置され、全てのcomponentsの定義がそこに含まれること
    • componentファイルは .yml という拡張子であること
    • templates ディレクトリ内はサブディレクトリも含められるが、各サブディレクトリにはエントリポイントとして template.yml というファイルが含まれていること
  • 推奨事項
    • .gitlab-ci.yml を使ってcomponentsのテストとリリースを行うこと
    • LICENSE.md ファイルを配置し、ライセンス情報を加えること

1つのcomponentファイルを含むリポジトリの場合は以下のような構成が推奨されています。

├── templates/
│   └── my-component.yml
├── LICENSE.md
├── README.md
└── .gitlab-ci.yml

cicd-catalog-test-component には、以下のようなファイルを用意します。今回は必要最小限のファイルのみ配置します。

├── templates/
│   └── my-component.yml
└── README.md

templates/my-component.yml

spec:
  inputs:
    stage:
      default: test
---
component-job:
  script: echo "This is from cicd-catalog-test-component project."
  stage: $[[ inputs.stage ]]

上記ファイルに含まれる spec とは、GitLab CI/CDで利用するHeader keywordの1つであり、 Pipelineに include が含まれたときの動作設定を定義できます。ここでは spec:inputs で実行時の入力パラメータとして stage を追加しています。また spec:inputs:default でデフォルトの入力値を設定します。

docs.gitlab.com

次に、上記componentを呼び出す側として cicd-catalog-test というProjectを作成し、 .gitlab-ci.yml ファイルからこれを呼び出します。

CI/CD componentsを呼び出す場合、 include:component を使用します。componentを指定するときは <FQDN>/<Projectパス>/<component名>@<バージョン> というフォーマットを使います。

  • FQDN: GitLabホストとマッチするFQDNを指定
  • Projectパス: componentを含むProjectのフルパスを指定
  • component名: 単一ファイルの場合はファイル名、複数の場合は template.yml を含むディレクトリ名を指定
  • バージョン: Commit SHA、タグ、ブランチ名、 latest などを指定

今回は .gitlab-ci.yml で以下のように定義しました。

stages:
  - build
  - test

build:
  stage: build
  script:
    - echo "This is from cicd-catalog-test project at build stage."

test:
  stage: test
  script:
    - echo "This is from cicd-catalog-test project at test stage."

include:
  - component: gitlab.com/fy0323/cicd-catalog-test-component/my-component@main
    inputs:
      stage: build

.gitlab-ci.ymlを配置すると、以下のようにパイプラインが実行され、定義通り build Stageで component-job Jobが実行されます。

ここで component-job を見ると、 cicd-catalog-test-component で定義した通りの文字列が表示されるのを確認できます。

CI/CD Catalogの利用

次にCI/CD Catalogを利用します。

まずCI/CD Catalogのリストを確認します。GitLab UIの 検索または移動先 を選択し、 検索 を選択します。画面左メニューに CI/CDカタログ と表示されるのでそこを選択すると、CI/CD Catalogが表示されます。なお操作時は全部で101のCatalogが登録されていました。

ここではその中から components/sast というCatalogを利用します。このCatalogは名前の通りSASTを実行するものであり、GitLabの提供するSASTと同一の機能を提供します。

各Catalogには README.md に記載された内容が表示され、基本的な使用方法などが紹介されています。

ここでは components/sast の内容に従い、以下のように .gitlab-ci.yml を修正します。またSASTのテスト用に適当なファイルを配置します。

※なお本検証はGitLab 16.8時点に行いましたが、GitLab 16.10からはSemantic versioningによる指定が強制されます

include:
  - component: gitlab.com/gitlab-components/sast/sast@1.0

パイプラインを起動すると、ここでは semgrep-sast というJobが実行されている様子を確認できます。

semgrep-sast を選択するとSASTが実行されていることが確認できます。またArtifactも配置されており、スキャン結果も確認できました。

GitLab Runnerのネットワーク要件を調査する

今回はGitLab Runnerのネットワーク要件を調査しました。なお検証後に該当のドキュメントを見つけてしまったので、検証の意味はあまりないです。

背景

以前GitLab Runnerの登録と利用を検証したのですが、GitLab Runnerを利用するときのネットワーク要件が明確に記載されたドキュメントを見つけられませんでした。いくつかのブログ等を見ると、GitLab SaaS版を使っている場合は gitlab.com へのhttps通信が許可されていればよいようでしたが、公式のドキュメントが見つからなかったので、念のため検証してみました。 検証後に再度探したところ、こちらのドキュメントに記載がありました。GitLabのネットワーク構成はこちらに一通り書かれているようです。

docs.gitlab.com

検証

GitLab Runnerの作成、トークン発行

あらかじめGitLab画面からRunnerを作成しておきます。

Amazon EC2インスタンスの作成

インスタンスの作成は、以前使用したCloudFormationテンプレートから gitlab-runner register の実行箇所を除外したものを使いました。

作成後、DockerのインストールやGitLab Runnerのパッケージが含まれていることを確認します。

[ec2-user@ip-10-0-0-138 ~]$ docker version
Client:
 Version:           20.10.25
 API version:       1.41
 Go version:        go1.20.12
 Git commit:        b82b9f3
 Built:             Fri Dec 29 20:37:18 2023
 OS/Arch:           linux/amd64
 Context:           default
 Experimental:      true

Server:
 Engine:
  Version:          20.10.25
  API version:      1.41 (minimum version 1.12)
  Go version:       go1.20.12
  Git commit:       5df983c
  Built:            Fri Dec 29 20:38:05 2023
  OS/Arch:          linux/amd64
  Experimental:     false
 containerd:
  Version:          1.7.11
  GitCommit:        64b8a811b07ba6288238eefc14d898ee0b5b99ba
 runc:
  Version:          1.1.11
  GitCommit:        4bccb38cc9cf198d52bebf2b3a90cd14e7af8c06
 docker-init:
  Version:          0.19.0
  GitCommit:        de40ad0
[ec2-user@ip-10-0-0-138 ~]$ yum list | grep gitlab
gitlab-runner.x86_64                     16.9.1-1                     @runner_gitlab-runner
gitlab-runner-fips.x86_64                16.9.1-1                     runner_gitlab-runner
[ec2-user@ip-10-0-0-138 ~]$

Security Groupのアウトバウンド通信制

次にSecurity Groupのアウトバウンド通信を制限します。ここでは443ポートのみを許可するよう設定しました。

GitLab Runnerの登録

Security Groupを修正後、GitLab Runnerの登録を実行します。

[ec2-user@ip-10-0-0-138 ~]$ sudo gitlab-runner register \
>   --non-interactive \
>   --url "https://gitlab.com/" \
>   --token <GitLab runner authentication token> \
>   --executor "docker" \
>   --docker-image alpine:latest \
>   --description "docker-runner"
Runtime platform                                    arch=amd64 os=linux pid=6649 revision=782c6ecb version=16.9.1
Running in system-mode.

Verifying runner... is valid                        runner=ssmZ-8R_n
Runner registered successfully. Feel free to start it, but if it's running already the config should be automatically reloaded!

Configuration (with the authentication token) was saved in "/etc/gitlab-runner/config.toml"
[ec2-user@ip-10-0-0-138 ~]$

GitLabパイプラインの実行

GitLab Runnerの登録ができたのでGitLabパイプラインからRunnerを使って処理を実行しています。以前使用した .gitlab-ci.yml を使ってパイプラインを起動すると、問題なく処理が完了することを確認できました。

GitLab includeでワークフローを再利用する

今回はGitLab CI/CDで利用できるキーワードの中から、.gitlab-ci.yml から同じProject上の別のファイル、別Project上のファイルを呼び出せる include について検証しました。

docs.gitlab.com

docs.gitlab.com

背景

ワークフローの定義ファイルを再利用することは、CI/CDをプロジェクトで運用する場合に重要です。パブリックに公開されたワークフローやプロジェクト共通のテンプレートを再利用することで、CI/CDの定義ファイルの記述量を削減したり、何度も同じ記述をコピーペーストしないことで、管理しやすいCI/CD定義ファイルの運用や人的ミスの削減を見込めます。

GitLabではCI/CDの実行を .gitlab-ci.yml で制御するのがデフォルトの設定です。例えば1つのGitLabプロジェクトで実行する全ての処理を .gitlab-ci.ymlスクリプトで記述すると、膨大な記述量となる場合もあります。GitLabでは include というキーワードを使うことで、同じProject上にある .gitlab-ci.yml 以外のファイルや、別Projectの定義ファイルを呼び出すことができます。

GitLabは現在6つの形式で include を利用できます。 components inputs は別の機会に触れるとして、ここでは他4つの利用方法を紹介します。

  • local : 同じProject上にある別ファイルを呼び出します。
  • project : 同じGitLabインスタンス上のプライベートな別Projectのファイルを呼び出します。
  • remote : 別のGitLabインスタンス上のファイルを呼び出します。
  • template : GitLabが公式に提供するテンプレートを呼び出します。
  • components : CI/CD componentという再利用可能なパイプラインのユニットを呼び出します。
  • inputs : 各 include を利用するときに変数を上書きするのに使用します。

なお、 include を利用するには、いくつかの制限事項があります。

  • include という名前のJobから呼び出す形でないと利用できません。
  • 呼び出すファイルの拡張子は yaml でないといけません。
  • project / remote / template / components の場合、呼び出し元のファイルが修正されてもCIは実行されません。
  • include を含むJobまたはパイプラインを再実行した場合、挙動が変化します。
    • Jobの場合: include で指定したファイルに修正を加えた後に再実行しても、その内容はJobに反映されません。
    • パイプラインの場合: include で指定したファイルに修正を加えた後に再実行すると、その内容はパイプラインに反映されます。
  • パイプラインあたりの include の最大数はデフォルトで150までです。

検証

ここから各手法を検証します。

include:local

docs.gitlab.com

include:local は同じProject上のファイルを呼び出す形です。1つのGitLab Projectで管理するような小規模プロジェクト、またはCI/CDの導入初期に利用するのがユースケースかと思います。

include:local を利用する場合は、 .gitlab-ci.yml に以下のように定義します。

include:
  - local: '<呼び出すファイルパスを指定>'

include:local を利用するうえでの注意事項は以下の通りです。

  • .gitlab-ci.yml と同じブランチ上のファイルしか対象にできません。
  • GIt submodule (ある Git リポジトリを別の Git リポジトリのサブディレクトリとして扱う) のパスでファイルを使用することはできません。

ここでは include-local-test というProject上に以下のようなファイルを配置しました。

.gitlab-ci.yml

build:
  stage: build
  script:
    - echo "Start include:local jobs."

include:
  - local: '.gitlab-ci.local.yml'
  - local: '/include/.gitlab-ci.local.yml'

.gitlab-ci.local.yml

test-from-include:
  stage: test
  script:
    - echo "This is from .gitlab-ci.local.yml."

include/.gitlab-ci.local.yml

deploy:
  stage: deploy
  script: 
    - echo "This is from include/.gitlab-ci.local.yml."

include:local.gitlab-ci.yml と別のディレクトリ上のファイルも呼び出し可能です。ここでは .gitlab-ci.local.yml include/.gitlab-ci.local.yml という2つのファイルを呼び出しています。

このProjectでCI/CDを実行すると、以下のように3つのStageに分かれて実行されます。

.gitlab-ci.local.yml を呼び出す test Jobを見ると、 This is from .gitlab-ci.yml.local (テキストを間違っておりました。。。) と表示されており、 .gitlab-ci.local.yml を呼び出しているのを確認できます。

include/gitlab-ci.local.yml を呼び出す deploy Jobも同様に確認できます。

include:project

docs.gitlab.com

include:project は同じインスタンス上の別Projectからファイルを参照します。複数のGitLab Projectにまたがって進むような大規模プロジェクトにおいて、include:project 専用のProjectを用意し、各Projectから利用することでプロジェクト内のCI/CDを利用しやすくする、などのケースが考えられます。

include:project は対象のプロジェクトとファイル名に加え、 ref というキーワードを使ってファイルの参照先 (デフォルトは HEAD を参照) を変更できます。

include:project は以下のように指定します。

include:
  - project: '<Project名を指定>'
    file: '<使用するファイル名を指定>'
    ref: '<ブランチ名、タグなどを指定 (オプション)>'

include:project の注意事項は以下の通りです。

  • パイプラインを実行するユーザーは include:project で指定したProjectのメンバーであり、適切な権限が必要です。
  • include:project で指定したファイルが更新されても、呼び出し元のCI/CDパイプラインは起動しません。またセキュリティの観点から、以下のように ref を使用して参照先を特定することが推奨されます。
    • 特定のSHAハッシュを指定する
    • 別プロジェクト上で protected branch / protected tag を使用する。この設定を適用した参照先は、Merge Requestなどの変更管理を通じて変更する確率が高くなります。

ここでは2つのGitLab Project ( include-project-test include-project-template-test ) を用意し、include-project-test から include-project-template-test 上のファイルを呼び出します。また include-project-template-test 上に feature ブランチを作成し、 ref を利用した場合も実施しています。

include-project-test/.gitlab-ci.yml

stages:
  - build
  - test
  - deploy

build:
  stage: build
  script:
    - echo "Start include jobs from another project."

include:
  - project: 'fy0323/include-project-template-test'
    file: '.gitlab-ci.project.yml'
  - project: 'fy0323/include-project-template-test'
    file: '.gitlab-ci.project.yml'
    ref: feature

include-project-template-test/.gitlab-ci.project.yml

project:
  stage: test
  script:
    - echo "This is from .gitlab-ci.project.yml at include-project-template-test"

include-project-template-test/.gitlab-ci.project.yml at feature branch

project-from-feature:
  stage: deploy
  script:
    - echo "This is from .gitlab-ci.project.yml at include-project-template-test"
    - echo "The branch name is feature."

include-project-test でCI/CDパイプラインを実行すると、以下のように3つのJobを実行します。 test Jobは include-project-template-testmain ブランチから、 deploy Jobは feature ブランチから呼び出しています。

それぞれのJobを見ると、確かに呼び出されていることを確認できます。

include:remote

docs.gitlab.com

include:remote は別インスタンス上のファイルを対象とする時に使用します。正直利用用途があまり浮かばなかったですが、例えば何らかの理由でGitLab Self-managedインスタンス / SaaSを複数利用しており、あるProjectのテンプレートを別のインスタンスでも利用したい場合、インスタンスをまたいでテンプレートを利用できます。ただし本機能は、参照元のProjectがPublicな場合に限り利用可能です。

include:remote は以下のように利用します。

include:
  - remote: '<GitLabインスタンスのURLを含むファイルパスを指定>'

include:remote を利用するうえでの注意事項は以下の通りです。

  • remote のターゲットにできるのは、 GET リクエストでアクセス可能なPublic Projectが対象です。
  • 認証情報を付与してのURLアクセスはサポートされていません。そのためPrivate Projectの利用は基本的にできません。

今回は include-remote-test というProjectで検証します。リモートURLには、以前作成したSelf-managedインスタンス上に作成したPublic Projectを使用しました。

.gitlab-ci.yml では以下のように指定します。

build:
  stage: build
  script:
    - echo "Start jobs from include:remote."

include:
  - remote: 'https://<GitLabインスタンスURL>/root/include-remote-template-test/-/raw/main/.gitlab-ci.remote.yml'

なお、リモートURLは、参照先リポジトリの以下の場所から取得できます。

パイプラインを実行すると、リモートURLから定義ファイルを取得し、問題なく処理が完了したのを確認します。

test jobを見ると、確かにリモートURLが呼び出され、定義された内容が呼び出されていることを確認できます。

include:template

docs.gitlab.com

include:template はGitLabの提供するテンプレートを利用する方法です。自分たちでイチからテンプレートを用意せずとも利用可能なので、用途に合ったものがあれば積極的に利用するのが良いと思います。

include:template は以下のように利用します。

include:
  - template: '<利用するテンプレートファイル名を指定>'

include:template の注意事項は以下の通りです。

  • include:templateで利用できるテンプレートはこちらに配置されています。ただし、すべてのテンプレートが include:template で利用できるわけではないため、利用前には各テンプレート中のコメントを確認する必要があります。

今回は Bash.gitlab-ci.yml というテンプレートを指定し、CI/CDから Bashスクリプトを呼び出します。

.gitlab-ci.yml は以下のように指定します。

build:
  stage: build
  script:
    - echo "Start jobs from include:template."

include:
  - template: Bash.gitlab-ci.yml

上記ファイルを配置し、問題なければパイプラインが完了します。Jobを確認すると、 Bash.gitlab-ci.yml で定義した内容が実行されているのを確認できます。

その他

Nested include

docs.gitlab.com

include入れ子構造で呼び出すことも可能です。 .gitlab-ci.yml から include で呼び出されるテンプレート内部で、さらに別のテンプレートを呼び出すことで、ワークフローのメンテナンス性と再利用性を向上することができます。

なお、Nested includeは1つのパイプラインあたり150ファイルが上限です。

GitLab RunnerとしてLinuxサーバーを登録する

今回はAmazon EC2インスタンスをGitLab Runnerとして登録する手順について紹介します。

背景

GitLab RunnerはGitLab CI/CDパイプラインの実行基盤です。GitLab SaaS版を利用する場合、Shared Runnerという共有リソースを利用できますが、セキュリティ等の理由から自前の仮想マシン・コンテナをRunnerとして管理したいケースもあると思います。

GitLab Runnerは以下の3種類があり、Runnerを登録するうえでどの形態が良いか確認する必要があります。今回はGitLab SaaS版に Project Runner を登録する手順を検証しました。

  • Project Runner: 特定のProject専用のRunner
  • Group Runner: Group内の全てのporjectが利用できるRunner
  • Instance (Shared) Runner: GitLab Instance内の全てのProjectが利用できるRunner※

※ドキュメントを見ると、GitLab 16.8までは Shared runner という名称でしたが、GitLab 16.9からは Instance runnerに変わったようです。

また、GitLab RunnerとGitLabの処理の流れは、GitLabドキュメントに記載されています。GitLab RunnerとGitLab間では、GitLab RunnerからGitLab方向の通信のみが発生します。そのため、GitLab Runnerは、GitLabに対するアウトバウンド方向の通信 (ポート: 443) を許可すれば利用可能です。

docs.gitlab.com

検証

Runner authentication tokenの取得

GitLab Runnerを登録するため、まずはGitLab側でRunnerの認証に使うトークンを発行します。GitLabの画面から 設定 CI/CD を選択します。

Runnerの項目を開き、 新規プロジェクトRunner を選択します。

次のページで、登録するRunnerの情報を入力します。ここでは以下のように設定しました。

  • プラットフォーム: Linux
  • Tags: project-test
  • その他: 空欄

上記設定を入力後に ランナーを作成 を選択すると、Runnerを登録するためのコマンドやRunner authentication tokenが表示されます。Runner authentication tokenはここでしか確認できないため、コピーしておきます。

Runnerのページに戻ると、Project Runnerは割り当てられていますが、黒三角のアイコンが表示されています。これはこのRunnerがまだ利用できない状態であることを示します。

AWS Secrets Managerへの登録

Runner authentication tokenはGitLab Runnerを登録する際に利用します。後ほどAWS CloudFormationテンプレートでAmazon EC2を作成する際にトークン情報を使いますが、テンプレートに直接記述するのを避けるため、事前にAWS Secrets managerに登録したものを呼び出す形にしました。

AWS Secrets managerのメニューを開き、シークレットの作成を行います。ここでは GitLabProjectRunnerToken という名称のシークレットを作成しました。

作成時は その他のシークレットのタイプ を選択し、プレーンテキストでRunner authentication tokenを記載しました。

Amazon EC2の作成

つづいて、GitLab RunnerをインストールするAmazon EC2インスタンスを用意します。ここではVPC/Security Group/IAM等も含めてリソースを作成しており、SSMでインスタンスにアクセスできるよう設定しています。

gitlab-runner-ec2.yaml

AWSTemplateFormatVersion: '2010-09-09'
Description: "EC2 instance for GitLab Runner"
Parameters:
  ImageId:
    Default: "/aws/service/ami-amazon-linux-latest/amzn2-ami-hvm-x86_64-gp2"
    Type: AWS::SSM::Parameter::Value<AWS::EC2::Image::Id>
  InstanceType:
    Default: "t3.large"
    Type: String
  KeyName:
    Type: String
  VPCCidr:
    Default: "10.0.0.0/16"
    Type: String
  PublicSubnetCidr:
    Default: "10.0.0.0/24"
    Type: String
  PublicSubnetAZ:
    Default: "ap-northeast-1a"
    Type: String
  EnvName:
    Default: "gitlab-runner-test"
    Type: String

Resources:
  VPC:
    Type: AWS::EC2::VPC
    Properties:
      CidrBlock: !Ref VPCCidr
      EnableDnsHostnames: true
      EnableDnsSupport: true
      Tags:
        - Key: Name
          Value: !Ref EnvName
  PublicSubnet:
    Type: AWS::EC2::Subnet
    DependsOn: AttachGateway
    Properties:
      CidrBlock: !Ref PublicSubnetCidr
      VpcId: !Ref VPC
      AvailabilityZone: !Ref PublicSubnetAZ
      Tags:
        - Key: Name
          Value: !Ref EnvName
  InternetGateway:
    Type: AWS::EC2::InternetGateway
    Properties:
      Tags:
        - Key: Name
          Value: !Ref EnvName
  AttachGateway:
    Type: AWS::EC2::VPCGatewayAttachment
    Properties:
      VpcId: !Ref VPC
      InternetGatewayId: !Ref InternetGateway
  PublicRouteTable:
    Type: AWS::EC2::RouteTable
    DependsOn: AttachGateway
    Properties:
      VpcId: !Ref VPC
      Tags:
        - Key: Name
          Value: !Ref EnvName
  PublicRoute:
    Type: AWS::EC2::Route
    Properties:
      RouteTableId: !Ref PublicRouteTable
      DestinationCidrBlock: 0.0.0.0/0
      GatewayId: !Ref InternetGateway
  PublicSubnetRouteTableAssociation:
    Type: AWS::EC2::SubnetRouteTableAssociation
    Properties:
      SubnetId: !Ref PublicSubnet
      RouteTableId: !Ref PublicRouteTable
  EIP:
    Type: AWS::EC2::EIP
    Properties:
      Domain: vpc
      Tags:
        - Key: Name
          Value: !Ref EnvName
  EIPAssociation:
    Type: AWS::EC2::EIPAssociation
    Properties:
      AllocationId: !GetAtt EIP.AllocationId
      InstanceId: !Ref GitLabInstance
  EC2SecurityGroup:
    Type: AWS::EC2::SecurityGroup
    Properties:
      GroupDescription: EC2SecurityGroup
      VpcId: !Ref VPC
      Tags:
        - Key: Name
          Value: !Sub ${EnvName}-EC2SecurityGroup
      SecurityGroupIngress:
        - IpProtocol: tcp
          FromPort: 443
          ToPort: 443
          CidrIp: '0.0.0.0/0'
  EC2IAMRole:
    Type: AWS::IAM::Role
    Properties:
      RoleName: !Sub ${EnvName}-SSM-role
      AssumeRolePolicyDocument:
        Version: 2012-10-17
        Statement:
          - Effect: Allow
            Principal:
              Service:
                - ec2.amazonaws.com
            Action:
              - sts:AssumeRole
      Path: /
      ManagedPolicyArns:
        - arn:aws:iam::aws:policy/AmazonSSMManagedInstanceCore
        - arn:aws:iam::aws:policy/SecretsManagerReadWrite
  EC2InstanceProfile:
    Type: AWS::IAM::InstanceProfile
    Properties:
      Path: /
      Roles:
        - Ref: EC2IAMRole
      InstanceProfileName: !Sub ${EnvName}-EC2InstanceProfile
  GitLabInstance:
    Type: AWS::EC2::Instance
    Properties:
      ImageId: !Ref ImageId
      KeyName: !Ref KeyName
      InstanceType: !Ref InstanceType
      SecurityGroupIds:
        - !Ref EC2SecurityGroup
      IamInstanceProfile: !Ref EC2InstanceProfile
      SubnetId: !Ref PublicSubnet
      UserData:
        Fn::Base64: |
          #!/bin/bash
          sudo yum update -y
          sudo amazon-linux-extras install -y docker
          sudo systemctl start docker
          sudo systemctl enable docker
          sudo usermod -a -G docker ec2-user

          curl -L "https://packages.gitlab.com/install/repositories/runner/gitlab-runner/script.rpm.sh" | sudo bash
          sudo yum install -y gitlab-runner

          RUNNER_TOKEN=$(aws --region=ap-northeast-1 secretsmanager get-secret-value --secret-id GitLabProjectRunnerToken --query 'SecretString' --output text)
          sudo gitlab-runner register \
            --non-interactive \
            --url "https://gitlab.com/" \
            --token $RUNNER_TOKEN \
            --executor "docker" \
            --docker-image alpine:latest \
            --description "docker-runner"

      Tags:
        - Key: Name
          Value: !Sub ${EnvName}-EC2

Runnerの登録の確認と動作確認

Amazon EC2が作成されてしばらくすると、GitLabのRunnerページに表示されるProject Runnerは緑丸のアイコンが表示されます。

この状態になると登録したGitLab Runnerは利用可能となるため、テスト用の .gitlab-ci.yml を作成します。登録したRunnerをGitLab CI/CDから呼び出すには、 tags に登録時に指定したタグを指定します (今回は project-test )。ここでは以下のファイルを使用しました。

job:
  stage: build
  tags:
    - project-test
  script:
    - echo "Hello, GitLab Runner!"

上記ファイルを作成するとパイプラインが起動します。

実行後のログを見ると、作成したRunnerを使ってJobを実行したことを確認できます。

CloudFormationのGit SyncをGitLabで動かしてみる

今回は2023年に公開されたCloudFormation Git Syncを紹介します。

aws.amazon.com

背景

AWS CloudFomationはAWSリソースを対象とするIaCツールです。CloudFormationのコード管理はGitHub / GitLabなどのGitホスティングサービスを利用することが多いですが、CloudFormationコードの開発を終えて動作確認のためデプロイする場合、これまではAWS CLIやマネジメントコンソールなどのインターフェイスにアクセスするか、専用のCI/CDを自前で構築する必要がありました。前者の場合はアクセス先を切り替える必要があり、ここでコンテキストスイッチが発生します。

また、近年ではGitOpsという、Gitリポジトリとデプロイ先の環境を同期することで、デプロイの手間の削減や組織内の責任分界点の設定などを実現する手法が普及しています。

※参考:

about.gitlab.com

blog.inductor.me

ただし現状GitOpsをやろうと思うと、基本的にはGitOpsを実現するソフトウェアを自前で用意する必要があります。またAWS CloudFormationのファイルをGitOpsで扱うとなるとさらに選択肢は限られ、AWS CloudFormation Template Sync Controller for Flux などが必要でした。これらOSSやその基盤となるKubernetesを管理する必要もあり、個人的にCloudFormationでGitOpsを実現するハードルはかなり高いものでした。

CloudFormation Git Syncは、一言でいえば AWS CloudFormationに対するGitOpsを提供するAWSマネージドな機能 であり、対象のGitリポジトリをモニタリングし、Gitで管理するCloudFormationファイルの変更を検知すると、それをCloudFormation Stackに自動的に反映する機能を提供します。

今回CloudFormationがGit Syncに対応したことで、コンテキストスイッチやGitOps導入ハードルといった課題を解消し、CloudFormationのコードの開発速度の向上やCloudFormationを中心に扱うプロジェクトでのGitOpsの実現をサポートすることが期待できます。

なおAWSブログではGitHub Codespaces / GitHub Actions / CloudFormation Linterを使ったモダンな開発環境の例も紹介しています。

aws.amazon.com

検証

ここからCloudFormation Git Syncを実際に検証します。なお今回は以下のような条件で検証をしています。

  • Gitサービス: GitLab
  • CloudFormationで扱うAWSリソース: Amazon S3

docs.aws.amazon.com

事前準備

CloudFormation Git Syncを利用するには、いくつかの条件があります。

  1. CloudFormationテンプレートを含むGitリポジトリを用意している
  2. AWS CodeStar Connectionを使ってGitリポジトリと接続可能である
  3. Git Syncを実行するためのIAMロールが用意されている
  4. CloudFormationとは別にパラメータを定義したファイルが必要である
Gitリポジトリの用意

1つ目の条件について、今回は cfn-git-test というGitLabプロジェクトを作成し、以下のようなテンプレートファイルを配置しておきます。

AWSTemplateFormatVersion: "2010-09-09"
Parameters:
  BucketName:
    Type: String

Resources:
  S3:
    Type: AWS::S3::Bucket
    Properties:
      BucketName: !Ref BucketName
      Tags:
        - Key: Test
          Value: false
AWS CodeStar Connectionによる接続

2つ目の条件は、AWSマネジメントコンソールにアクセスし、デベロッパー用ツールの画面から 設定 接続 を選択します。

接続を作成 を選択します。

接続先のGitプロバイダを選択します。今回は GitLab を選択し、接続名も適当なものを入力します。

画面が遷移し、GitLabにログインした状態だと以下のような画面が表示されます。ここではAWSからGitLabに対するアクセス権限を許可してくれるよう申請が出されているので、 Authorize を選択して許可します。

再びAWSの画面に戻るので 接続 を選択します。

これでAWSとGitLabとの接続が可能になりました。

IAMロールの用意

3つ目の条件ですが、Git Syncを利用する場合はIAMロールを新規に作成することも選択できます。今回は新規に作成するほうを選択しましたが、事前にIAMロールを作成する場合は以下のような権限が必要となります。

  • cloudformation:CreateChangeSet
  • cloudformation:DeleteChangeSet
  • cloudformation:DescribeChangeSet
  • cloudformation:DescribeStackEvents
  • cloudformation:DescribeStacks
  • cloudformation:ExecuteChangeSet
  • cloudformation:ListChangeSets
  • cloudformation:ValidateTemplate
  • events:PutRule
  • events:PutTargets

docs.aws.amazon.com

deploymentファイルの用意

4つ目の条件ですが、こちらもGit Sync利用時にテンプレートを作成することが可能です。事前に用意する場合は以下のようなファイルをGitリポジトリに格納します。

template-file-path: s3.yaml
parameters:
  BucketName: cfn-git-sync-gitlab-20231228
tags: {}

CloudFomationとGitLabの同期

ここからGit Syncを利用してAmazon S3を作成します。

AWSマネジメントコンソールからCloudFomrationのメニューに移動し、スタックの作成を選択します。メニューの中に Gitから同期 という新しい項目が増えているので、こちらを選択します。

続いてスタックの詳細を設定します。今回スタック名は cfn-git-sync-gitlab としました。次の デプロイファイルをスタック というのは、事前準備の4つ目の条件で上げたdeploymentファイルを指します。今回はここでファイルを新規に作成するため 次のパラメータを使用してファイルを作成し、リポジトリに配置します。 を選択します。

テンプレート定義リポジトリ では、アクセス先のリポジトリの情報を設定します。今回は新規にGitリポジトリとの接続を行うため、 Gitリポジトリをリンクする を選択します。それ以降は使用するCodeStar Connection名やリポジトリ・ブランチなど、必要な値を設定します。

続いてIAMロールの設定です。ここでは事前準備3つ目の条件にあったIAMロールを指定しますが、今回は新規で作成します。

デプロイファイルパラメーターには、CloudFormationの作成で必要なパラメータを指定します。今回は BucketName というパラメータを設定するため、以下のように設定しました。

次に スタックオプションの設定 ですが、ここでIAMロールを指定する必要があります。ここではスタック操作に使用するIAMロールを指定するため、CloudFormationに対する権限を付与したものを使用しました。

以降はデフォルトのまま進め、スタックの作成を行います。すると以下のように、リポジトリとの同期状態やプロビジョニングステータスが表示されます。

この時リソースを見ると GitSyncSetupWaitCondition というリソースが作成されています。

ここでGitLabに移動すると、AWSによって作成されたMerge Request (以降MR) が確認できます。

MRの内容はdeploymentファイルをコミットするものです。

ちなみにこのままの状態で再びCloudFormationのほうを見ると、プロビジョニングステータスが失敗していることを確認できます。

これはMRがマージされていないためにdeploymentファイルが見つからないためです。

GitLabプロジェクトに戻ってMRをマージします。

CloudFormationの画面を見るとリポジトリ同期のステータスやプロビジョニングステータスが変化します。

しばらくするとプロビジョニングに成功しました。

同期イベントを見ると、ChangeSetの作成と実行が行われた様子を確認できます。

リソースを見るとS3バケットが作成されたのを確認できます。

テンプレートファイルの変更と実リソースへの反映

続いてGitlab上のテンプレートに修正を加えてみます。今回はバージョニングを有効にする設定を追加しました。今回は横着してmainブランチに直接コミットしていますが、GitLab CI/CD等と組み合わせてMR作成時に構文チェックなどを走らせるのが安全でしょう。

コミットすると再びリポジトリ同期などのステータスが変化します。

しばらくするとプロビジョニングに成功し、変更も反映されているのを確認できました。

GitLab for Slack appを使用してGitLabのイベントをSlackに通知する

今回はGitLab for Slack appを設定する例を紹介します。

docs.gitlab.com

背景

GitLabとSlackを連携する方法は、現在は GitLab for Slack app の利用が推奨されています。以前は Slack notification も選択肢でしたが、こちらはGitLab 15.9 でdeprecatedとなり、18.0で廃止予定です。

docs.gitlab.com

検証

今回はGitLab SaaS版 (Freeプラン)で slack-notification-test というProjectを使用します。

まずは 設定 から 統合 を選択し、GitLabと統合可能なリストを表示します。

リストの中から GitLab for Slack app を探し、 設定する を選択します。

Slackアプリ用GitLabをインストール... を選択します。

画面が遷移し、Slackワークスペースのログイン画面が表示されます。インストール対象のSlackワークスペースにログインします。

GitLabがSlackにアクセスする権限を要求するので、許可します。

画面が遷移し、GitLab画面に戻ります。これでGitLabとSlackの統合が完了しました。

ここから動作確認をします。

GitLab for Slack app を導入すると、Slackから gitlab コマンドを実行可能になります。試しに /gitlab と入力すると、以下のように表示されます。

利用可能なコマンドはこちらのドキュメントに記載されています。ここでは /gitlab help コマンドを実行した結果を載せておきます。

続いて、GitLabのイベントをSlackに通知するよう設定します。GitLabの画面から 設定 統合 に移動し、 GitLab for Slack app に移動します。移動後の画面ではトリガーや通知設定などが表示されています。

ここではIssueの作成時にSlackに通知するよう設定します。 イシューが作成、更新、または完了されています という項目にチェックを入れると、次に通知先のSlackチャンネルを指定します。ここでは gitlab-test というチャンネルを用意しておき、設定します。

なお、通知先のSlackチャンネルがプライベートチャンネルの場合は、該当のチャンネルにGitLab for Slack appを追加する必要があります。

今回通知設定は特に変更しませんが、以下のような設定項目が並んでいます。特に Notify only boken pipelines は、パイプラインが失敗した時だけ通知を飛ばすので、便利かと思います。

画面下部の テスト設定 を選択すると、Slackとの接続テストを実行するようです。

上記変更後に 変更を保存 を選択し、新しい Issue を作成します。

作成するとSlackの通知が飛ぶことを確認できます。