東京ガス内製開発チーム Tech Blog

東京ガス内製開発チームのTech Blogです!

myTOKYOGASフロントチームによるDevSecOpsの旅路への第一歩

はじめまして!東京ガスでmyTOKYOGASフロントエンドチームを担当している中嶋です!

本稿では、当チームのプロダクト開発において、パッケージ更新とリリース作業を取り上げて、内製開発の様子をご紹介させて頂ければと思います。

プロダクトの技術スタックについては、こちらをご覧ください。

note.com

パッケージ更新について

パッケージ更新は地味な作業かもしれないですが、更新せずに長期間に渡って放置しておくとセキュリティに関する脆弱性の問題や重大なバグの修正が見落とされるリスクがあります。そのため、定期的に更新しながらプロダクトに反映し続けていくための体制や運用を整えていくことが重要かと思います。我々のチームでは基本的に1Sprint2週間のサイクルで回しており、新規機能開発やバグ修正などのリリースがない場合でも、アプリのパッケージ更新やコンテナの脆弱性診断を定期的に行いプロダクトに反映していくために、基本的に1Sprintで1回以上は定期リリースを行う方針で開発を行なっております。

Sprintのスケジュール
Sprintのスケジュール

Dependabotによる自動更新

パッケージ更新作業については、基本的には「dependabot」を利用しています。dependabotを導入することによって、依存するパッケージのアップデートを定期的にチェックすることができます。また、dependabotによって自動生成されたPull Request(以下、PR)では、changelogや対応するコミットの内容を確認することができます。

dependabotが自動生成したPull Request
dependabotが自動生成したPull Request

また、PRの作成をトリガーとしてテストコードを実行するGithub Actions(CIジョブ)を用意しているため、dependabotによるPRが自動生成されると、自動的にテストコードが動いて機能面でリグレッションが発生していないかを確認することができます。

テストコードを実行するCIジョブ
テストコードを実行するCIジョブ

当チームのプロダクトはフロント画面もBFFもTypeScriptを採用しているため、どちらもパッケージマネージャはnpmを利用しています。どちらもマイナーバージョンの更新から検知するように設定しており、BFFは1日1回の間隔で定期実行しています。フロントエンドで利用しているパッケージに関しては更新頻度が多い傾向もあって、現在は1週間に1回の頻度で定期実行しています。npmで管理されるパッケージには実際の稼働環境で使われているdependenciesと、開発時のみに利用されるdevDependenciesの2つがあります。dependenciesのパッケージに関して更新があった場合は、テストコードが全て動いていることを確認し、changelogの内容をチェックした上でマージするようにしています。一方のdevDependenciesのパッケージに関しては定期実行を待たずnpm-check-updatesなどのツールを使って一括でまとめて手動更新することもあります。

dependenciesのパッケージについては、プロダクトのコードのどの部分で、どの機能に依存しているのかを把握しておくことで、ある程度はパッケージに依存する機能に関するテストコードを書くことも可能かと思います。しかし、メジャーバージョンが更新されて後方互換性が失われるような変更が発生してしまうと事前に用意したテストコードも意味がなくなり、テストコード自体を改修するコストが生まれてしまうため、費用対効果としてはあまり採算が合わないと思います。そのためCIで動かすテストコードでは、パッケージが提供する機能に焦点を当てず、あくまでプロダクトとして期待される機能要件を満たしているかという観点で書くように心がけています。

コンテナのセキュリティ脆弱性診断について

フロントチームの開発領域として、バックエンドAPIから返却されるデータを統合的に集約してフロント画面に描画しやすいように整形する責務を担うBFF(Backend for Frontends)があります。BFFは、Azure App ServiceのWeb App for Containersの上で動いています。全体の構成としては、以下のイメージになります。

myTOKYOGASのシステム構成
myTOKYOGASのシステム構成

コンテナはVMと比較するとポータビリティの観点で優れている一方でゲストとホストの分離レベルが弱いため、セキュリティ脆弱性に対してはより細心の注意を払う必要があると思います。当チームでは、コンテナイメージのリスクについて、CIのテスト実行時と定期的に実行されるジョブを用いて脆弱性診断を実施する仕組みを導入しています。CIのワークフローとは別に定期実行のジョブを用意して脆弱性診断を行うことで、コンテナイメージの作成時だけでなく作成後に新しい脆弱性が見つかった場合にも検知できる体制を整えています。

脆弱性診断のワークフロー
脆弱性診断のワークフロー

コンテナイメージの脆弱性診断ツールとしては、Trivyを利用しています。CIのテスト実行時では、脆弱性の重大度レベル(severity)はMEDIUM,HIGH,CRITICAL、脆弱性タイプはosとlibraryを設定しています。また、開発の流れを止めないよう修正がまだ提供されていない脆弱性に関しては、エラーとしない設定にしております。一方で、定期実行するジョブの方では全ての重大度レベル(severity)を検証して特定のSlackチャンネルに通知する運用を行なっています。

脆弱性診断結果のslack通知
脆弱性診断結果のslack通知

すでに解消済みのバージョンが存在する脆弱性が見つかった場合は、コンテナイメージを再ビルドすることによって最新版のパッケージが反映されて、脆弱性のエラーが解消したことを確認できたら、デプロイを行う流れになります。一方で、新たにまだ未解消な脆弱性が見つかった場合は、エンジニアが脆弱性の内容を精査し、深刻度に応じて個別に対応要否を検討します。

リリース作業について

テスト用の検証環境

