AWS Compute Optimizer のレコメンデーションを AWS CLI で CSV 出力してみた

AWS Compute Optimizer のレコメンデーションはエクスポートが可能ですが、エクスポート機能を使わずに出力させてみました。

コンバンハ、千葉(幸)です。

AWS Compute Optimizer は、 AWS リソースの設定と使用率のメトリクスを分析し、ワークロードの最適利用を推奨してくれるサービスです。

一定期間 継続稼働している以下リソースに対して、インスタンスタイプやボリュームタイプといったリソースのプロビジョニングが最適かどうかを表示してくれます。

  • EC2 インスタンス
  • AutoScaling グループ
  • EBS ボリューム
  • Lambda 関数

分析結果の推奨事項であるレコメンデーションはマネジメントコンソールから確認するのがお手軽ですが、報告用に別の形式にして出力したい数が多いので必要な情報に絞って出力したい、という場面があるかもしれません。

そんな時はそう……、AWS CLI の出番ですね。

どのコマンドを叩くとどのような結果が返ってくるか、必要な情報だけに絞りたい時にはどう指定したらいいかの使い心地を確認していきます。

AWS Compute Optimizer レコメンデーションのエクスポート

AWS CLI の出番、と言っておきながらいきなり翻すようですが、レコメンデーションはコンソールから csv 形式でエクスポートすることもできます。

ポチッと押せばダウンロードが始まる……わけでなく、出力先の S3 バケットや結果に含める項目を選択する必要があります。

特に S3 バケットは、バケットポリシーで以下のようにサービスプリンシパルcompute-optimizer.amazonaws.comからのアクセスを許可しておく必要があります。

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Principal": {"Service": "compute-optimizer.amazonaws.com"},
            "Action": "s3:GetBucketAcl",
            "Resource": "arn:aws:s3:::myBucketName"
        },
        {
            "Effect": "Allow",
            "Principal": {"Service": "compute-optimizer.amazonaws.com"},
            "Action": "s3:GetBucketPolicyStatus",
            "Resource": "arn:aws:s3:::myBucketName"
        },
        {
            "Effect": "Allow",
            "Principal": {"Service": "compute-optimizer.amazonaws.com"},
            "Action": "s3:PutObject",
            "Resource": "arn:aws:s3:::myBucketName/[optional prefix]/compute-optimizer/myAccountID/*",
            "Condition": {"StringEquals": {
                    "s3:x-amz-acl": "bucket-owner-full-control",
                    "aws:SourceAccount": "myAccountID",
                    "aws:SourceArn": "arn:aws:compute-optimizer:myRegion:myAccountID:*"
                }
            }
        }
    ]
}

のっぴきならない事情があって、バケットポリシーを修正したり新規の S3 バケットを作成したりが気軽にできない環境もあることでしょう。そんなケースを想定し、別のアプローチを取ることにします。

aws compute-optimizer get-xxx-recommendations を使う

AWS CLI の aws compute-optimizer コマンドでは、 recommendation を Get するためのサブコマンドが各種リソースごとに用意されています。

今回は、直近で私が試す機会があった以下の 3 つを取り上げます。

  • get-ec2-instance-recommendations
  • get-ebs-volume-recommendations
  • get-auto-scaling-group-recommendations

(Lambda 関数はいいサンプルがありませんでした、、)

使用している AWS CLI のバージョンは以下です。

$ aws --version
aws-cli/2.2.39 Python/3.8.8 Darwin/20.6.0 exe/x86_64 prompt/off

AWS CLI による出力結果を jq で csv 形式に変換してみます。

get-ec2-instance-recommendations

リファレンスはこちら。

アウトプットの例は以下です。

