Kubernetes チュートリアル実践

https://kubernetes.io/docs/tutorials/kubernetes-basics/
公式のチュートリアルを実際に動かしたいと思います。
このチュートリアルでは Katakoba を利用していますが、今回は `yaml` を書いて動かします。

# Environment
- Master *1 (Ubuntu Server 18.04 on ESXi)
- Worker *8 (Ubuntu Server 18.04 on ESXi)
- Kubernetes v1.17.0
このチュートリアルではネームスペース `k8s-tutorial` にすべてのリソースを作成します。

# What Learn
- コンテナ化されたアプリケーションをクラスターにデプロイ
- Deploymentのスケーリング
- 新しいソフトウェアのバージョンでコンテナ化されたアプリケーションをアップデート
- コンテナ化されたアプリケーションのデバッグ

# Create Kubernetes Cluster
別記事にて紹介済みのため省略

# Create Namespace

```yaml:ns.yml
apiVersion: v1
kind: Namespace
metadata:
  name: k8s-tutorial
```

```sh
$ kubectl apply -f ns.yml
namespace/k8s-tutorial created
```

# Deploy Application

```sh
$ kubectl create deployment kubernetes-bootcamp --image=gcr.io/google-samples/kubernetes-bootcamp:v1
```

このコマンドを `yaml` に起こして投入します。

```yaml:deploy_01.yml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: kubernetes-bootcamp
  namespace: k8s-tutorial
spec:
  selector:
    matchLabels:
      app: kubernetes-bootcamp
  template:
    metadata:
      labels:
        app: kubernetes-bootcamp
    spec:
      containers:
      - name: kubernetes-bootcamp
        image: gcr.io/google-samples/kubernetes-bootcamp:v1
        resources:
          limits:
            memory: "128Mi"
            cpu: "500m"
```

```sh
$ kubectl apply -f deploy_01.yml
deployment.apps/kubernetes-bootcamp created
```

`created` が出ずにエラーになった場合は `deploy_01.yml` を確認してください。
`deployment` の状況を確認してみます。

```sh
$ kubectl get deploy -n k8s-tutorial -o wide
NAME                  READY   UP-TO-DATE   AVAILABLE   AGE     CONTAINERS            IMAGES                                         SELECTOR
kubernetes-bootcamp   1/1     1            1           2m47s   kubernetes-bootcamp   gcr.io/google-samples/kubernetes-bootcamp:v1   app=kubernetes-bootcamp
```

`READY` が `1/1` になっているので無事にデプロイされました。
一応、 `Pod` も確認してみましょう。

```sh
$ kubectl get po -n k8s-tutorial
NAME                                   READY   STATUS    RESTARTS   AGE
kubernetes-bootcamp-64cd8bd759-ncxfr   1/1     Running   0          54s
```

`STATUS` が `RUNNING` になっていますね。

この章はこれで終了ですのでお掃除しておきましょう。

