Slide 1

Slide 1 text

AWS勉強会 ECS編 メドピア株式会社 CTO室 SRE 侘美 怜

Slide 2

Slide 2 text

注意 本資料はメドピア社内で実施した勉強会の資料を外部公開向けに一部修正したものに なります。 講師の説明が無いと理解しづらい図などがありますのでご注意ください。

Slide 3

Slide 3 text

目的 ECSの仕組み・概念を理解する Fargateとは何かを理解する ECSのスケーリングについて理解する ECSのデプロイ時の挙動を理解する ECSのログの閲覧方法を理解する

Slide 4

Slide 4 text

目次 1. AWSの基本 ○ Region ○ Availability Zone ○ VPC/Subent 2. ECS ○ ECSの概念 ○ Fargate ○ スペックとスケール ○ ログ ○ デプロイ

Slide 5

Slide 5 text

1. AWSの基本

Slide 6

Slide 6 text

概念 Region Availability Zone VPC(Virtual Private Cloud) Subnet

Slide 7

Slide 7 text

Region AWSが展開する世界各地のロケーションのこと AWSの各種リソースは指定したRegionの中に作られる(例外あり) メドピアでは東京リージョン(ap-northeast-1)を使っている

Slide 8

Slide 8 text

Availability Zone リージョン内に複数あるデータセンターのこと 東京リージョンには3つのAZがある(ap-northeast-1a, 1c, 1d) 同時に複数のAZが落ちることはほぼないため、サービスを複数AZにまたがって構築す ることで、高い可用性を実現する Region (ap-northeast-1) AZ-A AZ-C AZ-D

Slide 9

Slide 9 text

一般的なMulti-AZ構成 Region (ap-northeast-1) AZ-C AZ-A Aurora(Writer) Aurora(Replica) ECS ALB ECS 簡略化のためにAZ2つで説明 どっちかのAZが落ちてもサービ スは稼働する構成 ただし、Region全体のネット ワーク障害などの場合は死ぬ AZ間通信の場合、AZ内通信に くらべて、10ms程度レイテンシ が発生する

Slide 10

Slide 10 text

AZ間通信のレイテンシがネックになる例(実話) 1回のSQL実行が5msのクエリがあったと仮定する。 このクエリを1000回実行するようなN+1の実装をしてしまった。 5ms * 1000回なので、5000msで応答する計算になる。 ローカルで計測してもその程度だった。 まあまあ重いページなので、5秒は許容範囲としリリースした。 リリース後、「2回に1回くらいの頻度で、エラーになる」という問い合わせが入ってしまっ た。 AZ間通信の場合、1回の通信ごとに10ms程度のレイテンシが発生する。 →1000回の通信では、10秒のレイテンシが発生する。 →レスポンスに15秒かかってしまい、タイムアウト。

Slide 11

Slide 11 text

VPC(Virtual Private Cloud) 仮想的なネットワーク空間 複数のAZをまたぐように作成できる production環境とstaging環境は別のVPCとすることで、ネットワーク的に分離する設計 が一般的 Region (ap-northeast-1) AZ-A AZ-C AZ-D VPC 10.0.0.0/16

Slide 12

Slide 12 text

Subnet VPCの中に作るサブ的なネットワーク空間 VPC内にEC2やRDSを作成する際に指定が必須になる Subnet単位でアクセスを制限したりする Availability Zone 1a Availability Zone 1c Availability Zone 1d VPC Private subnet 10.0.130.0/24 Public subnet 10.0.30.0/24 Private subnet 10.0.120.0/24 Public subnet 10.0.20.0/24 Private subnet 10.0.110.0/24 Public subnet 10.0.10.0/24

Slide 13

Slide 13 text

2. ECS

Slide 14

Slide 14 text

目次 1. AWSの基本 ○ Region ○ Availability Zone ○ VPC/Subent 2. ECS ○ ECSの概念 ○ Fargate ○ スペックとスケール ○ ログ ○ デプロイ

Slide 15

Slide 15 text

2.1. ECSの概念

Slide 16

Slide 16 text

