これは3/18に開催された、ROXX社内LT大会の資料です。

概要

今回は下記の内容を紹介します

  • back checkのリポジトリの状況
  • プレビュー環境に求められる要件
  • 参考にしたOSS
  • 実際の昨日のアウトプット
  • 具体的な実装

back checkのリポジトリの状況

backcheckには下記の3つのリポジトリが存在しています

  • LP
    • LPのフロントエンドが実装されている
    • Nuxt.js製
  • admin
    • 管理画面のフロントエンドが実装されている
    • Nuxt.js製
  • front
    • ユーザが使う画面が実装されている
    • Nuxt.js製
  • API
    • back checkが使うすべてのAPIが実装されてる
    • Laravel製

プレビュー環境に求められる要件

上記の様に、それぞれが別のリポジトリで実装されているため、下記のケースを想定したレビュー環境が必要となりました

  • LP, admin, front, APIそれぞれどれかだけの変更だけが反映されているもの
  • 複数のリポジトリにまたがった修正が反映されているもの

また、それ以外にプレビュー環境として、下記の要件を満たす必要がありました

  • プレビュー環境同士は完全に独立していること
    • 新規機能も多いためDBスキーマの変更にも耐えられるよう、DBも独立していること
  • githubのChatOpsでできること
  • Auth0と連携ができること
    • 環境作成時に、callbackURLなどが自動的に反映されること
    • https化されていること
  • PRがクローズされたら、自動で環境が削除されること
  • k8sとかの知識は不要で、AWS上で動くこと
  • リソースの上限とかはあんまり気にしたくない

参考にしたOSS

色々と探していたら、下記のOSS(というかプレゼン用に作ったと思われるなにか)が見つかった

https://github.com/clareliguori/clare-bot

clare-botというボットに対して、 preview this とメンションするだけで、プレビュー環境を作ってくれるというもの。使用している技術スタックも

Built with GitHub APIs, AWS Fargate, AWS CodeBuild, Amazon ECR, and AWS CloudFormation

とのことで、実装も簡単そうだったのでこれを使うことにした。

一方これだけでは最初の要件(マルチリポでよしなに使える)が適応できないため、ソースコードをいじる必要があった

実際の機能の使い方

  1. GitHub上でプルリクエストを作成します。
  2. プルリクエスト上で「@roxx-bot preview this」とコメントします。
  3. roxx-botさんがちょこちょことコメントを残していきます。(最長で30秒ほどかかります)
  4. しばらく経つとそのプルリクエスト(=base branch)のコードが反映された環境が立ち上がり、URLが表示されます。
  5. backcheckの環境は1リポジトリでは完結しないので、もう一方の環境を立ち上げます。

    つまり、apiのプルリクエストであればfrontを、frontのプルリクエストであればapiのプレビュー環境を立ち上げます。

    1. @roxx-bot preview front」または「@roxx-bot preview front」とコメントします。
  6. しばらく経つとそのプルリクエスト(=base branch)のコードが反映された環境が立ち上がり、URLが表示されます。

  7. URLのリンクを踏むとプレビュー環境を見ることができます。

preview XXX のあとに、githubのPRのURLを貼り付けることによって、そのPRで立ち上がっているURLを、環境変数に適応できるため、複数PRにまたがる施策でも問題なくアプリケーションが動くようになります。

具体的な実装

立ち上げ時

bot側

  1. 10秒ごとに自分宛てのメンションが来ているかの監視
  2. メンションが来ていたら、コメントの中身を解析し、環境立ち上げの関数を実行
    1. preview xxx のxxxの部分から、どのリポジトリの環境を立ち上げるか判断
    2. xxx以降に指定されたPRのURLをよしなに解析し、そのリポジトリで必要となる環境変数を書き換え
  3. 環境用のユニークIDを発行(これが環境立ち上げ時のURLのサブドメインにもなる)
  4. Auth0にcallback URLなどを追加
  5. 対象リポジトリにあるbuildspec.ymlをもとにCodeBuildを開始
  6. CodeBuildからCloudFormation用のymlが出力されるので、それをもとに対象環境を立ち上げ
  7. 立ち上げ後、成功したらGitHubのコメントにURL付きで通知

各リポジトリ側

  1. bot側から渡ってくる各種環境変数を変換し、必要に応じてdocker buildするときに使う
  2. gitのコミットハッシュをdocker tagとしコミット→ECRにpush
  3. 生成したイメージのタグ等を用い、CDKを実行(以下CDKの実装内容)
  4. 環境変数の中で、ECSの実行時に使うものだけを抽出し、環境変数化
  5. APIの場合はapiとmysqlを立ち上げ、フロント系の場合はnuxt generateしたものをnginxで静的ファイルとして参照する

cleanup時(bot側のみ)

  1. Githubのステータスがcloseになったことを検知
  2. Auth0のcallback URLなどを削除
  3. CloudFormationのスタックを削除
  4. 削除が完了し次第GitHubのコメントに通知

課題は解決したのか

課題結果コメント
他の環境に依存しない環境を立ち上げられたかAWSのフルマネージドな環境なので、無意識に無限に増やせる
Auth0との連携はできたかbotをTypeScriptで書いてるので柔軟性が高くそれほど困らずに作れた
複数リポジトリのPRにまたがる環境を作れたか一応作れたが、こういうのを作るためにモノレポ化はしたほうがいいなと改めて認識
デプロイの速度docker cacheとか色々挟んだが、どうしても時間はかかってしまう。何故かECSのタスクをアップデートするときに、過去のタスクが削除されるまでの時間が長く、トータルの時間が長くなってしまっている
DBの独立性backcheckはシーダーがいい感じに整っているので、特に問題なく環境構築はできた

今後の課題

  • ECSのタスクアップデート処理を見直して高速化
  • SSMを用いてAPIのコンテナ内に入れるようにする
    • プレビュー環境上でDBをいじりたかったりするので
  • 不要なコンテナは非稼働時には停止しておきたい
    • せっかくfargate使ってるのに、常に立ち上がっててコストがかかってる
  • botのコードのリファクタリング
    • 継ぎ足しになってるので不要なところとかは削除したい
    • 適度にモジュール化したい。現状は手続き型の典型的な処理の流れみたいになってる