febc技術メモ

Japanese version of http://febc-yamamoto.hatenablog.com

Docker/InfraKitのインスタンスプラグインの作り方

今回はDocker/InfraKitインスタンスプラグインの書き方について扱います。

はじめに

今回のゴール

当記事では以下2点をゴールとしています。

これらの解説のために、最小限の機能しか持たないインスタンスプラグインを(Go言語を用いて)ステップバイステップで作成してみます。

想定読者

  • InfraKitのチュートリアルを一通りこなした方
  • Go言語(初心者レベル)の開発スキルをお持ちの方

なお、インスタンスプラグインの作成に関しては、Dockerについての知識は不要です。

執筆時点でのInfraKitのバージョン

当記事執筆時点での最新版を利用しました。
InfraKitはガンガンとバージョンが上がり、SPIの定義も頻繁に変わります。
あくまで現時点での情報という点は留意ください。

0. 準備編

まずはInfraKitそのものについて全体像を簡単に押さえておきましょう。

InfraKitの役割

InfraKitとは一言で言うとインフラストラクチャのオーケストレーションを行うためのツールキットとのことです。

分散システムの構築や、インフラストラクチャの上流でコンテナなどのオーケストレーションを行うために、

- インフラストラクチャの状態を、
- ユーザがあらかじめ定義した状態に「保つ」こと

を目的としています。

状態を保つために、

- インフラストラクチャの現在の状態を把握
- 必要に応じて作成/破棄

を行います。

InfraKit = Group/Flavor/Instanceの3つのプラグイン(役割)の協調動作

以下の3つのプラグイン(役割)から構成されています。

Group(グループ)

個々のインスタンスをなんらかのルールで束ねたグループとして管理する役割を持ちます。
以下のような操作が行えるようになっています。

  • グループの定義を受け取り、グループの管理を開始する
  • グループの状態を把握する
  • グループを破棄する

Instance(インスタンス)

グループのメンバーとなるインスタンスの操作/管理を行う役割を持ちます。
グループからの指示を受け、以下のような作業を行います。

Flavor(フレーバー)

グループ内のメンバーがどう動作をすべきかを定義する役割を持ちます。

  • メンバーが実行すべきサービス(コマンド)の定義
  • サービス(コマンド)が動作しているかの確認(ヘルスチェック)

なお、NTTの大嶋さん(InfraKitのコアメンテナ!!)が以下のような全体図を書かれています。

全体像.png

引用元: Infrakitとは(SlideShare)

インフラ構成を定義としてグループに与えたら、構築や監視をよしなにやってくれるという感じですね。

プラグインの実体

プラグインは特定のAPIを持ったHTTPサーバで、UNIXドメインソケットでリッスンし、JSON-RPC2.0でやり取りします。

プラグインは共通のディレクトリ(通常は~/.infrakit/plugins)にソケットファイルを作成することで、 お互いに発見/呼び出しを行えるようになっています。

プラグインが持つべきAPIは以下にドキュメントがあります。

プラグインはGoで書く必要はなく、お好きなプログラミング言語を用いて作成することが可能です。
なお、Goを用いてプラグインを作成する場合、InfraKit側から提供されている便利なユーティリティ/ライブラリが利用可能です。

参考: https://github.com/docker/infrakit/tree/master/pkg/rpc

infrakitCLIについて

プラグインの状態確認や操作を簡単にするために、開発用CLI(コマンド)であるinfrakitが提供されています。

参考: https://github.com/docker/infrakit/blob/master/cmd/infrakit/README.md

InfraKitの実行自体に必須というわけではないのですが、あると便利ですので当記事でも利用していきます。
もしまだお手元にinfrakitコマンドがない場合はInfraKitのチュートリアルを参考にビルドしておいてください。

参考: InfraKitチュートリアル

ということで、これらの知識を持った上で早速プラグインの作成をしてみましょう。

今回作成するインスタンスプラグインについて

今回は最小限の機能しか持たないインスタンスプラグインinfrakit-instance-minimumという名前で作成してみます。

