正式なドキュメントは英語版であり、この日本語訳はAI支援翻訳により作成された参考用のものです。日本語訳の一部の内容は人間によるレビューがまだ行われていないため、翻訳のタイミングにより英語版との間に差異が生じることがあります。最新かつ正確な情報については、英語版をご参照ください。

AWS FargateでGitLab CIをオートスケールする

  • プラン: Free、Premium、Ultimate
  • 提供形態: GitLab.com、GitLab Self-Managed、GitLab Dedicated

Fargateドライバーは、コミュニティでサポートされています。GitLabサポートは、問題のデバッグを支援しますが、保証は提供しません。

カスタムexecutorのGitLabドライバーは、AWS FargateのAmazon Elastic Container Service (ECS) 上でコンテナを自動的に起動し、各GitLab CIジョブを実行します。

このドキュメントのタスクを完了すると、executorはGitLabから開始されたジョブを実行できます。GitLabでコミットが行われるたびに、GitLabインスタンスは新しいジョブが利用可能であることをRunnerに通知します。Runnerは、AWS ECSで設定したタスク定義に基づいて、ターゲットECSクラスターで新しいタスクを開始します。任意のDockerイメージを使用するようにAWS ECSタスク定義を設定できます。このアプローチでは、AWS Fargateで実行できるビルドのタイプを完全に柔軟に設定できます。

GitLab Runner Fargateドライバーアーキテクチャ

このドキュメントでは、実装の最初の理解を深めることを目的とした例を示します。これは本番環境での使用を目的としていません。AWSでは追加のセキュリティが必要です。

たとえば、2つのAWSセキュリティグループが必要になる場合があります:

  • GitLab Runnerをホストし、制限された外部IP範囲(管理アクセス用)からのSSH接続のみを受け入れるEC2インスタンスによって使用されるもの。
  • Fargateタスクに適用され、EC2インスタンスからのSSHトラフィックのみを許可するもの。

パブリックでないコンテナレジストリの場合、ECSタスクには、(AWS ECRのみの)IAM権限または非ECRプライベートレジストリのタスクのプライベートレジストリ認証のいずれかが必要です。

CloudFormationまたはTerraformを使用して、AWSインフラストラクチャのプロビジョニングとセットアップを自動化できます。

CI/CDジョブは、.gitlab-ci.ymlファイルのimage:キーワードの値ではなく、ECSタスクで定義されたイメージを使用します。ECSでは、ECSタスクに使用されるイメージをオーバーライドできません。

この制限を回避するには、次の操作を実行します:

  • Runnerが使用されるすべてのプロジェクトのすべてのビルド依存関係を含むECSタスク定義でイメージを作成して使用します。
  • 異なるイメージを持つ複数のECSタスク定義を作成し、FARGATE_TASK_DEFINITIONCI/CD変数にARNを指定します。
  • 公式のAWS EKS Blueprintsに従って、EKSクラスターの作成を検討してください。

詳細については、GitLab EKS Fargate Runnerを1時間とゼロコードで開始する方法を参照してください。

Fargateはコンテナホストを抽象化するため、コンテナホストのプロパティの設定が制限されます。これは、ディスクまたはネットワークへの高いIOを必要とするRunnerワークロードに影響します。これらのプロパティは、Fargateでの設定が制限されているか、または設定できないためです。FargateでGitLab Runnerを使用する前に、CPU、メモリ、ディスクIO、またはネットワークIOで高いコンピューティング特性を持つRunnerワークロードがFargateに適していることを確認してください。

前提要件

始める前に、以下が必要です:

  • EC2、ECS、およびECRリソースを作成および設定するための権限を持つAWS IAMユーザー。
  • AWS VPCとサブネット。
  • 1つ以上のAWSセキュリティグループ。

ステップ1: AWS Fargateタスク用のコンテナイメージを準備する

