Kubernetes の Go API クライアントライブラリを使って Kubernetes を直接操作する

__2016/11/15 10:15 追記__

この記事では kubernetes/kubernetes リポジトリに含まれているクライアントライブラリを紹介していますが、今年の8月だか9月になって API クライアントだけが別リポジトリとして切り出されました。

https://github.com/kubernetes/client-go

今後何かツールを作るときは、この client-go を使うのがよいでしょう。

というわけで、以下の内容は__記事初公開時点の、client-go がなかった時代の__ものとなっております。その点ご留意いただいた上でお読みください。

__追記終わり__

* * *

あまり知られてないようですが、Kubernetes には公式の Go 製 API クライアントライブラリがあります。`kubectl` もこのライブラリを用いて実装されています。

https://github.com/kubernetes/kubernetes/tree/master/pkg/client/

`kubectl` は確かに万能なんですが、実運用に投入するとなるとその万能さがマイナスに効いてくることもあります。で、自分たちが使いやすいように `kubectl` をラップした何かを作りたくなるのですが、他の言語から `kubectl` をサブコマンド呼び出しするのは筋が悪いです。API ライブラリを使ってネイティブレベルで Kubernetes を操作するようにしましょう。

## いきなりサンプルコード
というわけで、Pod の名前一覧を表示するだけのサンプルコードです。

```go:list_pods.go
package main

import (
	"fmt"
	"os"

	"k8s.io/kubernetes/pkg/api"
	client "k8s.io/kubernetes/pkg/client/unversioned"
	"k8s.io/kubernetes/pkg/client/unversioned/clientcmd"
)

func newKubeClient() (*client.Client, error) {
	loadingRules := clientcmd.NewDefaultClientConfigLoadingRules()
	loadingRules.ExplicitPath = clientcmd.RecommendedHomeFile

	loader := clientcmd.NewNonInteractiveDeferredLoadingClientConfig(loadingRules, &clientcmd.ConfigOverrides{})

	clientConfig, err := loader.ClientConfig()

	if err != nil {
		return nil, err
	}

	kubeClient, err := client.New(clientConfig)

	if err != nil {
		return nil, err
	}

	return kubeClient, nil
}

func main() {
	kubeClient, err := newKubeClient()

	if err != nil {
		fmt.Fprintln(os.Stderr, err)
		os.Exit(1)
	}

	pods, err := kubeClient.Pods(api.NamespaceDefault).List(api.ListOptions{})

	if err != nil {
		fmt.Fprintln(os.Stderr, err)
		os.Exit(1)
	}

	for _, pod := range pods.Items {
		fmt.Println(pod.Name)
	}
}
```

出力は以下のようになります。

```bash
$ go run listpods.go
hello-world-e2d3x
wordpress-mysql-488205646-t6v4k
```

ちなみに `kubectl get pods` だとこうなります。

```bash
$ kubectl get pods
NAME                              READY     STATUS    RESTARTS   AGE
hello-world-e2d3x                 1/1       Running   0          8d
wordpress-mysql-488205646-t6v4k   1/1       Running   0          8d
```

## コード解説
### API クライアントインスタンスの作成

最初に、ローカルのコンフィグファイルを読み込みます。

```go
	loadingRules := clientcmd.NewDefaultClientConfigLoadingRules()
	loadingRules.ExplicitPath = clientcmd.RecommendedHomeFile

	clientConfig, err := loader.ClientConfig()

	if err != nil {
		return nil, err
	}

	kubeClient, err := client.New(clientConfig)

	if err != nil {
		return nil, err
	}
```

`loadingRules.ExplicitPath` にコンフィグファイルのパスを指定します。[定数 `RecommendedHomeFile` は `~/.kube/config` を指しています](https://github.com/kubernetes/kubernetes/blob/4a78db61370df83a37957490749f7d171b00c28a/pkg/client/unversioned/clientcmd/loader.go#L48)。`kubectl` がデフォルトで見に行くパスですね。
`kubectl --kubeconfig /path/to/kubecfg` のようにパスを明示的に指定したい場合は、

```go
	loadingRules.ExplicitPath = /path/to/kubecfg
``` 

にそのパスを代入すればよいです。

### API を叩く

`kubeClient.リソース.操作` の形になっています。

```go
    pods, err := kubeClient.Pods(api.NamespaceDefault).List(api.ListOptions{})
```

まず `Pods` や `Secrets` のように操作対象リソースを指定します。引数には Namespace を指定します。自分で指定することもできますが、[`api.NamespaceDefault` や `api.NamespaceSystem` のように](https://github.com/kubernetes/kubernetes/blob/4a78db61370df83a37957490749f7d171b00c28a/pkg/api/types.go#L154-L161)定数も用意されてます。
続いて操作内容を指定します。名前を指定して Pod を取得するなら `Get(name)` ですし、全部の Pod を撮りたいなら `List()` です。

### 得られた Pod 一覧の中身を出力する

```go
	for _, pod := range pods.Items {
		fmt.Println(pod.Name)
	}
```

実際の配列は `pods` ではなく `pods.Items` に格納されています。

## 注意
`k8s.io/kubernetes` は Kubernetes 本体のリポジトリなので、API クライアントを import するには Kubernetes のソースコード (+ 依存リポジトリ、docker/docker とか) を全部落としてくる必要があります。数百 Mbyte あるので、Godeps みたいにリポジトリへ vendor を含める場合は要注意です。[glide](https://github.com/Masterminds/glide) 使いましょう。

## おわりに
Kubernetes の Go API クライアントライブラリを用いて、Go から直接 Kubernetes を操作する手法を紹介しました。最初に書き方さえ覚えてしまえば、あとはどのリソースや操作に対しても比較的簡単に実装できると思います。
自分は最近、このライブラリを使って [k8sec](https://github.com/dtan4/k8sec) というツールを作りました。k8sec は Heroku の config のように、`k8sec set key1=val1 key2=val2` の形で Kubernetes の Secret を管理できるコマンドラインツールです。あわせてご覧 & ご利用ください。

## REF
- https://godoc.org/k8s.io/kubernetes/pkg/api
- https://godoc.org/k8s.io/kubernetes/pkg/client/unversioned
    - ライブラリのドキュメントです。ただ、実装するときはエディタ補完や godef を駆使していくほうがやりやすいです（個人の感想です）
- [kubernetes/client-libraries.md at master · kubernetes/kubernetes](https://github.com/kubernetes/kubernetes/blob/master/docs/devel/client-libraries.md)
    - API クライアントライブラリ一覧。Go 以外の言語は有志が作成したものです。
- [How can I create a simple client app with the Kubernetes Go library? - Stack Overflow](http://stackoverflow.com/questions/32554893/how-can-i-create-a-simple-client-app-with-the-kubernetes-go-library)
- [timoreimann/kubernetes-goclient-example: An example implementation of a Golang client for Kubernetes.](https://github.com/timoreimann/kubernetes-goclient-example)