GitHub: infrakit-instance-minimum

このプラグインは以下のような機能を持っています。

InfraKitが提供しているインスタンスプラグインのサンプル実装のfileインスタンスプラグインを参考にしていますが、 より機能を削り必要最低限のコードしか持たないものになっています。

1. 何もしないプラグインの作成

最初の段階として、何もしないプラグインを実装しInfraKitで認識できるところまでを実装してみます。

infrakitコマンドで起動しているプラグインを確認

まずはinfrakitコマンドで起動しているプラグインを確認してみます。

$ build/infrakit plugin ls
  INTERFACE           LISTEN                                            NAME

現時点では何も起動していませんので何も表示されないのが正解です。
もし何かプラグインを起動しておいた場合は以下のような表示になります。

$ build/infrakit plugin ls
INTERFACE           LISTEN                                            NAME
Instance/0.6.0      ~/.infrakit/plugins/instance-file                 instance-file

プラグインの作成

GoでInfraKitのプラグインを作成する場合、InfraKit側から提供されているユーティリティを用いることで楽に実装可能です。
具体的には以下の実装を行うだけでインスタンスプラグインとして動作させることが可能です。

SPIとはService Provider Interfaceの略です。
以下のようにGoのインターフェースとして定義されていますので、これを満たす実装を行なっていきます。

// Plugin is a vendor-agnostic API used to create and manage resources with an infrastructure provider.
type Plugin interface {
    // Validate performs local validation on a provision request.
    Validate(req *types.Any) error

    // Provision creates a new instance based on the spec.
    Provision(spec Spec) (*ID, error)

    // Label labels the instance
    Label(instance ID, labels map[string]string) error

    // Destroy terminates an existing instance.
    Destroy(instance ID, context Context) error

    // DescribeInstances returns descriptions of all instances matching all of the provided tags.
    // The properties flag indicates the client is interested in receiving details about each instance.
    DescribeInstances(labels map[string]string, properties bool) ([]Description, error)
}

ソース: https://github.com/docker/infrakit/blob/master/pkg/spi/instance/spi.go

開発用にディレクトリ作成

まず、作成するプラグイン用のソースを格納するディレクトリを作成します。

mkdir infrakit-instance-minimum; cd infrakit-instance-minimum

以降はこのディレクトリ内で作業します。

インスタンスプラグインのSPIを実装(空の実装)

次にSPIの実装をplugin.goとして以下のように作成します。

plugin.go

package main

import
(
    "github.com/docker/infrakit/pkg/types"
    "github.com/docker/infrakit/pkg/spi/instance"
)

type plugin struct{}

func NewMinimumInstancePlugin() instance.Plugin {
    return &plugin{}
}

func (p *plugin) Validate(req *types.Any) error {
    return nil
}

func (p *plugin) Provision(spec instance.Spec) (*instance.ID, error) {
    return nil, nil
}

func (p *plugin) Label(instance instance.ID, labels map[string]string) error {
    return nil
}

func (p *plugin) Destroy(instance instance.ID, context instance.Context) error {
    return nil
}

func (p *plugin) DescribeInstances(labels map[string]string, properties bool) ([]instance.Description, error) {
    return []instance.Description{}, nil
}

ソース: https://github.com/yamamoto-febc/infrakit-instance-minimum/blob/361b02513d59659dedb603415bbe3bdd6caee51f/plugin.go

SPIを実装したプラグインを起動するためのエントリーポイント作成

次に、プラグインを起動するエントリーポイントとしてmain.goを以下のように作成します。

main.go

package main

import (
    "github.com/docker/infrakit/pkg/cli"
    instance_plugin "github.com/docker/infrakit/pkg/rpc/instance"
)

func main() {
    cli.RunPlugin("instance-minimum", instance_plugin.PluginServer(NewMinimumInstancePlugin()))
}

ソース: https://github.com/yamamoto-febc/infrakit-instance-minimum/blob/361b02513d59659dedb603415bbe3bdd6caee51f/main.go

ビルド & 起動

