プルリクエスト駆動型デプロイのすゝめ

2022年12月23日掲載

キービジュアル

こんにちは。ソフトバンクの辻です。

この記事は、ソフトバンク Advent Calendar 2022 の 23日目の記事です。

Happy Birthday to Me 〜♪
はい、今日12月23日は私自身の誕生日ということもあり、記念にこの記事を執筆しています。

私は業務で複数のアプリケーション開発プロジェクトに携わっています。
そのなかで、便利なフレームワークやCI/CD技術を効果的に導入することで、非効率な作業を撲滅し、エンジニアが開発作業に注力できる幸せな環境づくりを目指しています。

この記事では、プルリクエストを起点に生じる非効率な作業の撲滅を目的とした「プルリクエスト駆動型デプロイ」という考え方について解説します。

記事の後半では、導入事例としてNuxt.jsで作成したSPA (Single Page Application)を、「プルリクエスト駆動型デプロイ」の考え方に従って、Azure Blob Storageにデプロイする実装方法もご紹介します。

この記事の内容は2022年11月14日の SB Tech Night #9 LINEアプリ開発編 において「LINEアプリ開発をさらに加速させるフレームワークとCI/CD技術」というテーマで発表した内容から一部を抜粋して記事にしたものです。
当日の発表資料には、この記事で紹介していない内容についても触れていますので、もし興味があればご参照ください。

なお、LINEがテーマのイベントでの発表内容だったことから、この記事内の会話イメージ画像もLINEをイメージしたものになっています。

目次

これまでの課題

GitHubやGitLabなどの分散型バージョン管理システムを使ってソースコードを管理している場合、プルリクエスト (GitHub) や マージリクエスト(GitLab) によって開発者の成果物を取り込むスタイルが一般的です。

通常、プルリクエストを受け取ると、レビュアーは以下のような流れで動作確認やレビューを実施することになります。

  1. プルリクエストを起票(開発者)
  2. 開発ソースコードを取得(レビュアー)
  3. ビルド(レビュアー)
  4. 動作確認・レビュー(レビュアー)

このなかで、ソースコードの取得やビルドなど、コマンドラインで操作する場合も多いため、コマンド入力ミスによるロスタイムが生じたり、依存関係のインストールやビルド完了までの待ち時間が発生したりと、レビュアーの貴重な時間を必要以上に専有してしまうことがあります。
また、場合によっては環境起因のエラーが発生して、エラー解消に奔走したりということも起こりえます。

この結果、いざ動作確認やレビューをはじめようとする頃には、レビュアー自身が疲弊してしまい正常な判断力でレビューできないという可能性もあります。(だいぶ盛ってます)

従来のプルリクエスト確認の流れ

この記事でご紹介する「プルリクエスト駆動型デプロイ」は、このようなレビュアーの負担を軽減し、本来注力すべき作業に専念できる幸せな世界を実現することを目的としたものです。

「プルリクエスト駆動型デプロイ」とは?

従来、プルリクエストの確認や動作確認の際は、前述のようにレビュアーの負担になる非効率な作業が多々ありました。

今回ご紹介する「プルリクエスト駆動型デプロイ」は、プルリクエストをトリガーとして発生する非効率な作業を自動化して幸せになろうという考え方です。

ちなみに、一般用語ではなく私が勝手に名付けただけですので、検索しても追加の情報は出てきません。

プルリクエスト駆動型デプロイは、プルリクエストの度に発生するレビュアーの負担になる非効率な作業を撲滅します

具体的には、以下のような流れで実現します。

  1. プルリクエストを起票(開発者)
  2. 自動的にBotが起動して以下の処理を実行
    1. CI (Continuous Integration) 処理
      1. 静的解析
      2. ビルド
      3. テスト
    2. CI処理でエラーがなければ、CD (Continuous Delivery) 処理
      1. プルリクエストに紐づくデプロイ先を自動生成
      2. デプロイ
      3. デプロイ先のURLをPOST
  3. 従来どおりの動作確認・レビューを実施(レビュアー)
プルリクエスト駆動型デプロイ導入後のイメージ

これによって、従来レビュアーの負担になっていた諸作業をBotが担ってくれるため、レビュアーは動作確認やレビュー作業に注力することができるようになります。

以下に、プルリクエスト駆動型デプロイの導入前後での、各作業の役割分担の変化についてまとめました。

 従来プルリクエスト駆動型デプロイ
プルリクエスト起票 🧑‍💻 開発者🧑‍💻 開発者
開発ソースコード取得🙎 レビュアー🤖 Bot
ビルド
操作確認・レビュー🙎 レビュアー