コンテナイメージを準備します。このイメージをレジストリにアップロードできます。これは、GitLabジョブの実行時にコンテナの作成に使用できます。

  1. イメージに、CIジョブのビルドに必要なツールがあることを確認します。たとえば、JavaプロジェクトにはJava JDKや、MavenやGradleなどのビルドツールが必要です。Node.jsプロジェクトには、nodenpmが必要です。
  2. イメージに、アーティファクトとキャッシュを処理するGitLab Runnerがあることを確認してください。詳細については、カスタムexecutorドキュメントの実行ステージセクションを参照してください。
  3. コンテナイメージが公開キー認証を介してSSH接続を受け入れることができることを確認します。Runnerは、この接続を使用して、.gitlab-ci.ymlファイルで定義されたビルドコマンドをAWS Fargate上のコンテナに送信します。SSHキーは、Fargateドライバーによって自動的に管理されます。コンテナは、SSH_PUBLIC_KEY環境変数からのキーを受け入れることができる必要があります。

GitLab RunnerとSSH設定を含むDebianの例をご覧ください。Node.jsの例をご覧ください。

ステップ2: コンテナイメージをレジストリにプッシュする

イメージを作成したら、ECSタスク定義で使用するために、イメージをコンテナレジストリに公開します。

  • リポジトリを作成してイメージをECRにプッシュするには、Amazon ECRリポジトリドキュメントに従ってください。
  • AWS CLIを使用してイメージをECRにプッシュするには、AWS CLIを使用したAmazon ECRの開始方法ドキュメントに従ってください。
  • GitLabコンテナレジストリを使用するには、DebianまたはNodeJSの例を使用できます。Debianイメージは、registry.gitlab.com/tmaczukin-test-projects/fargate-driver-debian:latestに公開されています。NodeJSのイメージ例は、registry.gitlab.com/aws-fargate-driver-demo/docker-nodejs-gitlab-ci-fargate:latestに公開されています。

ステップ3: GitLab Runner用のEC2インスタンスを作成する

次に、AWS EC2インスタンスを作成します。次のステップでは、GitLab Runnerをインストールします。

  1. https://console.aws.amazon.com/ec2/v2/home#LaunchInstanceWizardにアクセスします。
  2. インスタンスの場合は、Ubuntu Server 18.04 LTS AMIを選択します。選択したAWSリージョンによっては、名前が異なる場合があります。
  3. インスタンスタイプの場合は、t2.microを選択します。次へ: インスタンスの詳細を選択します。
  4. Number of instances(インスタンス数)のデフォルトのままにします。
  5. ネットワークの場合は、VPCを選択します。
  6. Auto-assign Public IP(パブリックIPの自動割り当て)を有効に設定します。
  7. IAM role(IAMロール)で、Create new IAM role(新しいIAMロールを作成)を選択します。このロールはテスト目的のみのものであり、安全ではありません。
    1. ロールを作成するを選択します。
    2. AWS service(AWSサービス)を選択し、Common use cases(一般的なユースケース)でEC2(EC2)を選択します。次に、次へ: 権限を選択します。
    3. AmazonECS_FullAccess(AmazonECS_FullAccess)ポリシーのチェックボックスをオンにします。次へ: タグを選択します。
    4. 次へ: レビューを選択します。
    5. IAMロールの名前(例:fargate-test-instance)を入力し、ロールを作成するを選択します。
  8. インスタンスを作成しているブラウザータブに戻ります。
  9. Create new IAM role(新しいIAMロールを作成)の左側にある更新ボタンを選択します。fargate-test-instanceロールを選択します。次へ: ストレージの追加を選択します。
  10. 次へ: タグの追加を選択します。
  11. 次へ: セキュリティグループを選択します。
  12. Create a new security group(新しいセキュリティグループを作成)を選択し、fargate-testという名前を付け、SSHのルールが定義されていることを確認します(Type: SSH, Protocol: TCP, Port Range: 22)。インバウンドルールとアウトバウンドルールのIP範囲を指定する必要があります。
  13. Review and Launch(レビューと起動)を選択します。
  14. Launch(起動)を選択します。
  15. (オプション)Create a new key pair(新しいキーペアを作成)を選択し、fargate-runner-managerという名前を付けて、Download Key Pair(キーペアをダウンロード)を選択します。SSHのプライベートキーがコンピューターにダウンロードされます(ブラウザーで設定されているディレクトリを確認してください)。
  16. Launch Instances(インスタンスを起動)を選択します。
  17. View Instances(インスタンスを表示)を選択します。
  18. インスタンスが起動するまで待ちます。IPv4 Public IPアドレスを書き留めます。

