$30 off During Our Annual Pro Sale. View Details »

Cookpad Tech Kitchen #20 Amazon ECS の安定運用 / Building a steady ECS infrastructure

Kohei Suzuki
November 28, 2018

Cookpad Tech Kitchen #20 Amazon ECS の安定運用 / Building a steady ECS infrastructure

Kohei Suzuki

November 28, 2018
Tweet

More Decks by Kohei Suzuki

Other Decks in Technology

Transcript

  1. Amazon ECS の安定運用
    Kohei Suzuki

    View Slide

  2. アウトライン
    - コンテナインスタンスの管理
    - スポットインスタンス対応
    - コンテナインスタンスのオートスケーリング
    - ログ
    - 配送、保存、検索
    - モニタリング

    View Slide

  3. 規模感
    - ほとんどのアプリケーションが ECS 上で動いている
    - hako (自作ツール) を使ってデプロイやバッチジョブの起動を行っている
    - ECS クラスタ数: 40
    - 合計 ECS サービス数: 500
    - 1日あたりの RunTask 数: 80,000

    View Slide

  4. コンテナインスタンスの管理

    View Slide

  5. コンテナインスタンスの管理
    - オンデマンドインスタンスは AutoScaling Group
    - ECS クラスタと AutoScaling Group が一対一対応
    - スポットインスタンスは Spot Fleet
    - ECS クラスタと Spot Fleet が一対一対応
    - オンデマンド・スポット比: 1:4

    View Slide

  6. Fargate?
    - 一部では Fargate も使っているが、基本は自前管理のインスタンス
    - スポットインスタンスを使ったほうが安い
    - Fargate だと起動が遅い、起動までの時間が安定しない
    - Web アプリではそこまで困らない
    - そこそこの頻度で起動するバッチジョブだとつらい
    - Fargate を使うケース
    - ECS クラスタ自体を操作するジョブ (ECS クラスタのオートスケーリング等 )
    - 特別に大きな CPU、メモリリソースを必要とするジョブ

    View Slide

  7. オンデマンドクラスタ
    - 全クラスタで共通の AMI から起動 (※GPU クラスタを除く)
    - オートスケールは自前 (後述)
    - AutoScaling Group の役割
    - 「desired capacity を上下するだけで ECS クラスタの増減ができる」状態にする
    - インスタンス障害からのオートヒール

    View Slide

  8. AutoScaling Group でのサービスアウト
    - スケールアウトは簡単だが、スケールインをするには事前にサービスアウトして
    おく必要がある
    - lifecycle hook の Terminating:Wait の間にコンテナインスタンスを DRAINING
    状態にしてサービスアウト

    View Slide

  9. スポットクラスタ
    - オンデマンド同様、共通の Ubuntu ベースの AMI から起動
    - Spot Fleet で管理
    - こちらもオートスケールは自前 (後述)
    - Spot Fleet の役割
    - 「target capacity を上下するだけで ECS クラスタの増減ができる」状態にする
    - スポット価格上昇やインスタンス障害からのオートヒール

    View Slide

  10. Spot Fleet でのサービスアウト
    - Spot Fleet の target capacity を下げてスケールインするときも、スポット価格上
    昇で terminate されるときと同じように interruption notice が通知される
    - interruption notice を CloudWatch Events から SQS キュー経由で受け取り、
    デーモンがサービスアウト処理を行う

    View Slide

  11. Spot Fleet でのサービスアウト
    - AutoScaling Group とは異なり、通知がきてから2分以内にサービスアウトしき
    る必要がある
    - バッチジョブは諦めて StopTask API で止める
    - スポットクラスタでバッチジョブを動かすときはアプリケーション側に冪等性を要求する
    - DRAINING 状態にして ECS に任せるだけでは間に合わない可能性がある

    View Slide

  12. Spot Fleet でのサービスアウト
    - 先に自前で ELB の target group から deregister する
    - 先に DeregisterTargets してしまえば新規のリクエストはそのタスクにはこなくなる
    - DeregisterTargets し終わったら StopTask で止める
    - ECS や ELB に邪魔されないように deregistration_delay.timeout_seconds を一定
    以上にしておく
    - 突然一部のタスクが停止しても問題無いようにやや過剰なキャパシティを常に
    確保しておく
    - そうしてもスポットインスタンスのほうが安い

    View Slide

  13. Spot Fleet でのサービスアウト (w/o ELB)
    1. interruption notice Terminator
    3. StopTask
    2. UpdateContainerInstancesState (DRAINING)

    View Slide

  14. Spot Fleet でのサービスアウト (w/ ELB)
    1. interruption notice Terminator
    5. StopTask
    2. UpdateContainerInstancesState (DRAINING)
    3. DeregisterTargets
    4. DescribeTargetHealth

    View Slide

  15. オートスケーリング

    View Slide

  16. オートスケーリング
    - オンデマンドクラスタとスポットクラスタの間で戦略に差は無い
    - AutoScaling Group の desired capacity を調整するか、Spot Fleet の target
    capacity を調整するかの違いだけ
    - 3種類のスケールアウトと1種類のスケールイン

    View Slide

  17. スケールアウト
    - クラスタの CPUReservation、MemoryReservation を見て閾値を超えてたらス
    ケールアウト
    - 各 service の desired_count、running_count、pending_count を
    チェックして足りてなさそうだったらスケールアウト
    - hako oneshot (バッチジョブの起動) がリソース不足で RunTask に失敗した
    らスケールアウト

    View Slide

  18. スケールアウト (1)
    - CloudWatch に入っている CPUReservation、MemoryReservation を定期的に
    チェックして、閾値を超えていたらスケールアウト
    - スポットクラスタの場合はオンデマンドクラスタより閾値を低くしている

    View Slide

  19. スケールアウト (2)
    - 各 service の desired_count、running_count、pending_count を定
    期的にチェックして、desired_count > running_count +
    pending_count となっていたらリソースが足りずにデプロイが停滞している可
    能性があるので、スケールアウトする

    View Slide

  20. スケールアウト (3)
    - hako oneshot (バッチジョブの起動) がリソース不足で RunTask に失敗した
    ときに SNS トピックに通知するので、それを SQS 経由で受け取ってスケールア
    ウト

    View Slide

  21. スケールアウト (3)
    AutoScaler
    Hako
    1. RunTask (failed)
    2. Publish
    3. ModifySpotFleetRequest
    3. SetDesiredCapacity

    View Slide

  22. スケールイン
    - クラスタの CPUReservation、MemoryReservation を見て閾値を下回っていた
    らスケールイン
    - スポットクラスタの場合はオンデマンドクラスタより閾値を低くしている

    View Slide

  23. ログ

    View Slide

  24. ログ
    - コンテナの stdout/stderr に出たログをどこにどう保存するか
    - 現在のクックパッドでは Docker の fluentd logging driver から fluentd を経由
    して Amazon S3 に保存し、Amazon Athena で簡易検索できるようにしている

    View Slide

  25. CloudWatch Logs?
    - マネージドなログの保存、検索、閲覧サービス
    - しかしログの量が膨大すぎてピークタイムでは CloudWatch Logs にリアルタイ
    ムに入りきらない
    - 入っても検索したり取り出したりするのが遅い
    - ログを入れるときの料金 (ingestion) だけでもかなり高価に
    - 閲覧できるまでのラグや検索の柔軟性をやや犠牲にして、スケーラブルで安価
    な S3 をログストレージとして選択

    View Slide

  26. ログ配送
    Container instances
    service logs
    task logs
    S3 Event
    ecs-logs-router
    fluentd aggregator

    View Slide

  27. ログ配送
    Container instances
    service logs
    task logs
    S3 Event
    ecs-logs-router
    fluentd aggregator

    View Slide

  28. ログ配送
    - コンテナインスタンスのホスト側に fluentd を起動し、Docker の logging driver
    の設定でそこへ送信
    - 各コンテナインスタンスから fluentd の集約ノードへと転送する
    - 集約ノードには大量のログが送信されてくる
    - fluentd v1.0 からは複数プロセスで処理できるようになっているので、それを利用してがんばっ
    て処理しきる
    - https://docs.fluentd.org/v1.0/articles/multi-process-workers

    View Slide

  29. ログ配送
    Container instances
    service logs
    task logs
    S3 Event
    ecs-logs-router
    fluentd aggregator

    View Slide

  30. ログ配送
    - 集約ノードの fluentd が1分毎に
    s3://service-logs/${task_definition_family}/${container
    _name}/%Y/%m/%d/%Y%m%d%H%M_%{index}_%{flush_uuid}.gz に
    ログを出力する
    - タスクを横断して、service 毎 (task definition 毎) に閲覧するためのログ

    View Slide

  31. ログ配送
    Container instances
    service logs
    task logs
    S3 Event
    ecs-logs-router
    fluentd aggregator

    View Slide

  32. ログ配送
    - s3://service-logs に置かれたログを ecs-logs-router がタスク ID 毎に分
    解して
    s3://task-logs/${task_definition_family}/${container_na
    me}/${task_id}/ 以下に置く
    - タスク単位で閲覧するためのログ

    View Slide

  33. ログ検索
    - Amazon Athena で検索できるように、AWS Glue でカタログを日次で更新して
    いる
    - ${task_definition_family}_${container_name} というテーブル名
    - 日付でパーティションを切っている
    - 例: my-awesome-app の app コンテナの今日のログから ERROR を探す
    - select time, log from "my_awesome_app_app" where year = 2018 and
    month = 11 and day = 28 and log like '%ERROR%' order by time

    View Slide

  34. モニタリング

    View Slide

  35. モニタリング
    - CloudWatch に service 単位のメトリクスは存在しているが、タスク単位、コンテ
    ナ単位でのメトリクスは存在していない
    - したがって RunTask で起動した場合はメトリクスが一切存在しない
    - アプリケーション開発者にとって、主に見たいのはアプリケーションコンテナだけ
    のメトリクス
    - サイドカーとして起動している fluentd や Envoy 等は要らない

    View Slide

  36. モニタリング
    - cAdvisor でメトリクスを取得し、Prometheus からそれを scrape し、Grafana で
    可視化することにした
    - cAdvisor 自体に Prometheus 用のメトリクスを返すエンドポイントがあるので、
    これが一番簡単そうだった
    - cAdvisor は ECS の daemon scheduling を使って各コンテナインスタンスに配
    置した

    View Slide

  37. モニタリング

    View Slide

  38. モニタリング

    View Slide

  39. まとめ

    View Slide

  40. まとめ
    - ECS で様々なサービスを動かすためにやってることの一部を紹介した
    - オンデマンドインスタンス管理、スポットインスタンス対応
    - オートスケーリング戦略
    - ログ配送
    - モニタリング
    - 今回話さなかったトピック
    - ECS API の rate limit を回避するための工夫
    - コンテナインスタンス側の問題を調査するためのロギング

    View Slide

  41. 今後
    - センシティブな値を環境変数に入れる機能がついにきたので移行したい
    - https://aws.amazon.com/about-aws/whats-new/2018/11/aws-launches-secrets-support-f
    or-amazon-elastic-container-servic/
    - AutoScaling Group でスポットインスタンスを管理できるようになったので移行し
    たい
    - https://aws.amazon.com/jp/blogs/aws/new-ec2-auto-scaling-groups-with-multiple-instan
    ce-types-purchase-options/

    View Slide