Kubernetes覚え書き

# 概要

Kubernetesが難しいので調べたことを順次覚え書き的に書いていきます。

|役割|IPアドレス|
|---|---|
|master|10.136.3.183|
|node1|10.136.3.184|
|node2|10.136.3.185|

* 名前解決は利用せず、IPアドレスのみ使う
* masterはmaster nodeと呼ばれることもある
* nodeはworker nodeと呼ばれる
    * 昔はminionとも呼ばれていた(今も?)

# インストールの流れ

* 順番
    * etcd (masterのみ)
        * masterとは別のetcdサーバでも問題ないが、masterに入れられる事がほとんど
        * nodeはetcdサーバから情報を取得するだけなのでnode自体にetcdは入って無くても良い
    * flanneld
    * docker
    * kubernetes

* etcd　(masterのみ)
    * /etc/etcd/etcd.conf
    * `ETCD_LISTEN_CLIENT_URLS="http://10.136.3.183:2379,http://localhost:2379"`
    * `ETCD_ADVERTISE_CLIENT_URLS="http://localhost:2379"`
* flanneld
    * /etc/sysconfig/flanneld (master,node共通)
        * `FLANNEL_ETCD_ENDPOINTS="http://10.136.3.183:2379"`
        * `FLANNEL_ETCD_PREFIX="/flannel/network"`

# etcdについて

* 分散Key-Valuesストアを使い各種設定をノード間で共有するメカニズム
    * Key-Valuesストアデータは、ディレクトリ構造（=パス）で管理されている
    * Linuxの実際のディレクトリは別物。あくまでディレクトリ構造で管理されているということ
* Kubernetesでの用途
    * flannelやSkyDNS、その他関連コンポーネントのデータストア

## コマンドで確認

インストールとmaster,nodeの構築が終わっている状態で確認。ネットワーク以外にコンテナの情報もetcdで管理されているようだが簡単のためにここでは書かない。

``` shell
### インストール時、以下のコマンドでconfigを作成済み
$ sudo /usr/bin/etcdctl mk /flannel/network/config {"Network":"172.17.0.0/16"}
```

``` shell
[10.136.3.183(master)]
### 状態の確認
$ etcdctl cluster-health
================================================
member 8e9e05c52164694d is healthy: got healthy result from http://localhost:2379
cluster is healthy
================================================
-> memberはmasterのみ

### ディレクトリ構造の確認
$ etcdctl ls
================================================
/flannel
================================================

$ etcdctl ls /flannel --recursive -p
================================================
/flannel/network/
/flannel/network/config
/flannel/network/subnets/
/flannel/network/subnets/172.17.3.0-24
/flannel/network/subnets/172.17.67.0-24
/flannel/network/subnets/172.17.98.0-24
================================================
--recursive : 再帰的に表示
-p : ディレクトリの場合は後ろに「/」が付く

### キーバリューの確認
$ etcdctl get /flannel/network/config
================================================
{"Network":"172.17.0.0/16"}
================================================
-> このレンジのアドレスが各ホストにアサインされる

$ etcdctl get /flannel/network/subnets/172.17.3.0-24
================================================
{"PublicIP":"10.136.3.184"}
================================================
-> 10.136.3.184のホストはこのIPレンジがアサインされる

$ etcdctl get /flannel/network/subnets/172.17.67.0-24
================================================
{"PublicIP":"10.136.3.185"}
================================================

$ etcdctl get /flannel/network/subnets/172.17.98.0-24
================================================
{"PublicIP":"10.136.3.183"}
================================================

### nodeで値を取得
[10.136.3.184(node1)]
$ curl http://10.136.3.183:2379/v2/keys
================================================
{"action":"get","node":{"dir":true,"nodes":[{"key":"/flannel","dir":true,"modifiedIndex":4,"createdIndex":4}]}}
================================================

$ curl http://10.136.3.183:2379/v2/keys/flannel/network/subnets/172.17.3.0-24 | jq
================================================
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100   214  100   214    0     0   199k      0 --:--:-- --:--:-- --:--:--  208k
{
  "action": "get",
  "node": {
    "key": "/flannel/network/subnets/172.17.3.0-24",
    "value": "{\"PublicIP\":\"10.136.3.184\"}",
    "expiration": "2017-04-22T09:47:54.099017257Z",
    "ttl": 21188,
    "modifiedIndex": 2425,
    "createdIndex": 2425
  }
}
================================================
```
-> valueのホストにkeyのレンジがアサインされる