折り畳み
$ aws compute-optimizer get-ec2-instance-recommendations
{
    "instanceRecommendations": [
        {
            "instanceArn": "arn:aws:ec2:ap-northeast-1:012345678910:instance/i-0000000000000000",
            "accountId": "012345678910",
            "instanceName": "EXAMPLENAME",
            "currentInstanceType": "m5.large",
            "finding": "OVER_PROVISIONED",
            "findingReasonCodes": [
                "CPUOverprovisioned",
                "EBSIOPSOverprovisioned",
                "EBSThroughputOverprovisioned",
                "NetworkBandwidthOverprovisioned",
                "NetworkPPSOverprovisioned"
            ],
            "utilizationMetrics": [
                {
                    "name": "CPU",
                    "statistic": "MAXIMUM",
                    "value": 7.399999999999999
                },
                {
                    "name": "MEMORY",
                    "statistic": "MAXIMUM",
                    "value": 10.202603632391153
                },
                {
                    "name": "EBS_READ_OPS_PER_SECOND",
                    "statistic": "MAXIMUM",
                    "value": 0.013333333333333334
                },
                {
                    "name": "EBS_WRITE_OPS_PER_SECOND",
                    "statistic": "MAXIMUM",
                    "value": 4.253333333333333
                },
                {
                    "name": "EBS_READ_BYTES_PER_SECOND",
                    "statistic": "MAXIMUM",
                    "value": 29.296875
                },
                {
                    "name": "EBS_WRITE_BYTES_PER_SECOND",
                    "statistic": "MAXIMUM",
                    "value": 827996.4192708334
                },
                {
                    "name": "NETWORK_IN_BYTES_PER_SECOND",
                    "statistic": "MAXIMUM",
                    "value": 16982.868055555555
                },
                {
                    "name": "NETWORK_OUT_BYTES_PER_SECOND",
                    "statistic": "MAXIMUM",
                    "value": 96.98083333333334
                },
                {
                    "name": "NETWORK_PACKETS_IN_PER_SECOND",
                    "statistic": "MAXIMUM",
                    "value": 2.447388888888889
                },
                {
                    "name": "NETWORK_PACKETS_OUT_PER_SECOND",
                    "statistic": "MAXIMUM",
                    "value": 0.4082777777777778
                }
            ],
            "lookBackPeriodInDays": 14.0,
            "recommendationOptions": [
                {
                    "instanceType": "t3.micro",
                    "projectedUtilizationMetrics": [
                        {
                            "name": "CPU",
                            "statistic": "MAXIMUM",
                            "value": 7.399999999999999
                        },
                        {
                            "name": "MEMORY",
                            "statistic": "MAXIMUM",
                            "value": 81.62082905912922
                        }
                    ],
                    "performanceRisk": 3.0,
                    "rank": 1
                },
                {
                    "instanceType": "t3.small",
                    "projectedUtilizationMetrics": [
                        {
                            "name": "CPU",
                            "statistic": "MAXIMUM",
                            "value": 7.399999999999999
                        },
                        {
                            "name": "MEMORY",
                            "statistic": "MAXIMUM",
                            "value": 40.81041452956461
                        }
                    ],
                    "performanceRisk": 3.0,
                    "rank": 2
                },
                {
                    "instanceType": "t3.medium",
                    "projectedUtilizationMetrics": [
                        {
                            "name": "CPU",
                            "statistic": "MAXIMUM",
                            "value": 7.399999999999999
                        },
                        {
                            "name": "MEMORY",
                            "statistic": "MAXIMUM",
                            "value": 20.405207264782305
                        }
                    ],
                    "performanceRisk": 3.0,
                    "rank": 3
                }
            ],
            "recommendationSources": [
                {
                    "recommendationSourceArn": "arn:aws:ec2:ap-northeast-1:012345678910:instance/i-0000000000000000",
                    "recommendationSourceType": "Ec2Instance"
                }
            ],
            "lastRefreshTimestamp": "2021-08-13T18:26:26.010000+09:00"
        },
........
   ],
    "errors": []
}

--filtersで使えるnamevaluseの組み合わせは以下の通りです。

name values
Finding Underprovisioned , Overprovisioned , Optimized
RecommendationSourceType Ec2Instance , AutoScalingGroup
FindingReasonCodes 多数(以下参照)

FindingReasonCodesの値として指定できるコードは以下です。

CPUOverprovisioned
CPUUnderprovisioned
MemoryOverprovisioned
MemoryUnderprovisioned
EBSThroughputOverprovisioned
EBSThroughputUnderprovisioned
EBSIOPSOverprovisioned
EBSIOPSUnderprovisioned
NetworkBandwidthOverprovisioned
NetworkBandwidthUnderprovisioned
NetworkPPSOverprovisioned
NetworkPPSUnderprovisioned
DiskIOPSOverprovisioned
DiskIOPSUnderprovisioned
DiskThroughputOverprovisioned
DiskThroughputUnderprovisioned

コマンド例

「最適化済み」以外のレコメンデーションを以下項目のみ表示する場合の例です。

  • インスタンス ARN
  • インスタンス名
  • 現在のインスタンスタイプ
  • 結果
  • 推奨のインスタンスタイプ(オプション1)
$ aws compute-optimizer get-ec2-instance-recommendations\
  --filters name=Finding,values=Underprovisioned,Overprovisioned\
   | jq -r '
    ["インスタンスARN","インスタンス名","現在のインスタンスタイプ","結果","推奨のインスタンスタイプ"],
    (.instanceRecommendations[] | [.instanceArn,.instanceName,.currentInstanceType,.finding,.recommendationOptions[0].instanceType]) | @csv' > result.csv

CSV ヘッダレコードは 4 行目で任意のものを指定しているため、省略・変更が可能です。

出力結果の例は以下の通りです。

get-ebs-volume-recommendations

リファレンスはこちら。

アウトプットの例は以下です。

