おじんブログ

Webアプリに関する知見とか雑記です

テスト用にGitHub ActionsのサービスコンテナでDBを建ててアクセスするときのメモ

書いた契機

GitHub Actions上でテストを実行する際に、使い捨てのデータベースにアクセスしたいと思いドキュメントを読んでいた。

docs.github.com

これを読んでいて「コンテナーからサービスコンテナにアクセスするのと、ランナーマシンからサービスコンテナにアクセスするときの違いってどういうこと?」と思ったのであれこれ試行錯誤したメモ。

初歩的な理解ができていなかったので結構理解するのに詰まってしまった。

要約

  • GitHub ActionsのランナーマシンではDockerが動くので、コンテナ上でもJob を実行させることができる
  • サービスコンテナはJobを実行させるコンテナとは別にコンテナを起動させて、Jobと通信することができる
  • ランナーマシン上で直接Jobを実行する時はマシン内のDockerにアクセスするのでホストのパスが localhostになる (図の左)
  • コンテナ上でJobを実行する時は、同じDocker内のコンテナにアクセスするので ホストのパスがサービスコンテナのラベルになる ( postgres / redis ) (図の右)

Differences in accessing service containers from jobs

自分がコンテナ上でJobを実行させたことがほぼなかったので、直接ランナー上で実行する違いを全然理解していなくてちょっと混乱した。

docs.github.com

選べるならどっちがいいの?

個人的な開発スタイルだと、Node.jsなどはローカルで、必要なデータベースなどのミドルウェアはDockerで建てることが多い。ので、GitHub Actionsで動かすときも似たような環境がいいので、Jobはランナー本体、PostgreSQLはサービスコンテナで運用したい。

setup-node みたいなActionを使った方がcacheのコントロールもしやすいと思うので、その点でもランナー上で実行するようにしたい。

最終的にWorkflowはこんな感じになった。

name: PostgreSQL Service Example
on: push

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

    steps:
      - name: Check out repository code
        uses: actions/checkout@v4
      - name: Setup node
        uses: actions/setup-node@v4
        with:
          node-version: 20
          cache: "npm"
      - name: Install dependencies
        run: npm ci
      - name: Connect to PostgreSQL
        run: node client.js
        env:
          POSTGRES_HOST: localhost
          POSTGRES_PORT: 5432

コンテナでJob実行する場合の差分はこんな感じ。

 jobs:
   runner-job:
     runs-on: ubuntu-latest
-    container: node:20
     steps:
+      - name: Setup node
+        uses: actions/setup-node@v4
+        with:
+          node-version: 20
+          cache: "npm"
       - name: Connect to PostgreSQL
         run: node client.js
         env:
-          POSTGRES_HOST: postgres
+          POSTGRES_HOST: localhost

余談

actions/setup-node よりもコンテナーで実行した方がコンテナをPullする分時間がかかるんじゃないかと思ったけどそんなに変わらなかった。

おまけ: ローカルで実行するとき

GitHub Actionsの公式ドキュメントにPostgreSQLをサービスコンテナで起動して実行するスクリプトが例題としてある。

実際に開発するときはローカルでテストしてからPushしたい。

docs.github.com

こんな感じのスクリプトを実行したい。

// client.js
const { Client } = require("pg");

const main = () => {
  const pgclient = new Client({
    host: process.env.POSTGRES_HOST,
    port: process.env.POSTGRES_PORT,
    user: "postgres",
    password: "postgres",
    database: "postgres",
  });

  pgclient.connect();

  const table =
    "CREATE TABLE IF NOT EXISTS student(id SERIAL PRIMARY KEY, firstName VARCHAR(40) NOT NULL, lastName VARCHAR(40) NOT NULL, age INT, address VARCHAR(80), email VARCHAR(40))";
  const text =
    "INSERT INTO student(firstname, lastname, age, address, email) VALUES($1, $2, $3, $4, $5) RETURNING *";
  const values = [
    "Mona the",
    "Octocat",
    9,
    "88 Colin P Kelly Jr St, San Francisco, CA 94107, United States",
    "octocat@github.com",
  ];

  pgclient.query(table, (err, res) => {
    if (err) throw err;
  });

  pgclient.query(text, values, (err, res) => {
    if (err) throw err;
  });

  pgclient.query("SELECT * FROM student", (err, res) => {
    if (err) throw err;
    console.log(err, res.rows); // Print the data in student table
    pgclient.end();
  });
};

main();

これに対して用意したいdocker-compose.yaml をこんな感じで建てる。 使い捨ての前提なので、永続化のためのvolumesは指定しない。

# docker-compose.yaml
version: "3.9"

services:
  postgres:
    image: postgres
    ports:
      - 5432:5432
    environment:
      POSTGRES_USER: "postgres"
      POSTGRES_PASSWORD: "postgres"
      POSTGRES_DB: "postgres"

Docker Composeでコンテナを起動してスクリプトを実行。

$ docker compose up -d                                     
$ POSTGRES_HOST=localhost POSTGRES_PORT=5432 node client.js
null [
  {
    id: 1,
    firstname: 'Mona the',
    lastname: 'Octocat',
    age: 9,
    address: '88 Colin P Kelly Jr St, San Francisco, CA 94107, United States',
    email: 'octocat@github.com'
  }
]

Docker Desktopの代替?となるOrbStackで確認した。早くて良い。

docs.orbstack.dev