こんにちは。SREエンジニアのこばやし(id: kasei_san)です。
弊社サービス バス比較なび では、RDS Aurora にオートスケーリングを設定しています。
最近、この構成のメトリクスを見直していたのですが、その時点のリードレプリカ数が把握できず、ちょっと不便に感じました。
(例えば、リクエスト数が多いタイミングで、ちゃんとスケーリングが効いてレスポンスタイムが下がっているか? などを確認したいときなど)
そこで CloudWatch ダッシュボードにリードレプリカ数のグラフを出そうと、対応するメトリクスを探してみたのですが……見つからないんですね。
リードレプリカ数なんて、多くの人が気にしそうな項目ですし、「さすがにあるだろう」と思ってサポートに問い合わせてみたところ……
恐れ入りますが、AutoScaling で追加されたリーダーインスタンスの台数を報告するメトリクスはございません。
まさかの未サポート……!!!
というわけで、自分でメトリクスを取得する仕組みを作ることにしました。
やったこと
- RDSのオートスケール時にEventBridgeでlambdaを実行
- lambdaで、リードレプリカ数をCloudWatchMetricsに投げる(ついでにslackに通知)
- CloudWatchMetricsの値をダッシュボードで表示
Terraformのコード
# autoscalingの生成/削除イベントを拾う resource "aws_cloudwatch_event_rule" "rds_autoscale" { name = "rds-autoscale" event_pattern = jsonencode( { source = ["aws.rds"] detail = { EventCategories = ["creation", "deletion"] SourceIdentifier = [{ prefix = "application-autoscaling-" }] } } ) } # イベントとlambdaを紐づける resource "aws_cloudwatch_event_target" "rds_autoscale" { rule = aws_cloudwatch_event_rule.rds_autoscale.name arn = aws_lambda_function.rds_autoscale.arn } # 実行するlambda resource "aws_lambda_function" "rds_autoscale" { function_name = "rds-autoscale" role = aws_iam_role.rds_autoscale_lambda.arn handler = "rds-autoscale.lambda_handler" runtime = "ruby3.3" timeout = 300 filename = data.archive_file.rds_autoscale_lambda.output_path source_code_hash = data.archive_file.rds_autoscale_lambda.output_base64sha256 depends_on = [ aws_cloudwatch_log_group.rds_autoscale_lambda ] tracing_config { mode = "Active" } environment { variables = { SLACK_WEBHOOK_URL = "https://hooks.slack.com/triggers/********" } } } # 一部割愛します # lambdaにわたす権限 data "aws_iam_policy_document" "rds_autoscale_lambda" { # ログ statement { sid = "CreateCloudWatchLogs" resources = ["arn:aws:sqs:${data.aws_region.current.name}:${data.aws_caller_identity.current.account_id}:log-group:*"] actions = [ "logs:CreateLogGroup", "logs:CreateLogStream", "logs:PutLogEvents" ] } # DBクラスタの情報を取得する権限 statement { sid = "RdsDescribeDBClusters" # tfsec:ignore:AVD-AWS-0057 resources = ["*"] actions = [ "rds:DescribeDBClusters" ] } # CloudWatchMetricsに値を送る権限 statement { sid = "PutCloudWatchMetrics" resources = ["*"] actions = [ "cloudwatch:PutMetricData" ] } }
lambdaのコード
require 'json' require 'net/http' require 'aws-sdk-rds' require 'aws-sdk-cloudwatch' def lambda_handler(event:, context:) puts "event: #{event.inspect}}" puts "context: #{context.inspect}" message = event.dig('detail', 'Message') autoscaling_resourceId = event.dig('detail', 'Tags', 'application-autoscaling:resourceId') # 現状のインスタンス数を確認して、chatbot経由で通知 cluster_name = autoscaling_resourceId.split(':').last puts "cluster_name: #{cluster_name}" rds_client = Aws::RDS::Client.new response = rds_client.describe_db_clusters({ db_cluster_identifier: cluster_name }) puts "response: #{response.inspect}" db_cluster = response.db_clusters.first db_cluster_members_count = 0 if db_cluster db_cluster_members_count = db_cluster.db_cluster_members.count - 1 puts "db_cluster_members_count: #{db_cluster_members_count}" # CloudWatchメトリクスに送信 cloudwatch_client = Aws::CloudWatch::Client.new cloudwatch_client.put_metric_data({ namespace: 'AWS/RDS', metric_data: [ { metric_name: 'ClusterMembersCount', value: db_cluster_members_count, unit: 'Count', dimensions: [ { name: 'DBClusterIdentifier', value: cluster_name } ] } ] }) end response = slack_publish(cluster_name: cluster_name, message: message, cluster_members_count: db_cluster_members_count) puts "slack_publish response: #{response.inspect}" { event: JSON.generate(event), context: JSON.generate(context.inspect) } end def slack_publish(cluster_name:, message:, cluster_members_count:) uri = URI(ENV['SLACK_WEBHOOK_URL']) http = Net::HTTP.new(uri.host, uri.port) http.use_ssl = true request = Net::HTTP::Post.new(uri.path, { 'Content-Type' => 'application/json' }) request.body = { cluster_name: cluster_name, message: message, cluster_members_count: cluster_members_count.to_s }.to_json http.request(request) end
以上で、RDSのリードレプリカ数がCloudWatchMetricsに格納されるようになります!
結果
CloudWatchのダッシュボードでも、グラフが確認できるようになりました!
レプリケーション発生時に値を送っているだけなので、ちょっと見づらいですが、今は実用上十分なのでこれでOKです。
これだと見づらい! という人は、cronでもレプリカの数を CloudWatchMetrics に送ると良いと思います。
まとめ
このように、「CloudWatchMetricsに値がない! 」という時にも諦めずに、メトリクスを自作すれば、グラフ化による可視化、その値をトリガーとしたイベントの発火など、自由に活用することが可能です。
弊社でも、ECS タスク内の Nginx リクエスト数を CloudWatch Metrics に渡し、一定のリクエスト数を超えたら自動でスケールするような構成を採用しています。
本記事が皆様の監視ライフのQOL向上の一助になりましたら幸いです。