ステップ4: EC2インスタンスにGitLab Runnerをインストールして設定する

UbuntuインスタンスにGitLab Runnerをインストールします。

  1. GitLabプロジェクトの設定 > CI/CDに移動し、Runnerセクションを展開します。Set up a specific Runner manually(特定Runnerを手動でセットアップ)で、登録トークンを書き留めます。

  2. chmod 400 path/to/downloaded/key/fileを実行して、キーファイルに適切な権限があることを確認します。

  3. 次を使用して、作成したEC2インスタンスにSSHで接続します:

    ssh ubuntu@[ip_address] -i path/to/downloaded/key/file
  4. 正常に接続されたら、次のコマンドを実行します:

    sudo mkdir -p /opt/gitlab-runner/{metadata,builds,cache}
    curl -s "https://packages.gitlab.com/install/repositories/runner/gitlab-runner/script.deb.sh" | sudo bash
    sudo apt install gitlab-runner
  5. ステップ1でメモしたGitLab URLと登録トークンを使用して、このコマンドを実行します。

    sudo gitlab-runner register --url "https://gitlab.com/" --registration-token TOKEN_HERE --name fargate-test-runner --run-untagged --executor custom -n
  6. sudo vim /etc/gitlab-runner/config.tomlを実行し、次のコンテンツを追加します:

    concurrent = 1
    check_interval = 0
    
    [session_server]
      session_timeout = 1800
    
    [[runners]]
      name = "fargate-test"
      url = "https://gitlab.com/"
      token = "__REDACTED__"
      executor = "custom"
      builds_dir = "/opt/gitlab-runner/builds"
      cache_dir = "/opt/gitlab-runner/cache"
      [runners.custom]
        volumes = ["/cache", "/path/to-ca-cert-dir/ca.crt:/etc/gitlab-runner/certs/ca.crt:ro"]
        config_exec = "/opt/gitlab-runner/fargate"
        config_args = ["--config", "/etc/gitlab-runner/fargate.toml", "custom", "config"]
        prepare_exec = "/opt/gitlab-runner/fargate"
        prepare_args = ["--config", "/etc/gitlab-runner/fargate.toml", "custom", "prepare"]
        run_exec = "/opt/gitlab-runner/fargate"
        run_args = ["--config", "/etc/gitlab-runner/fargate.toml", "custom", "run"]
        cleanup_exec = "/opt/gitlab-runner/fargate"
        cleanup_args = ["--config", "/etc/gitlab-runner/fargate.toml", "custom", "cleanup"]
  7. プライベートCAを持つGitLab Self-Managedインスタンスがある場合は、次の行を追加します:

           volumes = ["/cache", "/path/to-ca-cert-dir/ca.crt:/etc/gitlab-runner/certs/ca.crt:ro"]

    証明書の信頼の詳細

    以下に示すconfig.tomlファイルのセクションは、登録コマンドによって作成されます。変更しないでください。

    concurrent = 1
    check_interval = 0
    
    [session_server]
      session_timeout = 1800
    
    name = "fargate-test"
    url = "https://gitlab.com/"
    token = "__REDACTED__"
    executor = "custom"
  8. sudo vim /etc/gitlab-runner/fargate.tomlを実行し、次のコンテンツを追加します:

    LogLevel = "info"
    LogFormat = "text"
    
    [Fargate]
      Cluster = "test-cluster"
      Region = "us-east-2"
      Subnet = "subnet-xxxxxx"
      SecurityGroup = "sg-xxxxxxxxxxxxx"
      TaskDefinition = "test-task:1"
      EnablePublicIP = true
    
    [TaskMetadata]
      Directory = "/opt/gitlab-runner/metadata"
    
    [SSH]
      Username = "root"
      Port = 22
    • Clusterの値とTaskDefinitionタスク定義の名前を書き留めます。この例は、リビジョン番号として:1を持つtest-taskを示しています。リビジョン番号が指定されていない場合は、最新のactive(アクティブ)なリビジョンが使用されます。

    • リージョンを選択します。RunnerマネージャーインスタンスからSubnetの値を取得します。

    • セキュリティグループIDを見つけるには:

      1. AWSで、インスタンスのリストで、作成したEC2インスタンスを選択します。詳細が表示されます。
      2. Security groups(セキュリティグループ)で、作成したグループの名前を選択します。
      3. Security group ID(セキュリティグループID) をコピーします。

      本番環境では、セキュリティグループのセットアップと使用に関するAWSガイドラインに従ってください。

    • EnablePublicIPがtrueに設定されている場合、SSH接続を実行するためにタスクコンテナのパブリックIPが収集されます。

    • EnablePublicIPがfalseに設定されている場合:

      • Fargateドライバーは、タスクコンテナのプライベートIPを使用します。falseに設定されているときに接続をセットアップするには、ソースがVPC CIDRである場合、VPCセキュリティグループにポート22(SSH)のインバウンドルールが必要です。
      • 外部依存関係をフェッチするには、プロビジョニングされたAWS Fargateコンテナがパブリックインターネットにアクセスできる必要があります。AWS Fargateコンテナにパブリックインターネットアクセスを提供するには、VPCでNATゲートウェイを使用できます。
    • SSHサーバーのポート番号はオプションです。省略した場合、デフォルトのSSHポート(22)が使用されます。

    • セクション設定の詳細については、Fargateドライバードキュメントを参照してください。

  9. Fargateドライバーをインストールします:

    sudo curl -Lo /opt/gitlab-runner/fargate "https://gitlab-runner-custom-fargate-downloads.s3.amazonaws.com/latest/fargate-linux-amd64"
    sudo chmod +x /opt/gitlab-runner/fargate

