GoogleCloudPlatform ContainerEngine（Kubernetes)でコンテナ管理入門(基本的な使い方、Registry、Blue/Green Deployment、ResizeCluster、MultiZoneClusterとか)

# はじめに

先日GoogleCloudOnBoardという大きなイベントに参加しまして、色々と感動したわけです。私もGCP布教に一役買おうと思いContainerEngineについて書いてみました。

前回はAWS ECSを書きましたけど、**やっぱりコンテナ管理といえばKubernetesですよ。**
今回も前回とだいたい同じシナリオでやってみたいと思いますが、Autoscalerだけはベータでの提供とのことで一旦手を出すのをやめときました。また試したいと思います。

ひととおりやるとKubernetesがちょっとわかる（かも）知れません。

1. Dockerのイメージを用意する
2. 使い方の基本
3. BlueGreenDeployment
4. その他試したこと

---

# Google Cloud Platform

https://cloud.google.com/

Googleが提供しているパブリッククラウドのサービスです。
詳しい説明は色々なところで語られているので割愛します。
60日間3万円までの無料枠があります。相当色々できます。
が、、期間がちょっと短いのでもう少し欲しいです・・・。

---

# Dockerのイメージを用意する

今回はGCPが提供するContainerRegistryにイメージを用意して、そこからコンテナにデプロイしてみようと思います。

## Container Registryにpushする

