エムスリーテックブログ

エムスリー(m3)のエンジニア・開発メンバーによる技術ブログです

AWS FargateでElixirのコンテンツ配信システムを本番運用してみた

こんにちは、エムスリーエンジニアの園田です。

エムスリーでは医療・ヘルスケアサイト向けのコンテンツ配信システムであるChuoiというサービスを運用しています。

以前このブログでも紹介しましたが、このサービスは Elixir/Phoenix で実装されていて、ElasticBeanstalk のCustom Platformを使って運用していました。

www.m3tech.blog

2018/07/04 に AWS Fargate が東京リージョンでローンチされたので、ElasticBeanstalk から Fargate での運用に切り替えました (2018/07/13 切り替え完了)。その際の手順やTipsを書きたいと思います。

今回は構成の説明のみで、実際のコードなどは別のポストで解説していきます。

2018/07/30 追記 実装編の記事を書きました。 www.m3tech.blog

構成変更の動機

以前の構成はこうになっていました。

f:id:ryoheisonoda:20180712182254p:plain

ElasticBeanstalk を利用しているため、アプリケーションのデプロイはawsebcliebコマンドをメインとしたデプロイでした。

f:id:ryoheisonoda:20180712185517p:plain

この構成では、Elixirおよび弊社特有の以下の課題が発生しました。

課題1. Custom Platformのビルドに相当な時間がかかる。

ElasticBeanstalkでは、Packer によるCustom Platform AMIを作成するための機能があり、ElixirやNodeがインストールされたAMIを作成していたのですが、Erlangのビルドに相当な時間がかかっていました。

rpm化も検討しましたが、障害切り分けが困難になるため実装はしたものの利用しませんでした。

GitHub - sonodar/erlang-rpm: Erlang rpm build

こういった背景によりちょっとしたサーバの変更が行いづらく、サーバの細かい修正(fluentdのプラグイン追加など)はebextensionsで済ませてしまうことが多々ありました。*1

これが問題で、ebextensionsはアプリケーションのデプロイ時に実行されるため、ただアプリケーションのソースを変更しただけなのに、全然関係のないfluentdプラグインインストールでエラーになったりして、アプリのデプロイが失敗するといったことが頻発しました。

あと、AWSのバグなのかわかりませんが、Custom Platform Builderの機能がビルドの完了を検知してくれずにずっとビルド状態のまま、という事象が起きていました。そのため、Custom PlatformのビルドはGitlabからでもCodeBuildからでもなく、ローカル端末でebpコマンドを叩いていました。

せっかく構築したCodeBuildのパイプラインは、1回使っただけでお役御免となりました。

課題2. Gitlab CIのIAMユーザに強権限が必要

構成図にあるように、弊社ではCI/CDにオンプレのGitlabを利用しています。そのため、デプロイなどのトリガもGitlab CIで行うことが今はメインになっています。

GitlabからElasticBeanstalkにアプリケーションをデプロイするためには、Gitlab CI上にAWSのシークレットキーなどを保存してeb deployコマンドを実行する必要があります。
これはElasticBeanstalkのダメなところですが、eb deployコマンドを実行するIAMには、リソースの削除権限を含むかなり強い権限が必要です。*2

オンプレのGitlabとはいえ、強い権限を持つIAMユーザを作成すること自体、AWSのプラクティスから外れています。

課題3. TerraformがCustom Platformに対応していない

弊社では(というより私は)AWSのプロビジョニングにTerraformを利用することが多く、このChuoiというサービスもTerraformでAWS環境を管理しています。

ElasticBeanstalkのCustom Platformを環境に適用するためには、aws_elastic_beanstalk_environmentリソースにplatform_arnというパラメータが必要なのですが、今だにこのパラメータはTerraformでサポートされていません。

AWS: aws_elastic_beanstalk_environment - Terraform by HashiCorp

そのため、platform_arnパラメータを追加してビルドしなおしたterraformの実行バイナリを使っていました。 ただ、本体にプルリクエストを送るほどちゃんとした作りにはしておらず、terraformのバージョンがあがるたびに差分を適用するという地味に面倒な作業を行う必要がありました。

上記3つの課題以外にも小さい不満がたくさんあり、いっそのことEC2にしてしまおうかとも思っていましたが、AWS SummitでFargateが東京に来るというのを聞いたとき、ChuoiはFargateにしてしまおう!と思った次第であります。

