0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

Azure PipelinesのジョブでDBのコンテナを立ててタスクで利用する

Posted at

要約

ドキュメントを見て試して、Pipelinesのジョブの中でPostgreSQLのコンテナを立ち上げてDBを使ったテストを動かしたという話です。

手順

Azure Pipelinesではジョブ単位でコンテナを立ち上げておき、その後タスクを実行できます。

これを利用して.Net coreなプロジェクトのテストプロジェクトで実際のDBを使ってみました。

- project.sln
- Fukasawah.Sample1
  - Fukasawah.Sample1.csproj
- Fukasawah.Sample1.Tests
  - Fukasawah.Sample1.Tests.csproj

という感じのファイル構成をしているとき、次のようなパイプラインを書きます。

trigger:
  - master

variables:
  DotnetVersion: 3.1.x
  BuildConfiguration: Release
  ProjectName: Fukasawah.Sample1 # 適宜書き換え
  TestDbName: testdb
  TestDbUser: testuser
  TestDbPassword: testpass

# ジョブで使うコンテナを定義して名前を付ける
resources:
  containers:
    - container: 'postgres'
      image: 'postgres:11-bullseye'
      ports:
        - 5432
      env:
        POSTGRES_DB: $(TestDbName)
        POSTGRES_USER: $(TestDbUser)
        POSTGRES_PASSWORD: $(TestDbPassword)
      # ヘルスチェックはコマンドに応じて
      options: >-
        --health-cmd pg_isready
        --health-interval 10s
        --health-timeout 5s
        --health-retries 5

stages:
  - stage: Build_and_Publish
    jobs:
      - job: Build
        pool:
          vmImage: "ubuntu-latest"

        services:
          # `サービス名: コンテナ名` で書く(複数同じサービスがあることはあまりないので、コンテナ名と一緒にすると紛れがないと思う)
          postgres: postgres
        variables:
          # テストプロジェクトで使う接続文字列を環境変数CONNECTION_STRINGで渡す。ポートだけは変わるので変数から埋め込む
          CONNECTION_STRING: "Host=localhost;Port=$(agent.services.postgres.ports.5432);Database=$(TestDbName);Username=$(TestDbUser);Password=$(TestDbPassword)"
        steps:
          - task: UseDotNet@2
            inputs:
              packageType: "sdk"
              version: $(DotnetVersion)
            displayName: "Install .NET Core SDK $(DotnetVersion)"

          - task: DotNetCoreCLI@2
            inputs:
              command: "build"
              projects: "**/$(ProjectName).csproj"
              arguments: "-c $(BuildConfiguration)"
            displayName: "dotnet build"

# TODO: DBの作成を行うジョブを書く場合はこのあたり。
#   接続文字列を読み取ってデータを流すスクリプトを用意したりする(dotnet ef migration && dotnet ef database updateとか。)
#   今回は、テストコード中で「dbContext.Database.EnsureCreated()」を呼び出して作成するので、やっていない。

          - task: DotNetCoreCLI@2
            inputs:
              command: "test"
              projects: "**/$(ProjectName).Tests.csproj"
              arguments: '-c $(BuildConfiguration) --collect:"XPlat Code Coverage"'
            displayName: "dotnet test"

          - task: PublishCodeCoverageResults@1
            inputs:
              codeCoverageTool: "Cobertura"
              summaryFileLocation: "$(Agent.TempDirectory)/**/coverage.cobertura.xml"

          - task: DotNetCoreCLI@2
            inputs:
              # publish時にzipまで行うには一工夫
              # ref: https://devadjust.exblog.jp/27915866/
              command: "publish"
              publishWebProjects: false
              projects: "**/$(ProjectName).csproj"
              arguments: "-c $(BuildConfiguration) -o $(Build.SourcesDirectory)/publish --no-restore --no-build -p:DebugType=None -p:DebugSymbols=false"
            displayName: "dotnet publish"

          - task: CopyFiles@2
            inputs:
              contents: '$(ProjectName)/$(ProjectName)*.zip'
              targetFolder: $(Build.ArtifactStagingDirectory)
            displayName: "Copy zip to artifacts staging directory"

          - task: PublishBuildArtifacts@1
            inputs:
              pathToPublish: $(Build.ArtifactStagingDirectory)
              artifactName: BuildOutputs
            displayName: "Publish build artifacts"

ポイントは以下です。ドキュメントに書いてある通りです。

  • resources.containers[]: に利用するコンテナの定義を書く
  • stages[].jobs[].services にresourcesで書いたコンテナを参照する
  • $(agent.services.サービス名.ports.ポート番号)でポート番号を変数から参照して埋め込む
    • コンテナの定義のポート番号はこの変数に使われるもので繋ぐためには使えない。うっかり重複してエラーになっても面倒なので変数で取るほうが良いと思う
    • ただ、dockerコマンドで起動しているだけなので、ポートマッピングしている場合はそのまま行けるかもしれない(5432ではなく5432:5432と書いたり)

docker-compose.ymlをazure-pipelines.ymlで書くようなイメージですね。

これを実行すると、こんな感じに実行されます。

パイプラインのジョブの実行結果

ジョブの最初の方のInitialize containersタスクでイメージのPullが行われるので、若干時間がかかっています。

コンテナが動く状態になるまで待つ(healthcheck)

コンテナの起動はしたものの、プロセスが立ち上がりきらない場合もあり、その時にテストが走るとダメです。
ビルド時間的に間に合うでしょうと高をくくっても良いのですが、optionsでDockerのHealthcheckオプションを指定できます。
PostgreSQLではpg_isreadyというユーティリティコマンドがあり、これを使うのが一般的なようです。(その分、次のタスクを実行するまでの待ち時間は増えます。)

resources:
  containers:
    - container: 'postgres'
      image: 'postgres:11-bullseye'
      ports:
        - 5432
      env:
        POSTGRES_DB: $(TestDbName)
        POSTGRES_USER: $(TestDbUser)
        POSTGRES_PASSWORD: $(TestDbPassword)
      options: --health-cmd pg_isready --health-interval 3s --health-timeout 3s --health-retries 10
0
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?