ECSとは Elastic Container Service Dockerコンテナの実行や管理を行うことができるサービス メドピア社内のサービスは一部の古いサービスを除き、ほぼこれで運用されている Kubernetesとは別物 Kubernetesはもっと複雑で、ロードバランサーやストレージ等様々なリソースを管理でき る

Slide 17

Slide 17 text

ECSの概念 ECSクラスター ECSサービス ECSタスク定義 ECSタスク Dockerコンテナ

Slide 18

Slide 18 text

クラスター/サービス/タスク/コンテナの関係 Cluster Service A Task A Container A Container B Service B Task A Container A Container B Task B Container C Task B Container C

Slide 19

Slide 19 text

AWSマネジメントコンソールで確認 ECSクラスター ECSサービス ECSタスク

Slide 20

Slide 20 text

タスク定義 ECSサービスがECSタスクを起動する 際の設定が書かれたリソース 「どんなコンテナをどんな設定で起動す るか」が書かれている デプロイする度に新しいリビジョンが作ら れる(新しいイメージが指定されている)

Slide 21

Slide 21 text

タスク定義を確認する AWSマネジメントコンソール上で現在作成されたタスク定義を確認できる 作成元のコードは社内のサービスによって管理されている場所が異なる ● 比較的最近のプロジェクト等はterraformのリポジトリに入っている ● ecs-cliを使ってプロジェクトは、docker-compose.yml形式に変換されたものがrails のリポジトリ内に入っている 例をマネジメントコンソールと実装で見てみる 参考:https://github.com/aws/amazon-ecs-cli

Slide 22

Slide 22 text

マネジメントコンソールで紹介

Slide 23

Slide 23 text

タスク定義と他リソースの関係 ECSサービスは属性として以下を持つ ● 使用するタスク定義とそのリビジョン ● 希望するタスク数 Task Definition Revision 10 Service Task Revision 1 ... Task Task Task Revision 10のタスク定義で4タスク起動する設定にした例

Slide 24

Slide 24 text

タスク定義とデプロイ ECSのデプロイは通常以下の方式で行う 1. 新しいDockerイメージをビルドする 2. 新しいDockerイメージを利用するタスク定義のリビジョンを作成する 3. ECSサービスで使用するタスク定義を最新のリビジョンに更新する 4. ECSサービスがコンテナを置き換える

Slide 25

Slide 25 text

タスク定義とデプロイ ① Dockerイメージをビルド ② タスク定義の新リビジョンの作成 ③ ECSサービスのタスク定義指定を更新 ④ ECSサービスがタスクを置き換える Task Definition Revision 10 Service Task(10) Image Image Revision 11 Task(10) Task(11) Task(11) Task Definition Revision 10 Service Task(10) Image Image Revision 11 Task(10) Task Definition Revision 10 Service Task(10) Image Image Revision 11 Task(10) Task Definition Revision 10 Service Task(10) Image Image Task(10)

Slide 26

Slide 26 text

実際の構成の例 ECSクラスター ECSサービス 起動タイプ ECSタスク (数) Dockerコンテナ prd-app prd-app Fargate prd-app (4) - nginx - rails - datadog prd-api prd-api Fargate prd-api (4) - nginx - rails - datadog prd-admin prd-admin EC2 prd-admin (1) - nginx - rails - datadog prd-worker prd-worker Fargate prd-worker (2) - worker(sidekiq) - datadog ※ 実際に運用しているサービスの名前とは微妙に異なります

Slide 27

Slide 27 text

2.2. Fargate

Slide 28

Slide 28 text

Fargateとは? Fargateとは、ECSサービスに指定する起動タイプの1種 起動タイプにはFargateとEC2がある

Slide 29

Slide 29 text

起動タイプEC2 ECSサービスの起動タイプをEC2に設定するとタスクはECSクラスターに登録された EC2インスタンスのDockerデーモン上で実行される このEC2インスタンスは自前で用意しなければならない 1つのEC2インスタンス上でCPUやメモリが足りていれば複数のタスクを実行することが できる EC2インスタンス Task A Container A Container B Task B Container C

Slide 30

Slide 30 text

起動タイプFargate Fargateはマネージドなコンテナ実行環境 起動タイプにFargateを指定した場合、タスクはFargate上で実行される FargateのスペックはCPUとメモリを指定でき、サイズに比例して料金がかかる Fargate Task A Container A Container B Task B Container C Fargate

Slide 31

Slide 31 text

比較 Fargate EC2 スケーリング速度 速い EC2の起動の分遅い 管理コスト 低 高 EC2の管理も自前 コスト 低 デプロイ時のキャパシティ的に メモリの料金に無駄が出る ssh可能 (基本的に)不可能 可能

Slide 32

Slide 32 text

スケーリング速度 Fargateのほうが倍程度速い EC2の場合は インスタンスの起動→クラスターへ登録→イメージのpull→コンテナ起動という流れ Fargateの場合は ENIの設定→イメージのpull→コンテナ起動という流れ インスタンス起動はインスタンスタイプにもよるが、2分程度かかるが、FargateのENIの 設定は10秒程度で終わる

Slide 33

Slide 33 text

管理コスト EC2の方が管理対象が増える SRE的には令和の時代にEC2インスタンスの管理はやりたくない(個人の感想) EC2インスタンスのインスタンスタイプ設定、OS、スケーリング管理等がFargateに比べ ると余計に必要になる ECSタスクで利用するCPU、メモリとタスク数からEC2のインスタンスタイプを決定する 等の設計のコストも高い EC2にどのようにタスクを配置するか等も設計しなければならない (先日これをミスって503エラーを出してしまった) 一方でFargateはタスク1つ1つのCPU、メモリサイズを決めるだけで良い

Slide 34

Slide 34 text

コスト CPUやメモリサイズで見ると、EC2とFargateにそこまで大きなコスト差はない ただし、デプロイ設定によってはEC2が大きな損をすることになる デフォルト設定である、デプロイ時のコンテナが2倍起動する設定にした場合、通常起動 するタスクのCPUとメモリの2倍が収まるサイズのEC2インスタンスを用意する必要があ る これは、現在のCPU/メモリ使用量ではなく、タスクの最大のCPU/メモリ使用量がEC2上 に空いていなければタスクを起動しないというECSの特性に起因する この問題はデプロイ時に一時的にタスクの数が少なくなることを許容すれば回避可能

Slide 35

Slide 35 text

コスト Fargate EC2 Fargate Task A cpu: 256 mem: 512 EC2 cpu 512, mem: 1024 Task A cpu: 256 mem: 512 デプロイ中 デプロイ後 Fargate Task A cpu: 256 mem: 512 Fargate newTask A cpu: 256 mem: 512 Fargate newTask A cpu: 256 mem: 512 EC2 cpu 512, mem: 1024 Task A cpu: 256 mem: 512 newTask A cpu: 256 mem: 512 EC2 cpu 512, mem: 1024 newTask A cpu: 256 mem: 512 平常時にタスクの倍のスペックの EC2を用 意する必要があり、無駄コストとなる

Slide 36

Slide 36 text

ssh可能か 起動タイプEC2の場合は、EC2へssh→ `docker exec` でコンテナへ接続できる Fargateの場合は、マネージドサービスなのでssh接続ができない sshをSessionManagerに置き換えても同じ Twitterでこんな意見も見た

Slide 37

Slide 37 text

sshする運用を廃止して Fargateを使おう

Slide 38

Slide 38 text

とはいえ、現実は... 社内でよくある例 adminクラスターは起動タイプEC2でssh(SessionManager)接続できるようにしておき、 appクラスターはFargateにする adminのタスクはCPU/メモリ共に小さいし、タスク数も少ないのでEC2に余計にコストが かかる点も微々たるもの ただし、社内の最新のイケてるプロジェクトは完全Fargateのみである

Slide 39

Slide 39 text

余談:stgはEC2、prdはFargate→ダメゼッタイ 社内でよくある構成でした 理由は、昔Fargateにリザーブドプラン(料金を前払いして割り引きする仕組み)が無かっ たため(今はある) ここ半年で2件ほど、Fargateでしか発生しない不具合を踏んだケースが発生 1件はsassc-rubyのバグ 1件はEC2とFargateの構成差によるRole関連の見落とし 本番環境とステージング環境は最大限努力して同一の環境にしましょう

