会社の本番環境をDocker(ECS)に置き換えるために準備したこと気づいたこと

このエントリーをはてなブックマークに追加

エンジニアの@macs_6です。 このブログでは社内のAWS EC2上で運用しているアプリケーション群をECS移行したプロジェクトについて紹介します。

ローカルの開発環境をDockerした話は以前の記事(複数の rails プロジェクトが共存する開発環境を Docker 化した話を晒してみる)で西辻が紹介しているので、そちらを参照して下さい。

概要

  1. プロジェクトを始める前に感じていた課題
  2. 目指す状態
  3. ECSを選択する理由
  4. 設計
  5. 移行のために必要な作業
  6. Digdagによるスケジューリングについて
  7. ECSを使って見て気づいたこと
  8. 今後やりたいこと

プロジェクトを始める前に感じていた課題

  1. ローカル・本番で再現性のある環境を簡単に作れるようにしたい
  2. 簡単にスケールできるようにしたい
  3. コストを抑えたい

ECS移行プロジェクトを始める前にはこれらの3つの事に課題感を持っていました。

1.ローカル・本番で再現性のある環境を簡単に作れるようにしたい

以前の記事にあるように、ローカルの開発環境構築に多くの時間を割いてきました。 新しいメンバーが入ってきた際、使っているミドルウェアの設定やバージョン変更があった際には「あれ、設定変わった?」という会話と共に環境を揃えるために大変労力をかけていました。

2.簡単にスケールできるようにしたい

サービスがテレビ等のメディアに掲載いただけると、目に見えてアクセスが急増します。 実際にサービス開始当初には5秒のテレビ出演でサービスが応答出来なくなったこともありました。(テレビ放送に向けての負荷対策(AWS x Rails の場合)[前編])

直前にメディア露出タイミングが分かっていればアクセス増加を予測が出来ますが、関連キーワードがテレビで紹介されることで不意にトラフィックが増えることもあります。理想的には「自動スケールする状態」、少なくとも「同じ横にサーバを並べれば動く状態」にはしたいと考えていました。

3.コストを抑えたい

API、キューイング、管理用アプリケーション、本番環境、検証環境・・・と必要なEC2インスタンスが増えてコストが増えていました。 特に定期的なスクリプト実行のためのリソースや、冗長化のために2台になっているアプリケーションついては、明らかにリソースが余っている状況も見受けられるため、それらが相互に余ったリソースを使えるような状態になっていると望ましいです。

目指す状態

上記のような課題を踏まえて、目指したのは以下の状態です。

  1. 容易にスケールできる状態
  2. システム稼働率の向上
    • 基本的な作業時にダウンタイムが発生しないこと(デプロイ時、スケールアウト・イン時など)
  3. インフラ構築・運用時間の短縮
  4. 現状よりもエラーが追いやすく、かつ検知できる状態
  5. インフラコストの削減

この状態を目指すべくECS移行プロジェクトは始まりました。

ECSを選択する理由

ローカルの開発環境はDocker化されており、エンジニアもデザイナーも使っている状態なので、学習コストや心理的な不安は少ない状態で移行できる状態です。 上記に挙げた状況を踏まえた上で、一番時間をかけずに移行できるのはECSだと判断しました。

ちなみに、ユーザ向けの機能開発がある中で、システム可用性を上げるタスクの優先度はなかなか上げられないのですが、今はスプリント中に確保されている改善デーを使って作業を進めています。(参考: スタートアップのCTOになって2ヶ月で作った開発サイクル)

設計

移行前の図

移行後の図

移行前後で基本的にシステムの概要は同じです。以下のことを考えて作っています。

  • クラスタ
    • 本番環境と検証環境の2つを作成
      • 運用が安定するまでは色々試したいので分離
    • 基本的にm4.largeを使ってクラスタを構成
      • 現在m4.largeはt2.largeとほぼ同じ価格
      • スポットインスタンスは運用が安定したら検討、それまではReserved Instancesを適用
      • CPUよりメモリが足りないといったことがあれば検討
  • タスク
    • cronで管理していたBatchの実行をDigdagを使ったスケジューラに移行
    • ポートが動的に変わるため各種アプリケーション向けにバランサを追加
      • このタイミングで管理ツール系のバランサを切り分け & まとめる
  • 永続的なデータ
    • ストレージ系のプロセスはECS上で動かさない

