エンジニア

GitHub ActionsでServerless FrameworkのCI/CDパイプラインを構築する

GitHub ActionsでServerless FrameworkのCI/CDパイプラインを構築する

CRMチームのyktakaha4です。
みなさま、消費増税対応お疲れ様でした…!

今日は、前回のブログでお伝えした荷札生成APIの続編として、
エンジニアサイドの運用上の課題を解決するためにGitHub Actionsを導入した話についてお伝えします。

荷札生成APIは、東急ハンズのECサイトであるところのハンズネットの出荷業務にて、DBに格納されている注文情報を元に梱包後のダンボールに貼り付ける荷札や商品明細の印字データをPDF形式で出力するもので、実装としてはServerless Frameworkを用いてLambda関数を定義しています。

Serverless Frameworkには、定義ファイルからLambdaをパッケージングしてAWS環境にデプロイしてくれるdeployというコマンドがあり、こいつは大変便利ですごいやつなのですが、
deployコマンドを実行する環境そのものまで用意してくれる訳ではないので、利用者各自で運用方法を考える必要があります。

荷札生成APIはリプレースで作成した新規プログラムで、開発者は基本的に私ひとり(RVはしてもらってましたが)という状況だったこともあり、今までは以下の図で示すようにローカル環境からデプロイをおこなっていましたが、
システムが本格運用に差し掛かる中で、デプロイ手順や環境が個人に紐づいている状況は望ましくないと思い、リポジトリ上のソースコードが更新されたら自動的にデプロイが行われる仕組みを構築する必要があると考えていました。

従来のデプロイフロー(もとい手作業)

また、システムを長期稼働させる上で、実行環境やライブラリのバージョンアップ時の動作保証をどのように担保するか、という課題がありました。

荷札生成APIは現在はNode.js v8.10上で動作させているのですが、こちらのバージョンは既にMaintenance LTSでEOLも年末に迫っているため、準備が整い次第バージョンアップを進めていく必要があります。
前回のブログにも書いたとおり、本APIは内部処理にてHTML帳票をPuppeteerを用いてPDF化していますが、実行環境がMacBookだった場合とLinuxだった場合で出力されるPDFに微差が生じることがわかっていました。

ダンボールに貼る印刷物を作っているという性質上、ちょっとした表示不具合でもデータリカバリだけでは済まされないような障害・事故に繋がりかねないため、プログラムやライブラリ、実行環境を更新した際に、最終的な出力結果にどのような変更差分が生じたか検知できる仕組みをデプロイフローの中に組み込めるとよさそうです。

ということで、消費増税対応のためのプログラム修正がひととおり済んだところで、前述の課題を解決するため、荷札生成APIへのCI/CD導入に取り組むこととしました。
ラボ内ではCircleCICodePipelineなどの利用実績がありましたが、今回はGitHub Actionsで実現することとしました。

早速図を貼ってしまいますが、GitHub Actions導入後は以下のようになりました。

GitHub Acsions導入後のフロー

GitHub Actionsは、2019年11月よりGAとなる予定の新機能で、プルリクエストやブランチへのプッシュといった操作に対して、Dockerイメージを用いた任意のワークフローを定義・実行できます。
CRMチームでも、thimi0412くんがECRにDockerイメージをプッシュするワークフローを作成したりなど、hcl形式で記述していた頃から積極的な利用をおこなっています。
(曰く、yaml移行はまあまあ大変だったらしいです…)

APIの開発中、CI/CDをはじめとした「時間ができたら着手したい…」と思っていた諸々の課題をIssuesに書き溜めていたところ、
今年CRMチームに新卒配属となったhomines22くんが拾ってくれて、デプロイ用のワークフローをささっと作ってくれました!
(具体的な定義内容は、こちらの記事にしっかりとまとめられています。)

彼らに限らず、ラボの若手エンジニアは本当に優秀な人物ばかりで、アラサーの自分としては助かる反面末恐ろしいです…

超ありがたい

テストについてはいくつかのアプローチが考えられましたが、前述したライブラリのバージョンアップ等に伴うデグレードを確実に検知できるようにするため、
スナップショットテスト(ビジュアルリグレッションテスト)のケースを重点的に作成し、一部カバレッジが低くかつ業務重要度の高い箇所に対しては単体レベルのテストを書く…という方針としました。

具体的なテスト方法としては、RDSに接続して注文データを取得する箇所のコードをスタブに置き換えて、テストパターン毎に用意したJSONファイルの値を返却するようにした上で、
それらのテストデータを使ってmasterブランチの内容で荷札出力したPDFも合わせてリポジトリに格納しておき、JSONから作成したPDFと前述のPDFの内容をGraphicsMagickでPNG画像化&比較するようにしています。

差分が発生したら比較ファイルを確認し、それが機能変更などの意図的な改修によるものであれば問題なしとして比較用PDFを更新してテスト完了、
逆に、ライブラリの更新などで生じた差分だった場合は、原因を調査…という感じになります。

スナップショットテストのイメージ

テストフレームワークとしては、デファクトスタンダードでありCRMチームでも実績のあったJestを使用することにしました。
また、テスト実施にあたって実際の環境との差異を少なくするため、docker-lambdaというLambdaの実行環境を模したDockerイメージを利用しています。

実際に動作イメージを交えながらデプロイフローを見ていきましょう。
GitHub上でトピックブランチのプルリクエストを作成すると、ブランチのコードに対してLint CheckとTestのワークフローが自動的に実行されます。
リポジトリのBranchesの設定により、各ワークフローが正常終了した場合のみマージを許可するようにしています。

アクションの実行

リポジトリのActionsタブを選択すると、各ワークフローの実行状況が確認できます。
ワークフローの大まかな流れは、ブランチをチェックアウト→パッケージのビルド・インストール→やりたいこと(構文解析やテストの実行、デプロイなど)→成功/失敗を通知…という感じになっています。

ワークフローの実行ログの確認

スナップショットテストについては、もしもリポジトリに登録されている比較用PDFとテストデータのJSON&プログラムから出力したPDFが異なっていた場合は、以下のように差分を赤色化した画像ファイルを作成してくれます。
なお、テストデータについては住所・氏名などの個人情報をダミーデータに置き換えたものを登録しています。

ふたつのPDFがこんな感じだったら…
こんな感じの比較画像が生成されます

本当はテスト失敗時に比較画像をプルリクエストのコメントに投稿したいのですが、時間が足らず実装できていません…今後の課題です。
代わりと言ってはなんですが、テスト成功時にはIstanbulのテストカバレッジを投稿します。

テストカバレッジ

構文チェックとテストの結果に問題なければ、AWS上へデプロイします。
トピックブランチをdevelopブランチへマージすると、今度はDeployのワークフローが実行されます。

デプロイの実行

予めリポジトリにAWSへの接続情報をSecretsとして設定しているため、GitHub Actions上からServerless Frameworkのコマンドが実行できています。
デプロイが成功(または失敗)したら、LintやTestの時と同様にSlackへ通知されて完了となります。

きれいな通知

ちなみに、ワークフローの実行結果をSlackにいい感じの見た目で通知してくれるslatifyは、homines22くんがOSSとして開発・公開しているものです!つよい!
先日大阪で行われたGitHub ActionsのMeetupで登壇した際の発表資料が公開されていますので、こちらもぜひご覧ください。

実際に作成・運用してみての感想としては、GitHub Actionsは導入にかかるコストも低く、CI/CDを手軽に始めるには非常に良い選択肢だと思いました。

今後の機能拡充にも大いに期待しつつ、どんどん使っていきたいですね!

一覧に戻る