```sh
$ kubectl delete -f deploy_01.yml
deployment.apps "kubernetes-bootcamp" deleted
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/204925/63e1cdcf-4bc8-97e5-6ebc-cc6dfbfa2c8b.png)

今回は1つだけでしたが `Deployment` を増やしたり `Pod` の `SideCar` を適用したりすることでこの図のような `Kubernetes Cluster` を作り上げていくことが可能です。

# Executed to Pod

対象の Pod 名を環境変数にセット

```sh
$ export POD_NAME = kubectl get pods -o go-template --template '{{range .items}}{{.metadata.name}}{{"\n"}}{{end}}'
```

対象の Pod 内の環境変数を表示

```sh
$ kubectl exec -n k8s-tutorial $POD_NAME env
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
HOSTNAME=kubernetes-bootcamp-5b48cfdcbd-2wq82
KUBERNETES_PORT=tcp://10.96.0.1:443
KUBERNETES_PORT_443_TCP=tcp://10.96.0.1:443
KUBERNETES_PORT_443_TCP_PROTO=tcp
KUBERNETES_PORT_443_TCP_PORT=443
KUBERNETES_PORT_443_TCP_ADDR=10.96.0.1
KUBERNETES_SERVICE_HOST=10.96.0.1
KUBERNETES_SERVICE_PORT=443
KUBERNETES_SERVICE_PORT_HTTPS=443
NPM_CONFIG_LOGLEVEL=info
NODE_VERSION=6.3.1
HOME=/root
```

対象の Pod 内で `bash` を起動

```sh
$ kubectl exec -n k8s-tutorial $POD_NAME -it bash
root kubernetes-bootcamp-xxxxxxxxxx-xxxxx:/# #このプロンプトは Pod 内の bash によるもの
```

Pod 内のファイルを確認

```sh
root kubernetes-bootcamp-xxxxxxxxxx-xxxxx:/# ls -la
total 460
drwxr-xr-x   1 root root   4096 Jan 17 06:28 .
drwxr-xr-x   1 root root   4096 Jan 17 06:28 ..
-rwxr-xr-x   1 root root      0 Jan 17 06:28 .dockerenv
drwxr-xr-x   2 root root   4096 Jun  8  2016 bin
drwxr-xr-x   2 root root   4096 May 30  2016 boot
-rw-------   1 root root 393216 Jun  9  2016 core
drwxr-xr-x   5 root root    360 Jan 17 06:28 dev
drwxr-xr-x   1 root root   4096 Jan 17 06:28 etc
drwxr-xr-x   2 root root   4096 May 30  2016 home
drwxr-xr-x   1 root root   4096 Jun  9  2016 lib
drwxr-xr-x   2 root root   4096 Jun  8  2016 lib64
drwxr-xr-x   2 root root   4096 Jun  8  2016 media
drwxr-xr-x   2 root root   4096 Jun  8  2016 mnt
drwxr-xr-x   2 root root   4096 Jun  8  2016 opt
dr-xr-xr-x 217 root root      0 Jan 17 06:28 proc
drwx------   1 root root   4096 Jul 22  2016 root
drwxr-xr-x   1 root root   4096 Jan 17 06:28 run
drwxr-xr-x   2 root root   4096 Jun  8  2016 sbin
-rw-r--r--   1 root root    742 Jul 29  2016 server.js
drwxr-xr-x   2 root root   4096 Jun  8  2016 srv
dr-xr-xr-x  13 root root      0 Jan 17 06:28 sys
drwxrwxrwt   1 root root   4096 Jul 22  2016 tmp
drwxr-xr-x   1 root root   4096 Jul 22  2016 usr
drwxr-xr-x   1 root root   4096 Jul 22  2016 var

# 実際に Pod 内で動いているコード(Node.js)
root kubernetes-bootcamp-xxxxxxxxxx-xxxxx:/# cat server.js
var http = require('http');
var requests=0;
var podname= process.env.HOSTNAME;
var startTime;
var host;
var handleRequest = function(request, response) {
  response.setHeader('Content-Type', 'text/plain');
  response.writeHead(200);
  response.write("Hello Kubernetes bootcamp! | Running on: ");
  response.write(host);
  response.end(" | v=1\n");
  console.log("Running On:" ,host, "| Total Requests:", ++requests,"| App Uptime:", (new Date() - startTime)/1000 , "seconds", "| Log Time:",new Date());
}
var www = http.createServer(handleRequest);
www.listen(8080,function () {
    startTime = new Date();;
    host = process.env.HOSTNAME;
    console.log ("Kubernetes Bootcamp App Started At:",startTime, "| Running On: " ,host, "\n" );
});

# 実際に動作を確認
root kubernetes-bootcamp-xxxxxxxxxx-xxxxx:/# curl localhost:8080
Hello Kubernetes bootcamp! | Running on: kubernetes-bootcamp-xxxxxxxxxx-xxxxx | v=1

# 抜け出す
root kubernetes-bootcamp-xxxxxxxxxx-xxxxx:/# exit
$ # Pod から脱出
```

# Access to kubernetes-bootcamp

先程の `deploy_01.yml` でデプロイした `kubernetes-bootcamp` は `8080/tcp` を受けていますので、それに対応した `Service` を作成します。

```yaml:svc_01.yml
apiVersion: v1
kind: Service
metadata:
  name: kubernetes-bootcamp
  namespace: k8s-tutorial
spec:
  selector:
    app: kubernetes-bootcamp
  type: NodePort
  ports:
    - port: 80
      targetPort: 8080
