😈

【Hasura】DBのマイグレーションをGit管理してGitHub Actionsで自動デプロイする方法 with Docker

2023/02/12に公開

はじめに

HasuraではDocker Composeを使用してローカル環境を構築できます。
Hasuraコンソール上での各種操作はマイグレーションファイル、メタデータとしてファイル生成されます。

.
└── hasura
    ├── config.yaml
    ├── metadata
    │   ├── actions.graphql
    │   ├── actions.yaml
    │   ├── allow_list.yaml
    │   ├── api_limits.yaml
    │   ├── backend_configs.yaml
    │   ├── cron_triggers.yaml
    │   ├── databases
    │   │   ├── databases.yaml
    │   │   └── default
    │   ├── graphql_schema_introspection.yaml
    │   ├── inherited_roles.yaml
    │   ├── metrics_config.yaml
    │   ├── network.yaml
    │   ├── opentelemetry.yaml
    │   ├── query_collections.yaml
    │   ├── remote_schemas.yaml
    │   ├── rest_endpoints.yaml
    │   └── version.yaml
    ├── migrations
    │   └── default
    │       ├── 1675684572606_init
    │       ├── 1675709384448_create_table_public_articles
    │       └── 1675712066438_create_table_public_article_categories
    └── seeds
        └── default
            └── 1675712324456_init_article_category.sql

参照:移行とメタデータ

今回はそこで生成されたメタデータをGit管理し、GitHub Actionsで自動デプロイを行います。
公式がGitHubリポジトリとの連携機能を提供しているためGitHub Actionsを使用しなくても実現可能です。

参照:GitHub Deployment

今回は他のジョブとの実行順序をコントロールする(例:Hasuraの変更後にフロントエンドをデプロイ)ことを想定してGitHub Actionsを使用します。

必要なもの

  • Hasuraアカウント
  • Hasura CLI
  • Docker Compose

やってみる

1. Hasuraプロジェクトの準備

こちらからプロジェクトを作成。

DBの接続設定をします。

Neonは1プロジェクトのみ無料でDBを作成できます。

2. GitHubリポジトリを作成

フリーアカウントの場合はパブリックで作成してください。

どうしてもプライベートで作成したい場合は、次の手順でEnvironmentではなくSecretsを使用してください。

3. リポジトリに本番環境用のEnvironmentを用意

GitHub Environmentsとは環境ごとに環境変数を作成する機能です。
ステージング環境と本番環境それぞれで環境変数を設定したいときなどに便利です。

今回は本番環境のみ作成するのでこちらをあえて使用する必要はありませんが、今後追加の環境を作成することを想定してこちらを採用します。

注意:フリーアカウントのプライベートリポジトリでは使用できません。

参照:Using environments for deployment

HasuraコンソールからHASURA_ADMIN_SECRETHASURA_ENDPOINTを取得します。

