変化の激しいウェブ開発の世界で、継続的インテグレーションと継続的デリバリー(CI/CD)は、高品質のソフトウェアを効率的に生み出すために不可欠な概念となっています。CI/CDによって、ビルド、テスト、デプロイのプロセスを自動化、さらにヒューマンエラーによるリスクを減らしながら、素早い反復を可能にします。
この記事では、CI/CDの重要性、CIパイプラインの作成方法、そして、GitHubリポジトリ(Actions)とKinsta APIを使ってCIパイプラインの継続的デプロイをプログラムを介して実行できるようにする方法をご説明します。
CI/CDの存在意義
Kinstaのアプリケーションホスティングプラットフォームには、ホストするGitリポジトリの特定のブランチに変更があるたびにそれを検知する、自動デプロイオプションがあります。しかし、これは複数のチームメンバーがいる大規模なプロジェクトでは、必ずしも理想的でない可能性があります。多くの開発者が、さまざまな理由から自動デプロイメントの有効化を避ける傾向にあります。
その理由のひとつとして、複数の開発者が同じプロジェクトに携わっているような共同作業環境で、ひとりの開発者がリポジトリを変更したことをきっかけに自動デプロイが始まると、不安定になったり予期せぬ問題が発生したりすることがあります。適切なテストと検証を行わないと、小さなコードの変更でさえ本番サイトに混乱を引き起こし、ダウンや好ましくないユーザー体験につながる可能性があります。
そんな状況を解決するのに便利なのが、CI/CDパイプラインです。入念に設計されたCI/CDワークフローを用意することで、コードの変更内容を本番サイトにデプロイする前に、確実にテストと検証を行うことができます。ソフトウェア開発にCI/CDを導入するツールはたくさんありますが、こちらの記事では、GitHub Actionsを使います。
GitHub Actionsとは
GitHub ActionsはGitHubが提供する強力な自動化ツールです。ソフトウェア開発プロジェクト内の様々なタスクやプロセス、ワークフローを自動化することができます。GitHubリポジトリと統合されているので、簡単に使いこなすことができます。
GitHub ActionsとKinsta APIを使えば、プロジェクトの要件に合ったカスタムワークフローを定義可能です。例えば、今回ご紹介するように、アプリケーションをテストし、KinstaへのデプロイをトリガーするCIパイプラインを設定することができます。
GitHub Actionsを利用する
GitHub Actionsは、ワークフローというコンセプトで機能します。ワークフローは、特定のイベントによってトリガーされる、または定期的にスケジュールされる自動化されたタスクのセットと定義できます。イベントには、コードのプッシュ、プルリクエスト、課題の作成などがあります。このようなイベントが発生すると、GitHub Actionsにより関連するワークフローが自動的に実行され、定義済みの一連のステップが実行されるというものです。
ワークフローの各ステップは、コードのビルドやテストの実行、デプロイ、通知の送信といった特定のアクションを表します。3つのタスクからなるワークフローを作成してみましょう。
- ESLintで構文をチェックする
- テストを実行する
- アプリケーションを再デプロイする
ステップ 1. GitHubリポジトリのセットアップ
GitHub Actions を使い始めるには、GitHubリポジトリが必要です。ここでは「ChatGPTクローンアプリケーションをReactとOpenAI APIで構築、デプロイする方法」で利用したこちらのGitHubリポジトリを使います。
GitHub のリポジトリに移動して「Use this template」>「Create a new repository」を選択してください。
このReactアプリケーションでは、各コンポーネントをテストするためにユニットテストが作成されています。また、完璧な構文とコードフォーマットを強制するためにESLintを使用しています。リポジトリにプッシュされたプルリクエストやマージされたコードがワークフローのテストに失敗すると、CIパイプライン内でデプロイがブロックされる仕組みです。
ステップ2. ワークフローファイルの作成
リポジトリの.github/workflowsディレクトリにYAMLファイルを作成してワークフローを定義します。このディレクトリはリポジトリのルートレベルにあるはずです。ワークフローファイルの命名規則は、name-of-the-workflow.ymlです。
- リポジトリ内に.githubディレクトリを作成します。
- .githubディレクトリの中に、workflowsという新しいディレクトリを作成します。
- workflowsディレクトリの中に、build-test-deploy.ymlのような名前で新しいファイルを作成します。
ステップ3. CI/CDワークフローの記述
ワークフローファイルを作成したので、ESLintで構文をチェック、テストを実行し、アプリケーションをデプロイするために必要なステップをワークフローとして定義します。
CIイベントの作成
CIパイプラインを作成する場合、最初のステップとして、ワークフローに名前を付け、ワークフローのトリガーとなるイベントを設定します。この例では、プルリクエストとメインブランチへのプッシュという2つのイベントを設定します。
name: Build, Test, and Deploy
on:
push:
branches: "main"
pull_request:
branches: "main"
特定のタスクに対して定期的なジョブ(cronジョブ)を設定したい場合には、これをワークフローに追加することができます。例えば、データベースのバックアップやデータのクリーニング、その他定期的なメンテナンス作業などがこれにあたります。
以下は、ワークフローにcronジョブを追加する方法の一例です。
on:
# pushとpull_requestの既存のイベントトリガー
# CRONジョブのスケジュールを追加
schedule:
- cron: "0 0 * * *"
上記の例では、cronスケジュールが0 0 * * *
に設定されているため、毎日午前0時(UTC時間)にワークフローがトリガーされます。状況に合わせてcronスケジュールをカスタマイズすることができます。
別の例として、毎週月曜日の午前8時にCI/CDワークフローを実行するようにスケジュールを設定したいとします。schedule
イベントを使用して、以下のようにcronジョブを設定することができます。
name: Build, Test, and Deploy
on:
push:
branches: "main"
pull_request:
branches: "main"
#ワークフローを毎週月曜日の午前8時(UTC時間)に実行するように設定
schedule:
- cron: "0 8 * * 1"
jobs:
#ジョブを追加
GitHub Actionsワークフローのschedule
イベントで使われているスケジュール構文は、UNIXのcron構文に基づいています。このようにワークフローが自動的に実行される時間や間隔を定義することができます。この構文は、スケジュールの様々な側面を表す5つのフィールドで構成され、各フィールドはスペースで区切られています。スケジュール構文の一般的なフォーマットは以下の通りです。
* * * * *
┬ ┬ ┬ ┬ ┬
│ │ │ │ │
│ │ │ │ └─ Day of the week (0 - 7) (Sunday to Saturday, where both 0 and 7 represent Sunday)
│ │ │ └─── Month (1 - 12)
│ │ └───── Day of the month (1 - 31)
│ └─────── Hour (0 - 23)
└───────── Minute (0 - 59)
では、各フィールドを分解してみましょう。
- 分(0 – 59):cronジョブがトリガーされる分。例えば、
15
は、毎時15分にワークフローが起動することを意味します。 - 時間(0 – 23):cronジョブが起動する時間。例えば、
8
は、ワークフローが午前8時にトリガーされることを意味します。 - 日(1 – 31):cronジョブが起動する月の日。例えば、
1
は、月の1日にワークフローが起動することを意味します。 - 月(1 – 12):cronジョブが起動する月。例えば、
6
は、ワークフローが6月に起動することを意味します。 - 曜日(0 – 7):cronジョブが起動する曜日。
0
と7
は日曜日を表し、1
は月曜日を表します。例えば、4
は木曜日を表すことになります。
特殊文字
*
(アスタリスク):そのフィールドのすべての値にマッチします。例えば、分フィールドに*
を使用すると、ワークフローが毎分トリガーされることになります。*/n
(スラッシュ):間隔を指定します。例えば、分フィールドの*/5
は、ワークフローが5分毎にトリガーされることを意味します。,
(カンマ):複数の特定の値を指定できます。例えば、分フィールドの1,15,30
は、ワークフローが1分、15分、30分毎にトリガーされることを意味します。-
(ハイフン):値の範囲を指定できます。例えば、曜日フィールドの1-5
は、ワークフローが月曜日から金曜日(1~5)までトリガーされることを意味します。?
(クエスチョンマーク):特定の値を指定しない場合に使用します。一般的には、曜日フィールドで(日付が指定されている場合に)使用されます。例えば、曜日フィールドに「?
」、日付フィールドに「15
」を指定すると、曜日に関係なく、ワークフローが毎月15日にトリガーされます。
ESLintで構文をチェックするCIジョブの作成
CIプロセスをセットアップするために、必要なジョブやタスクを作成します。各ジョブにはわかりやすい名前をつけましょう。最初のジョブはESLintを使ってコードの構文をチェックするので、eslint
とします。
さらに、任意ではありますが、人間が確認しやすいように説明を加えることもできます。次に、ジョブはUbuntu環境で実行するように指定し、2つのNode.jsのバージョン18.x
と20.x
についてコードをテストすることにします。
jobs:
eslint:
name: Check Syntax with ESLint
runs-on: ubuntu-latest
strategy:
matrix:
node-version: [18.x, 20.x]
次に、「ESLint」ジョブが実行するステップを定義します。実際のステップは、コードのチェックアウト、ESLintを実行するための指定したNode.jsバージョンのセットアップ、npmパッケージのキャッシュ、プロジェクト依存関係のインストール、そして最後にコードの構文を確認するESLintの実行となります。
steps:
- name: Checkout code
uses: actions/checkout@v3
- name: Use Node.js ${{ matrix.node-version }} to Check Lint
uses: actions/setup-node@v3
with:
node-version: ${{ matrix.node-version }}
cache: 'npm'
- name: Install Dependencies
run: npm ci
- name: Run ESLint
run: npm run lint
上のワークフローでは、GitHub Actionsからワークフローを確認するときにエラーやバグの原因を特定しやすいように、各ステップに名前と説明をつけています。特筆すべきは、3番目のステップで、npm ci
コマンドを使って依存関係をインストールするものです。これは、npm install
よりもクリーンなインストールができるので推奨されます。さらに、最後のステップでは、npm run lint
を使ってESLintを実行しますが、package.jsonファイルでこのコマンドを設定していることを前提としています。
以下は、ESLintでコードの構文をチェックする全体像です。
jobs:
eslint:
name: Check Syntax with ESLint
runs-on: ubuntu-latest
strategy:
matrix:
node-version: [18.x, 20.x]
steps:
- name: Checkout code
uses: actions/checkout@v3
- name: Use Node.js ${{ matrix.node-version }} to Check Lint
uses: actions/setup-node@v3
with:
node-version: ${{ matrix.node-version }}
cache: 'npm'
- name: Install Dependencies
run: npm ci
- name: Run ESLint
run: npm run lint
テストを実行するCIジョブの作成
テストを実行するCIジョブを追加するには、まずジョブを定義し、tests
のようなわかりやすい名前をつけます。このジョブはeslint
ジョブに依存します。つまり、tests
ジョブが実行される前にeslint
ジョブが実行されることになります。この依存関係により、テストを実行する前にコードの構文エラーがチェックされます。
tests:
name: Run Tests
needs: eslint
runs-on: ubuntu-latest
次に、tests
ジョブのステップを定義します。 前のジョブと同様に、コードをチェックアウトし、テストを実行するためにNode.jsのバージョン18.x
をセットアップし、npm ci
を使ってプロジェクトの依存関係をインストールし、npm run test
コマンドを使ってテストを実行します。
steps:
- name: Checkout code
uses: actions/checkout@v3
- name: Use Node.js 18.x to run Test
uses: actions/setup-node@v3
with:
node-version: 18.x
cache: 'npm'
- name: Install Dependencies
run: npm ci
- name: Run Tests
run: npm run test
Kinsta APIでデプロイするためのCIジョブの作成
Kinsta APIを使用してKinstaにデプロイするCIジョブを作成するために、ジョブを定義し、名前をdeploy
とします。このジョブは、eslint
、tests
ジョブに依存します。つまり、コードの構文エラーがチェックされ、テストに合格した後にのみデプロイが実行される構造です。利用可能な最新バージョンを使ってUbuntu環境で実行するようにジョブをセットアップします。
deploy:
name: Re-Deploy Application
needs: [eslint, tests]
runs-on: ubuntu-latest
次に、ステップを定義します。このケースでは、プログラムを介してKinsta APIとやりとりし、再デプロイメントをトリガーするためにcURLコマンドを実行します。続いては、Kinsta APIの基本、APIとやり取りするために必要な様々な知識、APIキーなどのAPI関連情報を安全にGitHubで取得/保存する方法を理解しましょう。
Kinsta APIを理解する
Kinsta APIを利用することで、プログラムを介してKinstaのサービスを操作することができます。APIを使用するには、MyKinstaで少なくとも1つのWordPressサイト、アプリケーション、またはデータベースのアカウントを所有する必要があります。また、APIを通してアカウントを認証し、アクセスするためにAPIキーの生成が必要になります。APIキーを生成する方法は次の通りです。
- MyKinstaにログイン
- 「APIキー」ページに移動((アカウント名)>「企業の設定」>「APIキー」)
- 「APIキーを作成」をクリック
- 有効期限を選択するかカスタムで指定
- キーに一意の名前を付ける
- 「生成」をクリック
APIキーを作成したら、コピーして安全な場所に保管してください(パスワードマネージャを使用することをお勧めします)。作成時にしか表示されませんのでご注意ください。
Kinsta APIでデプロイをトリガーする方法
APIを使用してKinstaにアプリケーションをデプロイするには、アプリケーションIDとブランチという2つのパラメータが必要です。まずアプリケーション一覧を取得し、アプリケーションのIDを把握することができます。
その後、APIの/applications/deploymentsエンドポイントにPOSTリクエストを行います。CIパイプラインには、URLとやりとりするコマンドラインツールであるcURLを使用します。
curl -i -X POST
https://api.kinsta.com/v2/applications/deployments
-H 'Authorization: Bearer <YOUR_TOKEN_HERE>'
-H 'Content-Type: application/json'
-d '{
"app_id": "<YOUR_APP_ID>",
"branch": "main"
}'
CI/CDパイプラインでcURLを使用してデプロイメントをトリガーする
Kinsta APIでデプロイをトリガーするには、CIパイプラインのrun
コマンドにcURLコマンドを追加します。ただし、APIキーとアプリケーションIDを安全に保存することが重要です。
シークレット(Secrets)をGitHubに保存しGitHub Actionsで使用するには、以下の手順に従ってください。
- シークレットを設定したいリポジトリに移動
- リポジトリのメニューから「Settings」タブをクリック
- 左サイドバーの「Options」カテゴリで「Secrets」を選択
- 「New repository secret」をクリック
- シークレットの名前(
KINSTA_API_KEY
のようなもの)を入力し、「Value」フィールドにKinsta APIキーを入力 - 名前と値を入力したら、「Add secret」ボタンをクリックして保存
- 他のシークレットでもこのプロセスを繰り返す
シークレットを追加したら、${{ secrets.SECRET_NAME }}
という構文を使って GitHub Actionsワークフローで参照することができます。
それでは、GitHub Actions CI/CD パイプラインのdeploy
ジョブを完成させましょう。先ほどと同じようにステップを定義し、Kinsta にデプロイするステップをひとつ追加します。まず、env
コマンドでシークレットを定義し、デプロイを実行するcURLコマンドを追加します。
steps:
- name: Deploy to Kinsta
env:
KINSTA_API_KEY: ${{ secrets.KINSTA_API_KEY }}
APP_ID: ${{ secrets.APP_ID }}
run: |
curl -i -X POST
https://api.kinsta.com/v2/applications/deployments
-H "Authorization: Bearer $KINSTA_API_KEY"
-H "Content-Type: application/json"
-d '{
"app_id": "'"$APP_ID"'",
"branch": "main"
}'
cURLコマンドでは、環境変数がコマンド内に追加され、デプロイプロセス中に安全にシークレットにアクセスできます。
最終的なCI/CDワークフローは以下のようになります。
name: Build, Test, and Deploy
on:
push:
branches: "main"
pull_request:
branches: "main"
jobs:
eslint:
name: Check Syntax with ESLint
runs-on: ubuntu-latest
strategy:
matrix:
node-version: [18.x, 20.x]
steps:
- name: Checkout code
uses: actions/checkout@v3
- name: Use Node.js ${{ matrix.node-version }} to Check Lint
uses: actions/setup-node@v3
with:
node-version: ${{ matrix.node-version }}
cache: 'npm'
- name: Install Dependencies
run: npm ci
- name: Run ESLint
run: npm run lint
tests:
name: Run Tests
needs: eslint
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v3
- name: Use Node.js 18.x to run Test
uses: actions/setup-node@v3
with:
node-version: 18.x
cache: 'npm'
- name: Install Dependencies
run: npm ci
- name: Run Tests
run: npm run test
deploy:
name: Re-Deploy Application
needs: [eslint, tests]
runs-on: ubuntu-latest
steps:
- name: Deploy to Kinsta
env:
KINSTA_API_KEY: ${{ secrets.KINSTA_API_KEY }}
APP_ID: ${{ secrets.APP_ID }}
run: |
curl -i -X POST
https://api.kinsta.com/v2/applications/deployments
-H "Authorization: Bearer $KINSTA_API_KEY"
-H "Content-Type: application/json"
-d '{
"app_id": "'"$APP_ID"'",
"branch": "main"
}'
ワークフローをコピーし、build-test-deploy.ymlファイルに貼り付けます。次に、このファイルをリポジトリのメインブランチに追加するプルリクエストを開始します。このプルリクエストが自動でワークフローをトリガーします。
これにより、リポジトリに加えられた変更内容をチェックし、プルリクエストの新しい変更内容が指定のチェック項目を満たしていることを確認した上で、コードベースにマージするかどうかを決めることができます。
プルリクエストをマージするGitHubリポジトリの「Actions」タブに移動すると、CI/CDワークフローが実行されていることがわかります。
各ジョブをクリックすると、そのジョブの中身を見ることができます(ジョブの各ステップに理解できる説明をつける必要があるのはこのためです)。
GitHubでのプルリクエストワークフローの強制
GitHubリポジトリで効果的なコード管理と共同作業を行うには、プルリクエストのワークフローを強制してメインブランチへの直接コミットをブロックするのが有効です。これによって、組織的な開発プロセスを確立することができます。すべての変更内容が、メインブランチにマージする前にプルリクエストとレビューを受けることになります。
この方法を採用することで、開発チームによるコードの質向上、バグを引き起こすリスクの最小化、変更履歴における透明性の維持が容易になるはずです。
以下に、プルリクエストワークフローの実施方法を説明します。
- GitHubリポジトリの「Settings」タブをクリック
- サイドバーの「Code and Automation」にある「Branches」を選択
- ルールがない場合は、「Add branch protection rule」をクリック
- ルールの名前を指定し、「Require a pull request before merging」にチェックを入れる(これで、設定のためのオプションがさらに表示されます)
- また「Require status checks to pass before merging」にもチェックを入れる
- 好みや要件に応じて、その他のオプションをカスタマイズ
- 「Create」ボタンをクリックしてルールを保存
以上の手順で、GitHubリポジトリでプルリクエストのワークフローを強制するルールの設定が完了しました。これで、すべての変更がメインブランチにマージされる前にレビューと自動チェックを経るようになります。信頼性の高い共同開発環境の構築にご活用ください。
まとめ
GitHub ActionsとKinsta APIを組み合わせることで、開発ワークフローを合理化し、開発チームの負担を軽減する効率的な環境を構築することができます。
本番環境への到達前に徹底的なテストが実行されるため、開発チームのメンバーは安心してコードの投稿を行えるはずです。その他利害関係者にとっても、デプロイプロセスが適切に管理される(エラーに強い環境)のは嬉しいものです。
あなたはKinsta APIをどのように使っていますか?APIに追加してほしいエンドポイントはありますか?Kinsta API関連のどんな解説をご希望ですか?コメント欄でお聞かせください。
コメントを残す