前記事 AWS ECS Fargate Autoscaling の実戦的な基礎知識 の続きというか派生的なところで、こんな監視項目がこんな理由でえぇんちゃうん、という基礎知識的なお話です。
当ブログでは『監視よければ全てヨシ!』という格言を推していますので、監視の仕込みをサボっている人は今からでも頑張っていきましょう。
目次
はじめに
もともと書くつもりでいた本タイトルは、公式からこのドキュメントが出たことで、ゴミ箱行きかと思いました。んが、まぁ所詮はドキュメントということで、ここではもう少し実戦に寄り添う形でまとめていければと思います。
あったら嬉しい監視項目をカテゴリごとに整理しつつ、その理由やら補足情報によって、楽しく監視できるようにしていきたいところです。合わせて読みたいところとしては、この辺もどうぞ。
Container Insights を有効にする
ECS Service の通常メトリクスは、基本的には Service 管理画面でも見れる以下の2つしか項目がありません。そのため、十分な項目を得るには Amazon ECS Container Insights メトリクス を有効にしてそちらを利用します。
有効にするには、aws_ecs_cluster に設定を追加するだけです。Service 単位での選択はありません。
Container Insights は少々の有料なので、費用についてはあらかじめ確認してください。変にクラスタを乱立して全部有効にするとよくないため、例えば本番環境だけ有効にする程度ならば、月額数十ドルの範囲で収まるはずです。
つい先日には、より詳細となる機能が追加されたのでチェックしておくとよさげです。
メトリクスの取り扱い
これは復習として、メトリクスの扱い方について基本を整理しておきます。人や環境によって異なる部分はあると思いますので、私の場合は、ということで。取得時間
CloudWatch から最新の値を取得する場合、今現在の時間を基準にしてしまうと、まだ値が存在しない場合が多いので、多少遅らせる形で取得します。例えばこんな感じで、100秒前 を終点時間、さらにその 60秒前 を始点時間に固定しつつ、
1 2 3 4 5 6 |
period = 60 delay_seconds = 100 def setTimes(self): self.end_time = datetime.now() - timedelta(seconds=self.delay_seconds) self.start_time = self.end_time - timedelta(seconds=self.period) |
Container Insights からデータを取得します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
def getCloudWatchStats(self, metric, statistics, cluster_name, service_name): cloudwatch = self.getCloudWatch() namespace = "ECS/ContainerInsights" dimensions = [ {"Name": "ClusterName", "Value": cluster_name}, {"Name": "ServiceName", "Value": service_name}, ] params = { "Namespace": namespace, "MetricName": metric, "Dimensions": dimensions, "StartTime": self.start_time, "EndTime": self.end_time, "Period": self.period, "Statistics": [statistics], } res = cloudwatch.get_metric_statistics(**params) value = 0 if res["Datapoints"]: value = res["Datapoints"][0][statistics] return value |
メトリクスによっては、3分 とか 30分 前にして取得しないとだったりするので、設定の追加時に容易に調整できるようにしておくと幸福度が高いです。
時間範囲は最小の 1分 が1つのデータポイントなので 60秒です。
単位
メトリクスによって、1分間のデータが、その1分間での平均値であったり、1分間の合計値であったりします。CPU やメモリはそのままの値を使えばよいですが、ネットワーク転送量だと1分間の合計値では扱いづらいので、60秒 で割って Bytes/s なり bps なりにすると、グラフとしては馴染みやすいと思います。
また、Statistics に Average や Maximum などありますが、これはそのグループ内リソースにおける平均値や、そのグループの中での単体リソースの最大値 を表しています。メトリクス単体としての平均値と、グループとしての平均値は意味が違うので、わかりづらいですが区別を理解しておきましょう。
グラフとアラート
監視の基本構成はグラフとアラートの2種類です。グラフは単体や複数の項目を1つのグラフとして、線グラフや積みグラフやその混合を描画したものです。視覚化することで、時間単位の変化を認識しやすくし、また他のグラフを並べた時に異常な形状になる特異点を探すことで、問題の原因を探すのに利用します。
アラートはキャパシティの観点で、値がそれ以上超えると障害になるリスクが高くなる、という状況を人間に通知する仕組みです。この辺は SRE の基本を学ぶと出てきますが、アラート慣れして放置せず、アラートをゼロにするよう必ず何かしらの対応をすることが大切です。
グラフのチェック
Autoscaling 台数調整などのための日々のザックリとしたチェックと、何かの原因を探す時のチェックは全く異なるものです。グラフの表示には時間範囲があるので、1時間以下などでは値は1分単位で確認できますが、数時間や1日以上となると値は10分や60分単位に丸められます。
丸められると、ある特定の1分間に起きた特異点を認識しづらくなります。何時何分から何時何分まで、特徴的な変化があった、などは調査において非常に重要な情報になるため、調査時は横着せずに必ず拡大して1分単位の値をチェックするようにしましょう。
CPU
それでは皆大好き CPU使用率 からいきましょう。CPU使用率は 100% (計算上、100% を超えることはある、という御託は不要) を上限とし、悪いと 100% にグラフが張り付きますし、95% あたりからそれに近い悪影響が出始めていると思ったほうがよいです。
100% に近いと各所の処理が遅延するので、レスポンスが遅くなったり、遅すぎるとタイムアウトでエラーになったり、処理が溜まる設計だと待ち行列的に増加して、ほぼ機能しなくなることもあります。なので、Autoscaling の時にも書きましたが、50% 前後で自分たちに都合のよい数値を保つように運用します。
ECS Service で扱いたい項目は3つです。
- CpuReserved
- CpuUtilized (Average)
- CpuUtilized (Maximum)
CpuReserved はタスク定義に指定する数値で、8vCPU なら 8192 とするので、メトリクスも 8192 となります。これが上限値 100% を表します。
CpuUtilized (Average) は、Service 内の全タスクの平均値で、日々の増減としてはこれを見て調整することになります。
CpuUtilized (Maximum) は、全タスクの中で最も高い数値を持つタスクの値です。例えば Average = 3200 くらいの時、Maximum は 3800 ~ 4000 くらいを表すかもしれません。最も余裕が小さく限界が近いタスクともいえ、キャパシティ的な意味ではこれを注視して調整します。たとえ Average が無事でも、Maximum が不都合な値になれば、その部分だけでも障害に近い現象が起こるからです。
メモリ
メモリはアプリケーション+ミドルウェアを動かすために必須なリソースです。……いや、他のリソースも必須なんですけど、他と違ってキャパシティに到達したら遅くなる、んじゃなくて到達したら障害になるからです。そういう意味ではストレージ容量もそうですね。アプリケーションのコードや扱うデータの容量、そしてミドルウェアの設定によって、1サーバーあたりに必要なメモリ容量はおよそ確定します。基本的には起動時には軽量な数値であるものの、稼働して一定時間経過すればある程度は膨れ上がって、そこから一定に安定します。
ただ安定するとはいえ、徐々に増加する傾向があってそれが止まらなかったり、機能やデータ傾向の変化によって必要容量も変わっていくので、常に余力を保つにはやはり以下の項目の監視が必要です。
- MemoryReserved
- MemoryUtilized (Average)
- MemoryUtilized (Maximum)
値はタスク定義同様、MB 単位の数値です。こちらは天井に到達すると遅延ではなく障害になるので、Maximum の値に特に注意してアラートをあげることになります。
Autoscaling と Maximum
さきほども述べた通り、メモリの使用量に絶対的な安定はなく、長く稼働するほど多く消費する傾向はあります。そのため、Average と Maximum では 10~20% ほどの差が出るかもしれません。その理由の1つに Autoscaling があります。ECS Service の Autoscaling というか、手動も含めて desired が減少する時、削除対象となるタスクには任意の条件が存在しません。EC2 だと OldestInstance といった指定があったのですが、現時点では自動的に AZ バランスを考慮して削除されるだけです。
この削除に選択されるタスクの条件は自動かつ固定なので、ある特定のタスクは残り続けることになり、例えば1日の中で 10~20 タスクに増減したとしても、デプロイしない限りは数台が何日も残り続けます。
どちらかというと、常に古いものから削除された方が状態としても均一に近くなるので嬉しいのですが、現状はそうなっていないので注意したい部分です。
メトリクス と プロセス情報
CloudWatch メトリクスのメモリ割合と、実際に OS でプロセス情報を集計してタスク割当メモリとで計算した割合は、以下のような特徴があるので混乱しないようにというお話です。ps コマンドで表示されるプロセス情報には通常 RSS という列があり、この合計は OS 全体の実際の消費量とはなりません。少し特殊な取得方法となる PSS が合計値としては正しく、詳しくはこちらを参照してください。
実戦で取得した例としては、こんな感じです。
1 2 3 4 5 6 7 8 9 |
RSS # ps aux | tail +2 | awk '{sum += $6} END {printf "%\047d KB\n",sum}' 20,590,848 KB PSS # apt update # apt install smem # smem -n | tail +2 | sed -e 's/^.* \([0-9]\+\) \+[0-9]\+ $/\1/g' | awk '{sum += $1} END {printf "%\047d KB\n",sum}' 17,656,693 KB |
この (PSS 合計値 / タスクメモリ容量) % は、メトリクスを使った割合と近似値となりますので、安心して Maximum に注意を払って運用しましょうね、という石橋叩きでした。
ストレージ
ECS Fargate のローカルストレージは 20 GiB ~ となっています。これは太古の昔から監視している項目ですが、コンテナ運用の基本の1つに『ローカルストレージに書き込まずストリームで吐き出すこと』というのがあります。これができていると盲信した場合、この監視を甘く見る可能性があり、実際にはいったんログとして書き込んで fluentd で吐き出すこともあるので、絶対に守れるわけではありません。
もしどこかで想定以上の過剰な書き込みが発生していたり、ログローテートなしでタスクが長期生存することで、ストレージ容量不足に陥る可能性はあります。なのでここでも、さきほどの Autoscaling での削除条件が関わってくるということです。
項目としてはそろそろしつこいですが、こうです。
- EphemeralStorageReserved
- EphemeralStorageUtilized (Average)
- EphemeralStorageUtilized (Maximum)
やはりこちらも Maximum を注視するように監視をします。
対応としてはシンプルにストレージ容量を増やすもよし、一度状況を精査して不要なログなどが残らないようにするなり、ログローテートするなりで根本的に必要容量を減らすのもよし、です。
ネットワーク
ここまでの項目に比べれば、ここが障害要因となる可能性はかなり低いので、用途としては正常確認・異常調査の補助となり、項目としてはこれらになります。- NetworkRxBytes
- NetworkTxBytes
1データポイントあたり1分間の Bytes になるので、60 で割ると馴染みやすい情報になると思われます。
キャパシティとしては公式情報は存在しなく、リソース量によって変化するようなので、正確に知りたければ iperf などを使って調査すると良いでしょう。
タスク数
基本形
タスクのカウント系はいくつかあるのでお好みになります。私の場合は、これらがあれば状況把握には十分と考えています。- DesiredTaskCount
- RunningTaskCount
- PendingTaskCount
desired を線にして他を積むと、わかりやすいはず。これと、CPU使用率の Average あたりを参考にして、コスパの良いところにリソース量を調整していきます。
FARGATE は vCPU に OnDemand | Spot でそれぞれ Quotas があるので、大規模な台数なら上限値も考慮したアラートにしてもよいかもだけど、その場合はタスク数とは別の監視にした方がよさげ。
SPOT状況
これは完全に独自に採取しているデータになりますが、スポットを活用した場合、どのくらいの割合でどの AZ に何台起動しているのか、を可視化しておくと役立つことがあります。ちゃんとスポットが起動しているのかとか、特定の AZ だけスポットが全部落ちたのはいつよ、とかですね。パーツとしてはこの3つを利用して、
- Capacity Provider : FARGATE or FARGATE_SPOT
- CPU Architecture : x86 or arm64
- Availability Zone
連結してアイテムとすることで、各AZの起動種別を判別して、積みます。
- 例1:FARGATE_arm64_ap-northeast-1a = 7
- 例2:FARGATE_SPOT_arm64_ap-northeast-1d = 6
これを作成するのにできるだけ省エネするため、クラスタ単位で list_tasks を取得し、describe_tasks で丸っと詳細を取得した後に service 単位に仕分けてカウントしています。
ALB
あとは ALB の方でこれはってのをピックアップしておきます。TargetGroup の方にもあるので、これもお好みですね。リクエスト数
メトリクス名は RequestCount で、1分間あたりの値を 60 で割ると RPS (Requests per second) になります。自分はどうにも分単位の RPM は好きになれないもので。まぁこれは1本の線グラフなので管理画面で見てもいいですが、CloudWatch から他のところへデータを移して長く記録しておくと、前回の大きなイベントの時や1年前はどのくらいだったとか、を確認できるので長期保存は役に立ちます。
他のグラフを調査する時も、まずはベースとなるトラフィックがどういう変化だったのか、を確認することは基本中の基本ですので、最も付き合いの深いメトリクスの1つとなります。
平均レスポンスタイム
メトリクス名は TargetResponseTime で、ドキュメントによると「リクエストがロードバランサーを離れ、ターゲットが応答ヘッダーの送信を開始するまでの経過時間 (秒)」なので、ザックリとしたパフォーマンス品質はこれでわかります。もしレスポンス品質を厳格に管理したいなら、これを使って 500ms だの 1.0s だのを基準にアラートを設定してもよいと思います。継続的な運用で徐々に遅くなって引っかかったり、特定の状況でのみ悪化したり、に敏感になることは大切です。
ただ、あくまで平均なので、一部の遅い処理とかは DataDog など別のシステムで調査する必要がありますし、そういう外部システムでは似たような数値をもっと詳細な percentile で見れたりするので、用途としては使い分けになります。
それから
冒頭で『監視よければ全てヨシ!』と書きましたが、これはそんなに単純なことを表現しているつもりはありません。正しく監視できているということは、どういうリソース・設定・ステータスがあるかを認識しているということ。そしてそれぞれが、どういう状況になると危険になるのかを理解しているということ。さらに、その値や変化に対して、どういう原因がありえるのかを推測したり、どういう対処をすれば修復・改善できるのかを考察できる、ということです。
今回は ECS Service 関連のみだったので、サービス全体を診るという意味では全くの一部でしかなく、逆を言えば ECS 程度の監視ができていなければ、全体の健全な運用には程遠い状況であるとも言えます。
インフラエンジニアや SRE といった職種において、本当の意味で正しくできるエンジニアは数少ないと考えているカテゴリは、【負荷試験】【監視】の2大タスクだと思っており、その1つとなります。1つ1つの項目に真剣に向き合い、しかもそれを膨大に理解することは時間も労力も、そしてヘタすると適性も必要だからです。
前に書いた インプットのすゝめ | 外道父の匠 からも繋がる話なのですが、監視というのは 準備・運用・対処 のうち、最初のインプットにあたる準備に相当します。
この準備が浅いほど対処の精度も浅くなりますし、深ければほぼ全ての症状を論理的に解決することが可能になります。そして解決が難航した場合は、解決後にまた新たな監視をすぐに検討する、の繰り返しです。
もし新参や若手として、それなりに仕上がった組織に参加している場合、既存の監視項目を1つ1つ噛み砕いて理解する時間を取ってみるとよいでしょう。
それぞれが必ずリスクに繋がっているか、調査に役立つはずで、一見意味不明な項目があっても、もしかしたら過去に何かあったから用意したのかもしれません。
監視なんてモンは、最初に理解して一手間かけるだけで長期的に役立つという意味ではコスパ最高なシステムなので、取り組めば取り組むほどお得な気持ちでガンガン整備していきましょう:-)