Site icon GRIP.News

AWS, RDS/Aurora Serverless! Slow Query, ElasticSearch를 사용한 시각화

Slow Query 는 공공의 적이다. 전체 서비스 품질을 저하 시킬 뿐만 아니라, Lock 이 걸리는 순간 제대로 된 트랜잭션이 이뤄지지 않는 경우도 있다. Slow Query 가 발생하는 이유는 여러가지다. 개발자의 잘못일 수도 있지만, 시스템의 오류일 수도 있다. (때문에 너무 개발 탓만 하지 말자) AWS는 CloudWatch Logs에 RDS 를 편입시켰다. 그러나, CloudWatch Dashboard 는 가장 단순화된 ‘상태 값’을 보여주기만 한다.

Slow Query 가 있다고 해서 시스템 성능이 바로 저하되는건 아니며, 추이를 살펴 보려면 로그 수집과 수집된 로그의 시각화는 안정적인 시스템 운용에 필수다. 앞선 Nginx Log의 시각화, Lambda 응용, MySQL 인덱스의 이해 등의 포스트는 이 포스트를 설명하기 위한 기본 작업이었다.

 

처리 구조


형태는 명확하다. RDS Service 의 로그 모니터링 API 를 사용하려면 CloudWatch를 사용하고, Lambda Function을 생성해 ElasticSearch 로 보낸다. 관리자는 Kibana 와 같은 시각화 도구로 모니터링이 가능하다. (이와 별개로 RDS 모니터링은 Zabbix 로 수집해 Telegram 으로 알람을 받고 있다)

이 포스트에서는 다음 과정을 다루고 있다. 모든 서비스는 Serverless 구조이며, AWS 의 인프라를 적극 활용하고 있다. (관리 서비스를 관리하는 인적 요소를 줄일 수 있다.)

  1. AWS RDS 로그를 CloudWatch Logs 로 내보내기
  2. AWS ElasticSearch Service 에 LogPass 설정
  3. AWS Lambda 에 LogPass 추가
  4. AWS CloudWatch Logs 를 ElasticSearch Service 에 전달

 

※로그 수집 및 시각화는 업무 효율(=시점 인지 및 기록)을 위해서 이기 때문에 알람은 반드시 필요하다.

 

RDS/Aurora 로그 CloudWatch Logs 로 내보내기


AWS CLI를 사용해 Slow Query 를 CloudWatch Logs 로 내보내도록 설정한다. 이 옵션은 웹 콘솔에서 작업할 수 없으므로 CLI 사용이 필수다.

# Serverless RDS Cluster 사용시
PS C:\Works> aws rds modify-db-cluster --db-cluster-identifier ${DB_CLUSTER_NAME} --cloudwatch-logs-export-configuration EnableLogTypes=slowquery

# RDS Instance 사용시
PS C:\Works> aws rds modify-db-instance --db-instance-identifier ${DB_INSTANCE_NAME} --cloudwatch-logs-export-configuration EnableLogTypes=slowquery

DB Instance 와 Serverless DB Cluaster 에 따라 차이가 있기 때문에 주의하자. 우리는 RDS/Aurora Serverless 를 사용중이며, CLI 실행 결과는 다음과 같다. EnabledCloudwatchLogsExports 부분에 slowquery 가 추가 된 것을 확인할 수 있다.