HASURA_ENDPOINTは「GraphQL API」のドメイン名まで(https://<project name>.hasura.app)です。

Environment名はproductionで作成します。

https://github.com/<username>/<repository name>/settings/environments

4. ローカル環境をセットアップ

git clone <repository url>
cd <repository url>
hasura init
.
└── hasura
    ├── config.yaml
    ├── metadata
    ├── migrations
    └── seeds

ルートディレクトリ直下にdocker-compose.ymlを作成します。

version: '3.6'
services:
  postgres:
    image: postgres:15
    restart: always
    volumes:
    - db-data:/var/lib/postgresql/data
    environment:
      POSTGRES_PASSWORD: postgrespassword
  graphql-engine:
    image: hasura/graphql-engine:v2.17.0
    ports:
    - "8080:8080"
    depends_on:
    - "postgres"
    restart: always
    environment:
      HASURA_GRAPHQL_METADATA_DATABASE_URL: postgres://postgres:postgrespassword@postgres:5432/postgres
      PG_DATABASE_URL: postgres://postgres:postgrespassword@postgres:5432/postgres
      HASURA_GRAPHQL_ENABLE_CONSOLE: "true"
      HASURA_GRAPHQL_DEV_MODE: "true"
      HASURA_GRAPHQL_ENABLED_LOG_TYPES: startup, http-log, webhook-log, websocket-log, query-log
volumes:
  db-data:

参照:hasura/graphql-engine

コンテナを起動。

docker-compose up --build

5. HasuraからDBに接続

cd hasura
hasura console
INFO console running at: http://localhost:9695/

DATAタブからDBの接続情報を設定します。

Database Display Nameをdefaultに、Environment VariableをPG_DATABASE_URLにします。

6. テーブルを追加

DATAタブから適当にテーブルを作成してください。

テーブル作成に合わせてマイグレーションファイルが作成されていることがわかります。

hasura/migrations
└── default
    └── 1675775890486_create_table_public_users
        ├── down.sql
        └── up.sql

7. GitHub Actionsのワークフローを作成

mainブランチにPushされた際、migration・metadata・seedの変更を反映します。

以下ワークフローを作成します。

.github/workflows/deploy.yml

name: deploy
on:
  push:
    branches:
      - main
    paths:
      - 'hasura/**'

jobs:
  deployment:
    environment: production
    runs-on: ubuntu-latest
    steps:
      - name: checkout repo
        uses: actions/checkout@v3
      - name: apply hasura migrations
        uses: tibotiber/hasura-action@v3.0
        with:
          args: migrate apply --all-databases
        env:
          HASURA_ENDPOINT: ${{ secrets.HASURA_ENDPOINT }}
          HASURA_ADMIN_SECRET: ${{ secrets.HASURA_ADMIN_SECRET }}
          HASURA_WORKDIR: hasura
          HASURA_ENGINE_VERSION: v2.17.1
      - name: apply hasura seed
        uses: tibotiber/hasura-action@v3.0
        with:
          args: seed apply --all-databases
        env:
          HASURA_ENDPOINT: ${{ secrets.HASURA_ENDPOINT }}
          HASURA_ADMIN_SECRET: ${{ secrets.HASURA_ADMIN_SECRET }}
          HASURA_WORKDIR: hasura
      - name: apply hasura metadata
        uses: tibotiber/hasura-action@v3.0
        with:
          args: metadata apply
        env:
          HASURA_ENDPOINT: ${{ secrets.HASURA_ENDPOINT }}
          HASURA_ADMIN_SECRET: ${{ secrets.HASURA_ADMIN_SECRET }}
          HASURA_WORKDIR: hasura
          HASURA_ENGINE_VERSION: v2.17.1

GitHub Environment名をproduction以外で作成した場合は、以下の箇所を修正してください。

jobs:
  deployment:
    environment: production

今回はテーブル追加しか行っていないため、metadataとseedの反映は不要ですが今後のために追加してあります。

8. Commit and Push

git add -A
git commit -m "add new table"
git push origin main

9. 確認

Hasuraコンソールからテーブルが正常に作成されていることを確認します。

エラーが表示されている場合はGitHub Actionsのログを確認してください。

Hints

既存プロジェクトをGit管理する

ローカルでHasuraプロジェクトを生成する際に、すでに作成済みのプロジェクトからエクスポート可能です。

hasura init <directory name> --endpoint <endpoint URL> --admin-secret <admin secretkey>

この方法で作成した場合、設定ファイルに本番環境のエンドポイントURLとシークレットが設定されます。

hasura/config.yaml

version: 3
endpoint: <endpoint URL>
admin_secret: <admin secret>
metadata_directory: metadata
actions:
  kind: synchronous
  handler_webhook_baseurl: http://localhost:3000

このままコンテナを起動すると本番環境に向いてしまうため、以下のように修正してください。

version: 3
endpoint: http://localhost:8080
metadata_directory: metadata
actions:
  kind: synchronous
  handler_webhook_baseurl: http://localhost:3000

シードデータを作成する

方法①:SQLファイルを手動生成

hasura seed create <seed name>

方法②:ローカル環境のテーブルのレコードから生成

ローカル環境のテーブルのデータをそのまま使用したい場合は--from-tableオプションを使用します。

hasura seed create <seed name> --from-table <table name>

プレビューアプリの作成

ベータ版ですがPRごとにプレビュー用のアプリを作成できます。
ただし使用するDBはこちらで用意する必要があります。

参照:Preview Apps

やっていることはざっくりとこんな感じです。

  1. プレビュー用のDB URLを複数設定
  2. PR作成時にHasuraプロジェクトが自動生成される
  3. 設定されたDB URLが自動で割り当てられる
  4. PRマージorクローズ時にアプリを削除

PRごとにDBが必要になります。
プレビューのために複数個DBを用意する必要があるため、あまり現実的はないですね。

またエンドポイントURLとシークレットは動的なため、フロントエンドのプレビュー用アプリと接続する際には注意が必要です。

          message: |
            Console URL available at ${{steps.hasura_cloud_preview.outputs.consoleURL}}
            GraphQL Endpoint available at ${{steps.hasura_cloud_preview.outputs.graphQLEndpoint}}

フロントエンドのデプロイ先にGitHub Actionsから環境変数を設定する方法があれば自動で連携できそうですが、そうでない場合は手動で設定が必要です。

おわりに

Git管理と自動デプロイができたところでやっと開発スタートです。

Discussion