また、「プルリクエスト駆動型デプロイ」を導入することで、不具合発生時の原因調査を効率化することもできます。

不具合検出時、過去のプルリクエスト単位での動作確認環境が全て残っていることから、各プルリクエストのデプロイ先URLで動作確認するだけで、そのプルリクエストによる変更が原因か否かを即座に確認することができるためです。

従来手法では、各プルリクエスト単位でブランチを切り替えながらビルドして動作確認して…という作業を繰り返すことがあったことを考えると、こちらも大幅な効率化に寄与するかと思います。

プルリクエスト駆動型デプロイのまとめ

導入事例

実際のプロジェクトに導入した際の事例を、サンプルコードを交えながら解説します。

なお、基本的な設定を実装済みのサンプルプロジェクトはGitHubにて公開しておりますので、試してみたい方はご活用ください。

今回ご紹介するサンプルプロジェクトは、Nuxt.jsで記述したSPAをベースとしており、プルリクエストが起票されると、Azure Blob Storage上に 静的Webサイトホスティング するというものです。

成果物がデプロイされると、対象のプルリクエストのコメントに以下のようなコメントが投稿されます。(画像では日本語ですが、サンプルプロジェクトは英語になっています。)

デプロイ完了後のコメント例

事前準備

1. Azureの設定

この記事でAzureの操作を行う場合は、原則としてBashのCloud Shellを使用します。
AzureポータルのWebUIでも同様の操作が可能ですが、この記事ではその方法については解説しません。

まず、Azureポータルにログインし、画面右上の Cloud Shell アイコンをクリックします。
しばらく待機すると、画面下部にプロンプト領域が展開します。

AzureポータルでのCloudShellの起動

プロンプト領域の左上のプルダウンで、Power Shell が選択されている場合は、 Bash を選択します。

CloudShellのプロンプト画面

Cloud Shellでは、以下の設定を実施します。

  • リソースグループの作成
  • ストレージアカウントの作成
  • ストレージアカウントに静的Webサイトホスティングを設定
  • 静的WebサイトホスティングのベースURLを取得
  • サービスプリンシパルの作成
  • サービスプリンシパルにストレージアカウントのBlobへのアクセス権を設定

なお、ここで作成するサービスプリンシパルは、GitHub ActionsのランナーからAzure Blob Storageにデプロイする時に利用します。

Azure Cloud Shellに、GitHubのサンプルプロジェクトの README に記載のコードを入力し実行します。
なお、このとき SETTINGS に記載の各項目は、対応する値に変更してから実行する必要があります。

2. GitHubリポジトリの設定

サービスプリンシパルを作成した際に生成された認証情報やストレージアカウントの情報を、GitHubリポジトリのSecretsに登録します。

GitHubリポジトリのSecretsに登録することで、認証情報を保護しつつ、GitHub Actionsで安全に呼び出して使用することができます。

ブラウザでGitHubリポジトリを開き、 Settings を開きます。
Settings が表示されない場合は、権限不足の可能性があるため、リポジトリの管理者への問い合わせが必要です。

GitHubリポジトリの設定ボタン

リポジトリの設定画面で、 Security > Secrets > Actions と辿り、New repository secret ボタンをクリックします。

GitHubのシークレット設定画面までの導線

シークレットの設定画面で、以下のように入力し、Add secret ボタンをクリックします。

シークレット名設定すべき値
AZURE_CREDENTIALSサービスプリンシパル作成時に保存しておいたjsonファイルの内容を貼り付け
AZURE_SA_NAMEアクセスしたいストレージアカウント名
BASE_URLストレージアカウント作成時に取得した静的WebサイトホスティングのベースURL
GitHubのシークレット設定画面

GitHub Actionsのワークフロー

プルリクエストをトリガーとするワークフローは、GitHub Actionsを活用します。
GitLabをお使いの場合はGitLab CI/CDになりますし、その他のCIツールでも同様の仕組みを構築することが可能です。

GitHub Actionsを使用するには、リポジトリ直下に .github/workflows というディレクトリを作成し、このディレクトリ配下にワークフローを定義した yaml 形式のファイルを格納します。
GitHub Actionsは、yamlファイルに定義された条件に合致した場合に、対応するワークフローが自動で実行されます。

GitHub Actionsで、「プルリクエスト駆動型デプロイ」とは? で解説した処理を実装したワークフローファイルはGitHub上のサンプルプロジェクトの .github/workflows を参照ください。

