🍣

GitHub Actionsで使い捨てのDBを利用する

2021/04/02に公開

はじめに

GitHub Actionsでテストを行う際に、毎回使い捨てのDBを立てて、そのDBを使ってテストをする方法について調べました。

環境

記事を書くにあたって作成したサンプルコードはGitHubのKentarouSuzuki/actions-service-containerにアップロードしてあるので、必要に応じてこちらを参照してください。
今回はGoで実装をしました。本来、Goでテストコードを書く際には hoge_test.go というファイル名にしますが、今回はコードを cmd/sample/main.go に書きました。

データベース PostgreSQL
言語 Go 1.14
マイグレーションツール rubenv/sql-migrate

テストの順序

次のような順序でテストを進めていきます。

  1. GitHubからソースコードをチェックアウトしてくる。
  2. DBマイグレーションを行う。
    • Accountテーブルの作成
  3. テストコードを実行する。
    1. データを追加
    2. 追加したデータを全て取得して、標準出力に表示
    3. データを全件削除する

サービスコンテナ

GitHub ActionsのワークフローでDBに接続してテストを行うには、GitHub Actionsのサービスコンテナを使います。このサービスコンテナの正体はDockerコンテナでDocker Hubのイメージを指定し、作成します。この記事ではPostgreSQLを使っていますが、これをMySQLといった他のRDBやRedis、nginxを使うこともできます。
例えば、ジョブを実行するにあたってPostgreSQLのサービスコンテナを用意するには以下のように書きます。

jobs:
  my-job:
    runs-on: ubuntu-latest
    services:
      postgres:
        # Docker Hubのイメージ
        image: postgres
        # 動かすコンテナで使う環境変数
        # この場合はpostgresqlのパスワードを指定している。
        env:
          POSTGRES_PASSWORD: postgres
        options: >-
          --health-cmd pg_isready
          --health-interval 10s
          --health-timeout 5s
          --health-retries 5

各ジョブの直下にあるservicesに動かしたいサービスコンテナのラベルを書き(この例ではpostgresですが、dbとかでも大丈夫です)、その下に動かしたいDockerイメージや環境変数、ヘルスチェックの設定を指定します。

サービスコンテナと通信する

ワークフロー中で作成したサービスコンテナと通信する方法はジョブを実行している環境によって変わってきます。そこで、まずはジョブを実行する環境について説明します。その後、それぞれの方法でサービスコンテナへ通信する方法について説明をします。

GitHub Actionsでジョブを実行する環境

GitHub Actionsでは以下の2つの環境でジョブを実行することができます。

  1. ランナーマシン上でジョブを直接実行する
  2. コンテナ内でジョブを実行する

ランナーマシン上でジョブを直接実行する

あくまで私が公式ドキュメントやQiitaなどの記事で見た限りですが、この方法で実行している場合が多いです。公式ドキュメントのクイックスタートもこの方法で実行しています。
この方法では以下のように各ジョブの runs-on で指定されているマシンの種類でジョブが実行されます。GitHub ActionsではデフォルトでWindows、macOS、Ubuntuのマシンが用意されています。ジョブの中に定義されている stepsruns-on で指定されたマシンで実行されます。この記事のようにサービスコンテナを実行するにはGitHub Actionの仕様でLinuxのランナーを指定しなければならないようです。[1]

jobs:
  my-job:
    runs-on: ubuntu-latest
    steps:
      - name: Hello, world
        run: |
          echo "Hello, world"

コンテナ内でジョブを実行する

こちらの方法はジョブ直下の runs-on でマシンの種類を、 steps で実行するジョブの内容を設定するところは同じです。しかし、それらに加えて container を設定します。この項目ではジョブが実際に実行するコンテナの設定を指定しています。この例の場合は、 go version はubuntu上に動いている golang:1.14 のコンテナ内で実行されます。
この方法でも先ほどのランナーマシン上でジョブを実行する時と同様にGitHub Actionsの仕様でLinuxのランナーを指定しなければならないようです。[1:1]

jobs:
  my-job:
    runs-on: ubuntu-latest
    container:
      # Docker Hubのイメージ
      image: golang:1.14
    steps:
      - name: Print Go version
        run: |
          go version

サービスコンテナとの通信方法

ランナーマシン上で動いているジョブ

まずは、ランナーマシン上で動いているジョブからサービスコンテナに通信する方法について説明します。
ジョブをランナーマシン上で動かしている場合、サービスコンテナはランナーマシンに対してデフォルトではポートを公開していません。したがって、GitHub Actionsでサービスコンテナを定義する際は、サービスコンテナとランナーマシンのポートをマップする必要があります。例えば、この記事を作成するにあたって実装したサンプルではPostgreSQLをサービスコンテナとしたので、ランナーマシンの5432番ポートとサービスコンテナの5432番ポートをマップします。実際の設定ファイルは以下のようになります。

jobs:
  runner-job:
    runs-on: ubuntu-latest
    env:
      ENV: runner-job
    services:
      postgres:
        image: postgres
        env:
          POSTGRES_USER: sue
          POSTGRES_PASSWORD: postgres
          POSTGRES_DB: postgres
        options: >-
          --health-cmd pg_isready
          --health-interval 10s
          --health-timeout 5s
          --health-retries 5
        ports:
          - 5432:5432

そして、サービスコンテナのホストは local または 127.0.0.1 に設定されています。したがって、 localhost:5432 に対してアクセスすればPostgreSQLと通信ができるようになります。実際に、サンプルでもランナーマシン上でジョブを実行している場合のsql-migrateは以下のように設定しており、 localhost:5432 に対してアクセスするようになっています。

runner-job:
   dialect: postgres
   datasource: host=localhost dbname=postgres user=sue password=postgres sslmode=disable
   dir: migrations
   table: migrations

コンテナ内で動いているジョブ

続いて、コンテナ内で動いているジョブからサービスコンテナに通信する方法について説明します。
コンテナ内でジョブを実行している時には、ランナーマシン上でジョブを実行している時よりもシンプルにサービスコンテナと通信することができます。この時にはDockerのユーザー定義ブリッジネットワークを使っています。そのため、任意のホスト名をつけられるので、GitHub Actionsではサービスコンテナのラベル名を使ってアクセスすることができます。また、コンテナはお互いに全てのポートを公開しあっているために、ポートのマッピングをする必要がありません。
なので、サンプルでもサービスコンテナを定義する際には以下のような設定をして、

  container-job:
    runs-on: ubuntu-latest
    container:
      image: golang:1.14
    env:
      ENV: container-job
    services:
      postgres:
        image: postgres
        env:
          POSTGRES_USER: sue
          POSTGRES_PASSWORD: postgres
          POSTGRES_DB: postgres
        options: >-
          --health-cmd pg_isready
          --health-interval 10s
          --health-timeout 5s
          --health-retries 5

sql-migrateでDBのマイグレーションを行う際の設定も

container-job:
    dialect: postgres
    datasource: host=postgres dbname=postgres user=sue password=postgres sslmode=disable
    dir: migrations
    table: migrations

のように設定し、 postgres:5432 にアクセスするようにしてあります。

脚注
  1. 公式ドキュメントのこちらの部分で言及されています。 ↩︎ ↩︎

Discussion