メドピア開発者ブログ

集合知により医療を再発明しようと邁進しているヘルステックリーディングカンパニーのエンジニアブログです。PHPからRubyへ絶賛移行中!継続的にアウトプットを出し続けられるようにみんなでがんばりまっす!

Rails × ECS 運用してみたわかった起動タイプ EC2, Fargate の使い所

メドピアマッスル部上腕二頭筋担当、CTO室 kenzo0107 です。
今回はメドピアの直近のプロジェクトで採用している Rails × ECS Fargate についてです。

直近プロジェクト

直近プロジェクトでは AWS ECS を採用しています。

2018年10月にリリースした スギサポ deli は、メドピアで Fargate 初採用となったプロジェクトです。

スギサポ deli とは?

sugisapo.ws

病気で食事制限が必要な方やシニアの方々、より健康な食生活を目指す方など、誰もが美味しく召し上がれるお食事をお届けするサービスです。

「食事制限」 と聞くと、簡素な食事をイメージされる方もいらっしゃると思いますが
一度見て頂くとお分かりの通り、かなりバラエティに富んだ内容となっており、目にも美味しい品々が並んでおります。

是非一度お試しいただければ幸いです♪

今回お話ししたいこと

以前、 Rails x ECS でオートスケーリング&検証環境の自動構築を執筆しました。

tech.medpeer.co.jp

今回は ECS を運用してきてわかった起動タイプ EC2, Fargate の使い所、また、運用時に有用だったことについて話したいと思います。

ECS についておさらい

まずは ECS の起動タイプ Fargate と EC2 について、軽くおさらいです。

起動タイプ Fargate

インフラレイヤーが抽象化されており、EC2 の管理が不要です。

以下の運用コストがなくなることが何よりも有難いです。

  • EC2 インスタンスの定期メンテ
  • ECS エージェントのバージョン管理
  • EC2 オートスケーリング管理
起動タイプ EC2

EC2 を起動し、その上でコンテナを起動しています。

これらの特性により起動タイプ Fargate, EC2 の使い所を検討しました。

検討事項

  • コンテナへのアクセスはどうやってするの?
  • AutoScaling 速いのどっち?
  • お値段はどう?

コンテナへのアクセスはどうやってするの?

f:id:kenzo0107:20190210235843p:plain

起動タイプ EC2

EC2 へ ssh さえできれば、docker ps でコンテナの起動状態を確認したり、 docker exec でコンテナにアクセスし、 rails console を実行することも可能です。

起動タイプ Fargate

インフラレイヤーが抽象化されている為、サーバへ ssh ログインできません。*1

docker exec でコンテナにアクセスする様なことはできません。

AutoScaling 速いのどっち?

起動タイプ EC2

ECS のバックエンドとして AutoScaling Group で EC2 を起動させ、ECS に紐づけた ALB に EC2 を追加する運用をしています。

その為、 EC2 をスケールアウトさせた後に、タスクをスケールアウトする様にしないと、タスクに偏りが生じる等、正しくタスク配置されない時がありました。

起動タイプ Fargate

タスクのみ考慮すればよいです。

f:id:kenzo0107:20190621221227p:plain

特に、EC2 のスケール分を考慮する必要がないとしても、Fargate のスケールアウトが安定的で速いです。

お値段はどう?

f:id:kenzo0107:20190211002727p:plain
Price

Fargate の価格はタスク数, CPU, Memory に正比例します。Fargate pricing

2019年1月に Fargate の価格が下がったとは言え、タスク数が増えることを考えると、まだ Fargate の方が割高?と思います。

本番・ステージング環境での Fargate, EC2 の使い分け

これまでの起動タイプ Fargate, EC2 の性質を加味して、プロジェクトの性質にも依りますが、以下の様な構成を採用しているケースが多いです。

f:id:kenzo0107:20190619234356p:plain

※ 以下の前提です。

  • 一般ユーザがアクセスする方を App、弊社からのみアクセスする管理画面を Admin
  • App, Admin 共に同じ Rails プロジェクトがデプロイされている