Slide 40

Slide 40 text

2.3. スペックとスケール

Slide 41

Slide 41 text

ECSのスペック ECSでスペックを指定できる箇所を起動タイプ別に以下に示す Fargate EC2 Task Fargate Container Container 必須 - CPU - メモリ FargateはTaskのCPUとメモリ のスペックで確保される 任意 - CPU/メモリ - 予約メモリ EC2 Instance 必須 - インスタンスタイプ Task Container Container Task Container 任意 - CPU/メモリ - 予約メモリ 必須 - メモリ 任意 - CPU

Slide 42

Slide 42 text

注意点 ● メモリ使用量100%になると、コンテナが落ちる ○ dockerのOOM Killerが発動し、コンテナが落ちる ○ 基本的にどのコンテナも必須なので、 1つのコンテが落ちたらタスク全体が落ちる設定が多い ● 起動タイプEC2の場合、空きがないとタスクは起動しない ○ タスクサイズ分のCPU/メモリの空きがEC2に無いと起動しない ○ この空きは現在の使用量ではなく、タスクサイズで計算される ○ 例:メモリ4GBのEC2上では、メモリ1.5GBのタスクは起動直後のメモリ消費が 100MBであっても、2 つまでしか起動できない

Slide 43

Slide 43 text

ECSのスケーリング 起動タイプFargateのオートスケーリングを設定できる箇所 タスクの数をスケール可能 スケーリングのトリガーはCPUとかメモリとか、CloudWatchで取得できる値ならなんでも Fargate Container Container Task Fargate Container Container Task

Slide 44

Slide 44 text

ECSのスケーリング 起動タイプEC2のオートスケーリングを設定できる箇所 タスクの数とEC2インスタンスの数の両方をスケール可能 Container Container Task Container Container Task EC2

Slide 45

Slide 45 text

注意点:よくある失敗 元々の状態 EC2メモリ4GB + メモリ1.5GBのタスク1つ スケールアウトして下記の状態となった EC2メモリ4GB + メモリ1.5GBのタスク2つ この状態だと、空きメモリは `4GB - 1.5GB * 2 = 1GB` しかない デプロイ時のmaxを200%にしていると、デプロイ時に新規にタスクが起動できないた め、デプロイがエラーになって終了する →起動タイプEC2の場合、EC2とタスクの両方のスケーリングを正しく設定する必要が ある(無駄コストを払わないようにこれを実現するのはかなり複雑なのでFargateを使お う)

Slide 46

Slide 46 text

2.4. ログ

Slide 47

Slide 47 text

ECSのログ ECSではDockerコンテナの標準出力がCloudWatch LogsというAWSのサービスへ送 信される 起動タイプEC2の場合、ssh接続しdockerコマンドを実行できるが、docker logsコマンド でコンテナのログを見ることはできないので注意

Slide 48

Slide 48 text

ログの見方① 直接CloudWatch Logsへ移動し確認する方法

Slide 49

Slide 49 text

ログの見方① 同じECSサービス→同じロググループへ出 力 コンテナ名がログストリームのprefixに設定さ れている

Slide 50

Slide 50 text

ログの見方② ECSの画面からコンテナを指定して、CloudWatch Logsを開くことも可能 クラスター→サービス→タスク→コンテナ→CloudWatchのログを表示

Slide 51

Slide 51 text

ログの見方③ ECSの画面上でもログを確認することができる クラスター→サービス→タスク→Logsタブ→コンテナを指定 ここだけログが降順で並ぶので注意

Slide 52

Slide 52 text

ECSにおけるログ出力設定の注意 標準出力と標準エラー出力の使い分けができない jsonにすると検索性がよくなる(らしい) CloudWatch Logsはログの量で課金されるので、ログレベルは `:info` にすることを徹 底していきたい

Slide 53

Slide 53 text

2.5. デプロイ

Slide 54

Slide 54 text