ステップ5: ECS Fargateクラスターを作成する

Amazon ECSクラスターは、ECSコンテナインスタンスのグループです。

  1. https://console.aws.amazon.com/ecs/home#/clustersに移動します。
  2. Create Cluster(クラスターの作成)を選択します。
  3. Networking only(ネットワークのみ)タイプを選択します。次のステップを選択します。
  4. fargate.tomlの場合と同じtest-clusterという名前を付けます。
  5. 作成を選択します。
  6. View cluster(クラスターの表示)を選択します。Cluster ARNの値から、リージョンとアカウントIDの部分を書き留めます。
  7. Update Cluster(クラスターの更新)を選択します。
  8. Default capacity provider strategyの横にあるAdd another provider(別のプロバイダーを追加)を選択し、FARGATEを選択します。更新を選択します。

ECS Fargateでのクラスターのセットアップと操作の詳細については、AWSドキュメントを参照してください。

ステップ6: ECSタスク定義を作成する

このステップでは、タイプFargateのタスク定義を作成し、CIビルドに使用できるコンテナイメージを参照します。

  1. https://console.aws.amazon.com/ecs/home#/taskDefinitionsに移動します。
  2. Create new Task Definition(新しいタスク定義を作成)を選択します。
  3. FARGATE(FARGATE)を選択し、次のステップを選択します。
  4. 名前をtest-taskにします。注: 名前はfargate.tomlファイルで定義されているのと同じ値ですが、:1はありません)。
  5. Task memory (GB)(タスク)メモリ(GB)およびTask CPU (vCPU)(タスクCPU(vCPU))の値を選択します。
  6. Add container(コンテナの追加)を選択します。次に:
    1. ci-coordinatorという名前を付けます。これにより、FargateドライバーがSSH_PUBLIC_KEY環境変数を挿入できます。
    2. イメージを定義します(例:registry.gitlab.com/tmaczukin-test-projects/fargate-driver-debian:latest)。
    3. 22/TCPのポートマッピングを定義します。
    4. 追加を選択します。
  7. 作成を選択します。
  8. View task definition(タスク定義を表示)を選択します。

1つのFargateタスクで、複数のコンテナを起動できます。Fargateドライバーは、ci-coordinator名を持つコンテナにのみSSH_PUBLIC_KEY環境変数を挿入します。Fargateドライバーで使用されるすべてのタスク定義に、この名前のコンテナが必要です。この名前を持つコンテナは、上記のように、SSHサーバーとすべてのGitLab Runner要件がインストールされているものである必要があります。

タスク定義のセットアップと操作の詳細については、AWSドキュメントを参照してください。

AWS ECRからイメージを起動するために必要なECSサービス権限については、Amazon ECSタスク実行IAMロールを参照してください。

