Contents
はじめに
前回より一歩踏み込んでaws-sdk-goのリファレンスを見てGetMetricStatisticsを使ってみます。
aws-sdk-goを使用するのに注意することとして、こちらのサイト様が参考になりました。
ちなみに私はgolang歴2ヶ月程度のド素人かつにわかです。↓で偉そうに書いてますがそんなやつが書いてると思ってください。
さらに私はぷろぐらむを書くのが上手ではありませんorz
リファレンスを見てみる
cloudwatchのGetMetricStatisticsについてリファレンスを見てみます。前回使ったやつですね。
該当箇所を見るとこんな風に書いてあります。
func (c *CloudWatch) GetMetricStatistics(input *GetMetricStatisticsInput) (*GetMetricStatisticsOutput, error)
*GetMetricStatisticsInput型を用意してGetMetricStatisticsを呼び出せば*GetMetricStatisticsOutput型とerrorが返ってくるということでしょうかね。メソッドとして使えばいいのか。
GetMetricStatisticsInputを見てみる
GetMetricStatisticsを使うのに*GetMetricStatisticsInputが必要だとわかったので*GetMetricStatisticsInputを見てみます。
ちょっと長いのでコメントアウト箇所を割愛したものがこちら
※このコメントアウトが大事なことがたくさん書いてあるので使うときは熟読した方がいいです。特にStartTimeとPeriod。
type GetMetricStatisticsInput struct {
Dimensions []*Dimension
type:"list"
EndTime *time.Time
type:"timestamp" required:"true"
ExtendedStatistics []*string
min:"1" type:"list"
MetricName *string
min:"1" type:"string" required:"true"
Namespace *string
min:"1" type:"string" required:"true"
Period *int64
min:"1" type:"integer" required:"true"
StartTime *time.Time
type:"timestamp" required:"true"
Statistics []*string
min:"1" type:"list"
Unit *string
type:"string" enum:"StandardUnit"
}
Dimensionsなら[]*Dimension型
EndTimeなら*time.Time型
飛んで
Unitなら*string型をそれぞれ用意してあげればいいということでしょうかね。
最低限required:”true”の項目だけでもOKっぽい。
Dimensionsは[]*DimensionなのでDimensionのstructも確認します。
type Dimension struct {
// The name of the dimension.
//
// Name is a required field
Name *stringmin:"1" type:"string" required:"true"
// The value representing the dimension measurement.
//
// Value is a required field
Value *stringmin:"1" type:"string" required:"true"
// contains filtered or unexported fields
}
Nameという*string型とValueという*string型がそれぞれrequired:”true”だから必須ですね。
結果であるGetMetricStatisticsOutputを見てみる
inputの次は取得できるoutputを見てみます。
type GetMetricStatisticsOutput struct {
// The data points for the specified metric.
Datapoints []*Datapointtype:"list"
// A label for the specified metric.
Label *stringtype:"string"
// contains filtered or unexported fields
}
DatapointsとLabelが入ったstructがもらえるということですかね。
これDatapointの中身も見ないとわからないな。。ということで↓
type Datapoint struct {
// The average of the metric values that correspond to the data point.
Average *float64type:"double"
// The percentile statistic for the data point.
ExtendedStatistics map[string]*float64type:"map"
// The maximum metric value for the data point.
Maximum *float64type:"double"
// The minimum metric value for the data point.
Minimum *float64type:"double"
// The number of metric values that contributed to the aggregate value of this
// data point.
SampleCount *float64type:"double"
// The sum of the metric values for the data point.
Sum *float64type:"double"
// The time stamp used for the data point.
Timestamp *time.Timetype:"timestamp"
// The standard unit for the data point.
Unit *stringtype:"string" enum:"StandardUnit"
// contains filtered or unexported fields
}
いろんなmetricがあるけどとりあえずAverageだけでいいや。
試してみる
コード1
前回のものから一部変更したものがこちら。
追記
session部分を公式のものに変更しました。
cloudwatch.goと名づけました。DimensionsにあるValueはEC2のインスタンス IDなのでぼかしています。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 |
package main import ( "fmt" "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/aws/session" "github.com/aws/aws-sdk-go/service/cloudwatch" "time" ) func main() { sess := session.Must(session.NewSession(&aws.Config{ Region: aws.String("ap-northeast-1"), })) svc := cloudwatch.New(sess) params := &cloudwatch.GetMetricStatisticsInput{ Dimensions: []*cloudwatch.Dimension{ { Name: aws.String("InstanceId"), Value: aws.String("i-xxxxxxxxxxxx"), }, }, EndTime: aws.Time(time.Now().UTC()), MetricName: aws.String("CPUUtilization"), Namespace: aws.String("AWS/EC2"), Period: aws.Int64(60), StartTime: aws.Time(time.Now().UTC().Add(time.Duration(5) * time.Minute * -1)), Statistics: []*string{ aws.String("Average"), }, Unit: aws.String(cloudwatch.StandardUnitPercent), } resp, _ := svc.GetMetricStatistics(params) fmt.Println("resp:", resp) } |
params := &cloudwatch.GetMetricStatisticsInput{}でGetMetricStatisticsInputを作っている。MetricNameはstring型で渡すのでaws.Stringにstringを渡して*stringに変換している様子。
ちなみに上の例だと5分前(StartTimeが5分前)から60秒間隔(Period)でデータを取ろうとしてます。
コード1結果
go run cloudwatch.goで実行した結果がこちら。
1 2 3 4 5 |
% go run cloudwatch.go resp: { Label: "CPUUtilization" } |
?LabelがあるけどDatapointsがない?
そんなばな(ry
データ取得できる間隔の違いがあることを知る
EC2の基本モニタリングだと5分ごとにしかデータポイントが出来ないそうで、お金払って詳細モニタリングにすれば1分間隔でデータポイントが出来るらしい。
そしてこのデータポイントをまたがないといけないそうで。
火の車家計の我が家に詳細モニタリングを試す余裕があるわけないので詳細モニタリングは却下。取得間隔を長くしてみます。
先ほどのcloudwatch.goのStartTimeを5分から10分に変更し、periodも60秒から300秒に変更して再トライ。
1 2 3 4 5 6 7 8 9 10 |
% go run oldcloudwatch.go resp: { Datapoints: [{ Average: 0.36670371399474, Timestamp: 2019-02-11 06:43:00 +0000 UTC, Unit: "Percent" }], Label: "CPUUtilization" } |
Datapointsの中にAverageがあり数値も取れてますね!やったぜ。
魔改造版
10分に変更すると↓こんな感じで2つデータが取れてしまうことがありました。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
% go run oldcloudwatch.go resp: { Datapoints: [{ Average: 0.36670371399474, Timestamp: 2019-02-11 06:43:00 +0000 UTC, Unit: "Percent" },{ Average: 1.1316939890709121, Timestamp: 2019-02-11 06:48:00 +0000 UTC, Unit: "Percent" }], Label: "CPUUtilization" } |
1つだけデータが欲しいときにこれだと困るので魔改造してみることに。
コード2
ちょっとお遊びでデータが1個だけ取れるようにしてみたのがこちら
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 |
package main import ( "fmt" "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/aws/session" "github.com/aws/aws-sdk-go/service/cloudwatch" "time" ) func main() { sess := session.Must(session.NewSession(&aws.Config{ Region: aws.String("ap-northeast-1"), })) svc := cloudwatch.New(sess) params := &cloudwatch.GetMetricStatisticsInput{ Dimensions: []*cloudwatch.Dimension{ { Name: aws.String("InstanceId"), Value: aws.String("i-xxxxxxxxxxxxx"), }, }, EndTime: aws.Time(time.Now().UTC()), MetricName: aws.String("CPUUtilization"), Namespace: aws.String("AWS/EC2"), Period: aws.Int64(300), StartTime: aws.Time(time.Now().UTC().Add(time.Duration(15) * time.Minute * -1)), Statistics: []*string{ aws.String("Average"), }, Unit: aws.String(cloudwatch.StandardUnitPercent), } resp, _ := svc.GetMetricStatistics(params) for i := 14; len(resp.Datapoints) >= 2; i-- { params.StartTime = aws.Time(time.Now().UTC().Add(time.Duration(i) * time.Minute * -1)) resp, _ = svc.GetMetricStatistics(params) if len(resp.Datapoints) >= 2 { continue } fmt.Println("i:", i) fmt.Println("resp:", resp) } } |
resp, _ := svc.GetMetricStatistics(params)以下を変更しました。resp.Datapointsの大きさが1以下になるまで15分から1分ずつStartTimeを下げてみました。continueはforを次のループに進めてくれる便利なやつです。少しでもnestしないようにこの頃愛用してます。
実は初めはStartTime5分くらいからresp.Datapointsがnilじゃなくなるまで1分ずつStartTimeを上げるものを作ったんですが、もしも「forループの条件式とかミスしている」だとか「別の理由でresp.Datapointsがずーとnil」だと、青天井で永遠にAPIを叩くことになりawsの請求金額がやばいことになりそう?だったのでこんな形にしてみました。
そういう意味ではクラウドのAPIを叩くものにforループを使うのは危険極まりない気もするのでやろうとする方は十分気をつけて自己責任でお願いします。
GetMetricStatistics、ListMetrics、PutMetricData、GetDashboard、ListDashboards、PutDashboard、DeleteDashboards のリクエスト 1,000 件あたり 0.01 USD
やっぱお金かかるよね。不具合で高額請求された方もいらっしゃるようなので注意。
コード2結果
こちらを実行した結果がこちら。
1 2 3 4 5 6 7 8 9 10 11 |
% go run cloudwatch2.go i: 10 resp: { Datapoints: [{ Average: 0.39785125497849, Timestamp: 2019-02-11 11:24:00 +0000 UTC, Unit: "Percent" }], Label: "CPUUtilization" } |
StartTime10分で1つだけのデータが取れたことがわかります。
補足
このデータポイントのプッシュ間隔はserviceやmetricsごとに変わようです。
dynamodbだとmetricsにより1分間隔と5分間隔がある様子。
ConsumedReadCapacityUnits、ConsumedWriteCapacityUnitsは1分間隔
ProvisionedReadCapacityUnits、ProvisionedWriteCapacityUnitsは5分間隔
kinesisは1分間隔
RDSも1分間隔
最後に
cloudwatchのGetMetricStatisticsについてリファレンスを見て色々試してみました。
やっぱりAPIは便利ですけどfor使うときは怖いですね。。皆さんどうしてるんですかね?forとかループ系使わないものなんですかね。
コメント