AWS Fargateを使ってTerraformを用いてサーバレスGitLabランナーをデプロイする

セキュアでスケーラブルなサーバレスGitLabランナーの完全なセットアップをTerraform IACを通してAWS Fargateで実現します。

(画像: アーキテクチャの概要)

私の組織でGitLabが広く使われるようになるにつれて、セキュリティの高いスケーラブルで効率的なCICDランナー隊を管理する方法が必要です。AWS Fargateのサーバレスセットアップは、そのシンプルなインフラ管理と労力のかからないスケーラビリティで魅力的です。

この投稿では、どんな環境でも容易に再現可能なセットアップのためにTerraformを用いて管理されるAWS Fargate上のサーバレスGitLabランナー隊の設定を共有します。

背景

GitLabには'AWS Fargate上でのGitLab CIのオートスケーリング'についてのガイドがありますが、ランナーマネージャーがEC2上にホストされるため、完全なサーバレスではありません。他の人々はマネージャーとワーカーの両方に対してFargateの完全なECSセットアップを共有しています。例として、Daniel Coutinho de Mirandaの'サーバレスGitLab CI/CD on AWS Fargate'や、Damiano Giorgiの'AWS上でのGitLab連携のためのサーバレス手法'があります。

しかし、これらの例は手動の設定に大きく依存しており、EC2/Fargateタスクで1つのマネージャープロファイルのみをサポートしています。

この投稿で説明されているセットアップの主な動機は以下の通りです:

  • より安全で再現可能かつ設定可能なセットアップのためにTerraformインフラ・アズ・コードを使用する。

  • Fargateインスタンス上で一つのマネージャープロファイルを複数サポートする。

アーキテクチャ概要

主要要素:

  • ECSサービスはAWS Fargate上でマネージャーサービスをホストし、このサービスは複数のランナーマネージャーを登録できます。

  • 各ランナーマネージャーは、このECSサービスの下で新しいECSタスクが作成されると、GitLabに自身を登録します。

  • GitLabからジョブがトリガーされると、適切なランナーマネージャーがジョブを割り当てられ、特定のサブネットとセキュリティグループで新しいワーカーECSタスクを実行してジョブを行います。

  • 各ワーカータスクは、事前定義された役割とコンテナイメージを持っています。

デプロイメントプロセス

ステップ 1:マネージャー用のコンテナイメージをビルドして公開する

コード:https://github.com/GovTechSG/fargate-gitlab-runner

  1. IMAGE_NAME, IMAGE_TAG, AWS_ACCOUNT_ID, AWS_REGIONの環境変数を希望する値に設定します。IMAGE_NAMEは後でECRに公開するためにECRドメイン${AWS_ACCOUNT_ID}.dkr.ecr.${AWS_REGION}.amazonaws.comで始める必要があります。

  2. docker-compose buildを使ってイメージをビルドします。またはdocker build -t ${IMAGE_NAME}:${IMAGE_TAG}を実行します。

  3. ECRにログインします。
    aws ecr get-login-password --region ${AWS_REGION} | docker login --username AWS --password-stdin ${AWS_ACCOUNT_ID}.dkr.ecr.${AWS_REGION}.amazonaws.com

  4. docker-compose pushまたはdocker push ${IMAGE_NAME}:${IMAGE_TAG}を使ってECRに公開します。

    この単一イメージはMANAGERS_CONFIGS環境変数からプロファイルを使用して複数のマネージャーを生成します。
    dockerの代わりにpodmanを使用する場合は、podman-composeもインストールしてください。

ステップ 2:ワーカータスク用のコンテナイメージをビルドして公開する

サンプルイメージはhttps://github.com/GovTechSG/fargate-gitlab-runner-workerにあります。

  1. 必要に応じてDockerfileを更新して、新たなツールをインストールします。

  2. 上記のマネージャーイメージの手順を使用して、イメージをビルドしECRに公開します。

ステップ 3:ECSサービスが必要とする追加のリソースを設定する

次のリソースは手動で設定するか、別のTerraformコードセット(推奨)を使って設定する必要があります:

  1. GitLabトークンのシークレット:GitLabからランナー用のトークンを取得し、AWSシークレットマネージャーまたはシステムマネージャーパラメータストアで新しいシークレットを作成します。使用するKMSキーに注意してください。

  2. マネージャーやワーカー両方に必要なVPC、サブネット、セキュリティグループ。マネージャーとワーカーのネットワークACLおよびセキュリティグループ間でポート22を介したSSH通信が許可されることに注意してください。

  3. ワーカータスクがAWSサービスにアクセスするために必要な適切なポリシーを持つワーカーECSタスクの役割。

ステップ 4:Terragruntを使ってECSサービスをデプロイする

