AWS Fargateのタスクメタデータエンドポイントを使ってリアルタイムなCPU使用率を取得する

# AWS Fargateのタスクメタデータエンドポイントを使ってリアルタイムなCPU使用率を取得する

## はじめに

AWS Fargateを使用してコンテナを実行している場合、コンテナのメトリクスを監視することは非常に重要です。一般的にCloudWatchメトリクスを使用してCPU使用率などを監視しますが、CloudWatchメトリクスには1分間の平均値しか表示されないという制限があります。

より細かい粒度でコンテナのメトリクスを取得したい場合、Fargateのタスクメタデータエンドポイントを活用することで、より詳細なメトリクスを取得することができます。今回は、タスクメタデータエンドポイントを使用して、より高頻度でCPU使用率を取得する方法をご紹介します。

## タスクメタデータエンドポイントとは

タスクメタデータエンドポイントは、Fargate上で実行されているコンテナ内から利用可能なHTTPエンドポイントです。このエンドポイントを通じて、コンテナのメタデータやメトリクス情報をリアルタイムに取得することができます。

重要な点として、このエンドポイントから取得できるメトリクスはDocker統計に基づいています。つまり、取得される値はDockerデーモンが収集する統計情報であり、特にCPU使用率に関してはホストCPUの使用率として報告されます。

### CPU使用率の解釈に関する注意点

タスクメタデータエンドポイントから取得されるCPU統計には、以下のような特徴があります：

1. ホストCPUベースの測定
   - 報告される値はホストCPUの使用率を基準としています
   - これはDockerの統計情報として提供される生の値です
   - Fargateタスクに割り当てられたvCPU（例：0.25 vCPU）の制限は、この統計値には直接反映されません

2. vCPUとの関係について（推測）
   - 注意: 以下の解釈は推測に基づくものであり、AWS公式のドキュメントでは明確に言及されていません
   - 0.25 vCPUのタスクで25%のCPU使用率を記録した場合、実際のタスク制限に対する使用率は100%である可能性があります
   - この関係性は以下のような計算で表現できると考えられます：
     ```python
     # 注: この計算方法は推測であり、実際の挙動と異なる可能性があります
     actual_usage = reported_usage / assigned_vcpu
     # 例：reported_usage = 25%, assigned_vcpu = 0.25 の場合
     # actual_usage = 25% / 0.25 = 100%
     ```

実際のアプリケーションの監視では、この計算値だけでなく、CloudWatchメトリクスやアプリケーションの実際の挙動と組み合わせて総合的に判断することをお勧めします。

バージョン4のエンドポイントアドレスは以下の環境変数で提供されます：
```
${ECS_CONTAINER_METADATA_URI_V4}
```

## メトリクス取得の実装例

以下のPythonスクリプトを使用して、タスクメタデータエンドポイントからCPU使用率を取得できます：

```python
import os
import requests
import time
import json

def get_container_metrics(assigned_vcpu=1.0):
    metadata_uri = os.environ['ECS_CONTAINER_METADATA_URI_V4']
    stats_endpoint = f"{metadata_uri}/stats"
    
    try:
        response = requests.get(stats_endpoint)
        stats = response.json()
        
        # CPU使用率の計算（Docker統計ベース）
        cpu_stats = stats['cpu_stats']
        precpu_stats = stats['precpu_stats']
        
        cpu_delta = cpu_stats['cpu_usage']['total_usage'] - precpu_stats['cpu_usage']['total_usage']
        system_delta = cpu_stats['system_cpu_usage'] - precpu_stats['system_cpu_usage']
        
        # ホストCPUベースの使用率（Docker統計の生の値）
        host_cpu_usage = (cpu_delta / system_delta) * len(cpu_stats['cpu_usage']['percpu_usage']) * 100
        
        # 割り当てられたvCPUに基づく推定使用率を計算
        # 注: この計算方法は推測であり、実際の制限との関係は明確ではありません
        estimated_cpu_usage = host_cpu_usage / assigned_vcpu if assigned_vcpu > 0 else host_cpu_usage
        
        return {
            'timestamp': time.time(),
            'host_cpu_usage_percent': round(host_cpu_usage, 2),
            'estimated_cpu_usage_percent': round(estimated_cpu_usage, 2)
        }
    except Exception as e:
        print(f"Error fetching metrics: {e}")
        return None

def main():
    while True:
        metrics = get_container_metrics()
        if metrics:
            print(json.dumps(metrics))
        time.sleep(5)  # 5秒間隔でメトリクスを取得

if __name__ == "__main__":
    main()
```