これだけでInfraKitのインスタンスプラグインとしての最低限の体裁が整っています。 早速ビルドして起動し、infrakitコマンドから認識できているか確認してみましょう。 (GOPATHの設定とかは適当にやっておいてくださいね。)

#ビルド
$ go build 
#起動(フォアグラウンド起動)
$ ./infrakit-instance-minimum
INFO[0000] Listening at: ~/.infrakit/plugins/instance-minimum 
INFO[0000] PID file at ~/.infrakit/plugins/instance-minimum.pid

起動できたら、別のコンソールなどからinfrakitコマンドで確認してみましょう。

$ build/infrakit plugin ls
INTERFACE           LISTEN                                            NAME
Instance/0.6.0      ~/.infrakit/plugins/instance-minimum     instance-minimum

無事に確認できましたね?おめでとうございます!これでインスタンスプラグインが作成できました!!!
とはいえ、実装は空ですのでこのままでは何もできません。次の段階ではもう少し実装を足してみましょう。

2. インスタンスの作成処理の実装

続いて、もう少しインスタンスプラグインらしくするために、インスタンスの生成処理を実装してみます。
具体的には、以下のような定義をグループプラグインに与えることでインスタンスの生成が行えるようにしてみます。

{
  "ID": "example",
  "Properties": {
    "Allocation": {
      "Size": 3
    },
    "Instance": {
      "Plugin": "instance-minimum",
      "Properties": {}
    },
    "Flavor": {
      "Plugin": "flavor-vanilla",
      "Properties": {}
    }
  }
}

グループプラグインの起動と定義ファイル(JSON)の作成

まずはグループプラグインを起動しておきましょう。
グループプラグインの実装には、InfraKitが提供するデフォルト実装であるinfrakit-group-defaultを利用します。

また、フレーバープラグインも必要となりますので起動しておきます。
フレーバープラグインの実装には、InfraKitが提供するサンプルであるinfrakit-flavor-vanillaを利用します。

#グループプラグイン(default)の起動
$ build/infrakit-group-default
#フレーバープラグイン(vanilla)の起動
$ build/infrakit-flavor-vanilla

起動後、infrakit plugin lsの結果が以下のようになるはずです。

$ build/infrakit plugin ls
INTERFACE           LISTEN                                 NAME
Flavor/0.1.0        ~/.infrakit/plugins/flavor-vanilla     flavor-vanilla
Group/0.1.0         ~/.infrakit/plugins/group              group
Metadata/0.1.0      ~/.infrakit/plugins/group              group
Instance/0.6.0      ~/.infrakit/plugins/instance-minimum   instance-minimum

続いて定義ファイル(JSON)を以下のように作成しておきます。

$ vi example.json
#以下の内容を記述
{
  "ID": "example",
  "Properties": {
    "Allocation": {
      "Size": 3
    },
    "Instance": {
      "Plugin": "instance-minimum",
      "Properties": {}
    },
    "Flavor": {
      "Plugin": "flavor-vanilla",
      "Properties": {}
    }
  }
}

infrakitコマンドでグループプラグインに定義を渡す

作成した定義ファイルをグループプラグインに渡してみましょう。 定義ファイルの内容は以下のようになっています。

以下のコマンドでグループプラグインに定義を渡せます。

$ build/infrakit group commit example.json
[...省略...]
INFO[0021] Committing group example (pretend=false)     
Committed example: Managing 3 instances
INFO[0021] Adding 3 instances to group to reach desired 3 

panic: runtime error: invalid memory address or nil pointer dereference
[signal SIGSEGV: segmentation violation code=0x1 addr=0x0 pc=0x140c474]

goroutine 40 [running]:
github.com/docker/infrakit/pkg/plugin/group.(*scaledGroup).CreateOne(0xc4201f2460, 0x0)
    /go/src/github.com/docker/infrakit/pkg/plugin/group/scaled.go:102 +0x6d4
github.com/docker/infrakit/pkg/plugin/group.(*scaler).converge.func2(0xc42016e9e0, 0xc420170730)
    /go/src/github.com/docker/infrakit/pkg/plugin/group/scaler.go:277 +0x63
