AI

Strands Agent와 MCP를 활용한 EKS 최적화 보고서 자동 생성하기

Hanhorang31 2026. 3. 17. 00:16
 

개요

요즘 핫한 AI를 통해 EKS 최적화 보고서를 만들겠습니다.

AI 에 대한 사전 지식으로 MCP, Strands Agent를 소개하고, EKS 최적화를 위한 에이전트 구성 과정을 공유합니다.

 

 

사전 기술 소개

EKS 최적화 에이전트 구성을 위해 MCPStrands Agents 관련 사전 지식이 필요합니다.

 

MCP

MCP(Model Context Protocal) : AI가 외부 데이터와 도구에 쉽게 접근할 수 있도록 표준화된 연결 방식을 제공하는 프로토콜입니다. Client(ChatGPT)-LLM-Sever를 이어준다고하여 USE-C포트와 유사합니다.

예를 들어, Claude에서 G메일에 접근한다고 가정하면, ClaudeAPI + Gmail API + Agent Workflow 를 다 구성해야했지만, MCP를 사용하게 되면 구성없이 손쉽게 구성이 가능해집니다.

MCP를 사용하게 되면 LLM(Claude)이 웹 검색을 할 수 있게되며, DB에도 접근이 가능해진다!

→ 기존 LLM 의 토큰 제한이 사라짐, 정확도 향상

 

예시) Claude에서 filesystem MCP 연결할 경우, Claude에서 파일시스템(디렉터리) 조회 가능

 

 

Strands Agents

Strands Agents는 모델 중심 접근 방식으로 단 몇 줄의 코드만으로 AI 에이전트를 구축하고 실행할 수 있는 오픈 소스 SDK입니다. Strands Agents는 간단한 사용 사례부터 복잡한 에이전트 사용 사례까지, 그리고 로컬 개발부터 프로덕션 환경 배포까지 확장 가능합니다.

 

핵심 아키텍처

→ 사용자가 모델, 프롬프트, 도구만 정의하면 에이전트가 알아서 오케스트레이션

 

Strands Agents는 Agentic Loop라는 핵심 실행 루프를 통해 작동합니다. 각 루프에서 LLM은 프롬프트와 에이전트 컨텍스트를 받아 자연어 응답을 생성하거나, 단계별 계획을 수립하거나, 이전 단계를 반성하거나, 하나 이상의 도구를 선택하여 실행합니다.

기존 프레임워크에는 각 로직을 구성해야 했지만, Strands Agents 는 모델 중심 접근으로 복잡한 오케스트레이션 로직 없이 모델, 프롬프트, 도구만 정의하면 에이전트 구축이 가능합니다.

 

(예제) 날씨 확인 이메일 발송 에이전트 구성

더보기

from anthropic import Anthropic

client = Anthropic()

# 도구만 정의
tools = [
    {
        "name": "check_weather",
        "description": "현재 날씨를 확인합니다",
        "input_schema": {"type": "object", "properties": {}}
    },
    {
        "name": "send_email",
        "description": "이메일을 보냅니다",
        "input_schema": {
            "type": "object",
            "properties": {
                "content": {"type": "string"}
            }
        }
    }
]

# 에이전트 실행 (Claude가 알아서 판단)
response = client.messages.create(
    model="claude-3-5-sonnet-20241022", # 모델 
    max_tokens=1024,
    tools=tools,
    messages=[{
        "role": "user",
        "content": "날씨 확인하고 추우면 이메일 보내줘" # 프롬프트
    }]
)

# Claude가 자동으로:
# 1. check_weather 호출
# 2. 결과 분석
# 3. 추우면 send_email 호출
# 4. 아니면 종료

 

Strands Agents 유연한 모델과 도구 지원이 가능합니다.

Model Context Protocol(MCP) 서버를 통해 수천 개의 공개 도구를 자동으로 활용이 가능합니다.

MCP 서버에 따른 도구 활용에 따라 사용자의 자연어 요청을 기반으로 복잡한 워크플로우를 손 쉽게 구성할 수 있습니다.

 

이번 글에서는 CFM Tips MCP Server를 도구로 활용하여 Strands Agent 기반의 EKS 최적화 보고서를 생성하는 방법을 소개합니다.

 

 

 

CFM Tips mcp server

CFM Tips ?

AWS 클라우드 재무 관리 기술 구현 플레이북(CFM TIP) – 조직에서 효과적인 클라우드 재무 관리 기능을 구현하고 개선하기 위한 완벽한 플레이북입니다.

AWS 웰 아키텍처 프레임워크에서는 조직에서 특정 기능을 구현하는 데 도움이 되는 접근하기 쉬운 단계별 플레이북을 제공합니다.

이를 통해 지속적이고 자립적인 비용 최적화 및 거버넌스를 보장하는 메커니즘을 구축할 수 있습니다.

 

 

CFM Tips mcp server

위 CFM Tips 를 기반으로 만든 MCP 서버 입니다. 서버 내 로직은 CFM Tips 플레이북을 기반으로 구성되어 있어 AWS 비용 분석 및 최적화 권장 사항을 제공합니다.

 

 

✅ Features

제공 기능은 다음과 같습니다.

 

Core AWS Services Integration

  • Cost Explorer - Retrieve cost data and usage metrics
  • Cost Optimization Hub - Get AWS cost optimization recommendations
  • Compute Optimizer - Right-sizing recommendations for compute resources
  • Trusted Advisor - Cost optimization checks and recommendations
  • Performance Insights - RDS performance metrics and analysis

Cost Optimization Playbooks

  • 🔧 EC2 Right Sizing - Identify underutilized EC2 instances with 12 specialized analysis tools
  • 💾 EBS Optimization - Find unused and underutilized volumes
  • 🗄️ RDS Optimization - Identify idle and underutilized databases
  • ⚡ Lambda Optimization - Find overprovisioned and unused functions
  • 🪣 S3 Optimization - Comprehensive S3 cost analysis and storage class optimization with 11 specialized tools
  • 📋 CloudTrail Optimization - Analyze and optimize CloudTrail configurations
  • 📊 CloudWatch Optimization - Optimize monitoring costs across logs, metrics, alarms, and dashboards
  • 💰 Database Savings Plans - Analyze and optimize database commitment plans for Aurora, RDS, DynamoDB, and more
  • 🌐 NAT Gateway Optimization - Identify underutilized, redundant, and unused NAT Gateways
  • 📈 Comprehensive Analysis - Multi-service cost analysis

Advanced Features

  • Real CloudWatch Metrics - Uses actual AWS metrics for analysis
  • Multiple Output Formats - JSON and Markdown report generation
  • Cost Calculations - Estimated savings and cost breakdowns
  • Actionable Recommendations - Priority-based optimization suggestions

 

📁 Project Structure