移行のために必要な作業

実際に移行をするにあたっては以下の作業が必要になりました。

  • ステートレスなアプリケーション設計の徹底(The Twelve-Factor App)
    • ログのwatchlogsへの移行
    • システムログ
    • アプリケーションログ
  • ECS向けのContinious Deployment(CD)の構築
  • ECS向けのリソース監視 & アラートの仕組みの構築
  • Auto Scaling Groupsの設計

「ステートレスなアプリケーション設計」における環境変数、画像、キャッシュ等々はすでに分離されていたため、今回は作業が発生しませんでした。

Digdagによるスケジューリングについて

cronで動いていたスクリプトはECS上のスケジューラに移行しています。 スケジューラにはTreasure Data製OSSのDigdagを使用しています。 シンプルなスクリプト実行であればcronやECSのScheduled Tasksでも十分ですが、Digdagを使うことで簡単な記法で処理間の依存を解決できるようにしています。

また、実行したいスクリプト毎にDockerイメージを作成してECSのタスク定義を作っていると、タスク数が膨大になり管理が大変になります。 それを避けるためにdocker runに渡す引数を以下のようにスケジューラから適宜渡すようにしています。これによりスクリプト群の入ったレポジトリ一つを含むDockerイメージを使うタスク定義で済むようになります。

timezone: Asia/Tokyo

_export:
  rb:
    require: 'util'

schedule:
  daily>: 02:00:00

+setup:
  rb>: Environment.setup

+do_something:
  _export:
    entry_point: Something.execute
    error_template: batch_error.txt
  rb>: ECS.run_batch
  _error:
    call>: error_slack_for_ecs.dig

+import_to_db:
  _export:
    entry_point: ResultImporter.execute
    argv: ${s3_output_path}/result.csv
    error_template: batch_error.txt
  rb>: ECS.run_batch
  _error:
    call>: error_slack_for_ecs.dig

+report:
  echo>: done!

詳細は公式ドキュメントを見ていただけると良いのですが、上記の定義では+setup+do_something+import_to_db+reportは依存があるため、失敗した場合は移行の処理が実行されないようになっています。 _exportで変数の設定を行い、変数に定義されたエントリーポイントに向けてECS.run_batchを実行するようにしています。

この構成を取ることの良いところは依存を張れるだけでなく、

  • ジョブの追加はエントリーポイントとscheduleの部分だけを書き換えて追加するだけなので簡単
  • エラーが起きた場合は該当のAmazon CloudWatch LogsへのリンクをSlackに飛ぶのでログへのアクセスが容易

といったことが挙げられます。エラーログへのリンクについてはスケジューラに処理を追加して実行中のコンテナIDからURLを生成しています。

ECSを使って見て気づいたこと

まだ移行作業中ではありますが、これまでで以下のことを学びました。

  • タスク定義でハードリミットを使うとメモリが足りない場合に実行が失敗する。
    • 対策としては十分に大きなインスタンス、または十分な数のインスタンスをクラスタに入れておく。
    • または、ソフトリミットで上限を指定しない運用にする。=> 現在はこれで大きな問題になっていません。
  • ECS用AMIのDockerエージェントが固まっていたことがある。
    • docker psで見ると認識されていないプロセスが残っていた。EC2の再起動で解決。Dockerエージェントのバージョンをアップデートしてからは起きていない。心当たりがあるのはクラスタのリソース不足や頻繁にタスクを停止したこと。再発したらテクニカルサポートに問い合わせるしかなさそう。
  • Docker化を進めることでアプリケーションのステートレス化が徹底される。
  • Ruby以外にも、JRubyやPython等々が必要な環境があるが、依存をDockerイメージごとに分離しておける。

今後やりたいこと

また移行作業が進んだ際には、その結果共有したいと思います!

まっくす


Housmartでは不動産業界を変えるカウルを支えるエンジニアを募集しています。
今話題のReTech!業界を変えるカウルを支えるエンジニアをWanted!

このエントリーをはてなブックマークに追加