こんにちは。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 やメモリにも余裕があるため、動作も非常に快適になりました。
まとめ
今回はシステム構成と構成の流れを中心に説明しました。前回の構成に比べてすっきりすることができ、運用・保守も非常に楽になりました。 次回は実際のコードを参考に、どのように実装されているかを紹介したいと思います。