コード:https://github.com/GovTechSG/fargate-gitlab-runner-terraform

  1. environments/sample-devからenvironments/<env>にコピーし、<env>を希望する環境名に置き換えます。

  2. environments/<env>/env_inputs.hclで環境設定を更新します。

  3. マネージャーやワーカーにECSクラスタがまだ無い場合は、environments/<env>/ecs-cluster-for-managersenvironments/<env>/ecs-cluster-for-workersのサンプルに従って新しいクラスタを作成します。

  4. environments/<env>/ecs-fargate-gitlab-runner-service/terragrunt.hclの変数値をあなたの環境に合わせて更新します。

  5. cd environments/<env>/ecs-fargate-gitlab-runner-service && terragrunt applyを使用してECSサービスをデプロイします。

  6. 数分後、ECSサービスのログを見直し、GitLabで新しいランナーが登録されたことを確認します。ランナーの数はmanager_instance_count * length(keys(managers_configs))の数と等しくなるはずです。

  7. あなた自身のジョブやこのようなシンプルなスクリプトで新しいGitLabランナーをテストします:

    test:
      tags:
        - dev
        - tool1
      script:
        - echo "It works!"
        - for i in $(seq 1 30); do echo "."; sleep 1; done

フルスクリーンモードを終了する

成功すれば、このような出力が得られます:

(画像: 成功した実行の出力)

簡単なテスト用にTerragruntの代わりにTerraformを使用したい場合は:

  • terraform_modules/ecs-fargate-gitlab-runner-serviceフォルダにenvironments/<env>/ecs-fargate-gitlab-runner-service/terragrunt.hclにあるすべての変数値を含むterraform.tfvarsファイルを作成します。
  • environments/terragrunt.hclからversions.tfprovider.tfの内容をterraform_modules/ecs-fargate-gitlab-runner-serviceのそれぞれのファイルにコピーします。
  • 最後にterraform applyを実行します。

Terraformによって作成されたリソース

Terraformモジュールによって作成された主要なリソースは以下の通りです:

  • マネージャーコンテナ用のECSサービス

  • 必要な環境変数を備えたそのコンテナ定義

  • ワーカーECSタスク定義を用いて新しいECSタスクを実行および停止することを許可するそのIAMロール

  • managers_configsにある各マネージャープロファイルに対して1つずつ、ワーカーECSタスク定義

何が起こったのか?

登録中:

  1. TerraformでECSサービスがデプロイされると、マネージャーコンテナイメージをホストするタスクを作成します。

  2. このコンテナが起動すると、環境変数を読み込み(特にMANAGERS_CONFIGS変数)、それぞれのマネージャーをGitLabにランナーとして登録し、GitLabランナーの完全なconfig.tomlファイルと個々のランナーのfargate_worker.tomlファイルを生成します。これにはワーカーのセキュリティ、ネットワーク設定とタスク定義が含まれます。

GitLabのランナー一覧には、MANAGERS_CONFIGSのキーごとに1つのランナー(manager_instance_countが1と仮定する)が表示されるはずです。

(画像: GitLab UI内のランナー)

ジョブ実行:

  1. そのランナーのタグリストのサブセットに一致するタグを持つジョブがトリガーされると、GitLabはジョブを適切なランナーマネージャーに送ります。

  2. ランナーマネージャーは、MANAGERS_CONFIGSで渡されたタスク定義を使用して新しいワーカーECSタスクを開始し、プロセスでそのSSH_PUBLIC_KEYを環境変数として追加します。

  3. ワーカーECSタスクは、マネージャーのSSH_PUBLIC_KEYauthorized_keysに追加された状態でsshdプロセス付きで開始されます。

  4. 最後に、マネージャーはsshを介してワーカーECSタスクでジョブを実行し、ジョブの出力とステータスが通常どおりGitLabに共有されます。

トラブルシューティング

以下は発生可能ないくつかの問題です:

クラスタにコンテナインスタンスが見つからないため、Fargateタスクを開始できないというエラー

  • ワーカー用のECSクラスタをチェックし、Default capacity provider strategyFARGATEに設定されていることを確認してください。

マネージャーがECSに接続してタスクを開始できない

  • マネージャーがプライベートサブネット内にホストされている場合、VPCエンドポイントをECSおよびECR用に作成し、マネージャーがそれらにアクセスできることを確認してください。

マネージャーがssh経由でワーカーECSタスクに接続できない

  • ワーカーコンテナイメージにopensshがインストールされていて、SSH_PUBLIC_KEYが正しいユーザーの~/.ssh/authorized_keysに追加されていることを確認してください。

  • マネージャーとワーカーのサブネットとセキュリティグループがポート22での通信を許可していることを確認してください。VPC Reachability Analyzerを使用して確認してください。

  • エラーがsignature algorithm ssh-rsa not in PubkeyAcceptedAlgorithmsの場合は、ワーカーコンテナイメージにRUN echo “PubkeyAcceptedKeyTypes +ssh-rsa” >> /etc/ssh/sshd_configを追加してssh-rsaを有効にします。

ワーカーECSタスクがAWSへのアクセス資格情報を持っていない

  • AWS_CONTAINER_CREDENTIALS_RELATIVE_URI変数をSSHセッションに共有するために、sshd実行にこれを追加します:-o "SetEnv=AWS_CONTAINER_CREDENTIALS_RELATIVE_URI=\"$AWS_CONTAINER_CREDENTIALS_RELATIVE_URI\""

既知の制限:

結論

AWS Fargateを使用してGitLabのサーバレスランナーの完全なセットを設定しました。Terraformを使用することで、さまざまな環境に簡単に設定できます。

さらに、さまざまなツールがインストールされたワーカーイメージの配列、コストを最適化するために

こちらの記事はdev.toの良い記事を日本人向けに翻訳しています。
https://dev.to/aws-builders/deploying-serverless-gitlab-runners-on-aws-fargate-with-terraform-898