自分用のメモ
さくらのクラウドとMackerelの連携ツール「sackerel」を実装した時のメモ。
mackerel-agentの0.34.0にてAWSインテグレーションができるようになったそうです。
詳細はMackerelブログ8月19日の記事にありました。
エージェント側でどういう風にインテグレーションを行っているか気になったので調べました。
以下は執筆時点でのmasterブランチ最新コミット(cd6a67a)を元に調査しています。
今後の開発により処理方法が変更になる可能性があります。
ホスト情報の収集(command/command.go)
それらしいものが以下にありました。
引用元 : command/command.go#L472
// collectHostSpecs collects host specs (correspond to "name", "meta", "interfaces" and "customIdentifier" fields in API v0)
func collectHostSpecs() (string, map[string]interface{}, []spec.NetInterface, string, error) {
// (省略)
// 479行目〜
cGen := spec.SuggestCloudGenerator()
// (省略)
// 485行目〜
var customIdentifier string
if cGen != nil {
customIdentifier, err = cGen.SuggestCustomIdentifier()
if err != nil {
logger.Warningf("Error while suggesting custom identifier. err: %s", err.Error())
}
}
// (省略)
collectHostSpecs()
はMackerelエージェントがホストの情報を収集するためのもののようです。
その中でCustomIdentifire
という値の収集のためにspec.SuggestCloudGenerator()
を呼んでいます。
こいつのSuggestCustomIdentifier()
がCustomIdentifier
を返してくれるようですね。
CustomIdentifire
はマニュアルなどに記載はないですが、MackerelのホストIDとは別に
任意のIDを持たせるためのフィールドのようです。
GET /api/v0/hosts
などの、ホスト情報参照APIでもレスポンス内に値がきちんと含まれていました。
それでは呼ばれる先のspec.SuggestCloudGenerator()
を見てみましょう。
クラウド事業者ごとにCustomIdentifireの生成をする(spec/cloug.go)
引用元 : spec/cloud.go#L47
// SuggestCloudGenerator returns suitable CloudGenerator
func SuggestCloudGenerator() *CloudGenerator {
if isEC2() {
return &CloudGenerator{&EC2Generator{ec2BaseURL}}
}
if isGCE() {
return &CloudGenerator{&GCEGenerator{gceMetaURL}}
}
return nil
}
CustomIdentifire
を生成するためのジェネレータを作成して返すファクトリメソッドです。
ここでisEC2()
またはisGCE()
でプラットフォームを判定し、適切なジェネレータを作成しています。
もし対応クラウドプラットフォームを増やしたい場合はここに追加対応ですね!
(過去のソースにDigitalOcean対応の残骸があったりします)
引用元 : spec/cloud.go#L59
func isEC2() bool {
cl := http.Client{
Timeout: timeout,
}
// '/ami-id` is may be aws specific URL
// (筆者注 : ec2BaseURL := "http://169.254.169.254/latest/meta-data")
resp, err := cl.Get(ec2BaseURL.String() + "/ami-id")
if err != nil {
return false
}
defer resp.Body.Close()
return resp.StatusCode == 200
}
メタデータを取得して判定していますね。
EC2であるかの判定で、/ami-id
をリクエストすることでOpenStackなどと区別するようですね。
実際のCustomIdentifire
生成処理は以下にあります。
引用元 : spec/cloud.go#L156
// SuggestCustomIdentifier suggests the identifier of the EC2 instance
func (g *EC2Generator) SuggestCustomIdentifier() (string, error) {
client := http.Client{Timeout: timeout}
key := "instance-id"
resp, err := client.Get(g.baseURL.String() + "/" + key)
// (省略)
instanceID := string(body)
// (省略)
return instanceID + ".ec2.amazonaws.com", nil
}
メタデータからインスタンスIDを取得し、インスタンスID.ec2.amazonaws.com
を返すだけです。
AWSやGCE以外のクラウドプラットフォーム対応する場合はここも実装が必要ですね。
収集したCustomIdentifier
は以下でMackerelへ送信されます。
ホスト情報の登録
引用元 : command/command.go#L29
// prepareHost collects specs of the host and sends them to Mackerel server.
// A unique host-id is returned by the server if one is not specified.
func prepareHost(conf *config.Config, api *mackerel.API) (*mackerel.Host, error) {
// (省略)
// (66行目〜)
doRetry(func() error {
hostID, lastErr = api.CreateHost(mackerel.HostSpec{
Name: hostname,
Meta: meta,
Interfaces: interfaces,
RoleFullnames: conf.Roles,
DisplayName: conf.DisplayName,
CustomIdentifier: customIdentifier,
})
return filterErrorForRetry(lastErr)
})
// (省略)
}
ホスト情報の更新
引用元 : command/command.go#L501
// UpdateHostSpecs updates the host information that is already registered on Mackerel.
func (c *Context) UpdateHostSpecs() {
// (省略)
hostname, meta, interfaces, customIdentifier, err := collectHostSpecs()
// (省略)
err = c.API.UpdateHost(c.Host.ID, mackerel.HostSpec{
Name: hostname,
Meta: meta,
Interfaces: interfaces,
RoleFullnames: c.Config.Roles,
Checks: c.Config.CheckNames(),
DisplayName: c.Config.DisplayName,
CustomIdentifier: customIdentifier,
})
// (省略)
}
というわけで、実装を見てみました。
mackerel-agent側でカスタムIDを生成すると何が嬉しいかというと、AWSインテグレーションみたいなエージェントレス監視に対し、後からエージェントをインストールした時に同一ホストかの判定ができるんですね。
さくらのクラウドとMackerelの連携ツール「sackerel」がまさにこれが必要でした。
以上です。