GKEでkubernetesを理解する

![image](https://qiita-image-store.s3.amazonaws.com/0/142397/484fc8d3-4c22-ce4e-00eb-69d4d822d9c6.png)



Kubernetesはコンテナを使ったシステムのライフサイクルを管理するオープンソースです。
大体のインフラ[^1]で動きますが、今回はKubernetesの環境が予め用意されているGKEを使って、使う側の視点で学びたいと思います。

[^1]: [Picking the Right Solution - kubernetes.io](http://kubernetes.io/docs/getting-started-guides/#custom-solutions)

## 大雑把な理解

まず、ざっくり構成を把握するために図を見てみます
。

![image](https://qiita-image-store.s3.amazonaws.com/0/142397/3f47ca33-0e1f-e464-4787-3fb9732d658f.png)
図：[Kubernetes architecture](https://github.com/kubernetes/kubernetes/blob/master/docs/design/architecture.md)

左側がKubernetesのコントローラーで、右側が管理対象のコンテナです。以下の単語を簡単に抑えておきます。

- Node・・Dockerが動くマシンのこと。
- Pod・・コンテナを配置する入れ物。この単位でスケールさせたりします。
- Proxy・・コンテナとの通信を経由するプロキシ。

APIやコマンドラインで管理するときに、以下の単語が頻繁に出てくるのでざっくり覚えておきます。

- Deployments [^2]・・Pod（コンテナ）を管理するもの
- Service [^3]・・Pod（コンテナ）と通信を維持するためのポリシーなどを定義するもの

[^2]: [Deployments - kubernetes.io](http://kubernetes.io/docs/user-guide/deployments/)
[^3]: [Services - kubernetes.io](http://kubernetes.io/docs/user-guide/services/)


## 何で学ぶ？

以下のチュートリアルを使ってkubernetesを理解していきたいと思います。

- [Redis と PHP を使用したゲストブックの作成][Tutorial]

[Tutorial]:https://cloud.google.com/container-engine/docs/tutorials/guestbook)

チュートリアルに記載してある情報が古い(ReplicptionControllerの作成からDeploymentの作成に変わっている)ので以下も参考にしながら進めます。

- [Guestbook Example](https://github.com/kubernetes/kubernetes/blob/release-1.2/examples/guestbook/README.md)


こんな形なものを作ります。

```
                +--------------+
                | LoadBalancer |
                +--------------+
                       |
                +--------------+
                |   frontend   |
                +--------------+
                   |        |
        +--------------+ +--------------+
        | redis-master | | redis-slave  |
        +--------------+ +--------------+
```

## コマンド環境の準備
`gcloud` コマンドと `kubectl` コマンドを以下の通りに従ってセットアップしておきます。
[Container Engine 開始方法](https://cloud.google.com/container-engine/docs/before-you-begin?authuser=1#install_the_gcloud_command_line_interface)

使う環境をデフォルトで設定しておく。コマンドを打つ度に指定しなくても済むので。

```bash
$ gcloud config set project PROJECT_ID # プロジェクトIDの指定

$ gcloud config set compute/zone asia-northeast1-a # ゾーンの指定

$ gcloud config list # 現在の設定を確認
Your active configuration is: [default]

[compute]
region = asia-northeast1
zone = asia-northeast1-a
[core]
account = EMAIL_ADDRESS
disable_usage_reporting = False
project = PROJECT_ID
```


## クラスタ（ノード）の作成
最初に、コンテナを配置するクラスタ（ノード）を作成しておきます。デフォルトで3ノード数作成されますが、今回はノード数を1に指定して作成しました。

```bash
$ gcloud container clusters create guestbook --num-nodes=1
Creating cluster guestbook...done.                                                 
Created [https://container.googleapis.com/v1/projects/{PROJECT_ID}/zones/asia-northeast1-a/clusters/guestbook].
kubeconfig entry generated for guestbook.
NAME       ZONE               MASTER_VERSION  MASTER_IP        MACHINE_TYPE   NODE_VERSION  NUM_NODES  STATUS
guestbook  asia-northeast1-a  1.4.7           104.198.127.169  n1-standard-1  1.4.7         1          RUNNING
```

既にKubernetesも起動しているので、以下のコマンドでローカルプロキシを起動させてみます。


```bash
$ kubectl proxy
Starting to serve on 127.0.0.1:8001
```

http://localhost:8001/ui/ へにアクセスするとKubernetesの画面が現れます。見た目がイケてますね。 :heart_eyes:


![image](https://qiita-image-store.s3.amazonaws.com/0/142397/0733ca35-fff9-cc22-aa67-ccb41372dc94.png)


## Redisマスターの作成
Deployment（Podとコンテナ）を作成します。

- redis-master-deployment.yaml ファイル

```yaml:redis-master-deployment.yaml 
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
  name: redis-master # 名前の指定（必須）
spec:
  replicas: 1 # コンテナのレプリカ数の設定
  template:
    metadata:
      labels: # ラベルをKV型で設定
        app: redis
        role: master
        tier: backend
    spec:
      containers:
      - name: master # コンテナの名前
        image: gcr.io/google_containers/redis:e2e # コンテナイメージの指定（必須）
        resources:
          requests:
            cpu: 100m # CPUを0.1コア確保
            memory: 100Mi # メモリ100MiB確保
        ports:
        - containerPort: 6379 # コンテナポートの指定
```

- `kube create`でこのファイルを読ませるとyamlに記載したとおりのコンテナを作成してくれます。

```bash
$ kubectl create -f redis-master-deployment.yaml
deployment "redis-master" created

- Deployment一覧を表示する

```bash
$ kubectl get deployment
NAME           DESIRED   CURRENT   UP-TO-DATE   AVAILABLE   AGE
redis-master   1         1         1            1           2m
```

- Pod一覧を表示する

```bash
$ kubectl get pod
NAME                           READY     STATUS    RESTARTS   AGE
redis-master-343230949-6dmjd   1/1       Running   0          2m
```

Redisのコンテナが出来ました :tada: 

### 寄り道（kubectl run コマンド）
yamlを作成しなくても、`kubectl run`コマンドで同じく作成することができます。

- `kubectl run` コマンド

```bash
$ kubectl run redis-master \
> --image=gcr.io/google_containers/redis:e2e \
> --labels="app=redis,role=master,tier=backend" \
> --requests="cpu=100m,memory=100Mi" \
> --port=6379
deployment "redis-master-run" created
```

実は、これだとコンテナ名(実際のコンテナ名ではない)が`redis-master`（デフォルトでPod名）になってしまい、完全に同一ではありません。コンテナ名を設定するコマンド引数[^4]が見つからなかったので、`--overrides`オプションで無理やり設定してみました。

[^4]: [kube run コマンドリファレンス](http://kubernetes.io/docs/user-guide/kubectl/kubectl_run/)

- `--overrides`オプションをつけて`kubectl run`

```bash
$ kubectl run redis-master \
> --image=gcr.io/google_containers/redis:e2e \
> --labels="app=redis,role=master,tier=backend" \
> --overrides='{"spec":{"template":{"spec":{"containers":[{"name":"master","image":"gcr.io/google_containers/redis:e2e","resources":{"requests":{"cpu":"100m","memory":"100Mi"}},"ports":[{"containerPort":6379}]}]}}}}'
deployment "redis-master" created
```

※ `--image`オプションは必須なため、`--overrides`内とコンテナイメージのロケーションを二重で記載する必要があります。。 :sweat_smile: 殆どの部分をyamlからjsonに変換してる感じでちょっと無意味感。。

念のため `kube describe pod <POD_NAME>`の内容を比較してみます。

```diff:describeした内容を比較
$ diff -u redis-master.describe redis-master_run.describe 
--- redis-master.describe 2016-12-29 23:38:58.000000000 +0900
+++ redis-master_run.describe 2016-12-29 23:41:49.000000000 +0900
@@ -1,17 +1,17 @@
-Name:    redis-master-343230949-6dmjd
+Name:    redis-master-343230949-n0sxq
 Namespace: default
 Node:    gke-guestbook-default-pool-af5eb675-xszz/10.146.0.2
-Start Time:  Thu, 29 Dec 2016 22:37:55 +0900
+Start Time:  Thu, 29 Dec 2016 23:40:41 +0900
 Labels:    app=redis
    pod-template-hash=343230949
    role=master
    tier=backend
 Status:    Running
-IP:    10.52.0.11
+IP:    10.52.0.15
 Controllers: ReplicaSet/redis-master-343230949
 Containers:
   master:
-    Container ID:  docker://2f57187ee653673df16d40532528086475aadbff9994540541147285600de612
+    Container ID:  docker://49226c07f4a0ab49503cc46eea01c9382ffe3b62d9d27170ccd8b21f04500365
     Image:   gcr.io/google_containers/redis:e2e
     Image ID:    docker://sha256:e5e67996c442f903cda78dd983ea6e94bb4e542950fd2eba666b44cbd303df42
     Port:    6379/TCP
@@ -19,7 +19,7 @@
       cpu:   100m
       memory:    100Mi
     State:   Running
-      Started:   Thu, 29 Dec 2016 22:38:24 +0900
+      Started:   Thu, 29 Dec 2016 23:40:42 +0900
     Ready:   True
     Restart Count: 0
     Volume Mounts:
```

IPアドレスや日付以外で変更点はないので、同一な設定で作成できていることが分かります :tada: 



### 寄り道（Deployment）

Deploymentは比較的新しく、Podの作成やローリングアップデートやロールバックなどを簡単にしてくれるものです。
[Using Deployment objects with Kubernetes 1.2](http://blog.kubernetes.io/2016/04/using-deployment-objects-with.html)

それまでは Replication Controller[^5]がその辺りの役割を担っていたこともあり、[Tutorial][]ではReplication Controller で Pod を作成して説明しています。今のバージョン（1.4.7）では、DeploymentsでPodを作成すると、Replication Controller の次世代版である `Replica Sets`[^6] が動き出します。Replication Controller は無くなってしまうのかなぁ。。

[^5]: [Replication Controller - kubernetes.io](http://kubernetes.io/docs/user-guide/replication-controller/)
[^6]: [Replica Sets - kubernetes.io](http://kubernetes.io/docs/user-guide/replicasets/)


ちなみに、Kubernetesの管理UIでは未だ Replication Controller がメインになっている様で、UIからPodを作成すると Replication Controller が作成されます。
試してみます。

- コマンドラインからコンテナ（Pod）を作成

```bash
$ kubectl run cli --image=gcr.io/google-samples/gb-frontend:v4
deployment "cli" created
```

〜 ここで、KubernetesのUIから`ui`という名前でコンテナを作成 〜

- 2つのPodが作成されています

```bash
$ kubectl get pod
NAME                   READY     STATUS    RESTARTS   AGE
cli-2068554765-12stb   1/1       Running   0          1m
ui-sbpaz               1/1       Running   0          22s
```

- UIから作成したコンテナは Replication Controller になっています

```bash
$ kubectl get replicationcontroller
NAME      DESIRED   CURRENT   READY     AGE
ui        1         1         1         45s
```

- コマンドラインからはDeploymentになっています

```bash
$ kubectl get deployment
NAME      DESIRED   CURRENT   UP-TO-DATE   AVAILABLE   AGE
cli       1         1         1            1           1m
```

yamlの設定自体は殆ど変わりません。強いて挙げると、metadataの箇所でlabelsを省略している所ぐらいです。

```diff:TutorialにあるRC用とDeployment用yamlの比較
--- legacy/redis-master-controller.yaml 2016-12-28 01:18:16.000000000 +0900
+++ redis-master-deployment.yaml  2016-12-28 01:43:37.000000000 +0900
@@ -1,13 +1,13 @@
-apiVersion: v1
-kind: ReplicationController
+apiVersion: extensions/v1beta1
+kind: Deployment
 metadata:
   name: redis-master
   # these labels can be applied automatically 
   # from the labels in the pod template if not set
-  labels:
-    app: redis
-    role: master
-    tier: backend
+  # labels:
+  #   app: redis
+  #   role: master
+  #   tier: backend
 spec:
   # this replicas value is default
   # modify it according to your case
@@ -15,9 +15,10 @@
   # selector can be applied automatically 
   # from the labels in the pod template if not set
   # selector:
-  #   app: guestbook
-  #   role: master
-  #   tier: backend
+  #   matchLabels:
+  #     app: guestbook
+  #     role: master
+  #     tier: backend
   template:
     metadata:
       labels:
```

### 寄り道（ノードにSSHログイン）

ノードにSSHログインすることもできます。

- `-o wide`でPodが配置されているノードIDを表示させます。

```bash
$ kubectl get pod -o wide
NAME                           READY     STATUS    RESTARTS   AGE       IP           NODE
redis-master-343230949-n0sxq   1/1       Running   0          11m       10.52.0.15   gke-guestbook-default-pool-af5eb675-xszz
```

- 上記ノードを指定してSSHログインします。

```bash
$ gcloud compute ssh gke-guestbook-default-pool-af5eb675-xszz
Warning: Permanently added 'compute.7579721538501918346' (RSA) to the list of known hosts.

Welcome to Kubernetes v1.4.7!

You can find documentation for Kubernetes at:
  http://docs.kubernetes.io/

The source for this release can be found at:
  /home/kubernetes/kubernetes-src.tar.gz
Or you can download it at:
  https://storage.googleapis.com/kubernetes-release/release/v1.4.7/kubernetes-src.tar.gz

It is based on the Kubernetes source at:
  https://github.com/kubernetes/kubernetes/tree/v1.4.7

For Kubernetes copyright and licensing information, see:
  /home/kubernetes/LICENSES
user@gke-guestbook-default-pool-af5eb675-xszz ~ $
```

- ログイン出来たので"docker ps"コマンドを実行してみます。kubernetes関連のコンテナが沢山動いてますね。

```bash
user@gke-guestbook-default-pool-af5eb675-xszz ~ $ docker ps
CONTAINER ID        IMAGE                                                                  COMMAND                  CREATED             STATUS              PORTS               NAMES
49226c07f4a0        gcr.io/google_containers/redis:e2e                                     "redis-server /etc/re"   12 minutes ago      Up 12 minutes                           k8s_master.b505b5b1_redis-master-343230949-n0sxq_default_c5e1c119-cdd4-11e6-9059-42010a920020_d5ed9c19
f2e1a93483ef        gcr.io/google_containers/pause-amd64:3.0                               "/pause"                 12 minutes ago      Up 12 minutes                           k8s_POD.d8dbe16c_redis-master-343230949-n0sxq_default_c5e1c119-cdd4-11e6-9059-42010a920020_594d5d61
ed54277b3108        asia.gcr.io/google_containers/addon-resizer:1.6                        "/pod_nanny --cpu=80m"   2 hours ago         Up 2 hours                              k8s_heapster-nanny.55df2c69_heapster-v1.2.0-1847815531-zrkw6_kube-system_378ff7ba-cdc2-11e6-9059-42010a920020_84f3de55
22a3bf8a8816        asia.gcr.io/google_containers/heapster:v1.2.0                          "/heapster --source=k"   2 hours ago         Up 2 hours                              k8s_heapster.d8c9b305_heapster-v1.2.0-1847815531-zrkw6_kube-system_378ff7ba-cdc2-11e6-9059-42010a920020_b3e26ab1
b4bb94588700        gcr.io/google_containers/pause-amd64:3.0                               "/pause"                 2 hours ago         Up 2 hours                              k8s_POD.d8dbe16c_heapster-v1.2.0-1847815531-zrkw6_kube-system_378ff7ba-cdc2-11e6-9059-42010a920020_f00e16d7
262de7613d9e        asia.gcr.io/google_containers/exechealthz-amd64:1.2                    "/exechealthz '--cmd="   2 hours ago         Up 2 hours                              k8s_healthz.aaa6418e_kube-dns-v20-8uddr_kube-system_25491b5f-cdc2-11e6-9059-42010a920020_19442f61
218ef4491c93        asia.gcr.io/google_containers/kube-dnsmasq-amd64:1.4                   "/usr/sbin/dnsmasq --"   2 hours ago         Up 2 hours                              k8s_dnsmasq.daf813d2_kube-dns-v20-8uddr_kube-system_25491b5f-cdc2-11e6-9059-42010a920020_da4ffbd1
02b6c5abe863        asia.gcr.io/google_containers/kubedns-amd64:1.8                        "/kube-dns --domain=c"   2 hours ago         Up 2 hours                              k8s_kubedns.dc925936_kube-dns-v20-8uddr_kube-system_25491b5f-cdc2-11e6-9059-42010a920020_226b6b62
9fc09d1f352d        asia.gcr.io/google_containers/defaultbackend:1.0                       "/server"                2 hours ago         Up 2 hours                              k8s_default-http-backend.8208ca2c_l7-default-backend-v1.0-shaxn_kube-system_251d5635-cdc2-11e6-9059-42010a920020_22aef95d
61d5ea514139        asia.gcr.io/google_containers/kubernetes-dashboard-amd64:v1.4.0        "/dashboard --port=90"   2 hours ago         Up 2 hours                              k8s_kubernetes-dashboard.44adcd81_kubernetes-dashboard-v1.4.0-ev5z4_kube-system_2538ca75-cdc2-11e6-9059-42010a920020_3c45962e
ddc295bd5b86        asia.gcr.io/google_containers/fluentd-gcp:1.21                         "/bin/sh -c 'rm /lib/"   2 hours ago         Up 2 hours                              k8s_fluentd-cloud-logging.9d1761e_fluentd-cloud-logging-gke-guestbook-default-pool-af5eb675-xszz_kube-system_9d40f2ab7a284a2e847ba6ea821d674b_08346356
b5a7f7a969fe        gcr.io/google_containers/pause-amd64:3.0                               "/pause"                 2 hours ago         Up 2 hours                              k8s_POD.d8dbe16c_kube-dns-v20-8uddr_kube-system_25491b5f-cdc2-11e6-9059-42010a920020_cfddc70c
2625088adb2d        gcr.io/google_containers/pause-amd64:3.0                               "/pause"                 2 hours ago         Up 2 hours                              k8s_POD.d8dbe16c_l7-default-backend-v1.0-shaxn_kube-system_251d5635-cdc2-11e6-9059-42010a920020_2eb5e129
6904e351db39        gcr.io/google_containers/pause-amd64:3.0                               "/pause"                 2 hours ago         Up 2 hours                              k8s_POD.d8dbe16c_kubernetes-dashboard-v1.4.0-ev5z4_kube-system_2538ca75-cdc2-11e6-9059-42010a920020_536e1b7b
7672d253bc84        gcr.io/google_containers/pause-amd64:3.0                               "/pause"                 2 hours ago         Up 2 hours                              k8s_POD.d8dbe16c_fluentd-cloud-logging-gke-guestbook-default-pool-af5eb675-xszz_kube-system_9d40f2ab7a284a2e847ba6ea821d674b_85d10a63
16bdffccc672        gcr.io/google_containers/kube-proxy:31be43b1cb619b7b0ab8fe01f997fe2f   "/bin/sh -c 'kube-pro"   2 hours ago         Up 2 hours                              k8s_kube-proxy.3ea31aa7_kube-proxy-gke-guestbook-default-pool-af5eb675-xszz_kube-system_0a4a2e1c974bec66debcc5815d60085a_0833b062
9cacc1697cf5        gcr.io/google_containers/pause-amd64:3.0                               "/pause"                 2 hours ago         Up 2 hours                              k8s_POD.d8dbe16c_kube-proxy-gke-guestbook-default-pool-af5eb675-xszz_kube-system_0a4a2e1c974bec66debcc5815d60085a_20190c5a
```

### 寄り道（SSHログインユーザー）
SSHログインの書式は、`gcloud compute ssh  [USER@]INSTANCE` でUSERをつけないとローカル環境のユーザー名で接続します（通常のSSHと同様な動き）。ユーザーは自動的に作成されGCE側へ登録されます。
登録されているユーザーとSSH公開鍵は以下で確認することができます。
https://console.cloud.google.com/compute/metadata/sshKeys


## Redis マスターのサービスの作成

コンテナと通信させるためのServiceを作成します。

- redis-master-service.yaml ファイル

```yaml:redis-master-service.yaml 
apiVersion: v1
kind: Service
metadata:
  name: redis-master
  labels:
    app: redis
    role: master
    tier: backend
spec:
  ports:
  - port: 6379
    targetPort: 6379
  selector:
    app: redis
    role: master
    tier: backend
```

- yamlファイルを読み込んでサービスを作成します。

```bash
$ kubectl create -f redis-master-service.yaml
service "redis-master" created
```

- サービスの情報を表示します。

```bash
$ kubectl get service redis-master
NAME           CLUSTER-IP      EXTERNAL-IP   PORT(S)    AGE
redis-master   10.55.246.114   <none>        6379/TCP   43s
```

- もちろん、コマンドラインでサービスを作成することもできます。

```bash
$ kubectl expose service redis-master \
> --labels="app=redis,role=master,tier=backend" \
> --selector="app=redis,role=master,tier=backend" \
> --port=6379 \
> --target-port=6379
```

## Redis スレーブのコンテナとサービス作成

Redisマスターと同様にコンテナとサービスを作成していきます。

- redis-slave-deployment.yaml ファイル

```yaml:redis-slave-deployment.yaml 
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
  name: redis-slave
spec:
  replicas: 2 # レプリカ数を2に指定
  template:
    metadata:
      labels:
        app: redis
        role: slave
        tier: backend
    spec:
      containers:
      - name: slave
        image: gcr.io/google_samples/gb-redisslave:v1
        resources:
          requests:
            cpu: 100m
            memory: 100Mi
        env:
        - name: GET_HOSTS_FROM # 環境変数をセットしています。PHPのアプリケーションで使います。
          value: dns
        ports:
        - containerPort: 6379
```

- コンテナを作成してPod数が2になっていることを確認します。

```bash
$ kubectl create -f redis-slave-deployment.yaml 
deployment "redis-slave" created

$ kubectl get deployment redis-slave
NAME          DESIRED   CURRENT   UP-TO-DATE   AVAILABLE   AGE
redis-slave   2         2         2            0           24s # 2になっている

$ kubectl get pod 
NAME                           READY     STATUS    RESTARTS   AGE
redis-master-343230949-08kqi   1/1       Running   0          7m
redis-slave-132015689-3x3qp    1/1       Running   0          48s # 1Pod目
redis-slave-132015689-b6cha    1/1       Running   0          48s # 2Pod目
```

redis-slaveのPodが2つあるのが分かります。次にサービスを作成します。

- redis-slave-service.yaml ファイル

```yaml:redis-slave-service.yaml 
apiVersion: v1
kind: Service
metadata:
  name: redis-slave
  labels:
    app: redis
    role: slave
    tier: backend
spec:
  ports:
  - port: 6379
  selector:
    app: redis
    role: slave
    tier: backend
```

- redis-slaveのサービスを作成して確認します。

```bash
$ kubectl create -f redis-slave-service.yaml
service "redis-slave" created

$ kubectl get svc redis-slave # servicesを"svc"と省略することができます。
NAME          CLUSTER-IP      EXTERNAL-IP   PORT(S)    AGE
redis-slave   10.55.255.111   <none>        6379/TCP   18s
```

## フロントエンド ウェブサーバーの作成

バックエンド側（redis）は作成したので、次からフロントエンドを作成していきます。

- front-deployment.yaml ファイル

```yaml:frontend-deployment.yaml
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
  name: frontend
spec:
  replicas: 3
  template:
    metadata:
      labels:
        app: guestbook
        tier: frontend
    spec:
      containers:
      - name: php-redis
        image: gcr.io/google-samples/gb-frontend:v4
        resources:
          requests:
            cpu: 100m
            memory: 100Mi
        env:
        - name: GET_HOSTS_FROM
          value: dns
        ports:
        - containerPort: 80
```

- frontendを作成して確認します。

```bash
$ kubectl create -f frontend-deployment.yaml
deployment "frontend" created

 kubectl get deployment frontend
NAME       DESIRED   CURRENT   UP-TO-DATE   AVAILABLE   AGE
frontend   3         3         3            3           1m

$ kubectl get pods
NAME                           READY     STATUS    RESTARTS   AGE
frontend-88237173-cp3rn        1/1       Running   0          1m
frontend-88237173-kxlgh        1/1       Running   0          1m
frontend-88237173-tw65g        1/1       Running   0          1m
redis-master-343230949-08kqi   1/1       Running   0          29m
redis-slave-132015689-3x3qp    1/1       Running   0          22m
redis-slave-132015689-b6cha    1/1       Running   0          22m
```

frontend用のサービスを作成します。frontendは複数台の負荷分散をしたいので`type: LoadBalancer`を指定しています。

- frontend-service.yaml ファイル

```yaml:frontend-service.yaml 
apiVersion: v1
kind: Service
metadata:
  name: frontend
  labels:
    app: guestbook
    tier: frontend
spec:
  type: LoadBalancer
  ports:
  - port: 80
  selector:
    app: guestbook
    tier: frontend
```

```bash
$ kubectl create -f frontend-service.yaml 
service "frontend" created

$ kubectl get svc frontend
NAME       CLUSTER-IP      EXTERNAL-IP     PORT(S)   AGE
frontend   10.55.254.255   104.198.125.6   80/TCP    3m
```

`type: LoadBalancer` を指定すると別途GCEのロードバランサーが作成されています。デフォルトだとヘルスチェックなどの設定がされてないので必要に応じて変更する必要があります。

![image](https://qiita-image-store.s3.amazonaws.com/0/142397/dc9aa996-ced6-929f-6cf8-e66e539dc4e0.png)


`EXTERNAL_IP`にブラウザでアクセスすると以下の様な画面がみれました。

![image](https://qiita-image-store.s3.amazonaws.com/0/142397/f85a577d-597b-cf84-08db-e4327b860f4a.png)


## PHPの中身
このアプリケーションはPHPで書かれているので何をしてるか確認してみたいと思います。

```guestbook.php
<?php

error_reporting(E_ALL);
ini_set('display_errors', 1);

require 'Predis/Autoloader.php';

Predis\Autoloader::register();

if (isset($_GET['cmd']) === true) {
  $host = 'redis-master';
  if (getenv('GET_HOSTS_FROM') == 'env') {
    $host = getenv('REDIS_MASTER_SERVICE_HOST');
  }
  header('Content-Type: application/json');
  if ($_GET['cmd'] == 'set') {
    $client = new Predis\Client([
      'scheme' => 'tcp',
      'host'   => $host,
      'port'   => 6379,
    ]);

    $client->set($_GET['key'], $_GET['value']);
    print('{"message": "Updated"}');
  } else {
    $host = 'redis-slave';
    if (getenv('GET_HOSTS_FROM') == 'env') {
      $host = getenv('REDIS_SLAVE_SERVICE_HOST');
    }
    $client = new Predis\Client([
      'scheme' => 'tcp',
      'host'   => $host,
      'port'   => 6379,
    ]);

    $value = $client->get($_GET['key']);
    print('{"data": "' . $value . '"}');
  }
} else {
  phpinfo();
} ?>
```

書き込みならRedisのマスターへ接続し、読み込みならRedisのスレーブへ接続する制御をしています。
Redisのマスターのホスト名は`redis-master`で、スレーブは`redis-slave`で接続しています。RedisのDeploymentを作成したときに指定した`name`がコンテナのDNS空間に反映されているということですね。

逆にDNSを使わない場合もあるので、その判別`if (getenv('GET_HOSTS_FROM') == 'env') {`を入れて環境変数を使う事も出来るようにしています。

frontend-deployment.yamlで以下の設定がされているので、このPHPではDNS名を使っています。

```yaml:frontend-deployment.yaml
...
        env:
        - name: GET_HOSTS_FROM
          value: dns            # envにするとPHPが環境変数を使うようになる
...
```



## レプリカ数を変更してみる
簡単に途中でレプリカ数を変更することができます。

- レプリカ数を5へ変更する

```bash
$ kubectl scale deployment frontend --replicas=5
deployment "frontend" scaled
```

- 5になっていることが分かります。

```bash
$ kubectl get deployment frontend
NAME       DESIRED   CURRENT   UP-TO-DATE   AVAILABLE   AGE
frontend   5         5         5            5           51m
```

- Podを見ると増えているのが分かります

```bash
$ kubectl get pods
NAME                           READY     STATUS    RESTARTS   AGE
frontend-88237173-cp3rn        1/1       Running   0          26m
frontend-88237173-kuyzx        1/1       Running   0          6s   # 増えたPod
frontend-88237173-kxlgh        1/1       Running   0          26m
frontend-88237173-sljba        1/1       Running   0          6s   # 増えたPod
frontend-88237173-tw65g        1/1       Running   0          26m
redis-master-343230949-08kqi   1/1       Running   0          53m
redis-slave-132015689-3x3qp    1/1       Running   0          47m
redis-slave-132015689-b6cha    1/1       Running   0          47m
```

### 寄り道（kube edit）

`kube edit`コマンドを使ってレプリカ数やその他の設定を変更することもできます。

```bash
$ KUBE_EDITOR=vim kubectl edit deployment frontend # デフォルトエディタはvi
```

上記コマンドで、設定ファイル（yaml）が開くので修正して保存すると設定を変更することができます。


## 障害発生時のPodの挙動
コマンドでPodを削除して、障害発生をシミュレーションしてみたいと思います。

- シミュレーションする前に状態を確認します。

```bash
$ kubectl get pods
NAME                           READY     STATUS    RESTARTS   AGE
frontend-88237173-cp3rn        1/1       Running   0          53m
frontend-88237173-kxlgh        1/1       Running   0          53m
frontend-88237173-tw65g        1/1       Running   0          53m
redis-master-343230949-08kqi   1/1       Running   0          1h
redis-slave-132015689-3x3qp    1/1       Running   0          1h
redis-slave-132015689-b6cha    1/1       Running   0          1h
```

- Podを一つ削除します。

```bash
$ kubectl delete pod frontend-88237173-cp3rn
pod "frontend-88237173-cp3rn" deleted
```

- 即座に新しいPodが作成されています。

```bash
$ kubectl get pods
NAME                           READY     STATUS    RESTARTS   AGE
frontend-88237173-0omu3        1/1       Running   0          3s   # 直ぐPodが作成された
frontend-88237173-kxlgh        1/1       Running   0          54m
frontend-88237173-tw65g        1/1       Running   0          54m
redis-master-343230949-08kqi   1/1       Running   0          1h
redis-slave-132015689-3x3qp    1/1       Running   0          1h
redis-slave-132015689-b6cha    1/1       Running   0          1h
```

見事にレプリカが再作成されました。 :tada: 


## ラベルを変更してみる
Kubernetesはラベルをみてレプリカ数をチェックしているとのことで、そのラベルに変更があった場合の挙動を確認してみたいと思います。

- ラベルが`tier=frontend` のPodを表示します。

```bash
$ kubectl get pod -l tier=frontend
NAME                      READY     STATUS    RESTARTS   AGE
frontend-88237173-0omu3   1/1       Running   0          21m  # このPodのラベルを変更する
frontend-88237173-fadzg   1/1       Running   0          1m
frontend-88237173-tw65g   1/1       Running   0          1h
```

- ひとつのPodのラベルを変更します。

```bash
$ kubectl label --overwrite pods frontend-88237173-0omu3 tier=middle
pod "frontend-88237173-0omu3" labeled
```

- ラベルが`tier=frontend`のPodを表示します。新しいPodに入れ替っているのが分かります。

```bash
$ kubectl get pod -l tier=frontend
NAME                      READY     STATUS    RESTARTS   AGE
frontend-88237173-fadzg   1/1       Running   0          2m
frontend-88237173-kkjpu   1/1       Running   0          4s   # 新しいPodに変わっている
frontend-88237173-tw65g   1/1       Running   0          1h
```

- frontendとしてレプリカ数は維持しています。

```bash
$ kubectl get deployment frontend
NAME       DESIRED   CURRENT   UP-TO-DATE   AVAILABLE   AGE
frontend   3         3         3            3           1h   # Podの数は変わらない
```

- 変更したPodはDeployment外に存在しています。

```bash
$ kubectl get pod -l tier=middle
NAME                      READY     STATUS    RESTARTS   AGE
frontend-88237173-0omu3   1/1       Running   0          22m # 変更したPodは存在している
```

こちらも見事にレプリカを維持してくれています。 :tada: 

## クリーンアップ
リソースを全て削除します。

```bash
$ kubectl delete service,deployment --all
service "frontend" deleted
service "kubernetes" deleted
service "redis-master" deleted
service "redis-slave" deleted
deployment "frontend" deleted
deployment "redis-master" deleted
deployment "redis-slave" deleted

$ gcloud container clusters delete guestbook
The following clusters will be deleted.
 - [guestbook] in [asia-northeast1-a]

Do you want to continue (Y/n)?  Y

Deleting cluster guestbook...done.                                                 
Deleted [https://container.googleapis.com/v1/projects/{PROJECT_ID}/zones/asia-northeast1-a/clusters/guestbook].
```


## 最後に

簡単にコンテナの運用ができると実感しました。
プロビジョニングも簡単ですが、それよりも運用中の管理が柔軟にできるのが最大な魅力だと思います。今度はローリングアップデートなどより実践に近いことを学んでいきたいと思います。

それでは :smiley: