DevOps/AWS

CloudWatch

kyoulho 2024. 7. 15. 19:24

AWS에서는 애플리케이션 성능 모니터링(APM, Application Performance Monitoring)을 위해 여러 도구와 서비스를 제공한다. 또한 외부 APM 도구(New Relic, Datadog, Dynatrace)와의 통합도 지원한다. 이러한 도구들은 AWS 인프라와 애플리케이션의 메트릭을 수집하고, 이를 기반으로 다양한 성능 분석 기능을 제공한다. 그중 주요 AWS 서비스는 Amazon CloudWatch와 AWS X-Ray이다.

Amazon CloudWatch


AWS CloudWatch는 AWS 리소스 및 애플리케이션의 성능 및 상태를 모니터링하는 서비스이다.
메트릭 및 로그 데이터의 수집, 이벤트 기반 알림 설정, 자동화된 작업 실행 등의 기능을 통해 클라우드 인프라의 관리와 운영 효율성을 향상시킬 수 있다.
AWS 서비스 및 애플리케이션에서 생성된 메트릭 데이터를 CloudWatch 에이전트나 API를 통해 수집한다. 수집된 메트릭 데이터는 기본적으로 1분 간격으로 CloudWatch에 저장된다. 특정 AWS 서비스는 더 자세한 간격으로 데이터를 전송할 수도 있다.

주요 기능

  1. 메트릭 모니터링 (Metric Monitoring):
    • CloudWatch는 AWS 리소스에서 생성된 여러 메트릭(CPU 사용률, 네트워크 트래픽)들을 수집하고 이러한 메트릭들을 시각화하여 실시간으로 모니터링할 수 있다.
    • 사용자 정의 메트릭을 생성하여 애플리케이션에서 수집한 특정 데이터를 CloudWatch에 전송하고 모니터링할 수도 있다.
  2. 로그 모니터링 (Log Monitoring)
    • AWS CloudWatch Logs를 사용하여 애플리케이션, 서비스 또는 AWS 리소스의 로그 데이터를 수집, 모니터링 및 저장할 수 있다.
    • 필터링, 검색, 경고 설정 등의 기능을 사용하여 로그 데이터를 분석하고 문제 해결에 도움을 줄 수 있다.
    • 로그 그룹을 생성하여 특정 로그 데이터를 구성하고, 서브스크립션을 통해 특정 패턴에 일치하는 로그에 대해 경고를 설정할 수 있다.
  3. 이벤트 기반 알림 (Event-based Alerts)
    • CloudWatch는 특정 메트릭 값이나 이벤트 발생 시 사용자가 설정한 경고를 트리거하도록 구성할 수 있다. 예를 들어, CPU 사용률이 설정한 임계값을 초과하거나, EC2 인스턴스가 비정상적으로 종료될 경우 알림을 받을 수 있다.
  4. 자동 조치 (Automated Actions)
    • CloudWatch Events 및 AWS Lambda와 통합하여 특정 이벤트 발생 시 자동으로 액션을 취할 수 있다. 예를 들어, EC2 인스턴스가 중지된 경우 자동으로 다시 시작하는 Lambda 함수를 트리거할 수 있다.
  5. 대시보드 (Dashboards)
    • 사용자 정의 대시보드를 만들어 다양한 메트릭과 로그 데이터를 한 화면에서 실시간으로 모니터링할 수 있다. 대시보드는 다양한 차트와 그래프를 통해 실시간으로 데이터를 시각화해 볼 수 있다. 또한, 대시보드를 공유하거나 이메일 알림 설정을 통해 팀 전체와 데이터를 공유할 수 있다.
  6. 비용 관리 및 성능 최적화
    • CloudWatch는 다양한 AWS 리소스의 사용량과 성능 데이터를 모니터링하여 비용을 관리하고, 자원 사용의 최적화에 이용할 수 있다.
    • 성능 데이터를 분석하여 병목 현상을 식별하고, 리소스 크기를 조정하여 애플리케이션의 성능을 최적화하는데 이용될 수 있다.

Gradle Kotlin DSL

dependencies {
    implementation("software.amazon.awssdk:cloudwatch")
}

application.properties 파일

aws.region=us-east-1
aws.accessKeyId=your-access-key-id
aws.secretKey=your-secret-key

CloudWatch 구성 클래스

import org.springframework.beans.factory.annotation.Value
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
import software.amazon.awssdk.auth.credentials.AwsBasicCredentials
import software.amazon.awssdk.auth.credentials.StaticCredentialsProvider
import software.amazon.awssdk.regions.Region
import software.amazon.awssdk.services.cloudwatch.CloudWatchClient

