コネヒト開発者ブログ

コネヒト開発者ブログ

Amazon ECSで2019年に導入した新機能

こんにちは。インフラエンジニアの永井(shnagai)です。

re:invent真っ最中で、EKS for Fargateが発表されたりFargate Spotが発表されたり今年も激熱ですね!!

今回は、日々アップデートされているECSの新機能の中から今年導入したものと今後導入を検討していきたいと思っているものについて書こうと思います。

内容はざっくりと下記3項目です。

  • パラメータストアに保管した値の環境変数への注入方法変更
  • Container Insightsを使ったタスクの監視
  • 今後導入していきたいこと
    • Savings PlansでのFargateのコスト削減
    • FireLensを使って脱CWLの柔軟なログルーティング

この記事はコネヒト Advent Calendar 2019 4日目の記事です。

パラメータストアに保管した値の環境変数への注入方法変更

アプリケーションで利用する秘匿変数(DB接続情報等)は、パラメータストアに暗号化して保管し、コンテナ起動時に復号化と環境変数へのセットをしています。

参考: The Twelve Factors

これまでは、別のツールを使ってコンテナ起動時に環境変数のセットと復号化を行っていたのですが、下記理由から今回新規サービスでは、Amazon ECS シークレットのサポートとして対応されたタスク定義でのパラメータストア値の環境変数への投入を導入しました。

  • メンテナンスするツールを少しでも少ない方が良い(同じことがより簡単に出来るならばそちらを採用)
  • 出来るだけシンプルな構成にしたい

AWSが隙間を埋めてくれるという話は、以前カヤックの藤原さんのスライドを見てすごく共感したのを思い出しました。

AWSの「隙間」を埋める隙間家具 OSS 開発 / AWS DevDay Tokyo 2019 - Speaker Deck

手順等は、公式ドキュメントに詳しく書いてあるので割愛して、一点注意点だけ書いておこうと思います。

パタメータストアでは、AWS上で管理する秘密鍵(KMSキー)を使った値の暗号化に対応しており、秘匿変数はこの機能を使うことが多いかなと思います。 暗号化した値を使うには復号が必要で、タスクに対してその値を環境変数にセットするには、復号に使うKMSキーへのアクセス権を持ったIAMロールを使ってパラメータストアにアクセスする必要があります。

どちらの方法でもこの機能を使うことはもちろん出来るのですが、環境変数に値をセットするタイミングが異なるため、タスク定義でKMSへのアクセス権を設定するロールが異なります。 最初この部分が理解出来ず少しハマりました。

まとめると、

  • ツール等でパラメータストアにアクセスするパターン

タスクが起動した後のDockerのCMD句の命令にてパラメータストアにアクセスするので、ECSのタスクロールにKMSキーへのアクセス権が必要

  • タスク定義でのパラメータストア値の環境変数投入を使うパターン

タスク起動時に、ECSコンテナエージェントがパラメータストアにアクセスするので、ECSのタスク実行ロールにKMSキーへのアクセス権が必要

まだパラメータストアのベストプラクティスとされている、階層化パラメータ(aws ssm get-parameters-by-path相当)の値の展開には対応されてないですがそのうちサポートされそうな気もします。

Container Insightsを使ったタスクの監視

これまでコンテナの細かいメトリクスを取得するには、Datadog等の別のモニタリングツールを使う必要があったのですが、今年Container Insightsが発表されてより詳細なメトリクスの取得が可能になりました。

Amazon ECS Container Insights のメトリクス - Amazon CloudWatch

Container Insightsをまだフル活用は出来ていないのですが、一部の監視を追加したのでそちらを紹介します。

普段のコンテナの監視としては、ECSサービスがぶら下がるALBの UnHealthyHostCount がしきい値以上ある場合にアラートを送る形でタスクが起動していない状態を監視していました。 ※UnHealthyHostCountもユーザ目線でサービス提供出来ていない状態を検知出来ているのでこれはこれで意味があります。

最近よく聞くDesign for Failureの精神に則り、セルフヒーリングを前提としてタスクが落ちた数ではなくタスク定義で宣言した状態が維持出来ているかを監視するべきとの思想の元「タスクが宣言数起動しているか」を確認する監視を追加しました。

ざっくり説明すると、

  • Container Insightsから該当Serviceの RunningTaskCount (現在起動しているタスク数)とDesiredTaskCount (Serviceで宣言されているタスク数)を取得
  • それぞれにidを振り、RunningTaskCount / DesiredTaskCount の値をメトリクスとする
  • 起動しているタスク数が宣言しているタスク数を下回っている場合は、期待した状態ではないのでそれが続くようであれば異常が発生していると言える

以下、Container Insightsの設定手順を書いていきます。

Container Insightsの有効化

まずは、既存のクラスタに対してContainer Insightsを有効化します。 既存クラスタに対しては、AWSコンソールからの変更はまだサポートされていないので下記のようにaws cli経由で実行します。