Fargate化のためにやったこと

さて、前置きが非常に長くなりましたが、今回Fargate対応をするにあたり以下のことを実施しました。

  • Elixir/PhoenixアプリのDocker化
  • Docker化したアプリのFargate動作確認
  • 社内GitlabからのCI/CDパイプライン構築
  • Terraformテンプレート作成

詳しいことは別の機会に書きますが、個人的な感想としては、かなり大変でした。

変更後の構成

変更後の構成はFargate構成としてわりと普通な形になりました。

f:id:ryoheisonoda:20180713112117p:plain

デプロイ周りは以下のような構成です。

f:id:ryoheisonoda:20180713115557p:plain

図に書き忘れたのですが、CloudWatch Event で CodePipeline の状態変更を検知し、Slackにデプロイの進捗を通知する仕組みも実装しました。

デプロイ構成については、検討を行う上で別の選択肢もありました。

  • DockerイメージのビルドとECRへのプッシュはGitlab上で行い、デプロイのみCodeDeployに実行させる。

    • メリット:

      • 事前にDockerイメージのビルドが完了するのでデプロイが高速
    • デメリット:

      • オンプレのGitlabは全チームで使っているため、キュー待ちが発生するとなかなかジョブが始まらない。
      • 環境ごとの差分をGitlab CIの設定ファイルやビルドスクリプトで実現しなければならず、バグが生じやすい。
      • パイプライン全体をTerraformで管理できない。
      • Dockerイメージに埋め込む環境変数などの管理が、Gitlabの貧弱なパラメータ管理に依存する。パラメータ定義をコードで管理できない。
  • Gitlab CIからCodeCommitにプッシュしてパイプラインを開始する

    • メリット:

    • デメリット

      • Gitlab CIに対してCodeCommitにプッシュ可能な権限(秘密鍵もしくはcredentials-manager)を持たせなければならない。
      • AWS全体で保持するソース容量が大きくなる。
  • Gitlab CIからecs-cliでデプロイする

    • メリット

      • Gitlab CIだけでパイプラインが完結する。
    • デメリット

      • Gitlab CI上で利用するIAMユーザの権限が強くなる。
      • パイプラインの構成管理ができない、もしくはしづらい。

どの手段も、メリットがデメリットを上回らないため、S3へアップロードする方式にしました。

今回の手法のメリット、デメリットは以下です。

  • メリット

    • Gitlab CIのIAMユーザはS3に対するPutObjectの権限のみあればいい。
    • AWS側も、外部入力としてS3オブジェクトのみを意識すればいいため疎結合
    • ビルド時の環境変数としてParameter Storeが利用できる。(これ重要)
    • パイプライン全体をTerraformで管理できる。
  • デメリット

    • デプロイまでそれなりに時間がかかる。
    • アプリケーションのGitリポジトリとTerraformのGitリポジトリとでコードが分散する。

デプロイの時間については、今のところ5〜6分で完了するため問題ありませんでした。ソースの分散については課題です。buildspec.ymlのパスにS3のパスが指定できるようになればいいのですが。

構成変更前の課題について

  • Docker化したことによって、インフラ構成のビルド時間が大幅に削減できました。イメージのビルド自体には2分程度しかかかりません。もともと1時間以上かかっていたので、3000%改善されました!

  • Gitlab CI上のIAMユーザはS3バケットPutObject権限のみを持つようになり、安心してアクセスキーをGitlabに登録できます。

  • すべての構成をTerraformでコード化できました。

今回はここまでです。この構成をどう実装したかはまた別の機会に解説します。

2018/07/30 追記 実装編の記事を書きました。 www.m3tech.blog

一緒に開発してくれる仲間を募集中です!

エムスリーでは AWS だけでなく、GCP や Rancher が適材適所で利用されています。実装言語も Elixir だけでなく、JavaRuby、Kotlin、Scala などが大活躍してます。

一緒に開発する仲間を絶賛募集中です!メンバーとカジュアルに話す場も設けてますので是非気軽にお問い合わせください!

jobs.m3.com

*1:ebextensionsはElasticBeanstalkのEC2内ファイルやデプロイフックを管理する仕組みです。https://docs.aws.amazon.com/ja_jp/elasticbeanstalk/latest/dg/ebextensions.html

*2:現在ではCodeDeployがElasticBeanstalkに対応したため、デプロイの入り口をCodeDeployにすることで権限を絞ることが可能