created by github.com/docker/infrakit/pkg/plugin/group.(*scaler).converge
    /go/src/github.com/docker/infrakit/pkg/plugin/group/scaler.go:278 +0x6c2

3つのインスタンスを追加しようとしていますが、エラーが出ていますね。
これはインスタンスプラグインの実装が空だからです。また実装後に改めて試すことにしましょう。

なお、グループプラグインが異常終了してしまうために~/.infrakit/plugin/groupにファイルが残ったままになっています。
このままだと、次回グループプラグインを起動するときにエラーとなりますので手動で削除し、改めてグループプラグインを起動しておきましょう。

$ rm ~/.infrakit/plugins/group
#あらためてグループプラグインを起動
$ build/infrakit-group-default

インスタンスの作成処理(Provision)の実装

ではもう少し実装を進めます。まずはインスタンスの作成処理を担当するProvisionメソッドを実装します。
plugin.goに追記していくのですが、追記内容が若干多いため、詳しい内容は以下の変更差分を参照してください。

Provisionメソッドは以下のように修正されています。

plugin.goのProvisionメソッド

func (p *plugin) Provision(spec instance.Spec) (*instance.ID, error) {
    // ランダムなIDを生成
    id := instance.ID(fmt.Sprintf("instance-%d", rand.Int63()))
    path := filepath.Join(instanceDir, string(id))

    // ディレクトリ(/tmp/infrakit-dummy-instances)がなければ作成
    _, err := os.Stat(instanceDir)
    if err != nil {
        err := os.MkdirAll(instanceDir, os.FileMode(0777))
        if err != nil {
            return nil, err
        }
    }

    // ファイル作成
    if _, err := os.Stat(path); err != nil {
        f, err := os.Create(path)
        if err != nil {
            return nil, err
        }
        defer f.Close()
    }
    return &id, nil
}

引数のinstance.Spec経由でグループプラグインに与えた設定内容を参照できるのですが、今回は利用していません。
本来はここで与えられた設定を読み込み、必要なインスタンス生成処理を行います。

今回はインスタンス生成の代わりに"instance-“+ランダムな数値という名前のファイルを作成するだけにしています。

次にProvisionの戻り値についてですが、instance.IDへの参照を返すことになっています。 今回はファイル名をそのままIDとして利用しています。

本来はインスタンスを一意に特定できるIDを生成または取得して返すように実装します。 (例:AWSだったらEC2のリソースIDを返す、など)

実行!!

ではビルドして実行してみましょう。古いインスタンスプラグインは一旦停止(ctrl+cでOK)し、改めてビルド&起動してください。
起動したら以下のコマンドを実行してみましょう。

Provisionを実装したインスタンスプラグインを実行

#グループプラグインへ設定ファイルを受け渡し 
$ build/infrakit group commit example.json
[...省略...]
INFO[2638] Committing group example (pretend=false)     
Committed example: Managing 3 instances
INFO[2638] Adding 3 instances to group to reach desired 3 
INFO[2638] Created instance instance-4559224122183623453 with tags map[infrakit.config_sha:cvb62mzivlzgdtja24rczqhdtktqx2sn infrakit.group:example] 
INFO[2638] Created instance instance-3099390593271315395 with tags map[infrakit.config_sha:cvb62mzivlzgdtja24rczqhdtktqx2sn infrakit.group:example] 
INFO[2638] Created instance instance-4785619793559779603 with tags map[infrakit.config_sha:cvb62mzivlzgdtja24rczqhdtktqx2sn infrakit.group:example] 

うまくいきましたね!!/tmp/infrakit-dummy-instances/配下にファイルが3つ作成されていることが確認できるはずです。

ですが、、、少々問題があることに気づくかもしれません。

問題: ファイルがどんどん増えていく!?!?

しばらく待つと以下のようなログが表示され、/tmp/infrakit-dummy-instances/配下にファイルがどんどん増えていっているはずです。