デプロイ先のURLに関する考慮事項

Webアプリケーションを通常通りデプロイする場合と、プルリクエスト駆動型デプロイによってデプロイする場合、デプロイ先のURLには以下のような違いがあります。

通常のデプロイhttps://{your-base-url}/
 ※要件によっては app/ 等が続く場合もある
プルリクエスト駆動型デプロイhttps://{your-base-url}/{your-repository-name}/{pullrequest-id}/

Nuxt.jsでルート以外にデプロイする場合、nuxt.config.jsrouter.base プロパティにパス情報を設定することで対応可能です。

他のフレームワークで開発した場合にも同様の考慮が必要となるため、詳細は各フレームワークの仕様をご確認ください。

実際には nuxt.config.js には直接設定せず、プロジェクトの環境変数に設定して nuxt.config.js では環境変数を参照することで、環境ごとに設定ファイルを作成する必要がなくなります。

また、デプロイする環境によってベースURLも変わるため、ベースURLも環境変数を用いて設定できるようにします。

ワークフローファイルで、ビルド直前に実行している以下のステップが、環境変数を設定している箇所です。

     - name: Generate .env file for Deployment 📝
       run: |
         cat <<EOF > .env
         BASE_URL=${{ secrets.BASE_URL }}
         ROUTER_BASE=/${{ github.event.repository.name }}/${{ github.event.number }}/
         EOF

プロジェクトのトップ階層に .env ファイルを配置することで、 Nuxt.js の Runtime Config 機能にビルトインされた dotenv によって、ビルド時に環境変数として取り込まれます。

さらに nuxt.config.js を以下のように設定することで、環境変数をアプリケーション内に取り込むことができます。

export default {
  // https://nuxtjs.org/docs/configuration-glossary/configuration-router/
  router: {
    base: process.env.ROUTER_BASE || "/",
  },

  // https://nuxtjs.org/docs/directory-structure/nuxt-config/#runtimeconfig
  publicRuntimeConfig: {
    baseURL: process.env.BASE_URL || "http://localhost:3000",

    // axiosを使用している場合は以下の設定も加える
    axios: {
      baseURL: `${process.env.BASE_URL || "http://localhost:3000"}${
        process.env.ROUTER_BASE || "/"
      }`,
    },
  },
  privateRuntimeConfig: {},
};

今回の例ではProduction環境やStaging環境へのデプロイについては考慮していませんが、これらを考慮する場合は、 BASE_URL や ROUTER_BASE に設定すべき値が環境ごとに変わります。

GitHubのパブリックリポジトリをお使いの場合、または GitHub Team / Enterprise でプライベートリポジトリをお使いの場合は、リポジトリの Environments 機能を活用すると環境によってシークレットを切り替えることができます。

Environments機能を活用する場合、以下のようにワークフローのジョブ定義に使用するEnvironmentを指定する必要があります。

  jobs:
    CI:
      environment: Development

GitHub Freeのプライベートリポジトリで環境ごとに環境変数を切り替えたい場合は、名前の異なる環境変数を複数用意し、ワークフロー側で環境変数を使い分ける必要があります。

パブリックリポジトリにおける注意事項

プルリクエスト駆動型デプロイ自体は便利ですが、安易にパブリックリポジトリに適用することは推奨できません。

悪意のあるユーザによるプルリクエストが作成されることで、以下のようなインシデントに繋がります。

  • 意図しないコードがデプロイされ、何らかの攻撃や踏み台に使われる
  • デプロイ先のストレージコストが意図せず高額になってしまう

パブリックリポジトリに適用する場合は、リポジトリの設定、ワークフローの実装方法、デプロイ先のセキュリティ設定などを慎重に検討した上で適用してください。

おわりに

この記事では、開発者やレビュアーの負担を軽減する「プルリクエスト駆動型デプロイ」の考え方について解説しました。

この考え方は、今回の事例としてご紹介したNuxt.jsのSPAに限らず、さまざまなプロジェクトに導入してご活用いただくことができます。
ぜひ、ご自身の携わっているプロジェクトにも導入をご検討していただき、エンジニアが開発作業に注力できる幸せな環境を一緒に作っていきましょう。

この記事が皆さまの開発の一助となれば幸いです。

関連サービス

Microsoft Azure

Microsoft Azureは、Microsoftが提供するパブリッククラウドプラットフォームです。コンピューティングからデータ保存、アプリケーションなどのリソースを、必要な時に必要な量だけ従量課金で利用することができます。

おすすめの記事

条件に該当するページがございません