2020年9月17日

TypeScript、PostgreSQL、Prisma を使ったバックエンド:CIとデプロイメント

このシリーズの第4部では、継続的インテグレーション(CI)と継続的デプロイメント(CD)をGitHub Actionsで設定し、バックエンドをHerokuにテストおよびデプロイします。

Backend with TypeScript, PostgreSQL & Prisma: CI & Deployment

はじめに

このシリーズの目標は、具体的な問題、つまりオンラインコースの採点システムを解決することで、現代のバックエンドにおける様々なパターン、問題、アーキテクチャを探求し、デモンストレーションすることです。この問題は、多様なリレーションタイプを持ち、実際のユースケースを表現するのに十分な複雑さを持っているため、選ばれました。

ライブストリームの録画は上にあり、この記事と同じ内容をカバーしています。

このシリーズで扱う内容

このシリーズでは、バックエンド開発のあらゆる側面におけるデータベースの役割に焦点を当て、以下の内容をカバーします。

トピックパートデータモデリングPart 1CRUDPart 1集計Part 1REST APIレイヤーPart 2バリデーションPart 2テストPart 2パスワードレス認証Part 3認可Part 3外部APIとの統合Part 3継続的インテグレーションPart 4 (現在)デプロイメントPart 4 (現在)

最初の記事では、問題領域のデータモデルを設計し、Prisma Clientを使用してデータをデータベースに保存するシードスクリプトを作成しました。

シリーズの2番目の記事では、最初の記事のデータモデルとPrismaスキーマの上にREST APIを構築しました。Hapiを使用してREST APIを構築し、HTTPリクエストを介してリソースに対するCRUD操作を実行できるようにしました。

シリーズの3番目の記事では、HapiとJSON Web Tokens (JWT)を使用して、REST APIを保護するためのメールベースのパスワードレス認証と認可を実装しました。さらに、ユーザーが何を行うことを許可されているかを定義するために、リソースベースの認可を実装しました。

今日の学習内容

この記事では、テストを実行し、バックエンドとPostgreSQLデータベースをホストするHerokuにバックエンドをデプロイするワークフローを定義することで、GitHub ActionsをCI/CDサーバーとして設定します。

HerokuはPlatform as a Service (PaaS) です。サーバーレスデプロイメントモデルとは対照的に、Herokuではリクエストがない場合でもアプリケーションが継続的に稼働します。サーバーレスには低コストや運用上のオーバーヘッドが少ないといった多くの利点がありますが、このアプローチでは、サーバーレスアプローチに一般的なデータベース接続のチャーンやコールドスタートの課題を回避できます。

Prismaを使用するアプリケーションのデプロイパラダイム間のトレードオフについては、Prismaデプロイメントドキュメントをご覧ください。

注: このガイド全体を通して、手順を正しく実行したかどうかを検証できる様々なチェックポイントがあります。

前提条件

GitHub Actionを使用してバックエンドをHerokuにデプロイするには、以下が必要です。

  • Herokuアカウント。
  • Heroku CLIがインストールされていること。
  • シリーズのパート3で作成した、メール送信用のSendGrid APIトークン。

継続的インテグレーションと継続的デプロイメント

継続的インテグレーション(CI)は、個々の開発者の作業をメインのコードリポジトリに統合し、統合バグを早期に発見し、共同開発を加速するための技術です。通常、CIサーバーはGitリポジトリに接続されており、コミットがリポジトリにプッシュされるたびにCIサーバーが実行されます。

継続的デプロイメント(CD)は、変更を迅速かつ一貫してデプロイできるように、デプロイプロセスを自動化することに関心があるアプローチです。

CIとCDは異なる責任を負いますが、関連性があり、しばしば同じツールを使用して処理されます。この記事では、CIとCDの両方をGitHub Actionsを使用して処理します。

継続的インテグレーションパイプライン

継続的インテグレーションにおいて、主要な構成要素はパイプラインです。パイプラインは、変更によってバグやリグレッションが導入されないことを保証するために定義する一連のステップです。例えば、パイプラインにはテストの実行、コードリンター、TypeScriptコンパイラのステップが含まれる場合があります。いずれかのステップが失敗した場合、CIサーバーは停止し、失敗したステップをGitHubに報告します。

プルリクエストを使用してコード変更が導入されるチームで作業する場合、CIサーバーは通常、すべてのプルリクエストに対してパイプラインを自動的に実行するように設定されます。

