GitLab CIでのTestcontainersテストの実行

Testcontainers は、エフェメラルDockerコンテナを使用して、データベース、メッセージキュー、検索エンジンなどの依存関係でテストを実行できるようにするテストライブラリです。 Testcontainersは、プログラム可能なAPIを使用してDockerコンテナのライフサイクルを管理し、必要なアプリケーションの依存関係の設定をより細かく制御できます。

GitLab は、人気のあるGitベースのソースコード管理(SCM)プラットフォームです。 GitLabは、SCMプラットフォームであるだけでなく、CI/CD、問題管理、コードレビューの機能を提供します。

この記事では、GitLab CI PlatformでTestcontainersベースのテストを実行する方法について説明します。 デフォルトのコンテナ化されたランナーにDocker環境がないという問題は、Docker-in-Dockerパターンを使用して解決し、Testcontainers Cloudで実行するようにテストを構成することで、セットアップを簡素化し、実行を高速化します。

ここでは、 Java/SpringBoot アプリケーションの例を使用します。これは、GitHub で見つけることができますので、詳しく見ていきましょう。

gitlab ciでtestcontainersテストを実行しているバナー

GitLab CI パイプラインのセットアップ

  • GitLab で新しいリポジトリを作成するか、 Import Project 機能を使用して既存のプロジェクトを GitLab にインポートします。
  • リポジトリのルートに .gitlab-ci.yml ファイルを作成し、次のようにジョブ「test」を含むパイプラインを定義します。
variables:
MAVEN_OPTS: >-
  -Dhttps.protocols=TLSv1.2
  -Dmaven.repo.local=$CI_PROJECT_DIR/.m2/repository
  -Dorg.slf4j.simpleLogger.showDateTime=true
  -Djava.awt.headless=true
MAVEN_CLI_OPTS: >-
  --batch-mode
  --errors
  --fail-at-end
  --show-version
  --no-transfer-progress
image: maven:3-eclipse-temurin-19
cache:
paths:
  - .m2/repository
test:
stage: test
script:
  - 'mvn $MAVEN_CLI_OPTS verify'

Docker コンテナベースの Executor を使用し、Maven を使用してテストを実行する test ステージ内で test ジョブを定義しました。次に、変更をコミットしてリポジトリにプッシュします。

パイプラインは自動的にトリガーされ、次のエラーで失敗します: 有効な Docker 環境が見つかりませんでした。

06:26:06.194 [testcontainers-lifecycle-0] INFO  org.testcontainers.dockerclient.DockerMachineClientProviderStrategy - docker-machine executable was not found on PATH ([/opt/java/openjdk/bin, /usr/local/sbin, /usr/local/bin, /usr/sbin, /usr/bin, /sbin, /bin])
06:26:06.198 [testcontainers-lifecycle-0] ERROR org.testcontainers.dockerclient.DockerClientProviderStrategy - Could not find a valid Docker environment. Please check configuration. Attempted configurations were:
   UnixSocketClientProviderStrategy: failed with exception InvalidConfigurationException (Could not find unix domain socket). Root cause NoSuchFileException (/var/run/docker.sock)As no valid configuration was found, execution cannot continue.
See https://www.testcontainers.org/on_failure.html for more details.

Executor で Docker 環境を使用できないため、Testcontainers ベースのテストが失敗しました。 この問題を解決するために、 Docker-in-Docker(DinD)アプローチ を使用して、エグゼキューター内にDocker環境を提供できます。

Docker-in-Docker の使用

.gitlab-ci.yml ファイルを編集して Docker-in-Docker サービス (docker:dind) を含め、DOCKER_HOST 変数を tcp://docker:2375 に、DOCKER_TLS_CERTDIR を空の文字列に設定します。

services:
- name: docker:dind
  command: ["--tls=false"]
variables:
DOCKER_HOST: "tcp://docker:2375"
DOCKER_TLS_CERTDIR: ""
DOCKER_DRIVER: overlay2
MAVEN_OPTS: >-
  -Dhttps.protocols=TLSv1.2
  -Dmaven.repo.local=$CI_PROJECT_DIR/.m2/repository
  -Dorg.slf4j.simpleLogger.showDateTime=true
  -Djava.awt.headless=true
MAVEN_CLI_OPTS: >-
  --batch-mode
  --errors
  --fail-at-end
  --show-version
  --no-transfer-progress
image: maven:3-eclipse-temurin-19
cache:
paths:
  - .m2/repository
test:
stage: test
script:
  - 'mvn $MAVEN_CLI_OPTS verify'

パイプラインが実行され、テストが正常に実行されます (図 1)。

テストコンテナのスクリーンショットは、 13の合計時間を含むテスト結果を示しています:03 分。
図 1: テスト結果が成功しました。

ただし、DinDの実行は必ずしも良い方法ではありません:セットアップは複雑で間違えやすいため、特にコンテナの実行が負荷の大部分を占める場合は、パフォーマンスの問題につながることがよくあります。

そこで登場するのが Testcontainers Cloud で、Testcontainersベースのテストをよりシンプルかつ確実に実行しやすくします。 

Testcontainers Cloudを使用すると、ランナーにDockerデーモンをインストールする必要がなくなります。 また、コンテナはオンデマンドのクラウド環境で実行されるため、ビルドに高CPU/メモリの強力なCIワーカーを使用する必要はありません。

最小限のセットアップでTestcontainers Cloudを使用し、Testcontainersベースのテストを実行する方法を見てみましょう。

Testcontainers クラウドベースのセットアップ

ここでは、GitLabパイプラインに Testcontainers Cloud を設定する方法をご紹介します。 一般に、Testcontainers Cloud の CI 構成には 2 つのことが必要です。 