ECSのデプロイ デプロイの仕組みというよりは、デプロイ時に発生する有名な問題の話を紹介 デプロイ自体はタスク定義の項で紹介したように以下の流れで行われる ● 最新のソースコードでDockerイメージを作成 ● ECRにimageをpush ● タスク定義に上記イメージを使用する新しいリビジョンを追加 ● ECSサービスで利用するタスク定義のリビジョンを最新に更新 ● あとはECSが設定に基づき新しいタスクを起動し、古いタスクを停止 余談:上記処理はどこから実行しても良いので、GitHub Actionsからデプロイする仕組 みも実験中

Slide 55

Slide 55 text

問題:ECSデプロイ時にassetsの参照で404エラー デプロイしている最中にサービスにアクセスすると、jsやcss等のassets系のGETリクエ ストが一定確率で404エラーになる問題 何も対策せずに、デフォルトのECSのデプロイ設定でRailsをデプロイすると発生する

Slide 56

Slide 56 text

仕組み Task Container V1 Task Container V2 ① htmlを新バージョンのアプリか ら取得(1/2の確率で発生) デプロイ中に新旧両方のバージョンが ロードバランサーに紐づくタイミングで発 生する ② htmlに記載されたcssを旧バー ジョンのアプリに取りに行く ③ 新バージョンのアプリの asset pipelineで作成されたcssは存在し ないので404を返す assets pipelineを使っているRailsが複数バージョン混 在するとエラーになるという当たり前の話 ELB

Slide 57

Slide 57 text

対策 メジャーな対策を2つ紹介する 現在社内ではどちらのパターンも存在しているが、今後は対策2の方を採用していく見 込み 対策1. assetsファイルをCloudFrontで配信 対策2. CodeDeployで一括でトラフィック切り替え

Slide 58

Slide 58 text

対策1. assetsファイルをCloudFrontで配信 Task Container V1 Task Container V2 /assets 等のパスはS3から取得す るように設定 新バージョンをデプロイする直前 にassets系ファイルをアップロード する デプロイ中は、CloudFrontから新旧両方の バージョンのassetsファイルを取得できるよう にする方法 S3に無限にassetsが溜まっていかないよう に、デプロイの最後、旧バージョンのコンテナ が停止した後で、古い assetsファイルを消す CloudFront S3 Bucket

Slide 59

Slide 59 text

対策1. assetsファイルをCloudFrontで配信 Task Container V1 Task Container V2 ドメインを別にしている亜種パターンもある assets.example.com example.com

Slide 60

Slide 60 text

対策1. のデメリット ロールバックができない ECSはタスク定義のリビジョンが残るため、本来であれば簡単な操作で1つ前のリビジョ ンにロールバックできる しかし、S3に前のバージョンのassetsファイルが無いため、ロールバックするとassets系 ファイルの取得がすべて404エラーになってしまう S3上に古いバージョンのassetsを残すようにすればよいが、コストが増えたり定期的に 消す仕組みの管理など複雑になりがち

Slide 61

Slide 61 text

対策2. CodeDeployで一括でトラフィック切り替え Task Container V1 Task Container V2 ELB Task Container V1 Task Container V2 ELB CodeDeployというAWSサービスと連携してECSへデプロイすることで、新旧バージョン が混在する瞬間を発生させずに、トラフィックを一括で切り替える タスクがいくつあっても一括で新旧 のバージョンへの トラフィックが切り替わる

Slide 62

Slide 62 text

対策案2. のメリット ロールバックができる デプロイがCodeDeployに管理されるため、ECSサービスのタスク定義のリビジョン指定 ではなく、CodeDeployからロールバックを指示することになる とはいえ、CodeDeployのロールバックもシンプルなのでワンボタンでロールバックでき る 案1よりコストに優れる 案1はCloudFrontが必須だが、こちらは任意なので管理画面とかにも気軽に使える

Slide 63

Slide 63 text

備考:カナリアデプロイに関して カナリアデプロイを行う場合は、新旧両方のassetsファイルを配信する必要があります。 CodeDeployに備わっているカナリアデプロイの機能を利用する場合は、案1、案2とは 異なる構成を検討する必要があります。

Slide 64

Slide 64 text

Fin.