MCP 내부에서의 플레이북은 각 자원별로 나뉘어 도구로 구성되어 있습니다. 구조 내역은 다음과 같습니다.

sample-cfm-tips-mcp/
├── playbooks/                           # CFM Tips optimization playbooks engine
│   ├── ec2/                             # EC2 optimization with 12 specialized tools
│   │   └── ec2_optimization.py          # EC2 right-sizing and comprehensive analysis
│   ├── ebs/                             # EBS volume optimization
│   │   └── ebs_optimization.py          # EBS volume optimization playbook
│   ├── rds/                             # RDS database optimization
│   │   ├── rds_optimization.py          # RDS database optimization playbook
│   │   └── database_savings_plans.py    # Database Savings Plans analysis
│   ├── aws_lambda/                      # Lambda function optimization
│   │   └── lambda_optimization.py       # Lambda function optimization playbook
│   ├── s3/                              # S3 storage optimization with 11 tools
│   │   └── s3_optimization_orchestrator.py # S3 cost optimization orchestrator
│   ├── cloudtrail/                      # CloudTrail optimization
│   │   └── cloudtrail_optimization.py   # CloudTrail optimization playbook
│   ├── cloudwatch/                      # CloudWatch optimization with 8 tools
│   │   └── cloudwatch_optimization.py   # CloudWatch cost optimization playbook
│   ├── nat_gateway/                     # NAT Gateway optimization
│   │   └── nat_gateway_optimization.py  # NAT Gateway optimization playbook
│   └── comprehensive_optimization.py    # Multi-service analysis
├── services/                             # AWS Services as datasources for the cost optimization
│   ├── s3_service.py                    # S3 API interactions and metrics
│   ├── s3_pricing.py                    # S3 pricing calculations and cost modeling
│   ├── cost_explorer.py                 # Cost Explorer API integration
│   ├── compute_optimizer.py             # Compute Optimizer API integration
│   └── optimization_hub.py              # Cost Optimization Hub integration
├── utils/                               # Cross-cutting utilities and analyzers
│   ├── analyzers/                       # Analysis engines for different optimization types
│   ├── logging_config.py                # Centralized logging configuration
│   ├── session_manager.py               # Session management for analysis results
│   └── parallel_executor.py             # Parallel execution utilities
├── mcp_server_with_runbooks.py           # Main MCP server with 50+ tools
├── runbook_functions.py                  # NAT Gateway and additional optimization functions
├── mcp_runbooks.json                     # Template file for MCP configuration file
├── requirements.txt                      # Python dependencies
├── tests/                               # Comprehensive test suite
├── diagnose_cost_optimization_hub_v2.py  # Diagnostic utilities
├── RUNBOOKS_GUIDE.md                     # Detailed usage guide
└── README.md                             # Project ReadMe

 

🔐 Security and Permissions - Least Privileges

CFM Tips MCP 서버는 AWS 보안 모범 사례를 준수하며 읽기 전용 권한만 필요합니다.

주요 보안 원칙은 다음과 같습니다.

  • Read-Only Access: All operations are read-only - no create, update, or delete permissions
  • Least Privilege: Grant only the minimum permissions required for cost analysis
  • No Resource Modification: The tool cannot modify, terminate, or create AWS resources
  • Audit Trail: All API calls are logged via CloudTrail for security monitoring
  • Credential Security: Supports multiple secure credential methods (IAM roles, profiles, etc.)

 

🧪  Demo

필자는 Kiro CLI 을 통해 MCP 서버와 연동하였습니다.

간단히 데모를 하면 테스트 환경에 대해 주요 기능 내용을 확인할 수 있습니다.

 

1. "Show me cost optimization recommendations"

 

2. "Identify unused or inefficient CloudWatch alarms"

→ MCP 서버에서 정의한 도구에 따라 원하는 결과가 잘 나오는 것을 확인할 수 있습니다.

 

다만, 원하는 기능이 많으면 많을수록 프롬프트를 구성해야 합니다.

또한, MCP 서버 내 제공하는 도구(tools) 가 아닌 경우 결과에 할루시네이션이 발생합니다.

EKS 내용 또한 그렇습니다.

 

위 CFM Tips MCP Server에 EKS 최적화는 포함되지 않았습니다!

요행은 없습니다.

CFM Tips EKS 플레이북을 분석하여 CFM Tips MCP Server에 분석 항목을 추가하고 결과를 확인하겠습니다.

 

 

EKS CFM Tips 이해

EKS를 사용하면 EKS 시간당 요금과 클러스터 구성에 사용되는 AWS 리소스(EC2/Fargate 노드, EBS 볼륨, ELB, 데이터 전송 등)에 대한 비용을 지불하게 됩니다. 따라서 전체 EKS 클러스터 비용을 파악하고 최적화하는 것은 어려운 과제입니다.

어려운 과제에 대해 CFM Tips 에서는 아래의 최적화 항목을 제공하고 있습니다.

 

  • Optimizing EKS Overall Cluster Costs : EKS 구별 태그 설정, EC2 최적화(자동스케일링, 적정크기 조정, 최신 세대 타입 또는 Gravition 타입 사용), 구매 옵션 최적화(RI&SP)
  • UnderStanding EKS in-Cluster Costs : EKS 내부 Pod 수준의 비용 및 사용량 데이터 분석
  • EKS extended Support : EKS 버전 EOS 관리

 

 

CFM Tips MCP Server 내 EKS 최적화 항목 추가

위 분석내용을 기반으로 MCP서버에 EKS 최적화 항목을 추가하겠습니다.

MCP 서버 수정은 바이브코딩을 통해 진행했습니다.

 

[EKS 최적화 항목]

eks_cluster_analysis - 클러스터 전체 분석

클러스터 버전 및 EOS 경고 (정확한 날짜 포함)
표준 지원, 확장 지원, 최종 지원 단계 구분
노드 그룹 현황
Container Insights 활성화 여부
eks_pod_rightsizing - 파드 리소스 Right Sizing 

Container Insights 메트릭 기반 분석
CPU/Memory 사용률 분석
Over-provisioned/Under-provisioned 파드 식별
리소스 요청/제한 최적화 권장사항
eks_node_optimization - 노드 그룹 최적화

Graviton 전환 기회 식별
Spot 인스턴스 기회
구세대 인스턴스 업그레이드 권장
eks_storage_optimization - 스토리지 최적화

PVC EBS 볼륨 사용률 분석
gp2 → gp3 마이그레이션 권장
미사용 볼륨 식별
eks_comprehensive_report - 종합 리포트

모든 분석 통합
전체 최적화 기회 요약

 

git clone ~ 
cd playbook

mkdir eks 

.. # 바이브코드 구현 

tree .
.
├── __init__.py
├── __pycache__
│   ├── __init__.cpython-312.pyc
│   ├── eks_optimization.cpython-312.pyc
│   └── eks_resource_identifier.cpython-312.pyc
├── eks_optimization.py # 최적화
└── eks_resource_identifier.py # 자원 식별 

 

