Google のコンテナ管理ツール Kubernetes を使ってみる


この記事は 2014 年 7 月 15 日に書かれたものです。
月日の経過と共に、情報が古くなる可能性があるのでご留意下さい。

# Kubernetes is ...？

Kubernetes (pronounced koo-ber-nay'-tace)

ク(ゥ)?ーバ(ー)?ネ(イ|ィ)テス とかそんな読み方。
Google I/O のビデオ内で作者が発音しているので聞いてみるとイメージしやすいかもしれません。

* (読み方参考):
	* https://twitter.com/kazunori_279/status/488232794943156225
 
Kubernetes はギリシャ語で "helmsman of a ship" (船の操縦手)、
もっと深い意味を取ると、"ruler" (統括者・支配者) という意味があるらしいです。粋な名前ですね。

Kubernetes に関するリンクをいくつか挙げます。

* Github:
	* https://github.com/GoogleCloudPlatform/kubernetes
 
* 日本語情報:
	* http://qiita.com/kazunori279/items/ebe8ea24601e606f8048
		* 言わずと知れた [@kazunori_279](https://twitter.com/kazunori_279/) さんの記事
	* http://qiita.com/bounscale/items/ad876e6bd256d5d58ebb
		* [Bounscale](https://github.com/bounscale/bounscale/) さんによる記事
	* http://www.atmarkit.co.jp/ait/articles/1407/11/news100.html
		* [@IT](http://www.atmarkit.co.jp) さんによる記事

上述の記事を読んだ方はお分かりかと思いますが、
Kubernetes は Docker を始めとした仮想コンテナによるクラスタリングを行うためのサービスです。

とは言え、使ってみないことにはイメージが沸かないと思うので、
とりあえず動かして見ました。というのが本記事の趣旨となります。

# Installation

## Kubernetes Design overview

Kubernetes の設計について書かれています。

* https://github.com/GoogleCloudPlatform/kubernetes/blob/master/DESIGN.md

	* Pod
	* Label

	* Kubernetes master
		* etcd
		* API server
		* Controller Manager Server

	* Kubernetes node
		* Kubelet
			* File
			* HTTP endpoint
			* etcd server
			* HTTP Server
		* Kubernetes Proxy

この辺りは、冒頭でも紹介した通り、[Bounscale](https://github.com/bounscale/bounscale/) さんによる記事 がとても良くまとまっています。

* http://qiita.com/bounscale/items/ad876e6bd256d5d58ebb
	* [Bounscale](https://github.com/bounscale/bounscale/) さんによる記事

# How to use

## with Google Cloud Platform

### Install

* Google Cloud Platform 上で動作させてみる。
	* 前提: Google Cloud Platform を使える状態になっている必要がある。

Google Cloud Platform を使える状態にするには、
弊社の下記の記事が参考になるかもしれません。

* http://heartbeats.jp/hbblog/2014/06/google-cloud-platform.html#more

Google Cloud Platform が使えるようになったら、
下記を見て、 Google Cloud SDK を導入する。

https://developers.google.com/compute/docs/gcutil/?hl=ja

```bash
$ curl https://sdk.cloud.google.com | bash
$ gcloud auth login
```

```bash
$ gcloud components update
```

```bash
$ gcloud config set project <project-id>
```

Kubernetes を clone してくる。

```bash
$ git clone https://github.com/GoogleCloudPlatform/kubernetes.git
```

初回のセットアップを実行する。

```bash
$ cd kubernetes
$ hack/dev-build-and-up.sh
```

この ```hack/dev-build-and-up.sh``` は内部的には、
```release/build-release.sh``` と ```cluster/kube-up.sh``` 
を実行している模様です。

スクリプトの実行が完了すると、

```Kubernetes cluster is running.  Access the master at:``` 

というようなメッセージとともに、kubernetes-master へのリンクが表示されます。
アクセスし、```Welcome to Kubernetes``` という文字列が表示されていればインストールに成功しています。

```cluster/kube-up.sh``` では色々な VM インスタンスやバケットを生成しています。
具体的には以下のものが標準の設定では生成されました。

* VM インスタンス
	* kubernetes-master
	* kubernetes-minion-1
	* kubernetes-minion-2
	* kubernetes-minion-3
	* kubernetes-minion-4
* Cloud Storage バケット
	* kubernetes-releases-xxxxx

どんな VM が生成されたかは、```gcutil listinstances``` コマンドで確認する事ができます。

生成されたインスタンスの kubernetes-master が、
Design overview のそのまま Kubernetes-master に対応していて、
kubernetes-mition-[1-9]+ が、Kubernetes-node に対応しているものと思われます。

この際に生成される VM インスタンスのゾーンや VM スペック等の設定は、
```cluster/config-default.sh``` を参照している模様です。

また、作られたインスタンスやバケット、割り当て等は ```cluster/kube-down.sh``` を実行すると消えます。

実際に、kubernetes-master に ssh 接続して確認してみます。

```bash
$ gcutil ssh kubernetes-master

yoshikawa@kubernetes-master:~$ cat /etc/debian_version
7.5
```

デフォルトの設定では、GCE の```backports-debian-7-wheezy``` のイメージが使われているので、
debian 系になります。
他のイメージでも正しく動くのかは検証していません。インストールスクリプト依存だと思います。

実際に、design overview で書かれていたプロセスが起動しているか確認してみます。

```bash
yoshikawa@kubernetes-master:~$ ps aufxww|grep [e]tcd
999      11179  0.0  0.2  83340  8180 ?        Sl   Jul14   0:08 /usr/local/bin/apiserver -cloud_provider gce -etcd_servers=http://xx.xx.xx.xx:4001 -minion_regexp 'kubernetes-minion.*' --machines kubernetes-minion-2.c.omega-strand-597.internal,kubernetes-minion-1.c.omega-strand-597.internal
998      15946  0.0  0.1  66588  4400 ?        Sl   Jul14   0:04 /usr/local/bin/controller-manager --master=127.0.0.1:8080 -etcd_servers=http://10.240.247.88:4001
etcd     16125  0.0  1.5 123692 57972 ?        Sl   Jul14   1:06 /usr/local/bin/etcd -peer-addr kubernetes-master:7001 -name kubernetes-master
```

* apiserver
* controller-manager
* etcd

が実際に起動していることが確認出来ました。
他にも、nginx がフロントとして動いていることを確認できます。

次に、kubernetes-minion-1 に ssh 接続して確認してみます。

```bash
$ gcutil ssh kubernetes-master

yoshikawa@kubernetes-minion-1:~$ cat /etc/debian_version
7.5
```

```bash
root@kubernetes-minion-1:~# ps aufxww|grep [d]ocker
root      5546  0.1  0.3 650852 11996 ?        Sl   Jul14   1:16 /usr/bin/docker -d -p /var/run/docker.pid --bridge cbr0 --iptables=false

root@kubernetes-minion-1:~# ps aufxww|grep [k]ube
kubelet  11328  0.0  0.1  68176  5460 ?        Sl   Jul14   0:11 /usr/local/bin/kubelet -config /etc/kubelet/data/kubernetes-minion-1 -etcd_servers=http://xx.xx.xx.xx:4001 -address=kubernetes-minion-1 -config=/etc/kubernetes/manifests
998      16087 98.0  0.0  75172  3728 ?        Rl   Jul14 1231:34 /usr/local/bin/kube-proxy --etcd_servers=http://xx.xx.xx.xx:4001
```

こんな感じで、docker, kubelet, kube-proxy の起動を確認することが出来ます。

### How to use

それでは実際に使ってみます。

まず、Kubernetes で使用する Go のコンポーネントをビルドします。

```bash
$ hack/build-go.sh
```

ビルドが完了したら、コンテナを 2 つ立ち上げてみます。

```bash
$ cluster/kubecfg.sh -p 8080:80 run dockerfile/nginx 2 myNginx
```

ちなみにこれは、次のコマンドと等価です。

```bash
$ ./output/go/kubecfg -h https://${kubernetes-master-ip}/ -p 8080:80 run dockerfile/nginx 2 myNginx
```

コマンドの実行が完了すると、以下のようなメッセージが出力されます。

```yaml
id: myNginx
desiredState:
  replicas: 2
  replicaSelector:
    name: myNginx
  podTemplate:
    desiredState:
      manifest:
        version: ""
        id: ""
        volumes: []
        containers:
        - name: ""
          image: dockerfile/nginx
          ports:
          - hostPort: 8080
            containerPort: 80
    labels:
      name: myNginx
labels:
  name: myNginx
```

pod に関するより詳細な情報は、Kubernetes-master の API 経由で受け取ることが出来ます。

```bash
$ curl --insecure --user user:password --max-time 5 https://xxx.xxx.xxx.xxx/api/v1beta1/pod
:
:
```

また、```cluster/kubecfg.sh list pods/``` とすることでより見やすい情報を得ることが出来ます。

```bash
$ cluster/kubecfg.sh list /pods
Name                                   Image(s)            Host                                               Labels
----------                             ----------          ----------                                         ----------
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx   dockerfile/nginx    kubernetes-minion-1.xxxxxxxxxx.internal/   name=myNginx,replicationController=myNginx
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx   dockerfile/nginx    kubernetes-minion-2.c.xxxxxxxxxx.internal/   name=myNginx,replicationController=myNginx
```

今回は、nginx のコンテナを 2 つ起動した為、
kubernetes-minion-1, kubernetes-minion-2 に ```dockerfile/nginx``` による、
Docker コンテナが起動しています。

実際にログインして確認することも可能です。

```
$ gcutil ssh kubernetes-minion-1 "ps aufx | grep -A 8 [d]ocker"
root      5546  4.0  0.5 709920 20688 ?        Sl   09:48   1:28 /usr/bin/docker -d -p /var/run/docker.pid --bridge cbr0 --iptables=false
root     16119  0.0  0.0   3148    76 ?        Ss   09:49   0:00  \_ cat nap
root     16372  0.6  0.1 283524  7548 ?        Ssl  09:53   0:12  \_ /gopath/bin/app
root     16551  0.0  0.0   3148    68 ?        Ss   09:54   0:00  \_ cat nap
root     16756  0.0  0.1  90264  4228 ?        Ss   09:55   0:00  \_ nginx: master process nginx
www-data 16768  0.0  0.0  90632  1812 ?        S    09:55   0:00      \_ nginx: worker process
www-data 16769  0.0  0.0  90632  1812 ?        S    09:55   0:00      \_ nginx: worker process
www-data 16770  0.0  0.0  90632  1812 ?        S    09:55   0:00      \_ nginx: worker process
www-data 16771  0.0  0.0  90632  1812 ?        S    09:55   0:00      \_ nginx: worker process
```

docker 上で nginx のプロセスが起動していることが確認できます。

Host の 8080 が Dokcer コンテナに繋がっているので、
8080 番ポートを指定して、アクセスすることが出来ます。

但し、設定を変更していない Kubernetes の VM インスタンスでは、
8080 番ポートの接続が許可されていません。

```cluster/config-default.sh``` がデフォルトの場合は、
Google Compute Engine の default という名前のネットワークが割り当てられます。

そこで、default という名前のネットワークに、
docker-nginx という名前の 8080 番ポートの接続許可を追加します。

※ ```default``` のルールを変更すると、Kubernetes 以外の VM インスタンスも影響を受ける可能性があります。

```bash
$ gcutil addfirewall docker-nginx --network=default --allowed="tcp:8080"
```

すると、下記の通り、curl で nginx のデフォルトページを見ることが可能になります。

```bash
curl `gcutil listinstances --format=csv --sort=external-ip --columns=external-ip --filter="name eq kubernetes-minion-1"|tail -n 1`:8080
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
    body {
        width: 35em;
        margin: 0 auto;
        font-family: Tahoma, Verdana, Arial, sans-serif;
    }
</style>
</head>
<body>
<h1>Welcome to nginx!</h1>
<p>If you see this page, the nginx web server is successfully installed and
working. Further configuration is required.</p>

<p>For online documentation and support please refer to
<a href="http://nginx.org/">nginx.org</a>.<br/>
Commercial support is available at
<a href="http://nginx.com/">nginx.com</a>.</p>

<p><em>Thank you for using nginx.</em></p>
</body>
</html>
```

※ ``` gcutil listinstances --format=csv --sort=external-ip --columns=external-ip --filter="name eq kubernetes-minion-1"|tail -n 1` ``` は任意のインスタンス名の IP を取得するワンライナーです。

# Conclusion

Google のコンテナ管理ツール Kubernetes をインストールし、使ってみました。

今回は試してみる目的で nginx コンテナを Kubernetes node 上に 2 つ作成するだけでしたが、
これが大規模になったり、より複雑なコンテナ作成を実現しようとした場合に、効果を発揮しそうです。

また、本記事はあくまで動かしてみただけであり、Kubernetes の本質は本記事で書いた事以上のものが多数あります。
(label, Replica Controller, ... and so on)

とりあえず動かす段階まで来たので、引き続き Kubernetes に関して検証を進めていきたいと思います。

## Notes

### hack/dev-build-and-up.sh が終わらない場合

```hack/dev-build-and-up.sh``` ないし、
その内部で実行されている ```cluster/kube-up.sh``` が、
以下のようにループしてしまい、終了しない場合。

```
Waiting for cluster initialization.

  This will continually check to see if the API for kubernetes is reachable.
  This might loop forever if there was some uncaught error during start
  up.

...............
```

この ```...``` の時間は、kubernetes-master サーバ内で、etcd を構築したり、
nginx を構築したりと、Kubernetes を使うための準備の時間のようです。

ただし、Kubernetes デフォルトの設定では、
us-central1-b ゾーンにインスタンスが作られる影響か分かりませんが、
curl の timeout 値である 1 秒以内に通信が完了せず、スクリプトがループしてしまう場合があります。

その場合は、```cluster/kube-up.sh``` 内 130 行目の --max-time の引数を増やすと上手く行きます。
僕の場合は 3 程度にしたら上手く行きました。

```bash
$ grep -n -- "--max-time" cluster/kube-up.sh

130:until $(curl --insecure --user ${user}:${passwd} --max-time 5 \ 
```

* 2014-0716 追記

上記の件、以下の通り PR が merge されたので次からはタイムアウトでループすることはないと思います。
https://github.com/GoogleCloudPlatform/kubernetes/pull/459

---

何か間違いや気付いた点などあれば、コメント頂けると嬉しいです。
余裕があれば、次回は Kubernetes + CoreOS を試してみたいと思います。

※ここに示された意見は個人のものであり、所属する組織を代表するものではありません※