GitHub Actionsでサービスコンテナとしてデータベースを起動して通信してみる

GitHub Actionsでサービスコンテナとしてデータベースを起動して通信してみる

GitHub Actionsジョブランナーからサービスコンテナと通信は、ポートマッピングしてホストから通信とブリッジネットワークでコンテナ間通信がある
Clock Icon2025.05.07

GitHub Actionsにはサービスコンテナという機能があり、データベースやキャッシュサーバーなど、ワークフローが依存するサービスをコンテナ起動させる仕組みがあります。

name: PostgreSQL service example
on: push

jobs:
  # Label of the container job
  container-job:
    # Containers must run in Linux based operating systems
    runs-on: ubuntu-latest
    # Docker Hub image that `container-job` executes in
    container: node:20-bookworm-slim

    # Service containers to run with `container-job`
    services:
      # Label used to access the service container
      postgres:
        # Docker Hub image
        image: postgres
        # Provide the password for postgres
        env:
          POSTGRES_PASSWORD: postgres
        # Set health checks to wait until postgres has started
        options: >-
          --health-cmd pg_isready
          --health-interval 10s
          --health-timeout 5s
          --health-retries 5

ランナージョブの実行方法は2通りあります

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

ローカル開発で、データベースだけコンテナ化した場合に相当するのが 「1.ランナーマシン上でジョブを実行」 の構成です。
ポートマッピングでDockerホスト環境からコンテナと通信できるように設定します。

Docker Compose のようにまるっとコンテナ化するのが 「2.コンテナ内でジョブを実行」 の構成です。
同じユーザー定義・ブリッジ・ネットワークでサービス名からコンテナ間通信するため、ネットワーク的にシンプルです。

Pythonからサービスコンテナで起動したPostgreSQLと通信する場合を例に、それぞれの構築例を紹介します。

本記事に対応するGitHubレポジトリを公開しています。

GitHub - quiver/github-actions-service-contaner-sample-python

1. ランナーマシン上でジョブを実行

PostgreSQLをサービスコンテナとして起動し、ランナー実行ホスト環境からコンテナと通信できるようにポートマッピング設定(ports)を追加します。

ランナーマシンからサービスコンテナと通信する場合のワークフローファイル

name: PostgreSQL service container running jobs in runner machines
on: push

jobs:
  # Label of the container job
  container-job:
    # Containers must run in Linux based operating systems
    runs-on: ubuntu-latest

    # Service containers to run with `container-job`
    services:
      # Label used to access the service container
      postgres:
        # Docker Hub image
        image: postgres:17
        # Provide the password for postgres
        env:
          POSTGRES_PASSWORD: postgres
        # Set health checks to wait until postgres has started
        options: >-
          --health-cmd pg_isready
          --health-interval 10s
          --health-timeout 5s
          --health-retries 5
        ports:
          # Maps tcp port 5432 on service container to the host
          - 5432:5432
    steps:
      # Downloads a copy of the code in your repository before running CI tests
      - name: Check out repository code
        uses: actions/checkout@v4

      - name: Install dependencies
        run: pip install -r requirements.txt

      - name: Connect to PostgreSQL
        run: python client.py
        env:
          # The hostname used to communicate with the PostgreSQL service container
          POSTGRES_HOST: localhost
          # The default PostgreSQL port
          POSTGRES_PORT: 5432

より具体的には services のブロックの最後の次の箇所が該当します。

        ports:
          # Maps tcp port 5432 on service container to the host
          - 5432:5432

アプリケーション(Dockerホスト)からは、ローカルホストにexposeしたポートでPostgreSQLと通信します。より具体的には次の env が該当します。

      - name: Connect to PostgreSQL
        run: python client.py
        env:
          # The hostname used to communicate with the PostgreSQL service container
          POSTGRES_HOST: localhost
          # The default PostgreSQL port
          POSTGRES_PORT: 5432

2. コンテナ内でジョブを実行

PostgreSQLをサービスコンテナとして起動し、ランナージョブもコンテナ起動し、サービスコンテナとコンテナ間通信します。

サービスコンテナとコンテナ間通信する場合のワークフローファイル

name: PostgreSQL service container running jobs in containers 
on: push