[1] 태그 구별 방법

 

EKS 리소스를 정확히 식별하기 위해 태그 기반 필터링을 구현했습니다.

더보기

def filter_eks_ec2_instances(self, cluster_name: Optional[str] = None) -> List[Dict[str, Any]]:
    """EKS 노드 EC2 인스턴스만 필터링"""
    try:
        filters = [
            {'Name': 'instance-state-name', 'Values': ['running']}
        ]
        
        if cluster_name:
            # 특정 클러스터의 노드만
            filters.append({
                'Name': f'tag:kubernetes.io/cluster/{cluster_name}',
                'Values': ['owned', 'shared']
            })
        else:
            # 모든 EKS 노드
            filters.append({
                'Name': 'tag-key',
                'Values': ['kubernetes.io/cluster/*']
            })
        
        response = self.ec2_client.describe_instances(Filters=filters)
        
        instances = []
        for reservation in response['Reservations']:
            for instance in reservation['Instances']:
                # 클러스터 이름 추출
                cluster_tag = None
                nodegroup_tag = None
                for tag in instance.get('Tags', []):
                    if tag['Key'].startswith('kubernetes.io/cluster/'):
                        cluster_tag = tag['Key'].replace('kubernetes.io/cluster/', '')
                    elif tag['Key'] == 'eks:nodegroup-name':
                        nodegroup_tag = tag['Value']
                
                instances.append({
                    'instance_id': instance['InstanceId'],
                    'instance_type': instance['InstanceType'],
                    'cluster_name': cluster_tag,
                    'nodegroup_name': nodegroup_tag,
                    'launch_time': instance['LaunchTime'],
                    'availability_zone': instance['Placement']['AvailabilityZone'],
                    'private_ip': instance.get('PrivateIpAddress'),
                    'tags': instance.get('Tags', [])
                })
        
        return instances
    except ClientError as e:
        print(f"Error filtering EKS EC2 instances: {e}")
        return []

def filter_eks_ebs_volumes(self, cluster_name: Optional[str] = None) -> List[Dict[str, Any]]:
    """EKS PVC EBS 볼륨만 필터링"""
    try:
        filters = [
            {'Name': 'tag-key', 'Values': ['kubernetes.io/created-for/pvc/name']}
        ]
        
        if cluster_name:
            filters.append({
                'Name': f'tag:kubernetes.io/cluster/{cluster_name}',
                'Values': ['owned']
            })
        
        response = self.ec2_client.describe_volumes(Filters=filters)
        
        volumes = []
        for volume in response['Volumes']:
            # PVC 정보 추출
            pvc_name = None
            pvc_namespace = None
            cluster_tag = None
            
            for tag in volume.get('Tags', []):
                if tag['Key'] == 'kubernetes.io/created-for/pvc/name':
                    pvc_name = tag['Value']
                elif tag['Key'] == 'kubernetes.io/created-for/pvc/namespace':
                    pvc_namespace = tag['Value']
                elif tag['Key'].startswith('kubernetes.io/cluster/'):
                    cluster_tag = tag['Key'].replace('kubernetes.io/cluster/', '')
            
            volumes.append({
                'volume_id': volume['VolumeId'],
                'size': volume['Size'],
                'volume_type': volume['VolumeType'],
                'state': volume['State'],
                'cluster_name': cluster_tag,
                'pvc_name': pvc_name,
                'pvc_namespace': pvc_namespace,
                'iops': volume.get('Iops'),
                'throughput': volume.get('Throughput'),
                'create_time': volume['CreateTime'],
                'tags': volume.get('Tags', [])
            })
        
        return volumes
    except ClientError as e:
        print(f"Error filtering EKS EBS volumes: {e}")
        return []

def check_container_insights_enabled(self, cluster_name: str) -> bool:
    """Container Insights 활성화 여부 확인"""
    try:
        # CloudWatch Logs에서 Container Insights 로그 그룹 확인
        log_group_name = f'/aws/containerinsights/{cluster_name}/performance'
        
        try:
            self.logs_client.describe_log_groups(
                logGroupNamePrefix=log_group_name,
                limit=1
            )
            return True
        except ClientError:
            return False
    except Exception as e:
        print(f"Error checking Container Insights: {e}")
        return False

 

 

[2] EKS 구성 AWS 자원 최적화

EKS 구성 자원으로 노드와 스토리지에서 최적화를 진행하도록 하였습니다.

  • 노드 최적화 : 구세대 인스턴스 감지, Graviton 전환 가능, Spot 식별, 스케일링 설정 검토
  • 스토리지 최적화 : EBS 볼륨 사용률 추적, gp2 식별, 미사용 볼륨 감지, 절감 예상치 계산
더보기

async def run_eks_node_optimization(arguments: Dict[str, Any]) -> List[TextContent]:
    """
    노드 그룹 최적화 분석
    - EC2 인스턴스 타입 최적화
    - Spot 인스턴스 기회
    - Graviton 전환 기회
    """
    region = arguments.get('region', 'us-east-1')
    cluster_name = arguments['cluster_name']
    
    try:
        identifier = EKSResourceIdentifier(region=region)
        
        # 노드 그룹 정보
        nodegroups = identifier.list_nodegroups(cluster_name)
        instances = identifier.filter_eks_ec2_instances(cluster_name)
        
        # 노드별 분석
        node_analysis = []
        for instance in instances:
            instance_type = instance['instance_type']
            
            # Graviton 호환성 확인
            is_graviton = instance_type.startswith(('t4g', 'c7g', 'm7g', 'r7g', 'r8g'))
            graviton_compatible = not is_graviton and _check_graviton_compatibility(instance_type)
            
            # 구세대 인스턴스 확인
            is_old_generation = _check_old_generation(instance_type)
            
            node_analysis.append({
                'instance_id': instance['instance_id'],
                'instance_type': instance_type,
                'nodegroup': instance.get('nodegroup_name'),
                'is_graviton': is_graviton,
                'graviton_compatible': graviton_compatible,
                'is_old_generation': is_old_generation,
                'recommendations': _generate_node_recommendations(
                    instance_type, is_graviton, graviton_compatible, is_old_generation
                )
            })
        
        # 노드 그룹별 요약
        nodegroup_summary = []
        for ng in nodegroups:
            ng_instances = [n for n in node_analysis if n['nodegroup'] == ng['nodegroupName']]
            
            nodegroup_summary.append({
                'nodegroup_name': ng['nodegroupName'],
                'instance_types': ng.get('instanceTypes', []),
                'capacity_type': ng.get('capacityType', 'ON_DEMAND'),
                'desired_size': ng.get('scalingConfig', {}).get('desiredSize'),
                'min_size': ng.get('scalingConfig', {}).get('minSize'),
                'max_size': ng.get('scalingConfig', {}).get('maxSize'),
                'node_count': len(ng_instances),
                'spot_eligible': ng.get('capacityType') == 'ON_DEMAND',
                'recommendations': _generate_nodegroup_recommendations(ng, ng_instances)
            })
        
        analysis = {
            "status": "success",
            "cluster_name": cluster_name,
            "region": region,
            "analysis_date": datetime.now().isoformat(),
            "nodegroups": nodegroup_summary,
            "nodes": node_analysis,
            "summary": {
                "total_nodes": len(instances),
                "total_nodegroups": len(nodegroups),
                "graviton_nodes": sum(1 for n in node_analysis if n['is_graviton']),
                "graviton_compatible_nodes": sum(1 for n in node_analysis if n['graviton_compatible']),
                "old_generation_nodes": sum(1 for n in node_analysis if n['is_old_generation'])
            }
        }
        
        return [TextContent(type="text", text=json.dumps(analysis, indent=2, default=str))]
        
    except Exception as e:
        return [TextContent(
            type="text",
            text=json.dumps({
                "status": "error",
                "message": f"Error analyzing node optimization: {str(e)}"
            }, indent=2)
        )]


