![見出し画像](https://assets.st-note.com/production/uploads/images/56677071/rectangle_large_type_2_918d3433ae2938500a40246f1b453394.png?width=1200)
RailsアプリケーションをAWS Elastic BeanstalkからAmazon EKSへ移行しました
こんにちは!
エンジニアの一条です!
会社の設立当初から稼動しているRailsアプリケーションはAWS Elastic Beanstalk(以降EBと記載)にコードを直接デプロイして動かしていたのですが、いくつかの問題があったためAmazon EKS(以降EKSと記載)へ移行しました。
本記事では、EB運用時の問題点、移行時につまづいたポイント、EKS移行後の変化について話します。
構成図
まずは移行で構成がどのように変わったのかを図で表します。
移行前の構成図
移行後の構成図
AWS Elastic Beanstalk運用時の問題点
そもそもなぜEBを使用していたのかと言うと、Railsアプリケーションを動かすインフラ環境の作成やデプロイが簡単にできることから、それらの開発にかける時間を少なくし、サービスの機能を開発する時間を確保するためでした。
これは2017年頃の話ですが、当時は会社も立ち上げたばかりでエンジニアも2、3名と少なかったと聞いています。
EBを3年ほど運用していく中で、以下の3つの問題点が挙げられました。
1. EBへのデプロイ時にエラーが発生した際に、ログの内容から原因を特定することが困難
2. Rubyのアップデートの作業をアプリケーション開発者のみで完結させ、アップデートを簡単にしたい
3. リリース時間を短縮したい
それぞれについて補足すると、
1はEBのデプロイ時にエラーが発生した際に、どのリソースの何の処理でエラーとなったかの特定が非常に大変でした。エラーの箇所がAmazon EC2(以降EC2と記載)起動時か、デプロイするコードか、ebextensionsで追加した処理かなどを切り分け、EBが出力する大量のエラーログから原因を特定する必要があります。この問題は簡単にデプロイができることとのトレードオフとも言えます。
2はRubyのアップデートの際にはEB側の修正が必要となります。アプリケーションとインフラの両方を修正、リリースする作業は二度手間なので、アプリケーション開発者のみでアップデートを完結出来るようにしたいという思いがありました。
3はEBのリリース時間が最速でもStaging、Productionでそれぞれ1時間かかっていました。ソースコード量が増えてきたため、EBへのアップロードに時間がかかるようになってきたことや、デプロイの都度EC2の起動とEB専用の初期処理を実行することが原因であると考えています。
これらの問題をコンテナ化することで解決できると考えました。
1はデプロイ時のエラーの箇所はコンテナに関連する範囲となるため、コンテナ内のアプリケーションでエラーログを正しく出力さえしていれば、原因の特定が容易になります。
2はコンテナをアプリケーション開発者の管理対象として、コンテナ内にRubyを含めることでアップデートも簡単にできます。
3はデプロイの都度EC2が起動することが少なくなることや、StagingとProductionで同一コンテナを使用することでコンテナの作成を1度だけにできたりと、短縮しようと思えばできることから、1時間よりは短縮できると判断しました。
Amazon EKSの採用理由
コンテナ化をしたらそれをどこで動かすのかと言う話になりますが、当社では既にEKSでサービスを運用していて安定稼動していること、Kubernetesがコンテナオーケストレーションツールのデファクトスタンダードとなっていることから、EKSを採用しました。
移行時につまづいたポイント
KubernetesやAWSのリソースに関してはスムーズに作成できました。では何につまづいたかと言うと、リリースフローとRailsのAssets Piplineです。
Productionリリースの失敗
コンテナ化したRailsアプリケーションを、StagingのEKSにリリースして問題がないことを確認し、そのままProductionリリースを実施したところ、CSSやJavaScriptなどの静的ファイルが読み込めない状態となりました。
何かあった時にはEBに切り戻せるように準備をしていたためすぐに戻しました。その日のリリースは失敗に終わりました。
リリースはアクセス数が少ない夜間に実施していたことと、静的ファイルが読み込めない時間も数分であったため、影響はそれほどありませんでした。
原因と対応
失敗の原因は、静的ファイルの参照先であるホスト名がStagingのものとなっていたことでした。
失敗時のリリースフローは以下の通りです。(当社ではCI/CDにCircleCIを使用しています)
まず前提として、Docker ImageはStagingとProduction両方で同じものを使用することが推奨されていたため、Stagingへのリリース時に一度だけDocker Imageをビルドして、それぞれの環境でProductionのDocker Imageを参照する方法でリリースフローを組んでいました。
また、Assets PipelineのツールはWebpackerを使用していました。
Docker Imageのビルド時にStagingの環境変数でAssetsのprecompileを実施し、Production環境へsyncしていたため、作成されたmanifest.jsonファイルが参照する各種静的ファイル内のホスト名がStagingのものとなっていたことが原因でした。
これを解決するために、Staging、Productionそれぞれの環境変数で、manifest-environment.jsonのように環境名を付与してprecompileを実施し、両方ともDocker Imageに含め、アプリケーション起動時に読み込み先のmanifest.jsonを動的に変えるパッチを当てました。
この対応後、再度Productionリリースを実施して無事にリリースできました。
Amazon EKS移行後の変化
移行前の問題点の解消
1. EBへのデプロイ時にエラーが発生した際に、ログの内容から原因を特定することが困難
→デプロイ時のエラーがほぼなくなりました。ある場合はKubernetesのエラーやアプリケーションのエラーのため、ログの内容から原因を特定することが容易になりました。
2. Rubyのアップデートの作業をアプリケーション開発者のみで完結させ、アップデートを簡単にしたい
→アプリケーション開発者がDocker Imageを管理することになったので、Rubyのアップデートをアプリケーション開発者のみで完結できるようになりました。
3. リリース時間を短縮したい
→Stagingで30分、Productionで55分も短縮できました。
見つかった新たな課題
EKS分の料金の増加と、クラスターアップデートによるオペレーションコストの増加です。
EKSの構成は障害時の影響範囲を小さくするために1サービス1EKSクラスターとしていましたが、サービスが増えてくるとその分EKSの料金は高くなっていきます。
また、EKSのクラスターアップデートも各サービス毎に行う必要があり、オペレーションコストも高くなります。
今後の対応
EKSクラスターを1つにし、複数サービスを1つのEKSクラスターに纏めようとしています。これにより課題は解決できますが、障害時の影響範囲も大きくなるため、なるべく小さくできる方法がないか模索しています。
さいごに
今回の移行を通じて、普段から自分たちが使用しているツールの把握が大事だと感じました。
移行に失敗した本質的な原因は以下の2点が挙げられます。
・既にEKSを運用しているサービスでは正常に動いていたリリースフローを今回移行するサービスにそのまま適用してしまった
・StagingとProductionで同じDocker Imageを使用することに固執してしまった
運用済サービス、移行するサービスでは同じRailsアプリケーションでも採用しているツールは異なります。これらをきちんと把握しておく必要があったと深く反省しました。
また、当社はStagingとProductionのリリース間隔が短いこともあり、それぞれのDocker Imageを作成する方法で解決してもよかったと考えています。
そのため、現在では各環境用のDocker Imageを作成する方法を採用しています。
少し大変ではありましたが、この移行によって全サービスでEKSおよび、Kubernetesが使われるようになったので、より一層Kubernetesを使いこなせるようになっていきたいと思います。
また、リフカムではエンジニアを募集しておりますので、ご興味がある方はお気軽にご連絡ください!