1 つ目は、ユーザー空間で実行され、特別な権限を必要とせず、実際の "実行中のテスト" ステップの一部としてダウンロードできるほど小さいエージェント アプリです。 

2つ目の要件は、エージェントがTestcontainers Cloud環境を使用するようにアクセストークンを設定することです。

  1. https://app.testcontainers.cloud/signup でTestcontainers Cloudアカウントにサインアップします。
  2. ログインしたら、組織を作成します。
  3. Testcontainers Cloud ダッシュボードに移動し、サービス アカウントを生成します (図 2)。
新しいサービスアカウントを作成するためのページのスクリーンショットで、アクセストークンを再度表示できなくなるため、アクセストークンをコピーするようにリマインダーが表示されます。
図 2: 新しいサービスアカウントを作成します。

次に、 TC_CLOUD_TOKEN を環境変数として設定する必要があります。

  • プロジェクトの [設定] > [CI/CD ] に移動し、[ 変数 ] セクションを展開します。
  • [ 変数の追加 ] を選択し、詳細を入力します。
    • キー: TC_CLOUD_TOKEN
    • 値: Service Account Access Tokenを入力します。
    • タイプ: Variable
    • チェックボックス Mask Variable オンにします。
    • 「変数の追加」を選択します。

次に、 .gitlab-ci.yml を更新してDinD設定を削除し、次のようにTestcontainersCloudエージェントをインストールします。

variables:
MAVEN_OPTS: >-
  -Dhttps.protocols=TLSv1.2
  -Dmaven.repo.local=$CI_PROJECT_DIR/.m2/repository
  -Dorg.slf4j.simpleLogger.showDateTime=true
  -Djava.awt.headless=true
MAVEN_CLI_OPTS: >-
  --batch-mode
  --errors
  --fail-at-end
  --show-version
  --no-transfer-progress
image: maven:3-eclipse-temurin-19
cache:
paths:
  - .m2/repository
test:
stage: test
script:
  - curl -fsSL https://app.testcontainers.cloud/bash | bash
  - 'mvn $MAVEN_CLI_OPTS verify'

Testcontainers Cloudを使用すると、Docker-in-Dockerセットアップと比較して、テストを高速に実行できます(図 3)。

テスト結果の詳細を示すtestcontainersショーケースのスクリーンショット、 8の合計時間:00 分。
図 3: ビルド時間を示すテスト結果.of 8:00 分。

Testcontainers Cloudを有効にするだけで、最初の13と比較して、ビルドをほぼ40%、8分短縮することができました。

また、Testcontainers Cloudのターボモードを、並列実行機能を備えたビルドツールと組み合わせて活用し、テストをさらに高速に実行することもできます。

Maven の場合、 -DforkCount=N システムプロパティを使用して並列化の度合いを指定できます。 Gradle の場合、 maxParallelForks プロパティを使用して並列化の度合いを指定できます。

DinDのセットアップと並行してテストを実行しようとしましたが、CPU/メモリリソースが限られているためにテストが失敗していたため、確実に動作させることができませんでした。 Testcontainers Cloudがなければ、すべてのフォークのすべてのコンテナとテスト自体を同じワーカーマシン上で実行することを余儀なくされ、その容量を使い果たすことを考えると、これは驚くべきことではありません。

Testcontainers Cloud では、ランナー ノードからコンテナーを移動できますが、テスト自体は並列実行中に大量のリソースを消費します。 ターボモードの効果を最大限に引き出すために、CPU/メモリのスペックが十分なマシンタイプを使用しましょう。

GitLab.com は、ニーズに基づいて選択できるさまざまなマシンタイプを提供します。 図 4 は、 Linux SaaS ランナーで使用可能なマシンタイプを示しています。

Linux SaaS ランナーのマシンタイプのスクリーンショット (小型、中型、大型のマシンの仕様を含む)。
図 4: Linux SaaS ランナーのマシンタイプ。

Linux ランナーで実行している場合、デフォルトのマシンタイプは small です。 別のランナーを使用するには、次のようにタグを指定できます。

test:
tags: [ saas-linux-medium-amd64 ]
stage: test
script:
  - curl -fsSL https://app.testcontainers.cloud/bash | bash
  - 'mvn $MAVEN_CLI_OPTS verify -DforkCount=4'

Testcontainers Cloud Turbo モードで "saas-linux-medium-amd64" ランナーを使用すると、テストの実行が高速になります (図 5)。

テスト結果の詳細を示すtestcontainersショーケースのスクリーンショット 4合計時間:24 分。
図5: 4のビルド時間を示すテスト結果:24 分。

ビルドツールの並列化機能とTestcontainers Cloud Turbo Modeを適切なCPU/メモリスペックのマシンタイプと組み合わせることで、テストがはるかに高速に実行されたことがわかります。 全体として、テストを並列化することで、ビルドの実行に 13分:03 分から、わずか 4分:24まで短縮でき、これは 3 倍高速になりました。

結論

この記事では、Docker-in-Docker サービスを使用して GitLab.com で Testcontainers ベースのテストを実行する方法について説明しました。 次に、Testcontainers Cloudを使用してセットアップを簡素化し、テストをより迅速に実行する方法を学びました。 また、 Testcontainers Cloud Turbo モードとビルドツールの並列実行機能を組み合わせて、テストをより高速に実行する方法も検討しました。 これにより、ビルドは元のソリューションよりも 3 倍高速になり、Docker-in-Docker 構成の複雑さを回避することができました。

ここでは Java プロジェクトを例にこのセットアップを示しましたが、Testcontainers ライブラリは他の一般的な言語にも存在し、同じ構成パターンに従って、Golang、.NET、Python、Node.js の GitLab CI で Testcontainers ベースのテストを実行できます。

さらに詳しく