INFO[2648] Created instance instance-1391249888231321150 with tags map[infrakit.config_sha:cvb62mzivlzgdtja24rczqhdtktqx2sn infrakit.group:example] 
INFO[2648] Created instance instance-4551305389484104975 with tags map[infrakit.config_sha:cvb62mzivlzgdtja24rczqhdtktqx2sn infrakit.group:example] 
INFO[2648] Created instance instance-102084934595871505  with tags map[infrakit.group:example infrakit.config_sha:cvb62mzivlzgdtja24rczqhdtktqx2sn] 

原因: InfraKitがインスタンスの状態を知らないから

これは、InfraKitが現在インスタンスがどのような状態かを知らない = 存在しないと思っているために、毎回インスタンスを作ろうとしてしまうことが原因です。
まあそんな処理は実装してないから当たり前ですね。

ということで、次はこの「インスタンスの状態を調べる」 = DescribeInstancesメソッドを実装していきます。

後片付け

このままではファイルが延々と増え続けてしまいます。このため、グループプラグインにこのグループの破棄を指示しておきましょう。
ついでに今回インスタンスプラグインが作成したファイルも削除しておきます。

# group destroyを実施(引数にはグループのIDを指定)
$ build/infrakit group destroy example
# インスタンスプラグインが作成したダミーファイルを削除しておく
$ rm /tmp/infrakit-dummy-instances/*

3. インスタンスの状態を調べるDescribeInstancesの実装

続いてDescribeInstancesを実装しましょう。
plugin.goに追記していくのですが、追記内容が若干多いため、詳しい内容は以下の変更差分を参照してください。

plugin.goのDescribeInstanceメソッド

func (p *plugin) DescribeInstances(labels map[string]string, properties bool) ([]instance.Description, error) {

    // ディレクトリ(/tmp/infrakit-dummy-instances)配下のファイルを取得
    entries, err := ioutil.ReadDir(instanceDir)
    if err != nil {
        return nil, err
    }

    result := []instance.Description{}
    // インスタンス情報の組み立て
    for _, entry := range entries {
        result = append(result, instance.Description{
            ID: instance.ID(entry.Name()),
        })

    }
    return result, nil

}

ここでは/tmp/infrakit-dummy-instancesディレクトリ配下に存在するファイルを参照し、インスタンス情報を組み立てています。
本来は、DBを参照したり、APIを呼び出すなどで現在のインスタンスたちの情報を組み立ててあげる処理となります。

実行!!

ではビルドして実行してみましょう。先ほどと同じく、古いインスタンスプラグインは一旦停止(ctrl+cでOK)し、改めてビルド&起動してください。
起動したら以下のコマンドを実行してみましょう。

Provisionを実装したインスタンスプラグインを実行

#グループプラグインへ設定ファイルを受け渡し 
$ build/infrakit group commit example.json
[...省略...]
INFO[3889] Committing group example (pretend=false)     
Committed example: Managing 3 instances
INFO[3889] Adding 3 instances to group to reach desired 3 
INFO[3889] Created instance instance-377673683573739334 with tags map[infrakit.group:example infrakit.config_sha:cvb62mzivlzgdtja24rczqhdtktqx2sn] 
INFO[3889] Created instance instance-8516032680601656640 with tags map[infrakit.group:example infrakit.config_sha:cvb62mzivlzgdtja24rczqhdtktqx2sn] 
INFO[3889] Created instance instance-6082236286152429340 with tags map[infrakit.group:example infrakit.config_sha:cvb62mzivlzgdtja24rczqhdtktqx2sn] 

今度はしばらく待ってもファイルがどんどん増えなくなっているはずです。

動作確認: インスタンス(の代わりのファイル)を消してみる

それではInfraKit自体の動作確認になるのですが、/tmp/infrakit-dummy-instances配下のファイルを消してみましょう。
InfraKitは変更を検知し、あらかじめ決められたインスタンス数(今回は3)を保つために新たなインスタンスの作成を行います。

$ rm /tmp/infrakit-dummy-instances/いずれかのファイル

# 数秒待つと、InfraKitが自動的に検知し、インスタンス(ファイル)が新たに作成される

問題: ファイルが消えない??

今度は逆にインスタンス(の代わりのファイル)を増やしてみましょう。 先ほどと同じように、あらかじめ決められたインスタンス数(今回は3)を保つためにインスタンスの削除が行われるはずです。

$ touch /tmp/infrakit-dummy-instances/instance-00000000

# 数秒待つと、InfraKitが自動的に検知するが、、、
INFO[4882] Removing 1 instances from group to reach desired 3 
INFO[4882] Destroying instance instance-000000000       
INFO[4892] Removing 1 instances from group to reach desired 3 
INFO[4892] Destroying instance instance-000000000       
INFO[4902] Removing 1 instances from group to reach desired 3 
INFO[4902] Destroying instance instance-000000000       
[...以降延々と続く...]

原因: インスタンスの削除処理を実装していないから

今回は予想がつくかと思いますが、インスタンスの削除処理を実装していないからですね。

ということで、次はこの「インスタンスを削除する」 = Destroyメソッドを実装していきます。

後片付け

先ほどと同じく後片付けを実行しておいてください。

# group destroyを実施(引数にはグループのIDを指定)
$ build/infrakit group destroy example
# インスタンスプラグインが作成したダミーファイルを削除しておく
$ rm /tmp/infrakit-dummy-instances/*

4. インスタンスを削除するDestroyの実装

続いてDestroyを実装しましょう。
plugin.goに追記していくのですが、追記内容が若干多いため、詳しい内容は以下の変更差分を参照してください。

plugin.goのDestroyメソッド

func (p *plugin) Destroy(instance instance.ID, context instance.Context) error {
    path := filepath.Join(instanceDir, string(instance))
    _, err := os.Stat(path)
    if err == nil {
        //ファイルが存在する場合は削除
        return os.Remove(path)
    }
    return nil
}

完成!実行してみましょう!!

先ほどまでと同じです。

#グループプラグインへ設定ファイルを受け渡し 
$ build/infrakit group commit example.json
[...省略...]
INFO[3889] Committing group example (pretend=false)     
Committed example: Managing 3 instances
INFO[3889] Adding 3 instances to group to reach desired 3 
INFO[3889] Created instance instance-377673683573739334 with tags map[infrakit.group:example infrakit.config_sha:cvb62mzivlzgdtja24rczqhdtktqx2sn] 
INFO[3889] Created instance instance-8516032680601656640 with tags map[infrakit.group:example infrakit.config_sha:cvb62mzivlzgdtja24rczqhdtktqx2sn] 
INFO[3889] Created instance instance-6082236286152429340 with tags map[infrakit.group:example infrakit.config_sha:cvb62mzivlzgdtja24rczqhdtktqx2sn] 

今度はインスタンスの削除処理が正常に行われるはずです。

$ touch /tmp/infrakit-dummy-instances/instance-00000000

# しばらく待つとInfraKitが検知してインスタンス削除処理を実施
INFO[5398] Removing 1 instances from group to reach desired 3 
INFO[5398] Destroying instance instance-000000000    

これで最低限の機能を持つインスタンスプラグインが作成できました。
本来はフレーバープラグインでの設定の反映なども行わないといけないのですが、その辺は必要に応じて既存のプラグインのソースを解析してみてください。

最後に、plugin.goのソース全体を載せておきます。

package main

import (
    "fmt"
    "github.com/docker/infrakit/pkg/spi/instance"
    "github.com/docker/infrakit/pkg/types"
    "math/rand"
    "os"
    "path/filepath"
    "io/ioutil"
)

var (
    instanceDir = "/tmp/infrakit-dummy-instances"
)

type plugin struct{}

func NewMinimumInstancePlugin() instance.Plugin {
    return &plugin{}
}

func (p *plugin) Validate(req *types.Any) error {
    return nil
}

func (p *plugin) Provision(spec instance.Spec) (*instance.ID, error) {
    // ランダムなIDを生成
    id := instance.ID(fmt.Sprintf("instance-%d", rand.Int63()))
    path := filepath.Join(instanceDir, string(id))

    // ディレクトリ(/tmp/infrakit-dummy-instances)がなければ作成
    _, err := os.Stat(instanceDir)
    if err != nil {
        err := os.MkdirAll(instanceDir, os.FileMode(0777))
        if err != nil {
            return nil, err
        }
    }

    // ファイル作成
    if _, err := os.Stat(path); err != nil {
        f, err := os.Create(path)
        if err != nil {
            return nil, err
        }
        defer f.Close()
    }
    return &id, nil
}

func (p *plugin) Label(instance instance.ID, labels map[string]string) error {
    return nil
}

func (p *plugin) Destroy(instance instance.ID, context instance.Context) error {
    path := filepath.Join(instanceDir, string(instance))
    _, err := os.Stat(path)
    if err == nil {
        //ファイルが存在する場合は削除
        return os.Remove(path)
    }
    return nil
}

func (p *plugin) DescribeInstances(labels map[string]string, properties bool) ([]instance.Description, error) {

    // ディレクトリ(/tmp/infrakit-dummy-instances)配下のファイルを取得
    entries, err := ioutil.ReadDir(instanceDir)
    if err != nil {
        return nil, err
    }

    result := []instance.Description{}
    // インスタンス情報の組み立て
    for _, entry := range entries {
        result = append(result, instance.Description{
            ID: instance.ID(entry.Name()),
        })

    }
    return result, nil

}

おまけ: 今回未実装のメソッド/追加で実装した方が良いメソッド

今回未実装のメソッド

今回はインスタンスプラグインのSPIのうち、以下を実装しませんでした。

  • Validate(req *types.Any) error
  • Label(instance instance.ID, labels map[string]string) error

それぞれ簡単に役割を説明しておきます。

Validate(req *types.Any) error

その名の通り検証を行うメソッドです。
グループプラグイン経由で定義したインスタンスプロパティ関連の値が渡ってくるため、ここで検証処理を行います。

Label(instance instance.ID, labels map[string]string) error

インスタンスに"ラベル"をつけるためのメソッドです。
具体的にはキーと値がlabelsに入ってくるため、これをインスタンスと紐付けます。

自前のDBに保持したり、クラウド上のインスタンスであればメタデータとして登録したりします。

なお、拙作infrakit.sakuracloudでは、さくらのクラウドのサーバのDescriptionという文字列を格納するための属性に保持するように実装しました。
さくらのクラウドではサーバにメタデータを紐づける仕組みがなかったために苦肉の策というところでした。
(なお、さくらのクラウドにはタグという機能もあるのですが、文字数の制限があるために利用できませんでした)

追加で実装した方が良いメソッド

全てのプラグインは以下のインターフェースについても実装しておいた方が良さそうです。

github.com/docker/infrakit/pkg/spiパッケージの

  • Vendorインターフェース
  • InputExampleインターフェース

インターフェースの定義(ソース)

それぞれCLIなどに対し情報を返すためのメソッドです。

Vendorインターフェースは各プラグインの名称やバージョンなどを返すためのメソッドです。
今の所InfraKit側でこの情報を使っている部分はなさそうなのですが、AWS/GCP/DigitalOceanなどのインスタンスプラグインでも実装されていますので、なるべく実装しておいた方が良さそうな感じがしています。

InputExampleインターフェースは、そのプラグインがどのようなプロパティが設定できるのかの例を定義するメソッドです。
cliなどから利用されています(infoサブコマンドなど)。

終わりに

当記事では最低限の機能を持つInfraKitのインスタンスプラグインをステップバイステップで作成しました。
実際にプラグインを作成する際はInfraKitのリポジトリ内にある他のインスタンスプラグインの実装も参考にするのが良いかと思います。

手始めにexamplesのfileプラグインのソースを読むことをお勧め致します。
これは当記事と同じようにインスタンスとしてダミーのファイルを作成する処理を行っています。 VendorインターフェースやInputExampleインターフェースの実装もされていますし、短いソースですので一度眼を通してみてください。

以上です。Enjoy!!

参考資料