GitLabインスタンスでホストされているものを含む、プライベートレジストリへのECS認証については、タスクのプライベートレジストリ認証を参照してください。

この時点で、RunnerマネージャーとFargateドライバーが設定され、AWS Fargateでジョブの実行を開始する準備ができました。

ステップ7: 設定をテストします

構成が使用できる状態になりました。

  1. GitLabプロジェクトで、.gitlab-ci.ymlファイルを作成します:

    test:
      script:
        - echo "It works!"
        - for i in $(seq 1 30); do echo "."; sleep 1; done
  2. プロジェクトのCI/CD > パイプラインに移動します。

  3. Run Pipeline(パイプラインの実行)を選択します。

  4. ブランチと変数を更新し、Run Pipeline(パイプラインの実行)を選択します。

.gitlab-ci.ymlファイルのimageおよびserviceキーワードは無視されます。Runnerは、タスク定義で指定された値のみを使用します。

クリーンアップ

AWS Fargateでカスタムexecutorをテストした後でクリーンアップを実行する場合は、次のオブジェクトを削除します:

  • ステップ3で作成されたEC2インスタンス、キーペア、IAMロール、およびセキュリティグループ。
  • ステップ5で作成されたECS Fargateクラスター。
  • ステップ6で作成されたECSタスク定義。

プライベートAWS Fargateタスクを構成する

高度なセキュリティを確保するために、プライベートAWS Fargateタスクを構成します。この構成では、executorは内部AWS IPアドレスのみを使用します。CI/CDジョブがプライベートAWS Fargateインスタンスで実行されるように、AWSからの送信トラフィックのみを許可します。

プライベートAWS Fargateタスクを構成するには、次の手順に従ってAWSを構成し、プライベートサブネットでAWS Fargateタスクを実行します:

  1. 既存のパブリックサブネットが、VPCアドレス範囲内のすべてのIPアドレスを予約していないことを確認します。VPCとサブネットのcirdアドレス範囲を調べます。サブネットcirdアドレス範囲がVPC cirdアドレス範囲のサブセットである場合は、手順2と4をスキップします。それ以外の場合、VPCに使用可能なアドレス範囲がないため、VPCとパブリックサブネットを削除して再作成する必要があります:

    1. 既存のサブネットとVPCを削除します。
    2. 削除したVPCと同じ構成でVPCを作成し、cirdアドレス(例:10.0.0.0/23)を更新します。
    3. 削除したサブネットと同じ構成でパブリックサブネットを作成します。VPCアドレス範囲のサブセットであるcirdアドレス(例:10.0.0.0/24)を使用します。
  2. パブリックサブネットと同じ構成でプライベートサブネットを作成します。パブリックサブネット範囲とオーバーラップしないcirdアドレス範囲(例:10.0.1.0/24)を使用します。

  3. NATゲートウェイを作成し、パブリックサブネット内に配置します。

  4. 宛先0.0.0.0/0がNATゲートウェイを指すように、プライベートサブルートテーブルを変更します。

  5. farget.tomlの設定をタグ付けします:

    Subnet = "private-subnet-id"
    EnablePublicIP = false
    UsePublicIP = false
  6. 次のインラインポリシーを、Fargateタスクに関連付けられたIAMロールに追加します(Fargateタスクに関連付けられたIAMロールは通常ecsTaskExecutionRoleという名前で、既に存在しているはずです)。

    {
        "Statement": [
            {
                "Sid": "VisualEditor0",
                "Effect": "Allow",
                "Action": [
                    "secretsmanager:GetSecretValue",
                    "kms:Decrypt",
                    "ssm:GetParameters"
                ],
                "Resource": [
                    "arn:aws:secretsmanager:*:<account-id>:secret:*",
                    "arn:aws:kms:*:<account-id>:key/*"
                ]
            }
        ]
    }
  7. セキュリティグループの「受信ルール」を変更して、セキュリティグループ自体を参照するようにします。AWS構成ダイアログで:

    • Typesshに設定します。
    • SourceCustomに設定します。
    • セキュリティグループを選択します。
    • すべてのホストからのSSHアクセスを許可する既存の受信ルールを削除します。

