GoのソースをMacでクロスコンパイルし、CentOS5でCloudWatchのカスタムメトリクスを取得する

2015.02.18

はじめに

最近、お気に入りの札幌ランチは炎のランチバイキングの横山です。 以下の記事を参考にCloudWatchのカスタムメトリクスを登録スクリプトをGoで作ったものをMacでビルドを行い、CentOS5で実行するまでの手順をまとめました。

前提

  • ビルドするMac
$ sw_vers -productVersion
10.10.2
$ brew --version
0.9.5
  • 実行するCentOS5
    • IAM Roleなし
    • Goをビルドする環境なし
    • PutMetricsDataを行う権限あり
$ uname -a
Linux ip-xxx-xxx-xxx-xxx 2.6.21.7-2.fc8xen #1 SMP Fri Feb 15 12:34:28 EST 2008 x86_64 x86_64 x86_64 GNU/Linux
$ cat /etc/redhat-release
CentOS release 5.4 (Final)

今回準備したソース

  • putMetricsData.go
package main

import (
  "fmt"
  "strings"
  "flag"
  "os"

  "github.com/awslabs/aws-sdk-go/aws"
  "github.com/awslabs/aws-sdk-go/gen/cloudwatch"
)

func main() {
  var nameSpace, metricName, unit, region, dimensions string
  var value float64

  accessKey     := os.Getenv("AWS_ACCESS_KEY_ID")
  secretKey     := os.Getenv("AWS_SECRET_ACCESS_KEY")

  if accessKey == "" || secretKey == "" {
    fmt.Println("Error: Undefined AWS_ACCESS_KEY_ID or AWS_SECRET_ACCESS_KEY")
    os.Exit(9)
  }
  creds := aws.Creds( accessKey,
                      secretKey,
                      "" )


  flag.StringVar(&nameSpace, "namespace", "", "")
  flag.StringVar(&metricName, "metric-name", "", "")
  flag.StringVar(&unit,"unit", "", "")
  flag.StringVar(&region, "region", "", "")
  flag.StringVar(&dimensions, "dimensions", "", "")
  flag.Float64Var(&value, "value", 0, "")
  flag.Parse()

  if len(dimensions) == 0 {
    fmt.Println("error: --dimensions value empty!")
    return
  }

  cli := cloudwatch.New(creds, region, nil)

  dimension_strings := strings.Split(dimensions, ",")
  dimension_items := make([]cloudwatch.Dimension, len(dimension_strings))
  for index, dimension_string := range dimension_strings {
    var items = strings.SplitN(dimension_string, "=", 2)
    if len(items) > 1 {
      dimension_items[index].Name  = aws.String(items[0])
      dimension_items[index].Value = aws.String(items[1])
    }
  }

  metricDataParam := &cloudwatch.MetricDatum {
    Dimensions: dimension_items,
    MetricName: aws.String(metricName),
    Unit:       aws.String(unit),
    Value:      aws.Double(value),
  }

  putMetricDataInput := &cloudwatch.PutMetricDataInput{
    MetricData: []cloudwatch.MetricDatum{*metricDataParam},
    Namespace:  aws.String(nameSpace),
  }

  fmt.Println(cli.PutMetricData(putMetricDataInput))
}
  • memoryUsage.sh
#!/bin/bash

export AWS_ACCESS_KEY_ID="AKIAxxxxxxxxxxxxxxxx"
export AWS_SECRET_ACCESS_KEY="xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"

program="./putMetricsData"
usage=$(free | head -n 3 | tail -n 1 | awk '{a = $3/($3 + $4)*100; print a}')
instanceId=$(curl -s http://169.254.169.254/latest/meta-data/instance-id)
region=$(curl -s http://169.254.169.254/latest/meta-data/placement/availability-zone | sed -e 's/.$//')

cw_opts="--namespace Classmethod/EC2"
cw_opts=${cw_opts}" --metric-name MemoryUtilization"
cw_opts=${cw_opts}" --dimensions InstanceId=${instanceId}"
cw_opts=${cw_opts}" --unit Percent"
cw_opts=${cw_opts}" --region ${region}"
cw_opts=${cw_opts}" --value ${usage}"


${program} ${cw_opts}

クロスコンパイルするための準備

すでにgoをインストールしている場合、クロスコンパイルを行おうとすると次の様なエラーが出ると思います。

$ GOOS=linux GOARCH=amd64 go build putMetricsData.go
go build runtime: linux/amd64 must be bootstrapped using make.bash

手動でできるかもですが、楽するために一度brewからGoをアンインストールし、 --with-cc-allオプション使ってインストールし直します。

$ brew uninstall go
Uninstalling /usr/local/Cellar/go/1.4.1...
$ brew install go --with-cc-all
==> Downloading https://storage.googleapis.com/golang/go1.4.1.src.tar.gz
######################################################################## 100.0%
==> ./make.bash --no-clean
==> ./make.bash --no-clean
==> ./make.bash --no-clean
==> ./make.bash --no-clean
==> ./make.bash --no-clean
==> ./make.bash --no-clean
==> ./make.bash --no-clean
==> ./make.bash --no-clean
==> ./make.bash --no-clean
==> ./make.bash --no-clean
==> ./make.bash --no-clean
==> ./make.bash --no-clean
==> ./make.bash --no-clean
==> ./make.bash --no-clean
==> ./make.bash --no-clean
==> ./make.bash --no-clean
==> ./make.bash --no-clean
==> ./make.bash --no-clean
==> ./make.bash --no-clean
==> ./make.bash --no-clean
==> Caveats
As of go 1.2, a valid GOPATH is required to use the `go get` command:
  http://golang.org/doc/code.html#GOPATH

`go vet` and `go doc` are now part of the go.tools sub repo:
  http://golang.org/doc/go1.2#go_tools_godoc

To get `go vet` and `go doc` run:
  go get golang.org/x/tools/cmd/vet
  go get golang.org/x/tools/cmd/godoc

You may wish to add the GOROOT-based install location to your PATH:
  export PATH=$PATH:/usr/local/opt/go/libexec/bin
==> Summary
瑳  /usr/local/Cellar/go/1.4.1: 7696 files, 1.5G, built in 19.3 minutes

参考

ビルド

  • Mac上
$ GOOS=linux GOARCH=amd64 go build putMetricsData.go
$ file putMetricsData
putMetricsData: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), statically linked, not stripped

実行

scpやFileZillaなどつかってCentOS5にバイナリを持っていきます。

実行し、問題がなければ出力に<nil>が表示されます。(失敗、問題があるとエラーが表示されます)

$ sh memoryUsage.sh
<nil>

エラー例

  • PutMetricDataを行う権限がない場合
$ sh memoryUsage.sh
User: arn:aws:iam::xxxxxxxxxxxx:user/test_putmetrics is not authorized to perform: cloudwatch:PutMetricData
  • IAM Roleがない場合
$ sh memoryUsage.sh
decoding <?xml version="1.0" encoding="iso-8859-1"?> IAM credentials

まとめ

以上、CentOS5のみですがクロスコンパイルの実行が確認できました。 これにより、監視するインスタンスにGoをインストールせずにバイナリを設置するだけで、Python2.6が入ってなくても、aws-cliが入ってなくてもカスタムメトリクスをCloudWatchに登録することが出来そうです。

また、確認はしてませんがRaspberryPiにGoのビルド環境を導入せずとも、Macでビルド後RaspberryPi側にバイナリだけ持っていけば動くということも出来そうです(未確認)。