Pod内からgolangでKubernetesのAPIを叩く

# 概要
Pod内でk8sのAPIを使いたかったので、やり方を調べました。
Goのk8sクライアントであるclient-goのexampleを動かしてみます。
https://github.com/kubernetes/client-go

# 環境
- kops
    - 1.9.1
- Kubernetes
    - 1.9.6


# 詳細
## in-cluster-client-configuration
以下のin-cluster-client-configurationというexampleを使います。
https://github.com/kubernetes/client-go/tree/master/examples/in-cluster-client-configuration

どうやら `rest.InClusterConfig()` を呼ぶとPod内の認証情報を使ってAPIアクセスを行ってくれるようです。
ここではServiceAccountが利用されているのですが、ServiceAccountについては以下の記事がわかりやすかったです（ステマ）。
https://qiita.com/knqyf263/items/ecc799650fe247dce9c5

つまりServiceAccountに適切なRoleを付与しておけばプログラムからは意識せずに利用できます。

## ビルド
正直ここが書きたかったです。

exampleには依存ライブラリが必要なのでdepで用意します。

```console
$ dep init
Importing configuration from godep. These are only initial constraints, and are further refined during the solve process.
Importing configuration from godep. These are only initial constraints, and are further refined during the solve process.
Importing configuration from godep. These are only initial constraints, and are further refined during the solve process.
  Locking in 1.0.0 (1df9eeb) for transitive dep github.com/modern-go/reflect2
  Locking in  (44d8105) for transitive dep github.com/google/gofuzz
  Locking in master (8014b7b) for transitive dep golang.org/x/sys
  Locking in v0.3.0 (f21a4df) for transitive dep golang.org/x/text
  Locking in  (c0656ed) for transitive dep github.com/gogo/protobuf
  Locking in master (f2b4162) for transitive dep github.com/json-iterator/go
  Locking in v2.2.1 (5420a8b) for transitive dep gopkg.in/yaml.v2
  Locking in kubernetes-1.10.0 (73d9036) for transitive dep k8s.io/api
  Using master as constraint for direct dep k8s.io/apimachinery
  Locking in master (5a80132) for direct dep k8s.io/apimachinery
  Locking in v0.9.0 (3887ee9) for transitive dep gopkg.in/inf.v0
  Locking in  (81e9090) for transitive dep golang.org/x/crypto
  Locking in  (1643683) for transitive dep github.com/golang/protobuf
  Locking in  (0c51083) for transitive dep github.com/googleapis/gnostic
  Locking in  (1c05540) for transitive dep golang.org/x/net
  Locking in 1.0.3 (bacd9c7) for transitive dep github.com/modern-go/concurrent
  Locking in  (44145f0) for transitive dep github.com/golang/glog
  Using ^7.0.0 as constraint for direct dep k8s.io/client-go
  Locking in v7.0.0 (23781f4) for direct dep k8s.io/client-go
  Locking in  (73d445a) for transitive dep github.com/ghodss/yaml
  Locking in  (f51c127) for transitive dep golang.org/x/time
```

そしてビルドします。

```console
$ GOOS=linux go build -o ./app .
# github.com/kubernetes/client-go/examples/in-cluster-client-configuration/vendor/k8s.io/client-go/discovery
vendor/k8s.io/client-go/discovery/restmapper.go:42:75: undefined: meta.VersionInterfacesFunc
vendor/k8s.io/client-go/discovery/restmapper.go:176:19: undefined: meta.VersionInterfacesFunc
```

エラーが出ました。Interfaceが存在しないと言われています。
エラーから察するに依存関係の解決がうまく行ってないです。
動作に必要なバージョンがうまく固定されておらず、動かないバージョンのmetaを持ってきているようです。

ここでしばらくハマったのですが、INSTALL.mdに答えは書いてありました。
https://github.com/kubernetes/client-go/blob/master/INSTALL.md#dep-not-supported-yet

**Dep (Not supported yet!)**

ということでサポートされてないらしいです。
glideかgodepsを使えと言われてます。
Kubernetesぐらい新しいプロジェクトなので、もっと最新の依存解決ツール使えと言われるのかと思いきや古いツールが使われてました。確かにGodepsディレクトリが置いてあります。
glideとか久々に使うのでコマンド忘れてましたが、何とか動きました。

```console
$ glide create
$ glide install -v
```

`-v` がないとvendorがネストされてエラーになるっぽいので気をつけて下さい。

ということであとはビルドすれば通ります

```console
$ GOOS=linux go build -o ./app .
```

## Dockerのビルド
これも普通にexampleのDockerfileを使うだけです。

```console
$ docker build -t in-cluster .
```

あとはこれをk8sへデプロイ可能な場所へイメージをpushしておきます。
自分の場合はAWSでk8sを構築していたのでECRにpushしました。


## Roleの付与
あとはDeploymentを使うだけ．．．かと思ったのですが、どうやらServiceAccountの `default` は何もRoleが付与されていないようです。
そのためPodの一覧取得などに失敗します。

======
※ @superbrothers　さんからコメントいただいたので追記
KubeCon 2017で以下の発表がされていました。
特定のツール・バージョンで構築した場合に `default:default` のServiceAccountに対して強い権限が与えられていることがあったようです。
https://schd.ws/hosted_files/kccncna17/d8/Hacking%20and%20Hardening%20Kubernetes%20By%20Example%20v2.pdf

そのため何も気にしないでPodを利用していると、デプロイされたPodがk8sのAPIを利用可能になってしまってセキュリティリスクがある、という問題があったようです。悪意あるPodがデプロイされた場合ですね。
自分が利用したkops 1.9.1では `default:default` は何も権限を持っておらず安全でした。

======

ということでPodの一覧取得権限などを `default` に付与します。
今回は面倒なのでClusterRoleのviewを付与しました。実際に使う場合は適切なRoleを作ってRoleBindingを作るのが良いと思います。

```
$ kubectl create clusterrolebinding default-view --clusterrole=view --serviceaccount=default:default
```

## デプロイ
これで準備は終わりなので、あとはデプロイするだけです。

成功していればPodの数が表示されます。

```
$ kubectl run --rm -i demo --image=in-cluster --image-pull-policy=Never
There are 23 pods in the cluster
Pod not found
There are 23 pods in the cluster
Pod not found
...
```

# まとめ
`kubernetes/client-go` のexampleを動かしてみました。
認証が一番面倒そうかなと思っていたので、そこが出来ればあとはGoをゴリゴリ書けば行けるんじゃないかと思ってます。