次に、検証環境について簡単にご紹介させて頂きます。リリースを頻繁かつ継続的に実施していく上で、テストを行うための検証環境の整備は不可欠かと思います。現在はWeb用、モバイル用、社内向けの3種類のデプロイ先があり、それぞれに対して複数の検証環境を用意しています。デプロイ先と環境数を掛け合わせると10種類以上の検証環境で日々の開発と並行しながらテストが実施されています。実際にテストアカウントを使って動作確認を行うチームがいるため、各関係者と連携しながら機能開発を進めていきます。そのため、基本方針として開発を行うフロントチーム側でレビューが完了し、デプロイ可能な状態になったとしてもすぐにマージはせず、現在の動作確認の対応状況に応じて適宜必要なものだけをマージしていくような流れとなっています。以下が全体のイメージになります。

デプロイ先と検証環境のイメージ
デプロイ先と検証環境のイメージ

ブランチ運用について

ブランチ運用については、基本的にdevelopをデフォルトブランチとしてレビュー済みになったPRを取り込んでいき、最新版のdevelopブランチを各検証環境に反映する流れになっています。検証環境には、モック用のサンプルデータを利用する環境とバックエンドや外部サービスのAPIと疎通して実データを利用する環境に大別されます。検証作業は各機能開発やバグ修正などの内容に応じて、それぞれの環境を適切に使い分けながら実施されています。すべての検証作業が無事に完了したら、mainブランチに反映して本番リリースを実施する流れになっています。

ブランチ運用
ブランチ運用

開発者はdevelopからfeature, hotfix, pkgなどの命名規則に従ったブランチを切り、Linearで管理されたチケット(機能開発, バグ修正, パッケージ更新など)に着手していきます(タスク管理については、こちらでも触れています)。これらの命名規則に従ったブランチを作成することでPRを作成する際に自動的に変更内容を表すラベルが付与される仕組みになっています。

Pull Requestに自動付与されるラベル
Pull Request(サンプル)に自動付与されるラベル

本番リリースが完了した後にリリースノートを作成します。その際にPRに付与されたラベルごとに変更内容を整理して自動でリリースノートを作成するジョブを動かしています。また、リリースノートを作成する際にはセマンティックバージョニングに基づいてバージョンタグを発行します(後方互換性の有無や修正内容を加味してリリースごとにバージョンを付けて管理しています)。

リリースノートの作成
リリースノートの作成(サンプル)

デプロイ作業について

デプロイする資材としては、Next.jsの静的なビルド(Static Exports)によって生成されたwebアセットとNestJSを動かすコンテナイメージが基本になります。それぞれAzure Static Web Appsと、Azure App ServiceにデプロイするためのGithub Actions(CDジョブ)が定義されているため、それらを実行するだけでデプロイ作業は完了します。

Azure Static Web Appsへのデプロイは、ビルドからデプロイまでを一気通貫で行ってくれるGithub Actions(Azure/static-web-apps-deploy)が用意されているため、それを利用するだけで簡単にデプロイ用のワークフローを構築することができます。また、デプロイ前後ではDatadogのRUM Development Trackingを利用して最新版のアプリが適切にエンドユーザーへ配信されたことを確認しています。

Azure Static Web Appsのデプロイ確認
Azure Static Web Appsのデプロイ確認

Azure App Serviceへのデプロイは、ダウンタイムを発生させないようスロット機能を利用したBlue-Greenのデプロイ方式を採用しています。リリースの前日にプレスロット環境に事前にデプロイしておき、当日は運用中スロットへスワップ作業のみを実施する方針にしています。また、リリース直後に何らかの不具合が発生した場合は、再度スワップを実行するだけでロールバックできる状態になっています。

Azure App Serviceにはアプリで利用される環境変数を手動で登録しています。一方、アプリ側では.envファイルを利用した環境変数の管理を行っています。新規機能開発に伴いアプリ上で新しい環境変数が追加されると、Azure App Service側で環境変数の追加設定を忘れてしまう可能性が考えられます。そのため、当チームではプレスロット環境へデプロイする際に環境変数の差分チェックを行うことで不整合が発生しないようにしています。

環境変数の差分チェック
環境変数の差分チェック

また、スワップ時にも変更前と変更後の環境変数の差分をチェックすることで、意図しない変更が発生しないように細心の注意を払っています。

スワップ時の環境変数の差分確認
スワップ時の環境変数の差分確認(サンプル)

Blue-Greenのデプロイ方式を導入することで、ダウンタイムの発生を防ぎながらデプロイにかかる実行時間も短縮することができます。

おわりに

今回は、パッケージ更新とリリース作業に焦点を当てて、それぞれの詳細に触れるというよりも、全体の流れや大枠のイメージを掴んで頂けるよう、フロントチームにおける開発業務の一部をご紹介させて頂きました。フロントチームはまだ発足したばかりの段階で、定まったルールや方針なども少なく、意見を出し合いながら柔軟に変化しながら成長していく過程にあります。また、解決したい課題や挑戦したいことなどもたくさん抱えている状況です。個人的な感想になってしまいますがチームの雰囲気としては非常に良く、自由に意見を言い合える風土があり、エンジニアとして働きやすい環境だと感じています。

現在ソフトウェアエンジニアのポジションにて積極的に採用を行なっています。

Application Developers www.wantedly.com

SRE www.wantedly.com

もし少しでも興味を持って頂けたら、カジュアル面談からでも良いのでご連絡頂けると嬉しいです!ぜひ、一緒により良いプロダクトを開発していきましょう!