{
    "DBCluster": {
        "AllocatedStorage": 1,
        "AvailabilityZones": [
            "ap-northeast-1d",
            "ap-northeast-1a",
            "ap-northeast-1c"
        ],
        "BackupRetentionPeriod": 1,
        "DBClusterIdentifier": "mtlabs-tokyo-dv-mtworks-serverless",
        "DBClusterParameterGroup": "default.aurora5.6",
        "DBSubnetGroup": "mtlabs-tokyo-dv-rds",
        "Status": "available",
        "EarliestRestorableTime": "2019-06-10T18:28:39.667Z",
        "Endpoint": "mtlabs-tokyo-dv-...-ap-northeast-1.rds.amazonaws.com",
        "MultiAZ": false,
        "Engine": "aurora",
        "EngineVersion": "5.6.10a",
        "LatestRestorableTime": "2019-06-12T01:21:37.325Z",
        "Port": 3306,
        "MasterUsername": "mtworks",
        "PreferredBackupWindow": "18:15-18:45",
        "PreferredMaintenanceWindow": "wed:14:56-wed:15:26",
        "ReadReplicaIdentifiers": [],
        "DBClusterMembers": [],
        "VpcSecurityGroups": [
            {
                "VpcSecurityGroupId": "sg-045a2762",
                "Status": "active"
            }
        ],
        "HostedZoneId": "Z000000L0SGTNB",
        "StorageEncrypted": true,
        "KmsKeyId": "arn:aws:kms:ap-northeast-1:211234567895:key/4000000-2169-4452-8a40-fbfasd3e3546",
        "DbClusterResourceId": "cluster-J7ABCDEFGI726EZIRQ",
        "DBClusterArn": "arn:aws:rds:ap-northeast-1:211234567895:cluster:mtlabs-tokyo-dv-mtworks-serverless",
        "AssociatedRoles": [],
        "IAMDatabaseAuthenticationEnabled": false,
        "ClusterCreateTime": "2018-09-06T05:42:50.353Z",
        "EnabledCloudwatchLogsExports": [
            "slowquery"
        ],
        "Capacity": 2,
        "EngineMode": "serverless",
        "ScalingConfigurationInfo": {
            "MinCapacity": 2,
            "MaxCapacity": 64,
            "AutoPause": true,
            "SecondsUntilAutoPause": 300,
            "TimeoutAction": "RollbackCapacityChange"
        },
        "DeletionProtection": true,
        "HttpEndpointEnabled": false,
        "CopyTagsToSnapshot": false
    }
}

그리고, Slow Query 를 활성화 하려면 RDS > 파라미터 그룹에 slow_query_log 값을 1로 수정해야 한다.

※ 원인과 이유를 모르겠지만 CloudWatch Logs 에 slowquery 가 생성되는데 3시간 가까이 걸렸다. 참고로, RDS Instance 에서 테스트 하면 거의 바로 생성된다. 물론, 강제로 Slow Query 를 만들어 테스트 했을 때 이야기 (sleep(30). MySQL 의 기본은 10초다)

 

ElasticSearch Service 로그패스 설정


(ElasticSearch 는 이미 생성되었다는걸 가정으로 한다. 또는 앞선 Nginx Log 를 수집 및 저장했던 도메인을 활용해도 무관하다) ElasticSearch Service 의 Ingest Node 를 사용해 전달 받은 로그 메시지 내 Slow Query를 분석하는데 사용할 SQL 구문 및 처리 실행시간 그리고 의미 있는 문자열의 패턴을 지정한다. 그리고 curl 을 이용해 Ingest Pipeline을 설정하자.

MySQL 계열(MySQL, Maria, Aurora)를 사용한다면 패턴에서의 차이는 거의 없다. 단, 아래 예제는 PowerShell 이기 때문에 Linux 계열에서 curl 을 사용한다면 약간의 수정이 필요하다.

# JSON 변수에 패턴 등을 넣는다.

PS C:\Works> $JSON = '
 {
  "processors" : [
    {
      "gsub": {
        "field": "@message",
        "pattern": "\n",
        "replacement": ""
      }
    },
    {
      "grok" : {
        "field" : "@message",
        "patterns" : ["# User@Host: %{WORD:user}\\[%{WORD}\\] @ %{WORD:host} \\[%{IP:ip}\\]%{SPACE}Id:%{SPACE}%{NUMBER:id}%{SPACE}# Query_time: %{NUMBER:query_time:float}%{SPACE}Lock_time: %{NUMBER:lock_time:float}%{SPACE}Rows_sent: %{NUMBER:rows_sent:int}%{SPACE}Rows_examined: %{NUMBER:rows_examined:int}%{SPACE}(use %{WORD:database};%{SPACE})?(SET timestamp=%{NUMBER:timestamp:int};%{SPACE})?%{GREEDYDATA:sql}"],
        "ignore_failure" : true
      }
    },
    {
      "date": {
        "field": "timestamp",
        "formats": ["UNIX"],
        "ignore_failure" : true
      }
    },
    {
      "remove": {
        "field": ["timestamp", "@message"],
        "ignore_failure" : true
      }
    }
  ]
}'

# curl 실행
PS C:\Works> curl -Method PUT -Uri "{ElasticSearch VPC Domain}/_ingest/pipeline/slowquerylog" -Headers @{"Content-Type"="application/json"} -Body $JSON -ContentType "application/json"

문제가 없다면 다음과 같은 내용이 회신된다.

StatusCode        : 200
StatusDescription : OK
Content           : {"acknowledged":true}
RawContent        : HTTP/1.1 200 OK
                    Connection: keep-alive
                    Access-Control-Allow-Origin: *
                    Content-Length: 21
                    Content-Type: application/json; charset=UTF-8
                    Date: Wed, 19 Jun 2019 02:13:37 GMT

                    {"acknowledged":true}...