$ aws ecs update-cluster-settings --cluster クラスタ名 --settings name=containerInsights,value=enabled
{
    "cluster": {
        "clusterArn": "arn:aws:ecs:ap-northeast-1:xxx:cluster/クラスタ名",
        "clusterName": "クラスタ名",
        "status": "ACTIVE",
        "registeredContainerInstancesCount": 0,
        "runningTasksCount": 0,
        "pendingTasksCount": 0,
        "activeServicesCount": 0,
        "statistics": [],
        "tags": [],
        "settings": [
            {
                "name": "containerInsights",
                "value": "enabled"
            }
        ]
    }
}

CloudWatchでの定義とメトリクスのサンプル

CloudWatchのメトリクスで下記のようなJSONを「発信元」に追加します。

{
    "metrics": [
        [ { "expression": "m0r1 / m0r0", "label": "hoge-task-count-health", "id": "e0", "region": "ap-northeast-1", "color": "#1f77b4" } ],
        [ "ECS/ContainerInsights", "RunningTaskCount", "ServiceName", "サービス名", "ClusterName", "クラスタ名", { "id": "m0r1", "label": "RTC", "visible": false, "color": "#ff7f0e" } ],
        [ ".", "DesiredTaskCount", ".", ".", ".", ".", { "id": "m0r0", "label": "DTC", "visible": false } ],
        [ { "expression": "m1r1 / m1r0", "label": "hoge1-task-count-health", "id": "e1", "region": "ap-northeast-1", "color": "#e377c2" } ],
        [ "ECS/ContainerInsights", "RunningTaskCount", "ServiceName", "サービス名", "ClusterName", "クラスタ名", { "id": "m1r1", "label": "RTC", "visible": false, "color": "#ff7f0e" } ],
        [ ".", "DesiredTaskCount", ".", ".", ".", ".", { "id": "m1r0", "label": "DTC", "visible": false } ]
    ],
    "id": "DesiredTaskCount",
    "legend": {
        "position": "right"
    },
    "region": "ap-northeast-1",
    "stat": "Minimum",
    "title": "ECS-Check-Task-Status",
    "view": "timeSeries",
    "period": 60,
    "yAxis": {
        "left": {
            "min": 0
        }
    }
}

すると下記のような形で、メトリクスが追加されます。 f:id:nagais:20191204101306p:plain

通知

後は、メトリクスで作ったe0 ,e1 をCLoudWatchアラームでそれぞれ監視します。

  • 1分単位で監視して、3回中2回以上1を下回ったら通知(セルフヒーリングを考慮し連続的に下回ってなければOK)
  • 通知は、AWS Chatbotを利用(これまでLambdaでやっていたようなコーディング不要に)

通知サンプル f:id:nagais:20191204101526p:plain

Container Insightsをうまく使うことでより下記のブログにもあるように、ECS運用におけるサイジングや柔軟なスケーリングも行えると思うのでうまく使ってリソースの最適化にも取り組んでいきたいと考えています。

Amazon ECS向けAmazon CloudWatch Container Insightsについて | Amazon Web Services ブログ

今後導入していきたいこと

最後に今後取り入れていきたいと思っているECS関連のアップデートについて少し触れようと思います。

Savings PlansでのFargateのコスト削減

※Fargate Spotが発表されたので、そちらも加味しながらこれから最適なプランを探ろうと思います

Fargate利用者もしくは検討している方には待望のSavings Plansという割引プランが発表されました。 コネヒトでは、EC2バックエンドで動かしているECSに関してRIを使うことでコスト削減を実現しているのですが、FargateはRIに対応しておらず運用コスト面を考えるとFargateはとても魅力的なのですが必要なリソースが大きいサービスでは費用面からFargate導入に踏み切れていませんでした。 Savings Plansは、RIとは概念が異なり、コンピューティングリソースの使用量をコミットするという考え方で横への自動適用が効くようなのでEC2/Fargateを意識せずに割引の恩恵を受けることが出来ます。

先日、AWSにSavings Plansの話を聞きに行ったところ、3年プランは割引率が高いので3年間を想定した最低利用金額をコミットするのがおすすめという話を聞いたので、試算してRI切れるタイミングでSavings Plansを購入しようと計画しています。

FireLensを使って脱CWLの柔軟なログルーティング

コンテナが標準出力したログをCloudWatchLogsに出力しているのですが、同じロググループに数種類のログがある時にLambdaで加工したり、CWLでフィルタするのが面倒という課題があります。 FireLensを使うと、ログのルーティングにFluentdを使った柔軟なログルーティングが出来ると思うのでこちらも今後試して導入していこうかと検討中です。 別コンテナで運用している、アグリゲータ的にFluentdに集約している処理もFireLensに置き換えて運用コストを下げられないかも同時に検討しようと思っています。

カスタムログルーティング - Amazon ECS

コネヒトではコンテナ周りの基盤を整備したりサービス改善を行うエンジニアを募集しています。 少しでも興味もたれた方は、是非気軽にオフィスに遊びにきていただけるとうれしいです。

AWSやDockerを駆使してサービスの信頼性を向上させるエンジニア募集 - コネヒト株式会社のインフラエンジニアの求人 - Wantedly