アジャイル開発では動くソフトウェアを短い時間間隔でリリースしていくことが重要になります。リリース作業そのものに手間や時間がかかってしまうと、アジャイルのスピード感を損なってしまいます。もしリリースしたものに問題があったときにすぐに戻せるしくみが用意されていないとリリースに対して慎重になるため、短い時間間隔でのリリースが難しくなります。
10年をこえるアジャイル開発の中でリリースフローをどのように改善してきたのか、その変遷についてご紹介します。
今回事例として紹介するのはAipoのリリースフローです。AipoはJavaで書かれていてTomcatで動いています。詳しい技術スタックは「3万人以上のビジネスユーザーが使うスケジュール管理SaaSを支える技術スタック」をごらんください。
目次
インスタンス起動時の処理
リリースフローの前提として、インスタンスを起動した際の処理について触れておきたいと思います。
EC2インスタンスではユーザーデータを使うことで、インスタンス起動時にコマンドを実行できます。このしくみを使い、インスタンス起動時にS3に配置したROOT.warをデプロイしてからTomcatを起動しています。(参考:起動時に Linux インスタンスでコマンドを実行する)
- S3上のROOT.warファイルを更新する
- インスタンスを起動する
という手順で起動したインスタンスでは最新バージョンのアプリケーションが適用されます。
1台構成のデプロイフロー
サービスを開始した当時はTomcatが動いているインスタンスが1台の構成でした。まだ体制が整っていなかったため、次のようなフローでリリース作業を行っていました。
- 開発者のマシンでGitHubからmasterブランチをpullする
- Mavenでビルドする
- ビルドされたROOT.warファイルをS3へアップロードする
- インスタンスへSSHログインする
- warファイルをデプロイしてTomcatを再起動する
インスタンス起動時にROOT.warをデプロイするしくみを流用してwarファイルのデプロイをしています。S3上のROOT.warを最新にした後にインスタンスを再起動することで最新バージョンのアプリケーションが反映されますが、ダウンタイムを減らすためTomcatだけを再起動していました。
また当時は稼働中のインスタンスに対して直接ミドルウェアのアップデートや設定変更を反映していたため、同じ構成のインスタンスをもう一台立ち上げて切り替えるという方法を取ることもできませんでした。
この手法の問題点
今ふりかえってみるといろいろとツッコミどころがありますが、リリースを担当する開発者はリリース作業に入ると開発を止め、手元の開発環境をmasterに戻して作業する必要がありました。また間違えてmaster以外のブランチをリリースしてしまったことがありました。
Jenkinsの導入
ビルド環境を統一化するため、Jenkinsを導入しました。
JenkinsでビルドとS3へのアップロードまでをおこない、デプロイを開発者が担当する流れに改善しました。
複数台構成のデプロイフロー
順調にアクセスが増え、インスタンスが複数台構成になりました。基本的なリリースフローは1台のときと変わりませんが、ロードバランサに複数のインスタンスがぶら下がっている状態になったため、S3にROOT.warファイルをアップロードした後の手順が次のように複雑になりました。
- インスタンスをロードバランサから外す
- インスタンスへSSHログインする
- warファイルをデプロイしてTomcatを再起動する
- インスタンスをロードバランサに加える
この手法の問題点
ロードバランサから外してアプリケーションを更新して再びロードバランサに戻す一連の操作をインスタンスの台数分おこなう必要があったため、リリース作業に非常に時間がかかるようになりました。リリース後になにか問題があった場合も同じ手順で差し戻す必要があるため、リリース作業が非常に負担のかかるものになっていました。
オートスケーリングの導入
オートスケーリングを導入し、リリース時にインスタンスの台数を増やすことで対応しました。
- オートスケーリングの最小インスタンス数を倍に増やす
- インスタンス起動時の処理により、新しく立ち上がったインスタンスは新しいバージョンがデプロイされる
- オートスケーリングの最小インスタンス数をもとにもどす
リリース前のサーバー構成
リリース前にインスタンスが3台立ち上がっているばあい、オートスケーリングの最小インスタンスを6台に増やします。
リリース作業中のサーバー構成
リリース作業中には新バージョン、旧バージョンのアプリケーションが混在しています。
リリース後のサーバー構成
オートスケーリングの最小インスタンスを3台に戻します。オートスケーリングではインスタンスの台数を減らす際のルールを決めることができます。最も古いインスタンスを削除する「OldestInstance」を選ぶことでインスタンスの台数を減らしたときに古いバージョンのインスタンスが削除されます。(参考:Amazon EC2 Auto Scaling 終了ポリシーを使用する)
Elastic Beanstalkを使ったBlue/Green デプロイフロー
そして現在はElastic Beanstalkを使ってBlue/Green デプロイをしています。Blue/Green デプロイとは古いバージョンのセットと新しいバージョンのセットを構築し、URLのスワップによってトラフィックを切り替える方法です。
Elastic Beanstalkをつかうメリット
日々のアップデートではアプリケーションの更新だけでなく、Java、Tomcat、Apacheなどミドルウェアのバージョンアップをはじめとするインフラの更新も発生します。
今まではアプリケーションのバージョンのみを管理していましたが、Elastic Beanstalkを使うことでインフラも含めた構成をかんたんに管理でき、インフラの更新も適用しやすくなりました。
Elastic Beanstalkを使うことで
「インフラのベースとなるAMIのバージョン」 x 「アプリケーションのバージョン」
で環境を構築できるようになりました。
Blue/Green デプロイのメリット
Blue/Green デプロイには以下のようなメリットがあります。
- 新バージョンと旧バージョンが混在している時間を短くできる
- 新バージョンを事前に検証できる
- なにか問題が起きたときにすぐに切り戻すことができる
新バージョンと旧バージョンが混在している時間を短くできる
新バージョンと旧バージョンが混在している時間が長くなると意図しない不具合が起こることがあります。特に複数のシステムが連携している場合、リリースの順番や混在している間の分岐処理など考えることが増えてしまいます。
事前に検証できる
新しい環境、古い環境それぞれでエンドポイントを持つため、リリース前に新しい環境のエンドポイントに直接アクセスして最終的な動作確認をしてからトラフィックを切り替えることができます。
Elastic Beanstalkでは環境ごとに
https://YOUR-APP-NAME-VERSION.ap-northeast-1.elasticbeanstalk.com
のような形でエンドポイントが用意されます。自分たちのドメイン用のSSL証明書を使っているため、このURLにそのままアクセスするとSSL証明書のエラーが出ます。そのため自分たちのドメインで検証用のURLを用意して、そこに新しい環境を割り当てて事前の確認をしています。
具体的にはJenkinsを使って新しい環境を立ち上げていますが、その中で
- S3上のROOT.warファイルを更新する
- Elastic Beanstalkの新しいアプリケーションバージョンを作成する
- Elastic Beanstalkの新しい環境を立ち上げる
- Route 53で事前確認用URLのDNSの向き先を新しい環境に変える
までを1クリックで実行できるようにしています。
なにか問題が起きたときにすぐに切り戻すことができる
なにか問題が起きたときにはトラフィックの向き先をもとに戻せばダウンタイムなしで差し戻すことができます。
実際にわたしたちもトラフィックを切り替えてから1時間くらいは同時稼働しておいて問題がないか様子を見るようにしています。
なおアプリケーションの環境はすぐに差し戻せますが、新しくリリースしたアプリケーションの問題によりデータベースに意図していないデータが入ってしまった場合はデータを差し戻す対応が必要になります。そのためデータの出し入れに関連するコードを変更した場合はリリース前のチェックで重点的に確認をしています。
一緒に働く仲間を募集しています。
新卒採用・中途採用を問わず、年間を通して、さまざまな職種を募集しています。「すぐに仕事がしたい」「話を聞いてみたい」「オフィスを訪問してみたい」など、ご応募をお待ちしています。共に未来をカタチにする仲間を待っています。