既存の受信ルールを削除すると、SSHを使用してAmazon Elastic Compute Cloudインスタンスに接続できなくなります。

詳細については、次のAWSドキュメントを参照してください:

トラブルシューティング

構成のテスト中にNo Container Instances were found in your clusterエラーが発生しました

error="starting new Fargate task: running new task on Fargate: error starting AWS Fargate Task: InvalidParameterException: No Container Instances were found in your cluster."

AWS Fargateドライバーでは、ECSクラスターがデフォルトのキャパシティープロバイダー戦略で構成されている必要があります。

さらに詳しく:

  • デフォルトのキャパシティープロバイダー戦略は、各Amazon ECSクラスターに関連付けられています。他のキャパシティープロバイダー戦略または起動タイプが指定されていない場合、タスクの実行時またはサービスの作成時に、クラスターはこの戦略を使用します。
  • capacityProviderStrategyが指定されている場合、launchTypeパラメータは省略する必要があります。capacityProviderStrategyまたはlaunchTypeが指定されていない場合は、クラスターのdefaultCapacityProviderStrategyが使用されます。

ジョブの実行中にメタデータfile does not existエラーが発生しました

Application execution failed PID=xxxxx error="obtaining information about the running task: trying to access file \"/opt/gitlab-runner/metadata/<runner_token>-xxxxx.json\": file does not exist" cleanup_std=err job=xxxxx project=xx runner=<runner_token>

IAMロールポリシーが正しく構成されており、/opt/gitlab-runner/metadata/にメタデータJSONファイルを作成する書き込み操作を実行できることを確認してください。本番環境以外の環境でテストするには、AmazonECS_FullAccessポリシーを使用します。組織のセキュリティ要件に従って、IAMロールポリシーをレビューしてください。

ジョブの実行中にconnection timed outが発生しました

Application execution failed PID=xxxx error="executing the script on the remote host: executing script on container with IP \"172.x.x.x\": connecting to server: connecting to server \"172.x.x.x:22\" as user \"root\": dial tcp 172.x.x.x:22: connect: connection timed out"

EnablePublicIPがfalseに構成されている場合は、VPCセキュリティグループに、SSH接続を許可する受信ルールがあることを確認してください。AWS Fargateタスクコンテナは、GitLab Runner EC2インスタンスからのSSHトラフィックを受け入れる必要があります。

ジョブの実行中にconnection refusedが発生しました

Application execution failed PID=xxxx error="executing the script on the remote host: executing script on container with IP \"10.x.x.x\": connecting to server: connecting to server \"10.x.x.x:22\" as user \"root\": dial tcp 10.x.x.x:22: connect: connection refused"

タスクコンテナの22番ポートが公開されており、ステップ6の指示に基づいてポートマッピングが構成されていることを確認してください: ECSタスク定義を作成します。ポートが公開され、コンテナが構成されている場合:

  1. Amazon ECS > Clusters > Choose your task definition > Tasks(Amazon ECS > クラスター > タスク定義を選択 > タスク)で、コンテナのエラーがあるかどうかを確認します。
  2. Stoppedのステータスのタスクを表示し、失敗した最新のタスクを確認します。logs(ログ)タブには、コンテナに障害が発生した場合の詳細が表示されます。

または、ローカルでDockerコンテナを実行できることを確認してください。

ジョブの実行中にssh: handshake failed: ssh: unable to authenticate, attempted methods [none publickey], no supported methods remainが発生しました

サポートされていないキータイプが、古いバージョンのAWS Fargateドライバーが原因で使用されている場合、次のエラーが発生します。

Application execution failed PID=xxxx error="executing the script on the remote host: executing script on container with IP \"172.x.x.x\": connecting to server: connecting to server \"172.x.x.x:22\" as user \"root\": ssh: handshake failed: ssh: unable to authenticate, attempted methods [none publickey], no supported methods remain"

この問題を解決するには、最新のAWS FargateドライバーをGitLab Runner EC2インスタンスにインストールします:

sudo curl -Lo /opt/gitlab-runner/fargate "https://gitlab-runner-custom-fargate-downloads.s3.amazonaws.com/latest/fargate-linux-amd64"
sudo chmod +x /opt/gitlab-runner/fargate