## 参考

* [etcd概要](http://qiita.com/pocket8137/items/ef44ca68ffc0f4e70995)

# flanneldについて

* flanneldが無い場合
    * あるホストで稼働するコンテナに、別のホストで稼働しているコンテナからブリッジネットワーク経由でアクセスできない
        * docker network lsでbridgeと表示されるネットワーク
        * docker0(よくある172.17.〜)のネットワーク
* flanneldは各ホストで起動しているデーモンプロセス
    * flanneldにより各コンテナで一意なアドレスを持つようにできる
    * ホスト間のコーディネーションのような仕組みではない
        * 同じデータストアでルーティングテーブルを共有しているだけ
        * データストアはetcdが使われることが多い
* flanneldのネットワークはプライベートなアドレス
    * Kubernetesの外側からは叩け無い
    * Kubernetesのサービスのネットワークも別物
        * これもKubernetesの外側からは叩けない(そもそもifocnfigやipで確認できない)

## コマンドで確認

etcdで確認したものと同じネットワークがそれぞれのホストに割り当てられている

``` shell
[10.136.3.183(master)]
$ cat /run/flannel/docker
================================================
DOCKER_OPT_BIP="--bip=172.17.98.1/24"
DOCKER_OPT_IPMASQ="--ip-masq=true"
DOCKER_OPT_MTU="--mtu=1472"
DOCKER_NETWORK_OPTIONS=" --bip=172.17.98.1/24 --ip-masq=true --mtu=1472"
================================================

$ cat /run/flannel/subnet.env
================================================
FLANNEL_NETWORK=172.17.0.0/16
FLANNEL_SUBNET=172.17.98.1/24
FLANNEL_MTU=1472
FLANNEL_IPMASQ=false
================================================

$ ip a
================================================
...
3: flannel0: <POINTOPOINT,MULTICAST,NOARP,UP,LOWER_UP> mtu 1472 qdisc pfifo_fast state UNKNOWN qlen 500
    link/none
    inet 172.17.98.0/16 scope global flannel0
       valid_lft forever preferred_lft forever
4: docker0: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc noqueue state DOWN
    link/ether 02:42:d6:c1:17:ba brd ff:ff:ff:ff:ff:ff
    inet 172.17.98.1/24 scope global docker0
       valid_lft forever preferred_lft forever
================================================

$ netstat -rn
================================================
Kernel IP routing table
Destination     Gateway         Genmask         Flags   MSS Window  irtt Iface
0.0.0.0         10.136.2.3      0.0.0.0         UG        0 0          0 eno16780032
10.136.0.0      0.0.0.0         255.255.128.0   U         0 0          0 eno16780032
172.17.0.0      0.0.0.0         255.255.0.0     U         0 0          0 flannel0
172.17.98.0     0.0.0.0         255.255.255.0   U         0 0          0 docker0
================================================

[10.136.3.184(node1)]
$ cat /run/flannel/docker
================================================
DOCKER_OPT_BIP="--bip=172.17.3.1/24"
DOCKER_OPT_IPMASQ="--ip-masq=true"
DOCKER_OPT_MTU="--mtu=1472"
DOCKER_NETWORK_OPTIONS=" --bip=172.17.3.1/24 --ip-masq=true --mtu=1472"
================================================

$ cat /run/flannel/subnet.env
================================================
FLANNEL_NETWORK=172.17.0.0/16
FLANNEL_SUBNET=172.17.3.1/24
FLANNEL_MTU=1472
FLANNEL_IPMASQ=false
================================================

$ ip a
================================================
3: flannel0: <POINTOPOINT,MULTICAST,NOARP,UP,LOWER_UP> mtu 1472 qdisc pfifo_fast state UNKNOWN qlen 500
    link/none
    inet 172.17.3.0/16 scope global flannel0
       valid_lft forever preferred_lft forever
4: docker0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1472 qdisc noqueue state UP
    link/ether 02:42:b3:f5:03:12 brd ff:ff:ff:ff:ff:ff
    inet 172.17.3.1/24 scope global docker0
       valid_lft forever preferred_lft forever
================================================

$ netstat -rn
================================================
Kernel IP routing table
Destination     Gateway         Genmask         Flags   MSS Window  irtt Iface
0.0.0.0         10.136.2.3      0.0.0.0         UG        0 0          0 eno16780032
10.136.0.0      0.0.0.0         255.255.128.0   U         0 0          0 eno16780032
172.17.0.0      0.0.0.0         255.255.0.0     U         0 0          0 flannel0
172.17.3.0      0.0.0.0         255.255.255.0   U         0 0          0 docker0
================================================

[10.136.3.185(node2)]
$ cat /run/flannel/docker
================================================
DOCKER_OPT_BIP="--bip=172.17.67.1/24"
DOCKER_OPT_IPMASQ="--ip-masq=true"
DOCKER_OPT_MTU="--mtu=1472"
DOCKER_NETWORK_OPTIONS=" --bip=172.17.67.1/24 --ip-masq=true --mtu=1472"
================================================

$ cat /run/flannel/subnet.env
================================================
FLANNEL_NETWORK=172.17.0.0/16
FLANNEL_SUBNET=172.17.67.1/24
FLANNEL_MTU=1472
FLANNEL_IPMASQ=false
================================================

$ ip a
================================================
3: flannel0: <POINTOPOINT,MULTICAST,NOARP,UP,LOWER_UP> mtu 1472 qdisc pfifo_fast state UNKNOWN qlen 500
    link/none
    inet 172.17.67.0/16 scope global flannel0
       valid_lft forever preferred_lft forever
4: docker0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1472 qdisc noqueue state UP
    link/ether 02:42:2c:d9:cf:88 brd ff:ff:ff:ff:ff:ff
    inet 172.17.67.1/24 scope global docker0
       valid_lft forever preferred_lft forever
================================================

$ netstat -rn
================================================
Kernel IP routing table
Destination     Gateway         Genmask         Flags   MSS Window  irtt Iface
0.0.0.0         10.136.2.3      0.0.0.0         UG        0 0          0 eno16780032
10.136.0.0      0.0.0.0         255.255.128.0   U         0 0          0 eno16780032
172.17.0.0      0.0.0.0         255.255.0.0     U         0 0          0 flannel0
172.17.67.0     0.0.0.0         255.255.255.0   U         0 0          0 docker0
================================================

### master(10.136.3.183)からnode2(10.136.3.185)のコンテナ(172.17.67.2)に疎通確認
[10.136.3.183]
$ ping 172.17.67.2
================================================
PING 172.17.67.2 (172.17.67.2) 56(84) bytes of data.
64 bytes from 172.17.67.2: icmp_seq=1 ttl=61 time=0.574 ms
64 bytes from 172.17.67.2: icmp_seq=2 ttl=61 time=0.422 ms
64 bytes from 172.17.67.2: icmp_seq=3 ttl=61 time=0.256 ms
================================================
```

＃＃ 参考

* [CoreOS + flannel で複数ホスト間コンテナ通信をする](http://qiita.com/dtan4/items/8f9cf40aabd2e6c9a494)

# Kubernetesについて

コンテナ型仮想化のDockerを管理するフレームワーク

* Kubernetesがやること
    * 複数のコンテナをデプロイ
        * どのホストにどのコンテナを配備するかを隠蔽
    * コンテナ間のネットワーク管理(名前空間含む）
        * サービスディスカバリ相当
    * コンテナの死活管理
        * コンテナが死んだら自動で起動
    * コンテナの負荷分散
        * 同一機能の複数コンテナへのアクセスをバランシング
    * コンテナのリソースアロケーション
        * コンテナ毎にCPUやメモリのわりあて
* ひとつのプロセスで動くプロセスの数を最小限にして、代わりにコンテナ群を管理する
    * 理屈で言えばコンテナは1つのOS相当なので複数のサービスを動かす事ができる
    * 簡易化のためにここでは1コンテナ1プロセス

## 用語

* Pod
    * 1つ以上のコンテナの集まり
    * 複数のコンテナがある場合は共有されたボリュームも管理する
    * Pod内のコンテナは必ず同じホスト
    * Label
        * Podにつけるラベル
        * 例) Production、Development、Staging、Frontend、Backendなど
        * 複数ラベルが付けられる
* ReplicaSet
    * Podを生成、管理する
        * PodTemplateから指定された数のレプリカを作成して維持する
        * 起動中のPodの監視をしていて何らかの原因でPodが減ったり増えたりしたら、元の数に戻す
    * Replication Controllerの後継
* Deployment
    * ReplicaSetを生成、管理
    * デプロイ管理
        * ローリングアップデート
            * 古いバージョンのコンテナから新しいバージョンのコンテナに徐々に置き換えていくなど
            * 新旧のPod共に共通するラベルを持っており、Serviceを通して新旧混ざった状態でサービスを提供できる
        * ロールバック
            * ローリングアップデート後もアップデート前のReplicaSetは一定数保持される
    * [Kubernetes: Deployment の仕組み](http://qiita.com/tkusumi/items/01cd18c59b742eebdc6a)
* Service
    * L3のプロキシみたいな役割
    * 設定したPodにラウンドロビンでアクセスを分配する

## コンポーネント

* master
    * kube-apiserver
        * Kubernetesのリソースを管理するAPIサーバー
    * etcd
    * kube-controller-manager
        * 各種コントローラーを起動し管理するマネージャー
        * Node Controller: ノードが落ちたときにアラートを出したら何か処理をする
        * Replication Controller: Podがちゃんと定められた数稼働しているかなどを管理する
        * Endpoints Controller: エンドポイントを移したりする
        * Service Account & Token Controllers:　デフォルトアカウントを作ったり新しいnamespaceにアクセストークンを作ったりする
    * cloud-controller-manager
        * クラウドプロバイダとやりとりするマネージャー
        * Kubernetes 1.6から
    * kube-scheduler
        * Podのノードへの割り当てを行うスケジューラー
    * DNS
* node
    * kubelet
        * Podを起動し管理するエージェント(Nodeのメイン処理)
    * kube-proxy
        * KubernetesのServiceが持つ仮想的なIPアドレス(cluster IP)へのアクセスをルーティングする
* client
    * kubectl
        * KubernetesのCLIクライアント

## コマンド

### Deployment関連

|レプリカセット|デプロイリビジョン|nginxのバージョン|
|---|---|---|
|rs/deployment-example-2748039730|1|1.10|
|rs/deployment-example-2829304371|2|1.11|

デプロイ

``` shell
### --record : kubectlのコマンドをアノテーションに保存
$ kubectl create -f deployment-example.yaml --record
================================================
deployment "deployment-example" created
================================================

### 設定したラベルを指定して関連リソース
### --selector : ラベルを指定するオプション
### app=<lavel>
$ kubectl get deployments,replicasets,pods --selector app=deployment-example
================================================
NAME                        DESIRED   CURRENT   UP-TO-DATE   AVAILABLE   AGE
deploy/deployment-example   2         2         2            0           33s

NAME                               DESIRED   CURRENT   READY     AGE
rs/deployment-example-2748039730   2         2         0         33s

NAME                                     READY     STATUS              RESTARTS   AGE
po/deployment-example-2748039730-2q8vr   0/1       ContainerCreating   0          33s
po/deployment-example-2748039730-sm7z3   0/1       ContainerCreating   0          33s
================================================
```

ローリングアップデート

``` shell
### deployment-exampleの中身をさっきと変更して実行
$ kubectl apply -f deployment-example.yaml --record
================================================
deployment "deployment-example" configured
================================================

### ローリングアップデートされている様子を表示
$　kubectl get deployments,replicasets,pods --selector app=deployment-example
================================================
NAME                        DESIRED   CURRENT   UP-TO-DATE   AVAILABLE   AGE
deploy/deployment-example   2         3         2            1           11m

NAME                               DESIRED   CURRENT   READY     AGE
rs/deployment-example-2748039730   1         1         1         11m
rs/deployment-example-2829304371   2         2         0         20s

NAME                                     READY     STATUS              RESTARTS   AGE
po/deployment-example-2748039730-2q8vr   1/1       Running             0          11m
po/deployment-example-2829304371-gnrf2   0/1       ContainerCreating   0          20s
po/deployment-example-2829304371-wmds5   0/1       ContainerCreating   0          20s
================================================

### ローリングアップデート完了後
$　kubectl get deployments,replicasets,pods --selector app=deployment-example
================================================
NAME                        DESIRED   CURRENT   UP-TO-DATE   AVAILABLE   AGE
deploy/deployment-example   2         2         2            2           16m

NAME                               DESIRED   CURRENT   READY     AGE
rs/deployment-example-2748039730   0         0         0         16m
rs/deployment-example-2829304371   2         2         2         5m

NAME                                     READY     STATUS    RESTARTS   AGE
po/deployment-example-2829304371-gnrf2   1/1       Running   0          5m
po/deployment-example-2829304371-wmds5   1/1       Running   0          5m
================================================
```

ロールバック

``` shell
### デプロイの履歴を表示
$ kubectl rollout history deployment deployment-example
================================================
REVISION	CHANGE-CAUSE
1		kubectl create -f deployment-example.yaml --record
2		kubectl apply -f deployment-example.yaml --record
================================================

### リビジョンの詳細確認
$ kubectl rollout history deployment deployment-example --revision=1
================================================
deployments "deployment-example" with revision #1
  Labels:	app=deployment-example
	pod-template-hash=2748039730
  Annotations:	kubernetes.io/change-cause=kubectl create -f deployment-example.yaml --record
  Containers:
   nginx:
    Image:	nginx:1.10
    Port:	80/TCP
    Volume Mounts:	<none>
    Environment Variables:	<none>
  No volumes.
================================================

### リビジョンを指定してロールバックを実行
### 指定しない場合は一つ前のリビジョンになる
$ kubectl rollout undo deployment deployment-example --to-revision=1
================================================
deployment "deployment-example" rolled back
================================================

### 前のバージョンに戻ったことを確認
$ kubectl get deployments,replicasets,pods --selector app=deployment-example
================================================
NAME                        DESIRED   CURRENT   UP-TO-DATE   AVAILABLE   AGE
deploy/deployment-example   2         2         2            2           34m

NAME                               DESIRED   CURRENT   READY     AGE
rs/deployment-example-2748039730   2         2         2         34m
rs/deployment-example-2829304371   0         0         0         23m

NAME                                     READY     STATUS    RESTARTS   AGE
po/deployment-example-2748039730-0rcn0   1/1       Running   0          1m
po/deployment-example-2748039730-44h4v   1/1       Running   0          1m
================================================

```

### Pod関連

``` shell
### 全てのネームスペースのポッドの情報を表示
$ kubectl get pods --all-namespaces
================================================
NAMESPACE       NAME                                    READY     STATUS    RESTARTS   AGE
default         deployment-example-2829304371-gnrf2     1/1       Running   0          10m
default         deployment-example-2829304371-wmds5     1/1       Running   0          10m
joruri          joruri-ldap-centos7                     1/1       Running   0          1d
kube-system     kubernetes-dashboard-4108885099-mc042   1/1       Running   0          2d
================================================

### Podの詳細情報を表示
$ kubectl describe pods --selector=app=deployment-example
================================================
Name:		deployment-example-2829304371-gnrf2
Namespace:	default
Node:		10.136.3.185/10.136.3.185
Start Time:	Sun, 23 Apr 2017 20:12:10 +0900
Labels:		app=deployment-example
		pod-template-hash=2829304371
Status:		Running
IP:		172.17.67.3
Controllers:	ReplicaSet/deployment-example-2829304371
Containers:
  nginx:
    Container ID:	docker://c4c539fb48f5a15b45f375eee0cfef4bd00039dc2fb2a75c9188e5e26d1db047
    Image:		nginx:1.11
    Image ID:		docker-pullable://docker.io/nginx@sha256:e6693c20186f837fc393390135d8a598a96a833917917789d63766cab6c59582
    Port:		80/TCP
    State:		Running
      Started:		Sun, 23 Apr 2017 20:12:48 +0900
    Ready:		True
    Restart Count:	0
    Volume Mounts:
      /var/run/secrets/kubernetes.io/serviceaccount from default-token-wpzd6 (ro)
    Environment Variables:	<none>
Conditions:
  Type		Status
  Initialized 	True
  Ready 	True
  PodScheduled 	True
Volumes:
  default-token-wpzd6:
    Type:	Secret (a volume populated by a Secret)
    SecretName:	default-token-wpzd6
QoS Class:	BestEffort
Tolerations:	<none>
Events:
  FirstSeen	LastSeen	Count	From			SubObjectPath		Type		Reason		Message
  ---------	--------	-----	----			-------------		--------	------		-------
  17m		17m		1	{default-scheduler }				Normal		Scheduled	Successfully assigned deployment-example-2829304371-gnrf2 to 10.136.3.185
  17m		17m		1	{kubelet 10.136.3.185}	spec.containers{nginx}	Normal		Pulling		pulling image "nginx:1.11"
  16m		16m		1	{kubelet 10.136.3.185}	spec.containers{nginx}	Normal		Pulled		Successfully pulled image "nginx:1.11"
  16m		16m		1	{kubelet 10.136.3.185}	spec.containers{nginx}	Normal		Created		Created container with docker id c4c539fb48f5; Security:[seccomp=unconfined]
  16m		16m		1	{kubelet 10.136.3.185}	spec.containers{nginx}	Normal		Started		Started container with docker id c4c539fb48f5


Name:		deployment-example-2829304371-wmds5
Namespace:	default
Node:		10.136.3.184/10.136.3.184
Start Time:	Sun, 23 Apr 2017 20:12:10 +0900
Labels:		app=deployment-example
		pod-template-hash=2829304371
Status:		Running
IP:		172.17.3.4
Controllers:	ReplicaSet/deployment-example-2829304371
Containers:
  nginx:
    Container ID:	docker://f9c7a7f709aa846993f4ac6c2da5ac3ad78ad09f012b8902c9a3d37668254068
    Image:		nginx:1.11
    Image ID:		docker-pullable://docker.io/nginx@sha256:e6693c20186f837fc393390135d8a598a96a833917917789d63766cab6c59582
    Port:		80/TCP
    State:		Running
      Started:		Sun, 23 Apr 2017 20:12:45 +0900
    Ready:		True
    Restart Count:	0
    Volume Mounts:
      /var/run/secrets/kubernetes.io/serviceaccount from default-token-wpzd6 (ro)
    Environment Variables:	<none>
Conditions:
  Type		Status
  Initialized 	True
  Ready 	True
  PodScheduled 	True
Volumes:
  default-token-wpzd6:
    Type:	Secret (a volume populated by a Secret)
    SecretName:	default-token-wpzd6
QoS Class:	BestEffort
Tolerations:	<none>
Events:
  FirstSeen	LastSeen	Count	From			SubObjectPath		Type		Reason		Message
  ---------	--------	-----	----			-------------		--------	------		-------
  17m		17m		1	{default-scheduler }				Normal		Scheduled	Successfully assigned deployment-example-2829304371-wmds5 to 10.136.3.184
  17m		17m		1	{kubelet 10.136.3.184}	spec.containers{nginx}	Normal		Pulling		pulling image "nginx:1.11"
  16m		16m		1	{kubelet 10.136.3.184}	spec.containers{nginx}	Normal		Pulled		Successfully pulled image "nginx:1.11"
  16m		16m		1	{kubelet 10.136.3.184}	spec.containers{nginx}	Normal		Created		Created container with docker id f9c7a7f709aa; Security:[seccomp=unconfined]
  16m		16m		1	{kubelet 10.136.3.184}	spec.containers{nginx}	Normal		Started		Started container with docker id f9c7a7f709aa
================================================
```

# 定義例

## Deployment

``` shell
apiVersion: extensions/v1beta1   ### ※1 Kubernetes APIのバージョンで変わる
kind: Deployment    ### オブジェクトの種類を指定
metadata:
  name: nginx-deployment   ### Deploymentの名前。Namespace内ではユニークである必要がある
spec:
  replicas: 2  ### レプリカ数の指定
  template:    ### Podのテンプレート(PodTemplate)
    metadata:
      labels:
        app: nginx-deployment   ### ラベル指定は必須
    spec:
      containers:
      - name: nginx
        image: nginx:1.10
        ports:
        - containerPort: 80     ### コンテナ側の待ち受けport
```

* ※1 [Kubernetes API Overview](https://kubernetes.io/docs/api/) を参照
    * Kubernetes API 1.5 は `apiVersion: extensions/v1beta1`
    * Kubernetes API 1.6 は `apiVersion: apps/v1beta1`
    * `kubectl --version ` でKubernetesのバージョンを確認できる
        * この環境の実行結果は `Kubernetes v1.5.2` なので `apiVersion: extensions/v1beta1`

# Namespace


# 参考

* [Kubernetesにまつわるエトセトラ](https://www.slideshare.net/WorksApplications/kubernetes-65070472)
* [Kubernetes: 構成コンポーネント一覧](http://qiita.com/tkusumi/items/c2a92cd52bfdb9edd613)