async def run_eks_storage_optimization(arguments: Dict[str, Any]) -> List[TextContent]:
    """
    EKS 스토리지 최적화 분석
    - PVC EBS 볼륨 사용률
    - 볼륨 타입 최적화
    - 미사용 볼륨 식별
    """
    region = arguments.get('region', 'us-east-1')
    cluster_name = arguments['cluster_name']
    
    try:
        identifier = EKSResourceIdentifier(region=region)
        volumes = identifier.filter_eks_ebs_volumes(cluster_name)
        
        # 볼륨별 분석
        volume_analysis = []
        for volume in volumes:
            # CloudWatch 메트릭으로 사용률 확인
            cloudwatch = boto3.client('cloudwatch', region_name=region)
            
            end_time = datetime.now()
            start_time = end_time - timedelta(days=7)
            
            try:
                metrics_response = cloudwatch.get_metric_statistics(
                    Namespace='AWS/EBS',
                    MetricName='VolumeReadOps',
                    Dimensions=[{'Name': 'VolumeId', 'Value': volume['volume_id']}],
                    StartTime=start_time,
                    EndTime=end_time,
                    Period=86400,
                    Statistics=['Sum']
                )
                
                read_ops = sum(dp['Sum'] for dp in metrics_response.get('Datapoints', []))
                is_unused = read_ops == 0
            except:
                is_unused = False
            
            # 볼륨 타입 최적화 권장사항
            volume_type_recommendation = _generate_volume_type_recommendation(
                volume['volume_type'], volume['size'], volume.get('iops')
            )
            
            volume_analysis.append({
                'volume_id': volume['volume_id'],
                'pvc_name': volume['pvc_name'],
                'pvc_namespace': volume['pvc_namespace'],
                'size_gb': volume['size'],
                'volume_type': volume['volume_type'],
                'state': volume['state'],
                'is_unused': is_unused,
                'recommendation': volume_type_recommendation
            })
        
        analysis = {
            "status": "success",
            "cluster_name": cluster_name,
            "region": region,
            "analysis_date": datetime.now().isoformat(),
            "volumes": volume_analysis,
            "summary": {
                "total_volumes": len(volumes),
                "total_size_gb": sum(v['size'] for v in volumes),
                "unused_volumes": sum(1 for v in volume_analysis if v['is_unused']),
                "gp2_volumes": sum(1 for v in volumes if v['volume_type'] == 'gp2'),
                "gp3_volumes": sum(1 for v in volumes if v['volume_type'] == 'gp3')
            }
        }
        
        return [TextContent(type="text", text=json.dumps(analysis, indent=2, default=str))]
        
    except Exception as e:
        return [TextContent(
            type="text",
            text=json.dumps({
                "status": "error",
                "message": f"Error analyzing storage optimization: {str(e)}"
            }, indent=2)
        )]

 