Point

  1. 本番環境 App のみ ECS 起動タイプ Fargate

    • ややコストは上がるものの、スケーラビリティに柔軟性がある
  2. その他は ECS 起動タイプ EC2

    • docker exec でコンテナに入りデバッグ可能にする
    • 前回の記事 の様に qa/* ブランチ毎のタスクが複数起動している為、タスク数が増えてもコストに影響しない
    • 本番環境 Admin は、高トラフィックとなる様なことは弊社ではない為、スケーリングを考慮する必要性がない。
    • 本番でも docker exec しコンテナに入りデバッグしたいという要望があり、Admin を 起動タイプ EC2 にすることで担保

開発時に有用だったこと

デプロイ

以下ブランチにマージすることで自動的に試験→デプロイする様にしています。

  • master
  • develop
  • qa/*

f:id:kenzo0107:20190619232701p:plain

CircleCI で試験をパスすると、 aws codepipeline start-pipeline-execution を実行し 指定の CodePipeline を開始する様にしています。

CodePipeline

こちらが実質 ECS へのデプロイをしている箇所です。

f:id:kenzo0107:20190528171331p:plain

デプロイ関連の処理は Capistrano でラップしています。

検証環境自動構築

f:id:kenzo0107:20190619235429p:plain

前回記事 検証環境の自動構築 をご参照ください。

ブランチ qa/* push により以下処理が実行され、検証環境が構築されます。

Rails master.key は?

AWS パラメータストアに登録しており、Rails イメージビルド時に aws ssm get-parameters で取得しています。

その他、イメージタグ付け( :tag_image )、ECR へ登録処理( :push_image_to_ecr )も併記しておきます。

namespace :rails do
  task :build_image do
    run_locally do
      within fetch(:deploy_work_path) do
        execute 'aws', 'ssm', '--profile', fetch(:profile).to_s,
                'get-parameters',
                '--with-decryption',
                '--region', 'ap-northeast-1',
                '--name', "/#{fetch(:application)}/rails/master_key",
                '--query', '"Parameters[0].Value"',
                '--output', 'text', '>', 'config/master.key'
        execute 'docker', 'build', '--no-cache=true',
                '-t', "#{fetch(:ecr_host)}/#{fetch(:env)}-#{fetch(:application)}-rails:#{fetch(:rails_tag)}",
                '--build-arg', "RAILS_ENV=#{fetch(:rails_env)}",
                '-f', 'docker/deploy/rails/Dockerfile', '.'
      end
    end
  end

  task :tag_image do
    run_locally do
      within fetch(:deploy_work_path) do
        execute 'docker', 'tag',
                "#{fetch(:ecr_host)}/#{fetch(:env)}-#{fetch(:application)}-rails:#{fetch(:rails_tag)}",
                "#{fetch(:ecr_host)}/#{fetch(:env)}-#{fetch(:application)}-rails:latest"
      end
    end
  end

  task :push_image_to_ecr do
    run_locally do
      within fetch(:deploy_work_path) do
        push_image_to_ecr("#{fetch(:ecr_host)}/#{fetch(:env)}-#{fetch(:application)}-rails:#{fetch(:rails_tag)}")
        push_image_to_ecr("#{fetch(:ecr_host)}/#{fetch(:env)}-#{fetch(:application)}-rails:latest")
      end
    end
  end
end

def push_image_to_ecr(image)
  execute 'ecs-cli', 'push', "#{image}",
          '--aws-profile', fetch(:profile).to_s,
          '--region', fetch(:region).to_s
end
イメージビルド処理短縮

以前は Rails イメージビルド時に asset_sync を利用し、 assets を S3 に同期していましたが、この同期処理に非常に時間が掛かっていました。

ですが、弊社フロントエンドエンジニア 村上 ( @pipopotamasu )medpacker により、 Sprockets によるアセットのビルド処理をしないようにした為、デプロイ時間が大幅に短縮されました。*2

是非以下ご一読ください。 tech.medpeer.co.jp

Rails メトリクスを Datadog へ送信

デプロイ後に Rails の以下メトリクスを Datadog に送信する様にしました。*3

  • Rails Load Time
  • Rails CodeStats
  • Gem Dependency Count

post rails metrics to datadog · GitHub

f:id:kenzo0107:20190625144500p:plain

こちらは以前 2018年9月12日 に開催された 『MedBeer -Rails開発での技術的負債との付き合い方-』にて、クックパッド社の 小室 直さん (@hogelog) の発表を参考にさせていただきました。

ありがとうございます!

tech.medpeer.co.jp

技術的負債となる指標をプロジェクト初期から意識することで、返済への意識も育まれると思います。(願い)

ロギング

f:id:kenzo0107:20190619235524p:plain

Lambda で LogGroup を S3 に日時バックアップする処理はこちらの Serverless Framework プロジェクトで構築しています。

github.com

ログ閲覧

CloudWatch Logs Insight で非常にログの閲覧がスムーズになりました。

以下の様なクエリで、logStream を rails をプリフィックスとしフィルターをかけると、Rails コンテナのログを抽出できます。

fields @timestamp, @message
| sort @timestamp desc
| limit 20
| filter @logStream like /^rails/

f:id:kenzo0107:20190530222814p:plain

時系列で複数コンテナログを閲覧したい場合は、以下の様にすれば簡単に取得できます。

fields @timestamp, @message, @logStream
| sort @timestamp desc
| limit 20
| filter @logStream like /^rails|^nginx/

以上からインサイトで検索しやすい様、ECS の Service 単位でコンテナのロググループは統一しています。

まとめ

  • 負荷の多い箇所は Fargate がオススメ
    • その他は EC2 がコスト的に良い
  • デプロイ自動化
    • テストは CircleCI、デプロイは CodePipeline と役割分け大事
  • Rails config/master.key は AWS パラメータストアで管理
  • medpacker で脱 webpacker & デプロイ時間短縮
  • Rails メトリクスを Datadog に Post で定点観測
  • ログは CloudWatch Logs に一時保存
    • 日次で S3 保存し長期保存
    • CloudWatch Logs Insight でログ閲覧がスムーズ
    • ECS Service 毎にロググループを統一しとくとコンテナ毎の時系列ログが確認しやすい

上記に加えて、開発時に最も有用な、ECS を利用した検証環境自動構築については、またの執筆の機会にと思います。

以上、参考になれば幸いです。


(☝︎ ՞ਊ ՞)☝︎是非読者になってください


メドピアでは一緒に働く仲間を募集しています。 ご応募をお待ちしております!

■募集ポジションはこちら

https://medpeer.co.jp/recruit/entry/

■開発環境はこちら

https://medpeer.co.jp/recruit/workplace/development.html

*1:ssh コンテナを起動させ、アクセスさせることは可能です。

*2:大凡15→8分程度に短縮

*3:Gem の最新度については、現在、定期的な bundle update 当番により解消している為、送信していないです。