
GitHub Actionsでサービスコンテナとしてデータベースを起動して通信してみる
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.ランナーマシン上でジョブを実行」 の構成です。
ポートマッピングで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
設定ファイルの違い
- ランナーマシン上でジョブを実行
- コンテナ内でジョブを実行
の違いを設定ファイルの 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通りあります。
ジョブもコンテナとして実行させると、ネットワーク面含めて一貫してきれいになる一方で、アドオン的にデータベース等がサービスコンテナで必要になった場合は、従来のワークフローファイルを極力維持し、ランナーマシン上からサービスコンテナの見えるようにサービスコンテナを追加したほうが変更・影響範囲を小さくできると思います。