CloudFormation を用いて CloudWatch 上に RDS アラームとカスタムメトリクスを設定する

# 概要

AWS を触り始めたばかりだった数年前、表題のやりかたがまったくわからず調べても、エージェントをいれるとかいれないとか CLI で設定しようとか「CloudFormation でアラームを作成したい」かつ「そもそも CloudWatch Alarm それほど詳しくない」人にやさしくない記事ばかりでてきたことがあり、その時に悲しみにかられながら自分用に残した記録です。誰かの役に立てばいいなと思います。

# やりたいこと
CloudFormation のスタックで RDS のメモリ残量を監視する CloudWatchAlarm とカスタムメトリクスを作りたい

# 作成したテンプレート

アラーム以外のリソース作成部分と Low Alarm 作成部分は名前が違うくらいなので省略します。

```yml

<~ 省略 ~>

# ------------------------------------
# Hight CPU CloudWatch Alarm
# ------------------------------------
  HighCpuAlarm:
    Type: "AWS::CloudWatch::Alarm"
    Properties:
      AlarmActions: 
        - !Ref SNSTopicName
      AlarmName: !Join
            - '-'
            - - !Ref Service
              - db
              - High_CPU_Utilization
      MetricName: CPUUtilization
      Namespace: AWS/RDS
      Statistic: Average
      Period: 60
      EvaluationPeriods: 3
      Threshold: 90
      TreatMissingData: notBreaching
      ActionsEnabled: true
      AlarmDescription: High_CPU_Utilization
      ComparisonOperator: GreaterThanThreshold
      Dimensions: 
        - Name: DBInstanceIdentifier
          Value: !Ref RdsInstance


# ------------------------------------
# Hight Memory CloudWatch Alarm
# ------------------------------------
  HighMemoryAlarm:
    Type: "AWS::CloudWatch::Alarm"
    Properties:
      ActionsEnabled: true
      AlarmActions:
        - !Ref SNSTopicName
      AlarmDescription: High_Freeable_Memory
      AlarmName: !Join
            - '-'
            - - !Ref Service
              - db
              - High_Freeable_Memory
      ComparisonOperator: GreaterThanThreshold
      EvaluationPeriods: 5
      DatapointsToAlarm: 2
      MetricName: !Join
            - '-'
            - - !Ref Service
              - db
              - High_Freeable_Memory
      Namespace: AUCFAN/PLUS
      Period: 60
      Statistic: Average
      Threshold: 1800000
      TreatMissingData: notBreaching

# ------------------------------------
# Hight Memory CloudWatch Alarm Metric
# ------------------------------------
  HighMemoryAlarmMetric:
    Type: AWS::Logs::MetricFilter
    Properties: 
      FilterPattern: '{ $.instanceID = "インスタンスの名前" }'
      LogGroupName: "RDSOSMetrics"
      MetricTransformations: 
        -   
          MetricName: !Join
            - '-'
            - - !Ref Service
              - db
              - High_Freeable_Memory
          MetricNamespace: AUCFAN/PLUS
          MetricValue: $.memory.active

<~ 省略 ~>

```

# 留意点

 * **RDS を監視する標準のアラーム ( 今回は Hight CPU CloudWatch Alarm )**
    1. 特別な設定が必要なければ `MetricName` は標準の物を指定する
         * `CPUUtilization` とか `FreeableMemory` とかあるので監視したい項目に合わせて指定してください。
    2. 特別な設定が必要なければ `Namespace` は標準の物を指定する
        * だいたいは AWS/〇〇 という書式らしいです。今回は RDS を監視するので `AWS/RDS`です。
    3. `Dimensions` で DB を指定する
       * `Dimensions` で Name に `DBInstanceIdentifier` を、Value にDB識別子を設定しないと CloudWatch はどこの値をみたらいいかわからないみたいです。また、一つでもメトリクスにない値を設定すると値が取れなくなります。シビア。
     4. あとの項目はこちらを頼りに
        *  https://docs.aws.amazon.com/ja_jp/AWSCloudFormation/latest/UserGuide/aws-properties-cw-alarm.html


 * **カスタムメトリクス を用いたアラーム ( 今回は Hight Memory CloudWatch Alarm )**
    1. まずは`AWS::Logs::MetricFilter`を作成する
         * RDS で拡張モニタリングが有効になっている場合、そのログは`RDSOSMetrics`へ出力されます。出力先の変更はできません。(たぶん)　なのでメトリクスフィルターもそこへ作成します。RDS の拡張モニタリングのアラームを作成しようと思う場合、`LogGroupName` の値は `"RDSOSMetrics"`でほぼ固定？です。
    2. フィルターパターンを設定する
        * アラームの設定ではなく、メトリクスがログストリーム上の何を参照するのか、という設定です。`RDSOSMetrics`へは複数の DB のログストリームが出力されているため、`instanceID`などで絞って必要な値をとってきます。 RDS のインスタンスには ID が用意されていないため、DB識別子がそのまま利用できます。フィルターの構文についてはこちらhttps://docs.aws.amazon.com/AmazonCloudWatch/latest/logs/FilterAndPatternSyntax.html 
         * ~~公式のドキュメントなのにJSON の構文しか書いてないのは不親切だと思いました。~~
     3. `MetricNamespace`は任意
        *  ただし AWS/〇〇 は AWS の予約語であるため使えません。このメトリクスを監視するアラームと同じ名前にしてください。これで `AWS::Logs::MetricFilter` の設定は終わりです。
     4. `AWS::CloudWatch::Alarm` の `MetricName` は作成したカスタムメトリクスを指定する
        * メトリクスフィルターの名前ではなく、メトリクスの名前です。よろしくお願いします。
     5. `Namespace`は作成したメトリクスフィルターと同じものにする
         * RDS 監視だから`AWS/RDS`かな～と思って設定すると値が取れません。
     5. `Dimensions`はいらない
        * `MetricName`と`Dimensions`の両方を設定すると値が取れません。

おわり
  
# 参考
https://docs.aws.amazon.com/AmazonCloudWatch/latest/logs/FilterAndPatternSyntax.html#matching-terms-events
https://engineers.weddingpark.co.jp/aws-cloudformation-cloudwatch-alarm/