前の手順で記述したテストは、APIのエンドポイントへのリクエストをシミュレートすることで機能します。これらのエンドポイントのハンドラーはデータベースと対話するため、テストの期間中、バックエンドのスキーマを持つPostgreSQLデータベースが必要になります。次のステップでは、テストデータベース(CI実行中のみ)を実行し、テストデータベースがPrismaスキーマと一致するようにマイグレーションを実行するようにGitHub Actionsを設定します。

注: CIは、作成したテストの質に依存します。テストカバレッジが低い場合、テストがパスしても誤った信頼感を生む可能性があります。

GitHub Actionsでワークフローを定義する

GitHub Actionsは、継続的インテグレーションに使用できる自動化プラットフォームです。GitHubでのイベントに基づいてワークフローをオーケストレーションするためのAPIを提供し、GitHubからコードをビルド、テスト、デプロイするために使用できます。

GitHub Actionsを設定するには、yamlを使用してワークフローを定義します。ワークフローは、コミットがリポジトリにプッシュされた時や、プルリクエストが作成された時など、様々なリポジトリイベントで実行されるように設定できます。

各ワークフローには複数のジョブを含めることができ、各ジョブは複数のステップを定義します。ジョブの各ステップはコマンドであり、テスト対象の特定のコミットのソースコードにアクセスできます。

注: CIサービスではパイプラインに対して異なる用語を使用します。例えば、GitHub Actionsでは同じものを指すのにワークフローという用語を使用します。

この記事では、リポジトリにあるgrading-appワークフローを使用します。

ワークフローを見てみましょう

grading-appワークフローには、testdeployの2つのジョブがあります。

テストジョブは以下を行います。

  1. リポジトリをチェックアウトします。
  2. Node.jsを設定します。
  3. 依存関係をインストールします。
  4. servicesを使用して開始されるテストデータベースにデータベーススキーマを作成します。
  5. テストを実行します。

注: servicesは追加のサービスを実行するために使用できます。上記のテストジョブでは、テスト用のPostgreSQLデータベースを作成するために使用されています。

デプロイジョブは以下を行います。

  1. リポジトリをチェックアウト
  2. 依存関係をインストール
  3. 本番データベースに対してマイグレーションを実行
  4. Herokuにデプロイ

注: on: pushは、プッシュされたコミットごとにワークフローをトリガーします。if: github.event_name == 'push' && github.ref == 'refs/heads/master'条件は、deployジョブがmasterブランチに対してのみトリガーされることを保証します。

リポジトリをフォークしてワークフローを有効にする

GitHub Actionsを設定するために、まずGitHubリポジトリをフォークすることから始めます。

注: すでにリポジトリをフォークしている場合は、元のリポジトリのmasterブランチから変更をマージしてください。

フォークしたら、GithubのActionsタブに移動します。

「Enable」ボタンをクリックしてワークフローを有効にします。

これで、リポジトリにコミットをプッシュすると、GitHubがワークフローを実行します。

Heroku CLIログイン

Heroku CLIでHerokuにログインしていることを確認してください。

Herokuアプリの作成

バックエンドアプリケーションをHerokuにデプロイするには、Herokuアプリを作成する必要があります。クローンしたリポジトリのフォルダから以下のコマンドを実行します。

注: YOUR_APP_NAMEの代わりに、任意のユニークな名前を使用してください。

チェックポイント: Heroku CLIは、アプリが正常に作成されたことをログに記録するはずです。

Heroku上でPostgreSQLデータベースをプロビジョニングする

以下のコマンドでデータベースを作成します。

チェックポイント: データベースが作成されたことを確認するには、以下が表示されるはずです。

注: Herokuは、アプリケーションのランタイムのためにDATABASE_URL環境変数を自動的に設定します。Prisma Clientは、Prismaスキーマで設定された環境変数と一致するため、DATABASE_URLを使用します。

GitHubでビルド時のシークレットを定義する

GitHub Actionsが本番データベースのマイグレーションを実行し、バックエンドをHerokuにデプロイできるように、GitHubワークフローで参照されている4つのシークレットを作成します。

注: ビルド時のシークレットとランタイムシークレットには区別があります。ビルド時のシークレットはGitHubで定義され、GitHub Actionsの実行中に使用されます。一方、ランタイムシークレットはHerokuで定義され、バックエンドによって使用されます。

シークレット

  • HEROKU_APP_NAME: 前の手順で選択したアプリの名前。
  • HEROKU_EMAIL: Herokuにサインアップした際に使用したメールアドレス。
  • HEROKU_API_KEY: Heroku APIキー
  • DATABASE_URL: デプロイ前に本番データベースのマイグレーションを実行するために必要なHeroku上の本番PostgreSQL URL。

本番のDATABASE_URLを取得する

データベースプロビジョニング時にHerokuによって設定されたDATABASE_URLを取得するには、以下のHeroku CLIコマンドを使用します。

