SmartHR Tech Blog

SmartHR 開発者ブログ

ECS を使った PR ごとの検証環境をバージョンアップした話 〜システム構成編〜

こんにちは。SmartHR SRE チームの吉成です。

先日 「ECS を使って PR ごとに検証環境を用意した話」というテーマで登壇しました! という記事で紹介したとおり、 SmartHR では GitHub のプルリクエストごとに動作確認のできる環境を用意しています。 しかし、以前発表した段階ではまだまだ不足している機能や不具合があり、一時的に利用を中止していました。

今回、不足機能の拡充を機にその構成をガラッと変更しましたので、改めて紹介します。

背景

前回の構成は以下のようなものでした。 1つの PR ごとに SmartHR(Rails), MySQL, Redis, Sidekiq 用のコンテナが起動する EC2 インスタンスを用意し、ALB を利用してポート番号でアクセスを振り分ける構成となっていました。

詳しくは前回のブログ及び発表資料をご確認頂きたいのですが、この構成には次のような問題がありました。

  • Docker イメージを CircleCI 上でビルドしていた
    • CircleCI の完了までに時間がかかる
  • AWS リソースの操作を CircleCI 上で行っていた
    • リソース作成時やコンテナの立ち上げに失敗すると CI がコケてしまう
  • PR ごとにコンテナインスタンスを立てたり落としたりしてた
    • 起動に時間がかかる
    • 利用料金の推定が難しい
    • AWS なので、10分くらいの環境でも1時間分の料金がかかってしまう
  • ALB の仕様上 10環境以上同時に用意できない
    • 開発がスムーズに進むと検証できない PR が出てしまう

今回はこれらの問題点を解決することを目的に、システム構成から見直しました。 結果として、構成はよりシンプルになり、全ての問題点を解決することができました。

概要

新しい構成では、ALB を利用してポート番号でアクセスを振り分けることはそのままで、「1つの大きなインスタンスを用意し、その中で大量のコンテナを立ち上げる」という構成を取りました。 また、イメージのビルドや AWS リソースに対する操作は CircleCI 上ではなく、専用のインスタンスを立ててその中で行います。

この構成では、1つの PR に対してSmartHR(Rails), MySQL, Redis, Sidekiq の合計 4コンテナが起動されます。 個々の PR に集中して検証できるように、 各環境はそれぞれ独立しており、個々のデータベースに対する変更やコードの変更がお互いに影響しないこと が重要です。つまり、環境が n個であれば、1つのインスタンス内でそれぞれのコンテナが n個ずつ起動することになります。 同じ機能を持ったコンテナ(例えば MySQL)が複数立ち上がるため、デフォルトのポート設定ではインスタンス内でポートがかぶってしまいます。 そこで、各コンテナは SmartHR のポート番号を基準に自身のポート番号をデフォルトから変更して起動するようにしました。 具体的には、 PR_A の環境で作られた SmartHR のポート番号が 30000 番であれば、MySQL は 33306、Redis は 36379、というように「SmartHR のポート番号 + 自身のデフォルトポート番号」という条件にしました。

システム構成図

構成図を以下に示します。

以下のような流れで環境の構築を行います。

  • 開発者が GitHub で PR を作成する
  • CircleCI は PR をトリガーにテストを実行する
  • テスト完了後、 CircleCI はビルド用サーバに対してコンテナ作成/起動の API を叩く
  • ビルド用サーバは以下の作業を行う
    • Docker イメージのビルド
    • Docker イメージを ECR にプッシュ
    • ELB のリスナー情報から利用可能なポート番号を取得
    • 空いているポート番号を元に各コンテナがインスタンス内で利用するポート番号を決定
    • 決定したポート番号を使って ECS 用のタスク定義を作成
    • ECS 用のサービスを作成(既に存在する場合は更新)
    • 作成したサービスを ECS のクラスターに紐付け
    • Slack および Github 上の該当 PR に通知

問題点の解決

上述の通り、前回の構成には複数の問題点がありました。 新しい環境ではどうなったのか、確認してみます。

  • CircleCI の完了までに時間がかかる
    • ビルド用サーバに作成を依頼するだけなので、 CircleCI の時間には影響しなくなった
  • リソース作成時やコンテナの立ち上げに失敗すると CI がコケてしまう
    • ビルド用サーバで作成するようになったので、失敗しても CI には影響しなくなった
  • 起動に時間がかかる
    • インスタンスは立ち上がりっぱなしなので、Docker 本来の速度で起動するようになった
  • 利用料金の推定が難しい
    • インスタンス台数がわかるので、推定しやすくなった
  • AWS なので、10分くらいの環境でも1時間分の料金がかかってしまう
    • インスタンスは立ち上がりっぱなしなので、無駄がなくなった
  • ALB の仕様上 10環境以上同時に用意できない
    • ELB を使うようにしたので、 100環境まで用意できるようになった

また、起動用のインスタンスでは 20環境の同時作成を想定して、 m4.2xlarge を利用しています。 CPU やメモリにも余裕があるため、動作も非常に快適になりました。

まとめ

今回はシステム構成と構成の流れを中心に説明しました。前回の構成に比べてすっきりすることができ、運用・保守も非常に楽になりました。 次回は実際のコードを参考に、どのように実装されているかを紹介したいと思います。