jobs:
  # Label of the container job
  container-job:
    # Containers must run in Linux based operating systems
    runs-on: ubuntu-latest
    # Docker Hub image that `container-job` executes in
    container: python:3.13-slim

    # Service containers to run with `container-job`
    services:
      # Label used to access the service container
      postgres:
        # Docker Hub image
        image: postgres:17
        # Provide the password for postgres
        env:
          POSTGRES_PASSWORD: postgres
        # Set health checks to wait until postgres has started
        options: >-
          --health-cmd pg_isready
          --health-interval 10s
          --health-timeout 5s
          --health-retries 5

    steps:
      - name: Check out repository code
        uses: actions/checkout@v4

      - name: Install dependencies
        run: pip install -r requirements.txt

      - name: Connect to PostgreSQL
        run: python client.py
        env:
          # The hostname used to communicate with the PostgreSQL service container
          POSTGRES_HOST: postgres
          # The default PostgreSQL port
          POSTGRES_PORT: 5432

より具体的には、次の container: python:3.13-slim のように、ジョブを実行するコンテナイメージを指定します。

jobs:
  # Label of the container job
  container-job:
    # Containers must run in Linux based operating systems
    runs-on: ubuntu-latest
    # Docker Hub image that `container-job` executes in
    container: python:3.13-slim

アプリケーション(ジョブコンテナ)からは、サービスコンテナの定義で利用したラベル名(postgres) でサービスコンテナと通信します。より具体的には次の env が該当します。

      - name: Connect to PostgreSQL
        run: python client.py
        env:
          # The hostname used to communicate with the PostgreSQL service container
          POSTGRES_HOST: postgres
          # The default PostgreSQL port
          POSTGRES_PORT: 5432

疎通確認

PostgreSQLサービスコンテナと通信するPythonクライアントアプリケーションです。

環境変数でわたってくるPostgreSQLのホスト(POSTGRES_HOST)の中身が実行方式によって異なります。

import os
import psycopg2

def main():
    host = os.environ.get("POSTGRES_HOST")
    port = os.environ.get("POSTGRES_PORT")
    user = "postgres"
    password = "postgres"
    dbname = "postgres"

    conn = psycopg2.connect(
        host=host,
        port=port,
        user=user,
        password=password,
        dbname=dbname
    )

    with conn.cursor() as cur:
        cur.execute("SELECT version()")
        result = cur.fetchone()
        print(f"PostgreSQL Version: {result[0]}")

if __name__ == "__main__":
    main()

GitHub Actionsのジョブ履歴から、期待通り実行されていることを確認しましょう

Run python client.py
  python client.py
  shell: /usr/bin/bash -e {0}
  env:
    POSTGRES_HOST: postgres
    POSTGRES_PORT: 5432

PostgreSQL Version: PostgreSQL 17.4 (Debian 17.4-1.pgdg120+2) on x86_64-pc-linux-gnu, compiled by gcc (Debian 12.2.0-14) 12.2.0, 64-bit

設定ファイルの違い

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

の違いを設定ファイルの diff から確認します

@@ -6,8 +6,6 @@
   container-job:
     # Containers must run in Linux based operating systems
     runs-on: ubuntu-latest
-    # Docker Hub image that `container-job` executes in
-    container: python:3.13-slim

     # Service containers to run with `container-job`
     services:
@@ -24,6 +22,9 @@
           --health-interval 10s
           --health-timeout 5s
           --health-retries 5
+        ports:
+          # Maps tcp port 5432 on service container to the host
+          - 5432:5432
     steps:
       - name: Check out repository code
         uses: actions/checkout@v4
@@ -35,6 +36,6 @@
         run: python client.py
         env:
           # The hostname used to communicate with the PostgreSQL service container
-          POSTGRES_HOST: postgres
+          POSTGRES_HOST: localhost
           # The default PostgreSQL port
           POSTGRES_PORT: 5432

diff ファイルで + となっている ランナーマシン上でジョブを実行方式 では、サービスコンテナをポートマッピングしてローカルホストから通信しています。

diff ファイルで- となっている コンテナでジョブを実行する方式 では、ジョブを実行するコンテナイメージを container で定義し、サービス名(postgres)でサービスコンテナと通信しています。

最後に

GitHub Actionsでサービスコンテナを利用する場合、ランナージョブとサービスコンテナの通信方式は2通りあります。

ジョブもコンテナとして実行させると、ネットワーク面含めて一貫してきれいになる一方で、アドオン的にデータベース等がサービスコンテナで必要になった場合は、従来のワークフローファイルを極力維持し、ランナーマシン上からサービスコンテナの見えるようにサービスコンテナを追加したほうが変更・影響範囲を小さくできると思います。

参考

Share this article

facebook logohatena logotwitter logo

© Classmethod, Inc. All rights reserved.