@Configuration
class CloudWatchConfig {

    @Bean
    fun cloudWatchClient(
        @Value("\${aws.region}") region: String,
        @Value("\${aws.accessKeyId}") accessKeyId: String,
        @Value("\${aws.secretKey}") secretKey: String
    ): CloudWatchClient {
        val awsCreds = AwsBasicCredentials.create(accessKeyId, secretKey)

        return CloudWatchClient.builder()
            .region(Region.of(region))
            .credentialsProvider(StaticCredentialsProvider.create(awsCreds))
            .build()
    }
}

예제 1: 서비스 응답 시간 모니터링

import org.aspectj.lang.annotation.AfterReturning
import org.aspectj.lang.annotation.Aspect
import org.springframework.stereotype.Component
import software.amazon.awssdk.services.cloudwatch.CloudWatchClient
import software.amazon.awssdk.services.cloudwatch.model.MetricDatum
import software.amazon.awssdk.services.cloudwatch.model.PutMetricDataRequest
import software.amazon.awssdk.services.cloudwatch.model.StandardUnit
import java.time.Duration
import java.time.Instant

@Aspect
@Component
class ResponseTimeMonitoringAspect(
    private val cloudWatchClient: CloudWatchClient,
) {

    @AfterReturning("execution(* com.example.service.*.*(..))")
    fun monitorResponseTime(joinPoint: JoinPoint) {
        val methodName = joinPoint.signature.name
        val startTime = Instant.now()

        try {
            // 원본 메소드 호출
            val result = joinPoint.proceed()

            // 응답 시간 계산
            val endTime = Instant.now()
            val durationInMillis = Duration.between(startTime, endTime).toMillis().toDouble()

            // CloudWatch에 메트릭 데이터 전송
            val datum = MetricDatum.builder()
                .metricName("ResponseTime")
                .unit(StandardUnit.MILLISECONDS)
                .value(durationInMillis)
                .timestamp(endTime)
                .build()

            val request = PutMetricDataRequest.builder()
                .namespace("MyApp")
                .metricData(datum)
                .build()

            cloudWatchClient.putMetricData(request)

            return result
        } catch (ex: Throwable) {
            // 예외 처리
            throw ex
        }
    }
}

예제 2: 특정 작업 수행 횟수 모니터링

import org.aspectj.lang.annotation.AfterReturning
import org.aspectj.lang.annotation.Aspect
import org.springframework.stereotype.Component
import software.amazon.awssdk.services.cloudwatch.CloudWatchClient
import software.amazon.awssdk.services.cloudwatch.model.MetricDatum
import software.amazon.awssdk.services.cloudwatch.model.PutMetricDataRequest
import software.amazon.awssdk.services.cloudwatch.model.StandardUnit
import java.time.Instant

@Aspect
@Component
class OperationCountMonitoringAspect(
    private val cloudWatchClient: CloudWatchClient,
) {

    private val operationCountMap: MutableMap<String, Long> = mutableMapOf()

    @AfterReturning("execution(* com.example.service.*.*(..))")
    fun monitorOperationCount(joinPoint: JoinPoint) {
        val methodName = joinPoint.signature.name

        // 메소드 실행 횟수 증가
        operationCountMap.compute(methodName) { _, count ->
            count?.plus(1) ?: 1
        }

        // 주기적으로 CloudWatch에 데이터 전송 (예: 매 5분마다)
        if (System.currentTimeMillis() % (5 * 60 * 1000) == 0L) {
            sendOperationCountMetrics()
        }
    }

    private fun sendOperationCountMetrics() {
        val timestamp = Instant.now()

        // 각 작업의 실행 횟수 메트릭 전송
        operationCountMap.forEach { (operationName, count) ->
            val datum = MetricDatum.builder()
                .metricName("$operationName-Count")
                .unit(StandardUnit.COUNT)
                .value(count.toDouble())
                .timestamp(timestamp)
                .build()

            val request = PutMetricDataRequest.builder()
                .namespace("MyApp")
                .metricData(datum)
                .build()

            cloudWatchClient.putMetricData(request)
        }

        // 메모리 내 작업 횟수 맵 초기화
        operationCountMap.clear()
    }
}

'DevOps > AWS' 카테고리의 다른 글

X-Ray  (0) 2024.07.16
OWASP Top 10 공격 패턴  (2) 2024.07.14
Session Manger 를 이용하여 EC2에 연결하기  (0) 2023.06.18
AWS Aurora DB  (1) 2023.06.18
[WAF] NestJS 애플리케이션 AWS에 배포하기 08  (0) 2023.04.25