[Container Registry への push](https://cloud.google.com/container-registry/docs/pushing?hl=ja&_ga=1.8121778.1225515176.1484523328)をよみましょう。
pushするにはプロジェクトID を、使用可能ないずれかの gcr.io ホスト名に追加する必要があります。gcr.ioは米国でホストされるらしいのですが、変わる可能性があるので**ローカライズされたホストを指定することが推奨されるようです。**
ということでアジアに保管してみましょう。

* タグをつける

```
docker tag uzresk/demo:ver1.0 asia.gcr.io/uzr-esk/demo:ver1.0
```

* タグが付いていることを確認する

```
[root@centos7 gcp]# docker images
REPOSITORY                                    TAG                  IMAGE ID            CREATED             SIZE
asia.gcr.io/uzr-esk/demo                      ver2.0               14d465cbed4a        5 days ago          652.3 MB
uzresk/demo                                   ver2.0               14d465cbed4a        5 days ago          652.3 MB
asia.gcr.io/uzr-esk/demo                      ver1.0               66717f366a03        5 days ago          652.3 MB
uzresk/demo                                   ver1.0               66717f366a03        5 days ago          652.3 MB
```

* ContainerRegistryにpushする

```
gcloud docker push asia.gcr.io/uzr-esk/demo:ver1.0
```

* ver2.0も合わせてpushしておきます。

![image](https://qiita-image-store.s3.amazonaws.com/0/57686/f3306e72-025a-5e14-c46f-778ef83813bc.png)


---

# 使い方の基本

## CloudShellを使おう

* AWSはマネジメントコンソールで全てできますが、GCPは基本的にCUIベースになります。**うっ**と思う人もいるかもしれませんがCUIのコマンドを使いやすくするためのCloudShellというものがGCPには用意されています。
* [CloudShell](https://cloud.google.com/shell/docs/)はGCPが用意してくれる一時的な仮想マシンで、gcloudとかkubectlなどのコマンドを特に何もしなくても利用できるようになります。
* ストレージは5GB用意してくれていますし、ウェブでプレビュー機能などを使えばポートフォワードみたいなことをしてブラウザで表示できたりします。
* なんと言っても**無料**です。

## CloudShellを有効にする

![image](https://qiita-image-store.s3.amazonaws.com/0/57686/29827e5c-5f49-12ea-ab70-d543c454390c.png)

* ちなみにChromeの拡張機能である[SSH For Google Cloud Platform](https://chrome.google.com/webstore/detail/ssh-for-google-cloud-plat/ojilllmhjhibplnppnamldakhpmdnibd?utm_source=chrome-app-launcher-info-dialog)をインストールすればコピペが楽になりますので入れておきましょう。

* 本題に入って、ここからコンテナクラスタに対してイメージをデプロイしてみたいと思います。

## クラスタを作る

* asia-northeast1-aに3ノードのクラスタを作ってみます。

```
$ gcloud container clusters create demo-cluster --zone asia-northeast1-a --machine-type g1-small --num-nodes=3 --min-nodes=3 --max-nodes=3
```

## kubectlを設定する

* デフォルトのクラスタの設定を行います。

```
$ gcloud config set container/cluster demo-cluster
Updated property [container/cluster].
```

* kubectlにクラスタ認証情報を渡す
    * これを設定しないとkubectlコマンドが使えません

```
$ gcloud container clusters get-credentials demo-cluster --zone asia-northeast1-a
Fetching cluster endpoint and auth data.
kubeconfig entry generated for demo-cluster.
```

## クラスタにイメージをデプロイする

* kubectl runコマンドで起動しても良いのですが、今回はyamlでpodの設定を行いデプロイしてみます。
* yamlの書き方については[kubernetesのドキュメント](https://kubernetes.io/docs/user-guide/pods/multi-container/)を見るとよさそうです。
* 少し補足
    * ポイントはLabelです。ここに記載されたLabelと後程でてくるロードバランサのSelectorがリンクしています。
    * livenessProbeとはコンテナに対するヘルスチェックの設定でして、詳しくは「[Kubernetesヘルスチェックの使い方](https://www.ianlewis.org/jp/kubernetes-health-check)」で詳しく解説してくれています。
    * 

```demo-ver1.0.yml
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
  name: demo-ver1.0
spec:
  replicas: 3
  template:
    metadata:
      labels:
        type: demo
    spec:
      containers:
      - name: demo
        image: asia.gcr.io/uzr-esk/demo:ver1.0
        livenessProbe:
          httpGet:
            path: /app/loginForm
            port: 8080
          initialDelaySeconds: 30
          timeoutSeconds: 1
        ports:
        - containerPort: 8080
```

* podを作ります

```
$ kubectl create -f demo-ver1.0.yml 
```

* podが作られているかを確認する

```
$ kubectl get pods
NAME                           READY     STATUS              RESTARTS   AGE
demo-ver1.0-3601253569-48haz   0/1       ContainerCreating   0          10s
demo-ver1.0-3601253569-5oati   0/1       ContainerCreating   0          10s
demo-ver1.0-3601253569-uucse   1/1       Running             0          10s
```

## ロードバランサを作ってみよう

* [Creating an External Load Balancer](https://kubernetes.io/docs/user-guide/load-balancer/)を参考にロードバランサを作ってみます。
* Configurationの設定は[Service Operations](https://kubernetes.io/docs/user-guide/services/operations/#service-configuration-file)を確認してみましょう

* LBのサービスは80で受けて、コンテナの8080に転送するように設定してます。

```LB-service.yml
apiVersion: v1
kind: Service
metadata:
  name: demo-app-lb
spec:
  type: LoadBalancer
  ports:
    - port: 80
      targetPort: 8080
      protocol: TCP
  selector:
    type: demo
```

* LBを作ります

```
$ kubectl create -f LB-Service.yml 
service "demo-app-lb" created
```

* external IPを確認しよう
    * ``kubectl get service``と``kubectl get svc``は同じ意味です。

```
$ kubectl get service
NAME          CLUSTER-IP      EXTERNAL-IP       PORT(S)        AGE
demo-app-lb   10.27.241.244   104.198.126.185   80:30157/TCP   3m
kubernetes    10.27.240.1     <none>            443/TCP        12m
```

* アクセスできましたね

![image](https://qiita-image-store.s3.amazonaws.com/0/57686/c6b72273-5017-d2a4-edb3-7b894a6ba84b.png)


## podを一つおとしてみるとどうなるのか

* まずは現在の状態を確認

```
$ kubectl get pods
NAME                           READY     STATUS    RESTARTS   AGE
demo-ver1.0-3601253569-48haz   1/1       Running   1          39m
demo-ver1.0-3601253569-5oati   1/1       Running   1          39m
demo-ver1.0-3601253569-uucse   1/1       Running   0          39m
```

* 一番したのインスタンスを停止してみましょう

```
$ kubectl stop pods demo-ver1.0-3601253569-uucse
Command "stop" is deprecated, use "delete" instead.
pod "demo-ver1.0-3601253569-uucse" deleted
```

* 一瞬で上がってきます。

```
$ kubectl get pods
NAME                           READY     STATUS    RESTARTS   AGE
demo-ver1.0-3601253569-48haz   1/1       Running   1          45m
demo-ver1.0-3601253569-5oati   1/1       Running   1          45m
demo-ver1.0-3601253569-pha65   1/1       Running   0          10s
```

## kubernetes dashboardにアクセスしてみよう

* kubernetesにはWEBのUIがついていてクラスタの情報が色々見れます
    * 参考：[OSS UI（ダッシュボード）の概要](https://cloud.google.com/container-engine/docs/oss-ui)
* CloudShellを使っている場合はこの手順ではアクセスできないので以下のように行います

### アクセスの仕方

* portを指定してproxyします。
* port8080で起動しているのはCloudShellのウェブでプレビュー機能で許可されているポートは8080 から 8084だからです。

```
$ kubectl proxy --port=8080
Starting to serve on 127.0.0.1:8080
```

* CloudShellのウェブでのプレビュー機能を使います

![image](https://qiita-image-store.s3.amazonaws.com/0/57686/afebbfe4-4f41-e7aa-e261-c19bf0430401.png)

* ブラウザが開きます

![image](https://qiita-image-store.s3.amazonaws.com/0/57686/ed78c419-534d-86f4-1756-724ccaa456af.png)

* not-authorizedと出てしまうので、URLを変更して(/ui)アクセスします。

```
https://8080-xxxxxxxxxxxxxxxxxx.com/ui/?authuser=0
```

![image](https://qiita-image-store.s3.amazonaws.com/0/57686/654c6a63-7622-783b-cf07-00fc2be9d805.png)

残念ながらローカルのIPを利用する部分の表示はできませんので、表示したい場合はkubectlをローカルにインストールしてproxyしてください。

---

# Blue/Green Deployment

* ver1.0,ver2.0のpodを作りましょう(labelにはversionが付与されているので注意してください）

```demo-ver1.0.yml
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
  name: demo-ver1.0
spec:
  replicas: 3
  template:
    metadata:
      labels:
        type: demo
        version: ver1.0
    spec:
      containers:
      - name: demo
        image: asia.gcr.io/uzr-esk/demo:ver1.0
        livenessProbe:
          httpGet:
            path: /app/loginForm
            port: 8080
          initialDelaySeconds: 30
          timeoutSeconds: 1
        ports:
        - containerPort: 8080
```

```demo-ver2.0.yml
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
  name: demo-ver2.0
spec:
  replicas: 3
  template:
    metadata:
      labels:
        type: demo
        version: ver2.0
    spec:
      containers:
      - name: demo
        image: asia.gcr.io/uzr-esk/demo:ver2.0
        livenessProbe:
          httpGet:
            path: /app/loginForm
            port: 8080
          initialDelaySeconds: 30
          timeoutSeconds: 1
        ports:
        - containerPort: 8080
```

* podの作成

```
$ kubectl create -f demo-ver1.0.yml
```

```
$ kubectl create -f demo-ver2.0.yml
```

* 起動していることを確認しましょう

```
$ kubectl get pods
NAME                           READY     STATUS    RESTARTS   AGE
demo-ver1.0-1983367677-2bq5c   1/1       Running   0          15m
demo-ver1.0-1983367677-oyfbv   1/1       Running   0          15m
demo-ver1.0-1983367677-v3dfl   1/1       Running   0          15m
demo-ver2.0-2166737407-gfb3k   1/1       Running   0          9s
demo-ver2.0-2166737407-mh2w2   1/1       Running   0          9s
demo-ver2.0-2166737407-ycrux   1/1       Running   0          9s
```

* ver1.0に紐付いたらロードバランサを作ります。

```LB-service.yml
apiVersion: v1
kind: Service
metadata:
  name: demo-app-lb
spec:
  type: LoadBalancer
  ports:
    - port: 80
      targetPort: 8080
      protocol: TCP
  selector:
    type: demo
    version: ver1.0
```

* ver2.0のpodに切り替えます。

```
$ kubectl edit service demo-app-lb
```

* viが上がってきますのでver1.0の部分をver2.0に編集します

![image](https://qiita-image-store.s3.amazonaws.com/0/57686/83b3608b-917d-326a-497c-1eee81c1d808.png)

* Selectorがver.2.0に切り替わってますね

```
$ kubectl describe service
Name:                   demo-app-lb
Namespace:              default
Labels:                 <none>
Selector:               type=demo,version=ver2.0
Type:                   LoadBalancer
IP:                     10.27.241.244
LoadBalancer Ingress:   104.198.126.185
Port:                   <unset> 80/TCP
NodePort:               <unset> 30157/TCP
Endpoints:              10.24.0.8:8080,10.24.0.9:8080,10.24.1.7:8080
```

* ちゃんとブラウザでも表示されました

![image](https://qiita-image-store.s3.amazonaws.com/0/57686/eae745a6-a039-acaa-91e7-2bdcc749e5e7.png)

---

## Cluster Autoscaler

* AWSと同じようにCPU使用率などを見てクラスタをスケールさせることができます。
    * [ClusterAutoScalerの概要](https://cloud.google.com/container-engine/docs/cluster-autoscaler)
* こちらの機能は2017/2現在ベータ版とのことです。ベータが取れたら試してみたいと思います。

# その他試したこと

## クラスタのリサイズ

* 現在のノード数を確認

```
$ kubectl get nodes
NAME                                          STATUS    AGE
gke-demo-cluster-default-pool-8860cc2f-3sx1   Ready     2h
gke-demo-cluster-default-pool-8860cc2f-pf2q   Ready     2h
```

* クラスタのサイズを上げてみます（レスポンスはすぐに返ってきます）

```
$ gcloud container clusters resize demo-cluster --size 3 --zone asia-northeas
t1-a
Pool [default-pool] for [demo-cluster] will be resized to 3.
Do you want to continue (Y/n)?  Y
Resizing demo-cluster...done.                                                                               
Updated [https://container.googleapis.com/v1/projects/uzr-esk/zones/asia-northeast1-a/clusters/demo-cluster]
.
```

* ちょっと時間がたつとノードが増えていますね。

```
$ kubectl get nodes
NAME                                          STATUS    AGE
gke-demo-cluster-default-pool-8860cc2f-3sx1   Ready     2h
gke-demo-cluster-default-pool-8860cc2f-8h93   Ready     40s
gke-demo-cluster-default-pool-8860cc2f-pf2q   Ready     2h
```

## マルチゾーン対応クラスタに更新する

* 今のデフォルトノード数が2なので、あと２つのリージョンに展開すると合計6ノードのクラスタができます。

```
gcloud beta container clusters update demo-cluster --additional-zones=asia-northeast1-b,asia-northeast1-c --zone asia-northeast1-a
```

* 更新している途中にブラウザでアクセスしてみましたが、サービスは提供され続けるようです。
* 確認してみましょう（コマンドライン）

```
gcloud container clusters describe demo-cluster --zone asia-northeast1-a
・・・
locations:
- asia-northeast1-a
- asia-northeast1-b
- asia-northeast1-c
```

* コンソール

![image](https://qiita-image-store.s3.amazonaws.com/0/57686/aeebfa11-382b-0d30-b7a7-55e7988624b1.png)

## ローカルにkubectlを設定するときにハマったところ


* こんなエラーが出てしまったら、KUBECONFIGという環境変数に.kube/configへのパスを設定しましょう。configは空でよいです。

```
ERROR: (gcloud.container.clusters.get-credentials) environment variable HOME or KUBECONFIG must be set to store credentials for kubectl
```

* こんなエラーが出てしまったら `gcloud auth application-default login` で認証情報を設定しましょう

```
error: google: could not find default credentials. See https://developers.google.com/accounts/docs/application-default-credentials for more information.
```

# さいごに

Kubernetesを初めて触りましたが奥深そうですのでジックリ勉強する必要がありそうです。Openstack上にKubernetesを浮かべたくなる人の気持ちも少しは分かるようになりました。

AWSは自分でいろいろ設定しないといけない分、動きが読めるので私には理解が進みました。AWSとGCPどちらも触ったことがなくコンテナ管理してみたいという人はGCPの方が入りやすいかもしれません。（今回の記事も調べながらやりながらで半日くらいでできましたので。）