チェックポイント: 出力にURLが表示されるはずです(例: postgres://username:password@ec2-12.eu-west-1.compute.amazonaws.com:5432/dbname)。

HEROKU_API_KEYを取得する

Heroku APIキーは、Herokuアカウント設定から取得できます。

Heroku API key in the Heroku account settings

GitHubでシークレットを作成する

4つのシークレットを作成するには、リポジトリ設定に移動し、Secretsタブを開きます。

GitHub repository secrets

New secretをクリックし、シークレット名に名前フィールド(例: HEROKU_APP_NAME)を使用し、値を設定します。

チェックポイント: 4つのシークレットを作成した後、以下が表示されるはずです。

GitHub repository secrets

Herokuで環境変数を定義する

バックエンドは、ランタイムに環境変数としてアプリケーションに渡される3つのシークレットを必要とします。

  • SENDGRID_API_KEY: SendGrid APIキー
  • JWT_SECRET: JWTトークンに署名するために使用されるシークレット。
  • DATABASE_URL: Herokuによって自動的に設定されたデータベース接続URL。

注: JWT_SECRETは、ターミナルで以下のコマンドを実行して生成できます: node -e "console.log(require('crypto').randomBytes(256).toString('base64'));"

Heroku CLIでそれらを設定するには、以下のコマンドを使用します。

チェックポイント: 環境変数が設定されたことを確認するには、以下が表示されるはずです。

テストを実行してデプロイするワークフローをトリガーする

ワークフローが設定され、Heroku上にアプリが作成され、すべてのシークレットが設定されたので、テストを実行してデプロイするワークフローをトリガーできるようになりました。

ビルドをトリガーするには、空のコミットを作成してプッシュします。

コミットをプッシュしたら、GitHubリポジトリのActionsタブに移動すると、以下が表示されるはずです。

コミットメッセージのあるテーブルの最初の行をクリックします。

testジョブのログを表示する

testジョブのログを表示するには、testをクリックすると、テストの結果を表示できます。例えば、下のスクリーンショットでは、テストの結果を見ることができます。

Herokuへのデプロイを確認する

deployジョブがHerokuに正常にデプロイされたことを確認するには、左側のdeployをクリックし、Deploy to Herokuステップを展開します。ログの最後に以下の行が表示されるはずです。

ブラウザからAPIにアクセスするには、クローンしたリポジトリフォルダから以下のHeroku CLIコマンドを使用します。

これにより、https://YOUR_APP_NAME.herokuapp.com/を指すブラウザが開きます。

チェックポイント: ブラウザに{"up":true}と表示されるはずです。これは、ステータスエンドポイントによって提供されています。

バックエンドのログを表示する

バックエンドのログを表示するには、クローンしたリポジトリフォルダから以下のHeroku CLIコマンドを使用します。

ログインフローをテストする

ログインフローをテストするには、REST APIに2回呼び出しを行う必要があります。

まず、APIのURLを取得します。

curlでログインエンドポイントにPOST呼び出しを行います。

メールで8桁のトークンを確認し、次に2回目の呼び出しを行います。

チェックポイント: レスポンスには200の成功ステータスコードが含まれ、JWTトークンを含むAuthorizationヘッダーがあるはずです。

まとめ

バックエンドがデプロイされ、稼働しました。よくできました!

GitHub Actionsワークフローを定義し、Herokuアプリを作成し、PostgreSQLデータベースをプロビジョニングし、GitHub Actionsを使用してバックエンドをHerokuにデプロイすることで、継続的インテグレーションとデプロイメントを設定しました。

リポジトリにコミットして変更をプッシュすることで新しい機能を導入すると、テストとTypeScriptコンパイラが自動的に実行され、成功すればバックエンドがデプロイされます。

Herokuダッシュボードにアクセスすると、メモリ使用量、応答時間、スループットなどのメトリクスを表示できます。これは、バックエンドが異なるトラフィック量をどのように処理するかを把握するのに役立ちます。例えば、バックエンドへの負荷が増加すると、応答時間が遅くなる可能性があります。

TypeScriptをPrisma Clientと共に使用することで、通常ランタイムで検出されデバッグを必要とする型エラーのクラスを排除できます。

バックエンドの完全なソースコードはGitHubで確認できます。

Prismaはリレーショナルデータベースとの作業を容易にすることを目指していますが、基盤となるデータベースとHerokuの具体的な詳細を理解することは有益です。

ご質問があれば、お気軽にTwitterでご連絡ください。

次回の投稿もお見逃しなく!

Prismaニュースレターに登録

© . All rights reserved.