## CloudWatchメトリクスとの比較

### CloudWatchメトリクスの特徴
- 1分間隔での平均値
- 設定が簡単
- 履歴データの保存が容易
- アラームの設定が可能

### タスクメタデータエンドポイントの特徴
- より高頻度（秒単位）でのメトリクス取得が可能
- リアルタイムな値を取得可能
- 追加コストなし
- コンテナ内からのみアクセス可能

## 実践的な使用例

### 1. 高負荷処理の詳細なモニタリング

バッチ処理やデータ処理など、短時間で高負荷がかかる処理の場合、1分間の平均値では見逃してしまう可能性のある瞬間的な負荷を検出できます。

```python
import logging
from datetime import datetime

logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)

def monitor_high_load_process():
    # メトリクス取得の開始
    start_time = datetime.now()
    peak_cpu = 0
    
    while (datetime.now() - start_time).seconds < 300:  # 5分間モニタリング
        metrics = get_container_metrics()
        if metrics:
            cpu_usage = metrics['cpu_usage_percent']
            peak_cpu = max(peak_cpu, cpu_usage)
            
            if cpu_usage > 80:  # 高負荷のしきい値
                logger.warning(f"High CPU usage detected: {cpu_usage}%")
        
        time.sleep(1)
    
    logger.info(f"Monitoring completed. Peak CPU usage: {peak_cpu}%")
```

### 2. オートスケーリングの判断材料として

より細かい粒度でのCPU使用率を監視することで、よりきめ細かなスケーリング判断が可能になります。

```python
def check_scaling_needs():
    samples = []
    
    # 30秒間のサンプリング
    for _ in range(30):
        metrics = get_container_metrics()
        if metrics:
            samples.append(metrics['cpu_usage_percent'])
        time.sleep(1)
    
    # 平均値と最大値の計算
    avg_cpu = sum(samples) / len(samples)
    max_cpu = max(samples)
    
    return {
        'average_cpu': avg_cpu,
        'max_cpu': max_cpu,
        'needs_scaling': avg_cpu > 70 or max_cpu > 85
    }
```

## 実装例の使用方法

タスクのvCPU設定に応じて、以下のように使用します：

```python
# 0.25 vCPUのタスクの場合
metrics = get_container_metrics(assigned_vcpu=0.25)
print(f"ホストCPU使用率（Docker統計値）: {metrics['host_cpu_usage_percent']}%")
print(f"推定CPU使用率（vCPU比）: {metrics['estimated_cpu_usage_percent']}%")
```

例えば、0.25 vCPUのタスクで以下のような出力が得られる場合：
```
ホストCPU使用率（Docker統計値）: 25%
推定CPU使用率（vCPU比）: 100%
```

この結果は、Docker統計として25%のCPU使用率が報告されており、これを0.25 vCPUに対する使用率として換算すると100%となることを示しています。ただし、この換算方法は推測であり、実際のFargateのCPU制限との正確な関係性は明確ではないことに注意してください。

より正確な状況把握のために、この値とCloudWatchメトリクス、アプリケーションの実際の挙動を組み合わせて監視することをお勧めします。

## 注意点

1. メトリクス取得の頻度
   - あまりに頻繁な取得はコンテナ自体の負荷になる可能性があります
   - 用途に応じて適切な間隔を設定しましょう

2. エラーハンドリング
   - ネットワークの問題やエンドポイントの一時的な不具合に備えたエラーハンドリングが重要です

3. データの永続化
   - タスクメタデータエンドポイントから取得したデータは永続化されないため、必要に応じて別途保存する必要があります

## まとめ

タスクメタデータエンドポイントを活用することで、CloudWatchメトリクスでは取得できない細かい粒度でのCPU使用率モニタリングが可能になります。これにより、より詳細なパフォーマンス分析やトラブルシューティングが可能になります。

ただし、これはCloudWatchメトリクスの代替ではなく、補完的な監視手段として使用することをお勧めします。両者の特徴を理解し、用途に応じて適切に使い分けることが重要です。

## 参考リンク

- [AWS Fargateタスクメタデータエンドポイントv4のドキュメント](https://docs.aws.amazon.com/AmazonECS/latest/developerguide/task-metadata-endpoint-v4.html)
- [ECSコンテナエージェントのGitHubリポジトリ](https://github.com/aws/amazon-ecs-agent)