★★ 中級

09. CloudWatch / SNS / SQS

運用に必須の 「ログ・メトリクス・通知・キュー」。CloudWatch でログとメトリクスを取り、メトリクスのしきい値超過を SNS で通知、Lambda 等の非同期処理は SQS で受ける。AWS 運用の基本セット。

aws_cloudwatch_log_group

CloudWatch Logs はログの保管庫。ロググループ という単位でログを束ねます。Lambda、ECS、API Gateway などはここに書き込みます。

resource "aws_cloudwatch_log_group" "lambda" {
  name              = "/aws/lambda/hello"
  retention_in_days = 14   # ← 重要。指定しないと「無期限」になり料金が膨らむ
}

resource "aws_cloudwatch_log_group" "api" {
  name              = "/aws/apigw/hello-api"
  retention_in_days = 14
}

resource "aws_cloudwatch_log_group" "ecs" {
  name              = "/ecs/myapp"
  retention_in_days = 30
  kms_key_id        = aws_kms_key.logs.arn   # 任意: KMS で暗号化
}
retention_in_days を必ず デフォルトは「無期限保持」= ログが永久に増え続けます。費用事故の代表例。初学者は必ず 7〜30 日で設定

サブスクリプションフィルタ(外部送信)

# CloudWatch Logs → Kinesis → 外部 SaaS という流れの最初の 1 ステップ
resource "aws_cloudwatch_log_subscription_filter" "to_kinesis" {
  name            = "to-kinesis"
  log_group_name  = aws_cloudwatch_log_group.lambda.name
  filter_pattern  = ""   # 全件
  destination_arn = aws_kinesis_stream.logs.arn
  role_arn        = aws_iam_role.logs_to_kinesis.arn
}

aws_cloudwatch_metric_alarm

「特定のメトリクスが、N 期間連続でしきい値を超えたら、X に通知する」を定義。

resource "aws_cloudwatch_metric_alarm" "lambda_errors" {
  alarm_name          = "lambda-hello-errors"
  alarm_description   = "Lambda hello のエラーが 5 分間で 5 回を超えた"
  comparison_operator = "GreaterThanThreshold"
  evaluation_periods  = 1
  threshold           = 5

  metric_name = "Errors"
  namespace   = "AWS/Lambda"
  period      = 300       # 5 分
  statistic   = "Sum"

  dimensions = {
    FunctionName = aws_lambda_function.hello.function_name
  }

  alarm_actions = [aws_sns_topic.alerts.arn]
  ok_actions    = [aws_sns_topic.alerts.arn]

  treat_missing_data = "notBreaching"
}

典型的なアラーム例

監視対象namespacemetric_name
Lambda エラーAWS/LambdaErrors
EC2 CPUAWS/EC2CPUUtilization
RDS CPUAWS/RDSCPUUtilization
RDS 接続数AWS/RDSDatabaseConnections
ALB 5xxAWS/ApplicationELBHTTPCode_Target_5XX_Count
SQS 積み残しAWS/SQSApproximateNumberOfMessagesVisible

aws_sns_topic と購読

SNS は 「pub/sub」 の仕組み。トピックに publish したメッセージが、購読者全員に配信されます。アラートメール/Slack 連携/他サービスへの fan-out で頻出。

resource "aws_sns_topic" "alerts" {
  name              = "alerts"
  kms_master_key_id = "alias/aws/sns"
}

# メール購読
resource "aws_sns_topic_subscription" "email" {
  topic_arn = aws_sns_topic.alerts.arn
  protocol  = "email"
  endpoint  = "ops@example.com"
}

# Slack: AWS Chatbot 経由(推奨)
resource "aws_sns_topic_subscription" "slack" {
  topic_arn = aws_sns_topic.alerts.arn
  protocol  = "https"
  endpoint  = var.chatbot_webhook   # AWS Chatbot で発行した URL
}

# Lambda 購読(イベント駆動の処理)
resource "aws_sns_topic_subscription" "lambda" {
  topic_arn = aws_sns_topic.alerts.arn
  protocol  = "lambda"
  endpoint  = aws_lambda_function.notify.arn
}

resource "aws_lambda_permission" "from_sns" {
  statement_id  = "AllowSNSInvoke"
  action        = "lambda:InvokeFunction"
  function_name = aws_lambda_function.notify.function_name
  principal     = "sns.amazonaws.com"
  source_arn    = aws_sns_topic.alerts.arn
}

メール購読は AWS から確認メールが飛び、ユーザーが「Confirm subscription」リンクをクリックして初めて有効になります。

aws_sqs_queue(DLQ 含む)

SQS は キュー。送信側と受信側を疎結合にし、受信側がダウンしてもメッセージが滞留するだけで消えません。

resource "aws_sqs_queue" "jobs_dlq" {
  name                       = "jobs-dlq"
  message_retention_seconds  = 1209600   # 14 日(最大)
  kms_master_key_id          = "alias/aws/sqs"
}

resource "aws_sqs_queue" "jobs" {
  name                       = "jobs"
  visibility_timeout_seconds = 60        # ワーカが処理する想定時間
  message_retention_seconds  = 345600    # 4 日
  kms_master_key_id          = "alias/aws/sqs"

  redrive_policy = jsonencode({
    deadLetterTargetArn = aws_sqs_queue.jobs_dlq.arn
    maxReceiveCount     = 5              # 5 回失敗したら DLQ へ
  })
}

キューポリシー(送信元の制限)

data "aws_iam_policy_document" "sqs_jobs" {
  statement {
    sid     = "AllowSNSToPublish"
    effect  = "Allow"
    actions = ["sqs:SendMessage"]

    principals {
      type        = "Service"
      identifiers = ["sns.amazonaws.com"]
    }

    resources = [aws_sqs_queue.jobs.arn]

    condition {
      test     = "ArnEquals"
      variable = "aws:SourceArn"
      values   = [aws_sns_topic.events.arn]
    }
  }
}

resource "aws_sqs_queue_policy" "jobs" {
  queue_url = aws_sqs_queue.jobs.id
  policy    = data.aws_iam_policy_document.sqs_jobs.json
}

SNS → SQS の fan-out パターン

同じイベントを複数の処理系に届けたい時の定番。

[Producer] ──→ [SNS Topic "events"]
                       │
            ┌──────────┼──────────┐
            ↓          ↓          ↓
        [SQS A]    [SQS B]    [Lambda]
            │          │          │
            ↓          ↓          ↓
       [Worker A] [Worker B] [Logger]
resource "aws_sns_topic_subscription" "to_sqs_a" {
  topic_arn = aws_sns_topic.events.arn
  protocol  = "sqs"
  endpoint  = aws_sqs_queue.a.arn
}

resource "aws_sns_topic_subscription" "to_sqs_b" {
  topic_arn = aws_sns_topic.events.arn
  protocol  = "sqs"
  endpoint  = aws_sqs_queue.b.arn
}

SNS が 1 メッセージを受けると、SQS A・B の両方に同じ内容が配信される。それぞれのワーカは自分のキューを独立した速度で処理できる、というのがこのパターンの強み。