```

```sh
$ kubectl apply -f svc_01.yml
service/kubernetes-bootcamp created
$ kubectl get svc -n k8s-tutorial
NAME                  TYPE       CLUSTER-IP   EXTERNAL-IP   PORT(S)        AGE
kubernetes-bootcamp   NodePort   10.96.46.6   <none>        80:32767/TCP   40s
```

`http://[MasterのIP]:[外部公開ポート]` にアクセスします。
上記の例だと `Master` の IP アドレスは `10.0.0.230` なので `http://10.0.0.230:32767` になります。

```sh
$ curl 10.0.0.230:32767
Hello Kubernetes bootcamp! | Running on: kubernetes-bootcamp-xxxxxxxxxx-xxxxx| v=1
```

`exec` した時に確認したものと同じ出力結果のはずです。

# Scale In/Out

```
$ kubectl get po -n k8s-tutorial
NAME                                   READY   STATUS    RESTARTS   AGE
kubernetes-bootcamp-64cd8bd759-8w268   1/1     Running   0          85m
```

現在 `kubernetes-bootcamp` の Pod 数は 1 ですが、これは増減させることができます。

```sh
$ kubectl scale -n k8s-tutorial deploy kubernetes-bootcamp --replicas 10
deployment.apps/kubernetes-bootcamp scaled
```

これで `kubernetes-bootcamp` の Pod 数を 10 に増やしました。
`Deployment` と `Pod` を確認してみましょう。

```sh
$ kubectl get deploy -n k8s-tutorial kubernetes-bootcamp
NAME                  READY   UP-TO-DATE   AVAILABLE   AGE
kubernetes-bootcamp   10/10   10           10          113m
$ kubectl get po -n k8s-tutorial
NAME                                   READY   STATUS    RESTARTS   AGE
kubernetes-bootcamp-64cd8bd759-8w268   1/1     Running   0          95m
kubernetes-bootcamp-64cd8bd759-cbbk5   1/1     Running   0          3m10s
kubernetes-bootcamp-64cd8bd759-mtl5p   1/1     Running   0          3m10s
kubernetes-bootcamp-64cd8bd759-ndk55   1/1     Running   0          3m10s
kubernetes-bootcamp-64cd8bd759-tdp4f   1/1     Running   0          3m10s
kubernetes-bootcamp-64cd8bd759-tg5cx   1/1     Running   0          3m10s
kubernetes-bootcamp-64cd8bd759-wx2v6   1/1     Running   0          3m10s
kubernetes-bootcamp-64cd8bd759-xgqlt   1/1     Running   0          3m10s
kubernetes-bootcamp-64cd8bd759-zhzql   1/1     Running   0          3m10s
kubernetes-bootcamp-64cd8bd759-zqjlx   1/1     Running   0          3m10s
```

`Deployment` では `10/10` になっており、 `Pod` では10個の Pod が出力されました。

では次は増やしたものを減らしてみましょう。

```sh
$ kubectl scale -n k8s-tutorial deploy kubernetes-bootcamp --replicas 3
deployment.apps/kubernetes-bootcamp scaled
$ kubectl get po -n k8s-tutorial
NAME                                   READY   STATUS    RESTARTS   AGE
kubernetes-bootcamp-64cd8bd759-mtl5p   1/1     Running   0          9m37s
kubernetes-bootcamp-64cd8bd759-wx2v6   1/1     Running   0          9m37s
kubernetes-bootcamp-64cd8bd759-zhzql   1/1     Running   0          9m37s
```

先程と同じコマンドでレプリカ数を変更するだけで完了します。

# Load Balancing

先程1つの Pod に対してアクセスを行いました。では Pod が複数存在した状態で同様にアクセスした場合はどうなるのでしょうか。

```sh
$ kubectl get po -n k8s-tutorial
NAME                                   READY   STATUS    RESTARTS   AGE
kubernetes-bootcamp-64cd8bd759-mtl5p   1/1     Running   0          52m
kubernetes-bootcamp-64cd8bd759-wx2v6   1/1     Running   0          52m
kubernetes-bootcamp-64cd8bd759-zhzql   1/1     Running   0          52m
```

3つの Pod があります。実際にアクセスしてみましょう。

```sh
$ curl 10.0.0.230:32767
Hello Kubernetes bootcamp! | Running on: kubernetes-bootcamp-64cd8bd759-wx2v6 | v=1
$ curl 10.0.0.230:32767
Hello Kubernetes bootcamp! | Running on: kubernetes-bootcamp-64cd8bd759-zhzql | v=1
$ curl 10.0.0.230:32767
Hello Kubernetes bootcamp! | Running on: kubernetes-bootcamp-64cd8bd759-mtl5p | v=1
```