折り畳み
$ aws compute-optimizer get-ebs-volume-recommendations
{
    "volumeRecommendations": [
        {
            "volumeArn": "arn:aws:ec2:ap-northeast-1:012345678910:volume/vol-0d0b8ddxxxxxxxxxx",
            "accountId": "012345678910",
            "currentConfiguration": {
                "volumeType": "gp3",
                "volumeSize": 800,
                "volumeBaselineIOPS": 3000,
                "volumeBurstIOPS": 3000,
                "volumeBaselineThroughput": 250,
                "volumeBurstThroughput": 250
            },
            "finding": "NotOptimized",
            "utilizationMetrics": [
                {
                    "name": "VolumeReadOpsPerSecond",
                    "statistic": "Maximum",
                    "value": 2464.34
                },
                {
                    "name": "VolumeWriteOpsPerSecond",
                    "statistic": "Maximum",
                    "value": 615.7633333333333
                },
                {
                    "name": "VolumeReadBytesPerSecond",
                    "statistic": "Maximum",
                    "value": 226064433.59375
                },
                {
                    "name": "VolumeWriteBytesPerSecond",
                    "statistic": "Maximum",
                    "value": 16606290.690104166
                }
            ],
            "lookBackPeriodInDays": 14.0,
            "volumeRecommendationOptions": [
                {
                    "configuration": {
                        "volumeType": "gp3",
                        "volumeSize": 800,
                        "volumeBaselineIOPS": 3000,
                        "volumeBurstIOPS": 3000,
                        "volumeBaselineThroughput": 145,
                        "volumeBurstThroughput": 145
                    },
                    "performanceRisk": 2.0,
                    "rank": 1
                },
                {
                    "configuration": {
                        "volumeType": "gp3",
                        "volumeSize": 800,
                        "volumeBaselineIOPS": 3000,
                        "volumeBurstIOPS": 3000,
                        "volumeBaselineThroughput": 177,
                        "volumeBurstThroughput": 177
                    },
                    "performanceRisk": 1.0,
                    "rank": 2
                }
            ],
            "lastRefreshTimestamp": "2021-09-18T18:20:52.372000+09:00"
        },
        ......
    ],
    "errors": [
        {
            "identifier": "arn:aws:ec2:ap-northeast-1:012345678910:volume/vol-010xxxxxxxxxx",
            "code": "UNSUPPORTED_CONFIGURATION",
            "message": "Unsupported volume type with type sc1."
        }
    ]
}

--filtersで使えるnamevaluseの組み合わせは以下の通りです。

name values
Finding Optimized , NotOptimized

コマンド例

「最適化されていない」レコメンデーションの以下項目を表示する場合のコマンド例は以下です。

  • ボリューム ARN
  • 現在の設定
    • ボリュームタイプ
    • ボリュームサイズ
    • ボリュームベースライン IOPS
    • ボリュームバースト IOPS
    • ボリュームベースラインスループット
    • ボリュームバーストスループット
  • 推奨の設定(オプション1)
    • ボリュームタイプ
    • ボリュームサイズ
    • ボリュームベースライン IOPS
    • ボリュームバースト IOPS
    • ボリュームベースラインスループット
    • ボリュームバーストスループット
$ aws compute-optimizer get-ebs-volume-recommendations\
  --filters name=Finding,values=NotOptimized\
   | jq -r '
    (.volumeRecommendations[] | [.volumeArn,.currentConfiguration[],.finding,.volumeRecommendationOptions[0].configuration[]]) | @csv' | pbcopy

項目数が多いのでヘッダーレコードの指定は省略しました。また、わたしは Mac を使用しているため pbcopy を用いてクリップボードにコピーする、というパターンもよく使用します。

出力結果の例は以下の通りです。

get-auto-scaling-group-recommendations

リファレンスはこちら。

アウトプットの例は以下です。