[3] EKS 내부 파드 모니터링 분석

  • Container Insights 메트릭 기반 CPU/Memory 사용률 분석(Cloudwatch Insight 활성화 방법은 아래 기술)
  • 메트릭 지정(과다 프로비저닝(평균 사용률 < 20%) 및 과소 프로비저닝(최대 사용률 > 90%) 감지 및 파드별 리소스 요청/제한 조정 권장사항 제공
더보기

async def run_eks_pod_rightsizing(arguments: Dict[str, Any]) -> List[TextContent]:
    """
    파드 리소스 Right Sizing 분석
    - Container Insights 메트릭 기반
    - CPU/Memory 사용률 분석
    - 리소스 요청/제한 최적화 권장사항
    """
    region = arguments.get('region', 'us-east-1')
    cluster_name = arguments['cluster_name']
    lookback_days = arguments.get('lookback_days', 14)
    
    try:
        identifier = EKSResourceIdentifier(region=region)
        
        # Container Insights 활성화 확인
        if not identifier.check_container_insights_enabled(cluster_name):
            return [TextContent(
                type="text",
                text=json.dumps({
                    "status": "warning",
                    "message": f"Container Insights is not enabled for cluster {cluster_name}",
                    "recommendation": "Enable Container Insights to get pod-level metrics for right-sizing analysis",
                    "enable_command": f"aws eks update-cluster-config --name {cluster_name} --logging '{{\"clusterLogging\":[{{\"types\":[\"api\",\"audit\",\"authenticator\",\"controllerManager\",\"scheduler\"],\"enabled\":true}}]}}'"
                }, indent=2)
            )]
        
        # CloudWatch Logs Insights 쿼리로 파드 메트릭 분석
        logs_client = boto3.client('logs', region_name=region)
        cloudwatch = boto3.client('cloudwatch', region_name=region)
        
        end_time = datetime.now()
        start_time = end_time - timedelta(days=lookback_days)
        
        log_group_name = f'/aws/containerinsights/{cluster_name}/performance'
        
        # 파드별 CPU/Memory 사용률 쿼리
        query_string = """
        fields @timestamp, PodName, Namespace, pod_cpu_utilization, pod_memory_utilization,
               pod_cpu_request, pod_memory_request, pod_cpu_limit, pod_memory_limit
        | filter Type = "Pod"
        | stats avg(pod_cpu_utilization) as avg_cpu_util,
                max(pod_cpu_utilization) as max_cpu_util,
                avg(pod_memory_utilization) as avg_mem_util,
                max(pod_memory_utilization) as max_mem_util,
                avg(pod_cpu_request) as avg_cpu_request,
                avg(pod_memory_request) as avg_mem_request
          by PodName, Namespace
        | sort avg_cpu_util desc
        """
        
        # 쿼리 실행
        query_response = logs_client.start_query(
            logGroupName=log_group_name,
            startTime=int(start_time.timestamp()),
            endTime=int(end_time.timestamp()),
            queryString=query_string
        )
        
        query_id = query_response['queryId']
        
        # 쿼리 결과 대기
        import time
        max_wait = 30
        waited = 0
        while waited < max_wait:
            result = logs_client.get_query_results(queryId=query_id)
            if result['status'] == 'Complete':
                break
            time.sleep(2)
            waited += 2
        
        if result['status'] != 'Complete':
            return [TextContent(
                type="text",
                text=json.dumps({
                    "status": "error",
                    "message": "Query timeout - Container Insights data may not be available"
                }, indent=2)
            )]
        
        # 결과 분석
        pod_metrics = []
        for row in result.get('results', []):
            pod_data = {field['field']: field['value'] for field in row}
            
            # Right-sizing 권장사항 생성
            avg_cpu = float(pod_data.get('avg_cpu_util', 0))
            max_cpu = float(pod_data.get('max_cpu_util', 0))
            avg_mem = float(pod_data.get('avg_mem_util', 0))
            max_mem = float(pod_data.get('max_mem_util', 0))
            
            recommendation = _generate_pod_rightsizing_recommendation(
                avg_cpu, max_cpu, avg_mem, max_mem
            )
            
            pod_metrics.append({
                'pod_name': pod_data.get('PodName'),
                'namespace': pod_data.get('Namespace'),
                'avg_cpu_utilization': avg_cpu,
                'max_cpu_utilization': max_cpu,
                'avg_memory_utilization': avg_mem,
                'max_memory_utilization': max_mem,
                'recommendation': recommendation
            })
        
        analysis = {
            "status": "success",
            "cluster_name": cluster_name,
            "region": region,
            "lookback_days": lookback_days,
            "analysis_date": datetime.now().isoformat(),
            "pods_analyzed": len(pod_metrics),
            "pod_metrics": pod_metrics,
            "summary": _generate_rightsizing_summary(pod_metrics)
        }
        
        return [TextContent(type="text", text=json.dumps(analysis, indent=2, default=str))]
        
    except Exception as e:
        return [TextContent(
            type="text",
            text=json.dumps({
                "status": "error",
                "message": f"Error analyzing pod right-sizing: {str(e)}"
            }, indent=2)
        )]

def _generate_pod_rightsizing_recommendation(avg_cpu: float, max_cpu: float, 
                                             avg_mem: float, max_mem: float) -> Dict:
    """파드 right-sizing 권장사항"""
    recommendations = []
    
    # CPU 분석
    if avg_cpu < 20:
        recommendations.append({
            'resource': 'CPU',
            'issue': 'Over-provisioned',
            'current_utilization': f'{avg_cpu:.1f}%',
            'action': 'Consider reducing CPU request by 30-50%'
        })
    elif max_cpu > 90:
        recommendations.append({
            'resource': 'CPU',
            'issue': 'Under-provisioned',
            'peak_utilization': f'{max_cpu:.1f}%',
            'action': 'Consider increasing CPU limit by 20-30%'
        })
    
    # Memory 분석
    if avg_mem < 20:
        recommendations.append({
            'resource': 'Memory',
            'issue': 'Over-provisioned',
            'current_utilization': f'{avg_mem:.1f}%',
            'action': 'Consider reducing memory request by 30-50%'
        })
    elif max_mem > 90:
        recommendations.append({
            'resource': 'Memory',
            'issue': 'Under-provisioned',
            'peak_utilization': f'{max_mem:.1f}%',
            'action': 'Consider increasing memory limit by 20-30%'
        })
    
    return {
        'status': 'optimized' if not recommendations else 'needs_adjustment',
        'recommendations': recommendations
    }

 

[4] EOS 관리

EKS EOS 일정은 AWS EKS 문서를 참고하여 구성하였습니다.

오늘 날짜를 기준으로 클러스터의 버전을 식별하고 남은 일자를 데이터에 넣습니다.

더보기

class EKSResourceIdentifier:
    """EKS 리소스 식별 및 필터링"""
    
    # EKS 버전별 지원 종료 날짜
    EKS_VERSION_SUPPORT = {
        '1.35': {
            'upstream_release': '2025-12-17',
            'standard_support_end': '2027-03-27',
            'extended_support_end': '2028-03-27'
        },
        '1.34': {
            'upstream_release': '2025-08-27',
            'standard_support_end': '2026-12-02',
            'extended_support_end': '2027-12-02'
        },
        '1.33': {
            'upstream_release': '2025-04-23',
            'standard_support_end': '2026-07-29',
            'extended_support_end': '2027-07-29'
        },
        '1.32': {
            'upstream_release': '2024-12-11',
            'standard_support_end': '2026-03-23',
            'extended_support_end': '2027-03-23'
        },
        '1.31': {
            'upstream_release': '2024-08-13',
            'standard_support_end': '2025-11-26',
            'extended_support_end': '2026-11-26'
        },
        '1.30': {
            'upstream_release': '2024-04-17',
            'standard_support_end': '2025-07-23',
            'extended_support_end': '2026-07-23'
        },
        '1.29': {
            'upstream_release': '2023-12-13',
            'standard_support_end': '2025-03-23',
            'extended_support_end': '2026-03-23'
        }
    }
  
    
     def _check_version_support(self, version: str) -> Dict[str, Any]:
        """버전 지원 상태 확인"""
        today = datetime.now().date()
        
        if version not in self.EKS_VERSION_SUPPORT:
            return {
                'status': 'unknown',
                'message': f'Version {version} support information not available',
                'severity': 'warning'
            }
        
        support_info = self.EKS_VERSION_SUPPORT[version]
        standard_end = datetime.strptime(support_info['standard_support_end'], '%Y-%m-%d').date()
        extended_end = datetime.strptime(support_info['extended_support_end'], '%Y-%m-%d').date()
        
        # 지원 상태 판단
        if today > extended_end:
            status = 'end_of_life'
            message = f'Version {version} has reached end of life (Extended support ended: {extended_end})'
            severity = 'critical'
        elif today > standard_end:
            status = 'extended_support'
            days_remaining = (extended_end - today).days
            message = f'Version {version} is in extended support (additional charges apply). {days_remaining} days until extended support ends'
            severity = 'critical'
        else:
            status = 'standard_support'
            days_remaining = (standard_end - today).days
            if days_remaining < 90:
                message = f'Version {version} standard support ends soon. {days_remaining} days remaining'
                severity = 'medium'
            else:
                message = f'Version {version} is in standard support. {days_remaining} days remaining'
                severity = 'low'
        
        return {
            'status': status,
            'message': message,
            'severity': severity,
            'standard_support_end': support_info['standard_support_end'],
            'extended_support_end': support_info['extended_support_end'],
            'days_until_standard_end': (standard_end - today).days,
            'days_until_extended_end': (extended_end - today).days
        }

 

EKS 최적화 보고서 생성

EKS 구성은 테라폼으로 구성하였습니다.

테라폼 코드는 CloudNet@ AEWS 4기 스터디 내용을 참고하였습니다.

# 코드 다운로드
git clone https://github.com/gasida/aews.git
cd aews/1w

tree .
.
├── eks.tf 
├── var.tf
└── vpc.tf

EKS는 한국 리전(ap-northeast-2), EKS 버전 1.34버전, t3.medium 2대의 노드 그룹으로 구성됩니다.


# 변수 지정
aws ec2 describe-key-pairs --query "KeyPairs[].KeyName" --output text
export TF_VAR_KeyName=$(aws ec2 describe-key-pairs --query "KeyPairs[].KeyName" --output text)
export TF_VAR_ssh_access_cidr=$(curl -s ipinfo.io/ip)/32
echo $TF_VAR_KeyName $TF_VAR_ssh_access_cidr

# 배포 : 12분 정도 소요
terraform init
terraform plan
nohup sh -c "terraform apply -auto-approve" > create.log 2>&1 &
tail -f create.log


# 자격증명 설정
aws eks update-kubeconfig --region ap-northeast-2 --name myeks

# 노드 확인 
kubectl get nodes -A 
NAME                                               STATUS   ROLES    AGE   VERSION
ip-192-168-1-248.ap-northeast-2.compute.internal   Ready    <none>   16m   v1.34.4-eks-f69f56f
ip-192-168-3-81.ap-northeast-2.compute.internal    Ready    <none>   16m   v1.34.4-eks-f69f56f

# 파드 확인
kubectl get pods -A  
NAMESPACE     NAME                      READY   STATUS    RESTARTS   AGE
kube-system   aws-node-2dx75            2/2     Running   0          68s
kube-system   aws-node-k7gwb            2/2     Running   0          68s
kube-system   coredns-cc56d5f8b-nxm2n   1/1     Running   0          67s
kube-system   coredns-cc56d5f8b-tspdq   1/1     Running   0          67s
kube-system   kube-proxy-jjtls          1/1     Running   0          68s
kube-system   kube-proxy-lgs4v          1/1     Running   0          68s

파드 리소스 최적화를 위해 추가 addon(cloudwatch-observability, Pod identity) 구성이 필요합니다.

AWS 콘솔 EKS > 추가 기능 > 추가 기능 가져오기에서 addon을 추가합니다.

 

  • EKS Pod Identity 에이전트
  • Cloudwatch Observability

addon 구성이 완료되면 EKS 관찰성 > Cloudwatch Container Insight에서 파드 및 지표 확인이 가능합니다.

 

 

EKS 최적화 보고서 생성

EKS 최적화 보고서 생성으로 위 MCP 서버(cfm-tip mcp)를 사용하는 Strands 에이전트를 구성하겠습니다.

Strands 에이전트 구성 내용으로 모델과 프롬프트는 다음과 같습니다.

모델 : global.anthropic.claude-sonnet-4-5-20250929-v1:0

async def setup_mcp_client():
    """Initialize and connect to CFM Tips MCP server"""
    
    # AWS Profile 설정 (여기서 변경 가능)
    aws_profile = "default"  # 또는 원하는 프로파일 이름
    aws_region = "ap-northeast-2"  # 서울 리전
    
    print("🔧 Initializing MCP Client...")
    print(f"   Server: {project_root / 'mcp_server_with_runbooks.py'}")
    print(f"   AWS Profile: {aws_profile}")
    print(f"   Region: {aws_region}")
    
    # Create MCP client using stdio transport (Strands 공식 방식)
    mcp_client = MCPClient(
        lambda: stdio_client(
            StdioServerParameters(
                command="python3",
                args=[str(project_root / "mcp_server_with_runbooks.py")],
                env={
                    "AWS_DEFAULT_REGION": aws_region,
                    "AWS_REGION": aws_region,  # 일부 SDK는 이것도 필요
                    "AWS_PROFILE": aws_profile,  # Profile 사용
                    "HOME": os.path.expanduser("~"),  # AWS credentials 파일 접근
                    "PYTHONPATH": str(project_root)
                }
            )
        )
    )
    
    print("✅ Connected to CFM Tips MCP Server\n")
    
    return mcp_client


async def list_available_tools(mcp_client):
    """List all available tools from the MCP server"""
    
    # Get tools from MCP server (Strands 공식 방식)
    with mcp_client:
        tools = mcp_client.list_tools_sync()
    
    print(f"📋 Available Tools: {len(tools)}\n")
    
    return tools  # Return tools for agent creation


def create_agent(mcp_client):
    """Create Strands Agent with CFM Tips tools"""
    
    # Get tools from MCP server (Strands 공식 방식)
    with mcp_client:
        tools = mcp_client.list_tools_sync()
    
    agent = Agent(
        model="global.anthropic.claude-sonnet-4-5-20250929-v1:0",  # Claude Sonnet 4.5 Global
        tools=tools  # MCP 도구들을 직접 전달 (Strands 공식 방식)
    )
    
    print("✅ Agent created with CFM Tips MCP tools")
    print(f"   - Model: global.anthropic.claude-sonnet-4-5-20250929-v1:0 (Claude Sonnet 4.5 Global)")
    print(f"   - Tools: {len(tools)} MCP tools from CFM Tips server\n")
    return agent

 

 

프롬프트

더보기

Generate a comprehensive EKS (Elastic Kubernetes Service) Optimization Report in HTML format:
  
  **Report Date:** {today.strftime('%B %d, %Y %H:%M:%S')}
  **Analysis Period:** {analysis_start} to {analysis_end} (Last 7 days)
  **Region:** ap-northeast-2 (Seoul)
  
  **Analysis Scope:**
  Analyze current EKS infrastructure inventory and recent performance metrics (last 7 days) in ap-northeast-2 (Seoul) region:
  
  1. **Current Cluster Inventory** (as of now)
  2. **Cluster Version & EOS Warnings** (current status)
  3. **Pod Right-Sizing** (last 7 days metrics)
  4. **Node Group Optimization** (current configuration)
  5. **Storage Optimization** (current PVC/EBS status)
  6. **Cost Optimization Opportunities** (based on current state)
  
  ---
  
  ## **ANALYSIS TASKS:**
  
  ### **1. EKS CLUSTER ANALYSIS (Current Inventory)**
  
  **Tool:** eks_cluster_analysis
  
  **Tasks:**
  - List all EKS clusters currently running in ap-northeast-2
  - Check current Kubernetes version for each cluster
  - Identify clusters approaching End of Support (EOS)
  - Verify Container Insights enablement status
  - Count current nodegroups and active nodes per cluster
  - Inventory current EBS volumes (PVCs)
  
  **EOS Version Warnings (as of {today.strftime('%B %d, %Y')}):**
  - Kubernetes 1.35: Standard support ends 2027-03-27, Extended ends 2028-03-27
  - Kubernetes 1.34: Standard support ends 2026-12-02, Extended ends 2027-12-02
  - Kubernetes 1.33: Standard support ends 2026-07-29, Extended ends 2027-07-29
  - Kubernetes 1.32: Standard support ends 2026-03-23, Extended ends 2027-03-23
  - Kubernetes 1.31: Standard support ends 2025-11-26, Extended ends 2026-11-26
  - Kubernetes 1.30: Standard support ends 2025-07-23, Extended ends 2026-07-23
  - Kubernetes 1.29: Standard support ends 2025-03-23, Extended ends 2026-03-23
  
  **Critical Alerts:**
  - Clusters currently in Extended Support (additional charges apply)
  - Clusters approaching Standard Support end (< 90 days from today)
  - Clusters without Container Insights (missing observability)
  
  ---
  
  ### **2. POD RIGHT-SIZING ANALYSIS (Last 7 Days)**
  
  **Tool:** eks_pod_rightsizing
  
  **Analysis Period:** {analysis_start} to {analysis_end} (7 days)
  
  **Prerequisites:**
  - Container Insights must be enabled
  - Requires CloudWatch Logs Insights data from last 7 days
  
  **Tasks:**
  - Analyze pod CPU and memory utilization over the last 7 days
  - Identify over-provisioned pods (avg utilization < 20%)
  - Identify under-provisioned pods (max utilization > 90%)
  - Calculate resource waste from over-provisioning
  - Generate right-sizing recommendations based on recent usage
  
  **Optimization Opportunities:**
  - Reduce CPU/Memory requests for over-provisioned pods (30-50% reduction)
  - Increase limits for under-provisioned pods (20-30% increase)
  - Implement Vertical Pod Autoscaler (VPA) for automatic right-sizing
  - Use Horizontal Pod Autoscaler (HPA) for dynamic scaling
  
  **Cost Impact:**
  - Over-provisioned pods waste node capacity
  - Can reduce node count by right-sizing pods
  - Estimated savings: 20-40% of compute costs
  
  ---
  
  ### **3. NODE GROUP OPTIMIZATION (Current Configuration)**
  
  **Tool:** eks_node_optimization
  
  **Tasks:**
  - Analyze current EC2 instance types used in nodegroups
  - Identify old generation instances (t2, m4, c4, r4) currently running
  - Find Graviton-compatible workloads
  - Check current capacity type (On-Demand vs Spot)
  - Evaluate current nodegroup scaling configuration
  
  **Optimization Opportunities:**
  
  **A. Graviton Migration (20% cost savings)**
  - Identify x86 instances compatible with ARM/Graviton
  - Recommend migration to t4g, m7g, c7g, r7g, r8g instances
  - Most containerized workloads are Graviton-compatible
  
  **B. Spot Instances (70-90% savings)**
  - Identify fault-tolerant workloads suitable for Spot
  - Recommend mixed capacity (On-Demand + Spot)
  - Use Spot for batch jobs, stateless apps, dev/test
  
  **C. Instance Generation Upgrade**
  - Migrate t2 → t3/t4g (better performance, lower cost)
  - Migrate m4 → m6i/m7g (10-15% better price-performance)
  - Migrate c4 → c6i/c7g (improved compute efficiency)
  - Migrate r4 → r6i/r7g (better memory bandwidth)
  
  **D. Nodegroup Scaling**
  - Review current min/max/desired size settings
  - Implement Cluster Autoscaler for dynamic scaling
  - Use Karpenter for advanced node provisioning
  
  ---
  
  ### **4. STORAGE OPTIMIZATION (Current Status)**
  
  **Tool:** eks_storage_optimization
  
  **Tasks:**
  - Analyze current EBS volumes attached to EKS (PVCs)
  - Identify unused/unattached volumes (as of now)
  - Check current volume types (gp2 vs gp3)
  - Analyze volume utilization (last 7 days)
  - Review current storage class configurations
  
  **Optimization Opportunities:**
  
  **A. Volume Type Migration (20% savings)**
  - Migrate gp2 → gp3 for all PVCs
  - gp3 offers same/better performance at lower cost
  - Update StorageClass to use gp3 by default
  
  **B. Unused Volume Cleanup**
  - Delete unattached EBS volumes from deleted PVCs
  - Implement volume retention policies
  - Use reclaimPolicy: Delete for dynamic provisioning
  
  **C. Volume Right-Sizing**
  - Identify oversized volumes with low utilization
  - Implement volume expansion only when needed
  - Monitor volume usage with CloudWatch metrics
  
  **D. Snapshot Management**
  - Review EBS snapshot retention
  - Delete old/unused snapshots
  - Implement automated snapshot lifecycle
  
  ---
  
  ### **5. COMPREHENSIVE REPORT**
  
  **Tool:** eks_comprehensive_report
  
  **Tasks:**
  - Combine all analyses into unified report
  - Calculate total potential savings based on current state
  - Prioritize recommendations by impact
  - Generate implementation roadmap
  
  ---
  
  ## **REPORT STRUCTURE:**
  
  ### **1. Executive Summary**
  - Report generation date and time: {today.strftime('%B %d, %Y %H:%M:%S')}
  - Analysis period: Last 7 days ({analysis_start} to {analysis_end})
  - Total EKS clusters analyzed (current count)
  - Total nodes and pods (current count)
  - Total monthly EKS infrastructure cost (estimated)
  - Total potential monthly savings
  - Critical version warnings
  - Top 5 optimization opportunities
  
  ### **2. Current Cluster Inventory Dashboard**
  - Cluster name, version, status (as of {today.strftime('%B %d, %Y %H:%M')})
  - EOS warnings (color-coded by severity)
  - Nodegroup count and instance types (current)
  - Total node count (running now)
  - Pod count and resource utilization (current)
  - Storage: PVC count and total size (current)
  - Container Insights status (current)
  
  ### **3. Version & EOS Analysis**
  - Clusters by Kubernetes version (current)
  - Support status (Standard/Extended/Final) as of today
  - Days until support ends (from {today.strftime('%B %d, %Y')})
  - Upgrade recommendations with priority
  - Extended support cost implications
  
  ### **4. Pod Right-Sizing Analysis (Last 7 Days)**
  - Total pods analyzed
  - Over-provisioned pods (count, wasted resources)
  - Under-provisioned pods (count, risk level)
  - Optimized pods (no action needed)
  - Resource waste breakdown (CPU, Memory)
  - Estimated savings from right-sizing
  - Top 10 pods needing adjustment
  
  ### **5. Node Optimization Analysis (Current State)**
  - Current instance type distribution
  - Old generation instances (count, upgrade path)
  - Graviton-compatible instances (count, savings)
  - Spot-eligible workloads (count, savings)
  - On-Demand vs Spot breakdown (current)
  - Nodegroup scaling recommendations
  - Estimated savings by optimization type
  
  ### **6. Storage Optimization Analysis (Current State)**
  - Total PVC count and size (current)
  - Volume type breakdown (gp2, gp3, io1, etc.)
  - Unused/unattached volumes (as of now)
  - gp2 → gp3 migration opportunities
  - Oversized volumes
  - Snapshot cleanup opportunities
  - Estimated storage cost savings
  
  ### **7. Cost Optimization Summary**
  
  **Quick Wins (< 1 week):**
  - Delete unused EBS volumes (found today)
  - Release unattached volumes (found today)
  - Clean up old snapshots
  - Enable Container Insights (if missing)
  
  **Short-term (1-4 weeks):**
  - Migrate gp2 → gp3 volumes
  - Right-size over-provisioned pods (based on last 7 days data)
  - Update pod resource requests/limits
  - Implement pod autoscaling (HPA)
  
  **Medium-term (1-3 months):**
  - Upgrade old generation instances
  - Migrate to Graviton instances
  - Implement Spot instances for suitable workloads
  - Deploy Cluster Autoscaler or Karpenter
  - Upgrade clusters approaching EOS
  
  **Long-term (3-6 months):**
  - Implement Vertical Pod Autoscaler (VPA)
  - Adopt Karpenter for advanced provisioning
  - Purchase Compute Savings Plans
  - Implement FinOps practices and cost allocation
  
  ### **8. Implementation Roadmap**
  
  **Week 1-2: Assessment & Quick Wins**
  - Enable Container Insights on all clusters
  - Delete unused volumes and snapshots (identified today)
  - Document current state
  
  **Month 1: Storage & Pod Optimization**
  - Migrate gp2 → gp3
  - Right-size top 20 over-provisioned pods
  - Implement HPA for scalable workloads
  
  **Month 2: Node Optimization**
  - Upgrade old generation instances
  - Start Graviton migration (dev/test first)
  - Implement Spot for batch workloads
  
  **Month 3: Version Upgrades & Automation**
  - Upgrade clusters approaching EOS
  - Deploy Cluster Autoscaler
  - Implement cost monitoring and alerts
  
  ### **9. Risk Assessment**
  - Version EOS risks (downtime, security)
  - Under-provisioned pod risks (OOM kills, throttling)
  - Spot instance risks (interruptions)
  - Migration risks (compatibility, downtime)
  
  ### **10. Well-Architected Recommendations**
  
  **Cost Optimization:**
  - Right-size pods and nodes based on actual usage
  - Use Graviton and Spot instances
  - Implement autoscaling
  - Purchase Savings Plans
  
  **Performance Efficiency:**
  - Upgrade to latest instance generations
  - Use appropriate volume types
  - Implement pod resource limits
  - Monitor with Container Insights
  
  **Reliability:**
  - Upgrade clusters before EOS
  - Use Multi-AZ nodegroups
  - Implement pod disruption budgets
  - Regular backup and disaster recovery
  
  **Security:**
  - Keep Kubernetes version current
  - Enable audit logging
  - Implement pod security policies
  - Use IAM roles for service accounts
  
  **Operational Excellence:**
  - Enable Container Insights
  - Implement GitOps for deployments
  - Automate scaling and upgrades
  - Monitor costs and set budgets
  
  ---
  
  ## **HTML FORMATTING:**
  - Professional styling with responsive design
  - Color-coded severity indicators:
    - 🔴 Critical (EOS, under-provisioned)
    - 🟠 High (approaching EOS, old generation)
    - 🟡 Medium (over-provisioned, optimization opportunities)
    - 🟢 Low (informational, best practices)
  - Summary cards for key metrics
  - Detailed tables with drill-down
  - Charts for cost breakdown and savings
  - Print-friendly CSS
  - Include report generation date and time: {today.strftime('%B %d, %Y %H:%M:%S')}
  - Include analysis period: Last 7 days ({analysis_start} to {analysis_end})
  
  ---
  
  ## **AVAILABLE EKS TOOLS:**
  - eks_cluster_analysis - Analyze current clusters, versions, EOS warnings
  - eks_pod_rightsizing - Pod resource optimization using last 7 days metrics (requires Container Insights)
  - eks_node_optimization - Current node instance type and capacity optimization
  - eks_storage_optimization - Current EBS volume and PVC optimization
  - eks_comprehensive_report - Unified report with all current analyses
  
  **IMPORTANT NOTES:**
  - Use lookback_days=7 for pod right-sizing analysis
  - Report should reflect CURRENT infrastructure state (inventory as of now)
  - Performance metrics should be from LAST 7 DAYS
  - If Container Insights is not enabled on a cluster, skip pod right-sizing for that cluster and recommend enabling it
  - All cost estimates should be based on current configuration and recent usage patterns
  
  Use these tools to gather current EKS infrastructure data and recent performance metrics, then generate an actionable HTML report with specific recommendations and estimated savings.

 

보고서 확인

Strands 에이전트를 실행하면 정상적으로 보고서를 생성합니다.

보고서 생성에 약 2분이 소요됩니다.

python strands_agent_cfm_tips.py

 

 

보고서 내용은 다음과 같습니다.

 

전체 요약

 

 

현재 인벤토리 대시보드

 

 

버전 EOS 정보

 

 

파드 최적화 분석

 

 

노드 그룹 분석

 

 

스토리지 분석

 

 

비용 요약 및 로드맵

 

 

→ EKS 월 유지 비용이 138$(약 207,000원)으로 상당합니다..!

최적화 항목으로 Graviton 노드(t4g.medium) 이 있어 검토하니, 설정 자체는 가능했습니다.

단, ARM아키텍처에서만 가능하니 사전 애플리케이션에 대한 호환성이 필요합니다.

 

보고서에 넣을 좋은 내용이 있다면 댓글로 달아주세요~ 

 

이슈 정리

[1] Bedrock 모델 지역 호출 : 한국 리전에서는 claude sonnet 4.5 모델을 지원하지 않습니다. 교차 리전 추론 모델로 호출해주세요(본문 참고)

❌ Error: An error occurred (ValidationException) when calling the ConverseStream operation: The provided model identifier is invalid.
Traceback (most recent call last):
...
botocore.errorfactory.ValidationException: An error occurred (ValidationException) when calling the ConverseStream operation: The provided model identifier is invalid.
└ Bedrock region: ap-northeast-2
└ Model id: us.anthropic.claude-sonnet-4-5-20250929-v1:0

 

 

[2] Pod Identity addon을 구성하지 않은 경우 : Cloudwatch 에서 메트릭 수집이 안됩니다. 추가 기능으로 Pod Identity를 추가로 구성합시다.