Master に対してアクセスしているのに3つの Pod に振り分けられています。
`Service` を作成する際に `spec.type` を `NodePort` にしたのでいい具合に分散してくれるようになっています。

# Rolling Update

このチュートリアルでは `kubernetes-bootcamp:v1` を使用してきました。ここでは `v1` から `v2` にイメージを更新します。

```sh
$ kubectl set image deploy kubernetes-bootcamp kubernetes-bootcamp=jocatalin/kubernetes-bootcamp:v2 -n k8s-tutorial
deployment.extensions/kubernetes-bootcamp image updated
```

`set image` で `jocatalin/kubernetes-bootcamp:v2` に更新しました。
実際に `Pod` を確認してみましょう。

```sh
$ kubectl get po -n k8s-tutorial
NAME                                   READY   STATUS    RESTARTS   AGE
kubernetes-bootcamp-8686fc5f77-5wggs   1/1     Running   0          97s
kubernetes-bootcamp-8686fc5f77-bsxbb   1/1     Running   0          90s
kubernetes-bootcamp-8686fc5f77-znnq4   1/1     Running   0          103s
```

LoadBalancing の章で確認したときは `kubernetes-bootcamp-64cd8bd759-xxxxx` でしたが、イメージを更新したため Pod が作り直されて `kubernetes-bootcamp-8686fc5f77-xxxxx` になりました。 `AGE` も新しい時刻になっています。
使用されているイメージを確認するには `--output wide` や `describe` します。

```sh
$ kubectl get deploy -n k8s-tutorial -o wide
NAME                  READY   UP-TO-DATE   AVAILABLE   AGE     CONTAINERS            IMAGES                             SELECTOR
kubernetes-bootcamp   3/3     3            3           4h52m   kubernetes-bootcamp   jocatalin/kubernetes-bootcamp:v2   app=kubernetes-bootcamp
```

確かに `IMAGES` が `jocatalin/kubernetes-bootcamp:v2` になっています。
`v2` イメージに更新できたので実際にアクセスしてみましょう。

```sh
$ curl 10.0.0.230:32767
Hello Kubernetes bootcamp! | Running on: kubernetes-bootcamp-8686fc5f77-znnq4 | v=2
$ curl 10.0.0.230:32767
Hello Kubernetes bootcamp! | Running on: kubernetes-bootcamp-8686fc5f77-5wggs | v=2
$ curl 10.0.0.230:32767
Hello Kubernetes bootcamp! | Running on: kubernetes-bootcamp-8686fc5f77-bsxbb | v=2
```

`v=2` になっています。

`set` でイメージ更新を行った場合、不具合等でもとのイメージに戻したくなったらコマンド1つで戻すことができます。

```sh
$ kubectl rollout undo deploy kubernetes-bootcamp -n k8s-tutorial
deployment.apps/kubernetes-bootcamp rolled back
```

これでもとに戻すことができます。
実際に確認してみましょう。

```sh
$ kubectl get po -n k8s-tutorial
NAME                                   READY   STATUS        RESTARTS   AGE
kubernetes-bootcamp-64cd8bd759-4bl2s   1/1     Running       0          12s
kubernetes-bootcamp-64cd8bd759-g4grz   1/1     Running       0          14s
kubernetes-bootcamp-64cd8bd759-ng69z   1/1     Running       0          17s
kubernetes-bootcamp-8686fc5f77-5wggs   1/1     Terminating   0          14m
kubernetes-bootcamp-8686fc5f77-bsxbb   1/1     Terminating   0          13m
kubernetes-bootcamp-8686fc5f77-znnq4   1/1     Terminating   0          14m
```

`v2` が適用された `kubernetes-bootcamp-8686fc5f77-xxxxx` が削除されて、もとの `v1` が適用された `kubernetes-bootcamp-64cd8bd759-xxxxx` が作成されています。

ここまでで Kubernetes を使用すると簡単にデプロイ・公開・アップデート・ロールバックが行えることがわかりました。

以上で公式チュートリアルは終了です。今後もサンプルアプリ等を用いながら Kubernetes の機能を学習していきます。