Forms             : {}
Headers           : {[Connection, keep-alive], [Access-Control-Allow-Origin, *], [Content-Length, 21], [Content-Type, a
                    pplication/json; charset=UTF-8]...}
Images            : {}
InputFields       : {}
Links             : {}
ParsedHtml        : mshtml.HTMLDocumentClass
RawContentLength  : 21

 

CloudWatch Logs 의 로그 데이터를 ElasticSearch 로 내보내기


앞서 RDS/Aurora 는 Slow Query 에 대한 내용을 CloudWatch 로 보내기 시작했다. 그리고 ElasticSearch 는 로그 데이터를 받을 준비가 완료됐다. 이제 CloudWatch Logs 에 저장된 로그 데이터를 ElasticSearch Service 로 전달하면 된다. 로그 그룹 명칭은 /aws/rds/cluster/{Cluster Name}/slowquery 이다.

 

로그 그룹 선택 후 ‘작업(Action) > Amazon Elasticsearch Service 측 스트림’을 선택하면 스트림의 Lambda Function 이 자동 생성된다. 특별한 주의사항은 없지만, IAM 은 반드시 Lambda 의 실행 권한을 갖고 있어야 하며(Execution Permission) 로그 형식은 기타(Other)를 선택한다.

구독 항목에 LogToElasticsearch 가 추가된 것을 확인할 수 있다.

 

Lambda Function 수정


AWS Lambda Function 을 살펴보면 새로 생긴 Function 이 보인다. (위에서 구독 중인 명칭) 자동 생성된 Lambda Function 을 수정해야 한다.

  1. pipeline Query String 을 지정한다
  2. canonicalString 에 pipline 을 추가한다
  3. request path 를 변경한다.

변경된 내용을 요약해 보면 다음과 같다.

var endpoint = 'vpc-nrt-dv-...ap-northeast-1.es.amazonaws.com';
var pipeline = 'pipeline=slowquerylog'; // pipline 추가
.
.
    var canonicalString = [
        request.method,
        request.path, // ,'' 제거
        pipeline, // 추가 
        canonicalHeaders, '',
        signedHeaders,
        hash(request.body, 'hex'),
    ].join('\n');

.
.

    request.path = request.path + "?" + pipeline; // 추가
    return request;

큰 차이점은 없다. 오타만 나지 않게 조심하면 된다. 잘 동작하는지 여부는 CloudWatch Logs 내 로그 스트림을 확인해 보자.

간혹 ElasticSearch 의 설정을 바꾸고 403 Error 가 발생하는 경우가 있다. IAM 권한 문제인데 어떤 권한을 넣어야 하는지 잘 모른다면 Lambda Function 삭제(및 구독된 내용 제거) 후 다시 생성해 보자. AWS Wizard 가 필요한 권한에 대해 설명해 줄 것이다.

 

Kibana 에서 확인


AWS ElasticSearch Service에서의 자료는 Kibana 에서 시각화 된다. 먼저 인덱스 패턴을 지정해야 한다. 물론, 매칭되는 인덱스 패턴이 없다. cwl* 을 선택하고, 시간 기준은 @timestamp 를 선택 하면 된다. 이는 앞서 작성한 Lambda Function 에 그렇게 선언 했었기 때문.

var timestamp = new Date(1 * logEvent.timestamp);

// index name format: cwl-YYYY.MM.DD
var indexName = [
    'cwl-' + timestamp.getUTCFullYear(),              // year
    ('0' + (timestamp.getUTCMonth() + 1)).slice(-2),  // month
    ('0' + timestamp.getUTCDate()).slice(-2)          // day
].join('.');

var source = buildSource(logEvent.message, logEvent.extractedFields);
source['@id'] = logEvent.id;
source['@timestamp'] = new Date(1 * logEvent.timestamp).toISOString();
source['@message'] = logEvent.message;

 

Discover 를 누르면 로그 데이터를 볼 수 있다. (아래는 쌓인 데이터가 없어서 이쁘지않음..)

 

결론


관리를 위한 관리 도구를 관리해야한다. 참 난해한 문제다. 더욱이 콘솔에 붙어 로그를 봐야하고, 통계도 내기 쉽지 않다. AWS 의 Lambda 와 ElasticSearch 의 조합은 확장 가능한 사례가 무궁무진하다. 이 포스트의 RDS/Aurora 뿐만 아니라, EC2 등 다양한 서비스로의 확장이 가능하다. 물론 .. 아마존 자체의 문제로 삽질을 좀 했지만.

Exit mobile version