折り畳み
$ aws compute-optimizer get-auto-scaling-group-recommendations
{
    "autoScalingGroupRecommendations": [
        {
            "accountId": "012345678910",
            "autoScalingGroupArn": "arn:aws:autoscaling:ap-northeast-1:012345678910:autoScalingGroup:562xxxxx-fxxx-4xxx-9xxx-fbaxxxxxxxx:autoScalingGroupName/EXAMPLENAME",
            "autoScalingGroupName": "EXAMPLENAME",
            "finding": "OPTIMIZED",
            "utilizationMetrics": [
                {
                    "name": "CPU",
                    "statistic": "MAXIMUM",
                    "value": 41.825
                },
                {
                    "name": "EBS_READ_OPS_PER_SECOND",
                    "statistic": "MAXIMUM",
                    "value": 0.04
                },
                {
                    "name": "EBS_WRITE_OPS_PER_SECOND",
                    "statistic": "MAXIMUM",
                    "value": 5.4
                },
                {
                    "name": "EBS_READ_BYTES_PER_SECOND",
                    "statistic": "MAXIMUM",
                    "value": 768.2291666666666
                },
                {
                    "name": "EBS_WRITE_BYTES_PER_SECOND",
                    "statistic": "MAXIMUM",
                    "value": 860442.7083333334
                },
                {
                    "name": "NETWORK_IN_BYTES_PER_SECOND",
                    "statistic": "MAXIMUM",
                    "value": 23469.210000000003
                },
                {
                    "name": "NETWORK_OUT_BYTES_PER_SECOND",
                    "statistic": "MAXIMUM",
                    "value": 19396.498055555556
                },
                {
                    "name": "NETWORK_PACKETS_IN_PER_SECOND",
                    "statistic": "MAXIMUM",
                    "value": 24.26527777777778
                },
                {
                    "name": "NETWORK_PACKETS_OUT_PER_SECOND",
                    "statistic": "MAXIMUM",
                    "value": 22.950833333333332
                }
            ],
            "lookBackPeriodInDays": 14.0,
            "currentConfiguration": {
                "desiredCapacity": 1,
                "minSize": 1,
                "maxSize": 1,
                "instanceType": "m5.large"
            },
            "recommendationOptions": [
                {
                    "configuration": {
                        "desiredCapacity": 1,
                        "minSize": 1,
                        "maxSize": 1,
                        "instanceType": "m5.large"
                    },
                    "projectedUtilizationMetrics": [
                        {
                            "name": "CPU",
                            "statistic": "MAXIMUM",
                            "value": 41.825
                        }
                    ],
                    "performanceRisk": 1.0,
                    "rank": 1
                },
                {
                    "configuration": {
                        "desiredCapacity": 1,
                        "minSize": 1,
                        "maxSize": 1,
                        "instanceType": "m5n.large"
                    },
                    "projectedUtilizationMetrics": [
                        {
                            "name": "CPU",
                            "statistic": "MAXIMUM",
                            "value": 41.825
                        }
                    ],
                    "performanceRisk": 1.0,
                    "rank": 2
                },
                {
                    "configuration": {
                        "desiredCapacity": 1,
                        "minSize": 1,
                        "maxSize": 1,
                        "instanceType": "m5zn.large"
                    },
                    "projectedUtilizationMetrics": [
                        {
                            "name": "CPU",
                            "statistic": "MAXIMUM",
                            "value": 30.061718749999997
                        }
                    ],
                    "performanceRisk": 1.0,
                    "rank": 3
                }
            ],
            "lastRefreshTimestamp": "2021-09-18T18:03:51.563000+09:00"
        },
        ......
    ],
    "errors": []
}

--filtersで使えるnamevaluseの組み合わせは以下の通りです。

name values
Finding Optimized , NotOptimized
RecommendationSourceType Ec2Instance , AutoScalingGroup

リファレンスではFindingReasonCodesを使用できるとありますが、AWS CLI の v2.2.39 では以下のようなエラーが発生しました。

$ aws compute-optimizer get-auto-scaling-group-recommendations\
  --filters name=FindingReasonCodes,values=CPUOverprovisioned

An error occurred (InvalidParameterValueException) when calling the GetAutoScalingGroupRecommendations operation: Invalid filter value.

アウトプットには get-ec2-instance-recommendations と異なりfindingReasonCodesが含まれていないので、現状のバージョンでは使用できないものと思います。

コマンド例

すべてのレコメンデーションに対して以下項目を出力する場合の例です。

  • AutoScaling名
  • 結果
  • 現状の設定
    • 希望の容量
    • 最小台数
    • 最大台数
    • インスタンスタイプ
  • 推奨の設定(オプション1)
    • 希望の容量
    • 最小台数
    • 最大台数
    • インスタンスタイプ
$ aws compute-optimizer get-auto-scaling-group-recommendations\
   | jq -r ".autoScalingGroupRecommendations[] | [.autoScalingGroupName,.finding,.currentConfiguration[],.recommendationOptions[0].configuration[]] | @csv" | pbcopy

出力結果の例は以下です。

余談ですが、Compute Opimizer による分析対象となる AutoScaling グループは 希望の容量/最小台数/最大台数 が同一のものである必要があります。

こういった要件は以下から確認できます。

終わりに

AWS Compute Optimizer レコメンデーションを AWS CLI で出力してみました。jq と組み合わせることで、csv 形式での出力にも対応できます。

単発での実行を考えると、わざわざコマンドを組み立てるよりはマネジメントコンソールからエクスポートした方がお手軽かと思います。

エクスポート先の S3 バケットを準備するのにハードルがある、何度も実行したいため都度エクスポートしたくない、という場合には本エントリのコマンドをご参考ください。

以上、 チバユキ (@batchicchi) がお送りしました。