初めてのkubernetes（Minikube） Windows環境でチュートリアル＋α

# 概要

前回の「[初めてのkubernetes（Minikube） Windows環境構築編](https://qiita.com/comefigo/items/f7bc852a63797934d641)」を終えて、Kubernetes（以降 **k8s**とする）の[公式チュートリアル](https://kubernetes.io/docs/tutorials/hello-minikube/)を実践した内容と理解したことを残しています
おまけとして公式チュートリアル内にはなかったマニュフェストファイルからデプロイも追加しています

※以下のminikubeコマンドは**管理者権限で実行**してください

# 実行環境

- Windows 10 pro
- Minikube v0.28.0
- Docker Version 18.06.1-ce-win73 (19507)

# 事前準備

後述ではビルドしたDockerイメージをMinikubeにデプロイしますが、Dockerイメージのビルド先はローカル(Moby)のDocker Hostではなく、MinikubeのDocker Hostにビルドするため、以下のように一時的に環境変数を設定します
なお、Docker HubなどのMinikubeが参照できるレジストリからイメージを取得する場合は、こちらの設定は必要ありません
その際はイメージ名は適宜に変更してください

![docker-env.png](https://qiita-image-store.s3.amazonaws.com/0/30522/84d16ad8-0179-9e58-b042-70c603cf0f55.png)

```powershell:powershell
> minikube docker-env --shell powershell

# MinikubeのDocker Hostの環境変数が取得できる
$Env:DOCKER_TLS_VERIFY = "1"
$Env:DOCKER_HOST = "tcp://192.168.1.47:2376"
$Env:DOCKER_CERT_PATH = "C:\Users\user\.minikube\certs"
$Env:DOCKER_API_VERSION = "1.35"
```
上記の環境変数を設定することでDocker Hostを一時的にMinikube側にします
(コンソールを閉じれば設定が消えますのでご安心を)

```powershell:powershell
> minikube docker-env --shell powershell | Invoke-Expression
```

前述の環境変数が設定されていることを確認

```powershell:powershell
> Get-ChildItem env:
```

Minikube内のDockerイメージを参照
k8s関連のイメージがざっと確認できます

```powershell:powershell
> docker images

REPOSITORY                                 TAG                 IMAGE ID            CREATED             SIZE
k8s.gcr.io/kube-proxy-amd64                v1.10.0             bfc21aadc7d3        5 months ago        97MB
k8s.gcr.io/kube-controller-manager-amd64   v1.10.0             ad86dbed1555        5 months ago        148MB
k8s.gcr.io/kube-scheduler-amd64            v1.10.0             704ba848e69a        5 months ago        50.4MB
k8s.gcr.io/kube-apiserver-amd64            v1.10.0             af20925d51a3        5 months ago        225MB
k8s.gcr.io/etcd-amd64                      3.1.12              52920ad46f5b        6 months ago        193MB
k8s.gcr.io/kube-addon-manager              v8.6                9c16409588eb        6 months ago        78.4MB
k8s.gcr.io/k8s-dns-dnsmasq-nanny-amd64     1.14.8              c2ce1ffb51ed        8 months ago        41MB
k8s.gcr.io/k8s-dns-sidecar-amd64           1.14.8              6f7f2dc7fab5        8 months ago        42.2MB
k8s.gcr.io/k8s-dns-kube-dns-amd64          1.14.8              80cc5ea4b547        8 months ago        50.5MB
k8s.gcr.io/pause-amd64                     3.1                 da86e6ba6ca1        8 months ago        742kB
k8s.gcr.io/kubernetes-dashboard-amd64      v1.8.1              e94d2f21bc0c        8 months ago        121MB
gcr.io/k8s-minikube/storage-provisioner    v1.8.1              4689081edb10        10 months ago       80.8MB
```

・ログインが必要なセキュアなプライベートレジストリを利用したい場合は、認証情報などを管理する「[Secret](https://kubernetes.io/docs/tasks/configure-pod-container/pull-image-private-registry/)」に登録すると利用できます
・アンセキュアなプライベートレジストリを利用する場合は、`insecure-registry`に登録すれば、利用できます


# 手始めに

## Minikubeのバージョン確認

```powershell:powershell
> minikube version

minikube version: v0.28.0
```
## Minikubeの設定確認

前回の環境構築編で設定した値が確認できます

```powershell:powershell
> minikube config view

- hyperv-virtual-switch: external-switch
- vm-driver: hyperv
```

`~/.minikube/config/config.json`に保存された値が表示されます

## kubectlでServer/Clientのバージョン確認

サーバとクライアントのバージョンが異なっていても問題はない
**※マイナーバージョン2つ前まで互換性があります**

```powershell:powershell
> kubectl version

Client Version: version.Info{Major:"1", Minor:"11", GitVersion:"v1.11.0", GitCommit:"91e7b4fd31fcd3d5f436da26c980becec37ceefe", GitTreeState:"clean", BuildDate:"2018-06-27T20:17:28Z", GoVersion:"go1.10.2", Compiler:"gc", Platform:"windows/amd64"}
Server Version: version.Info{Major:"1", Minor:"10", GitVersion:"v1.10.0", GitCommit:"fc32d2f3698e36b93322a3465f63a14e9f0eaead", GitTreeState:"clean", BuildDate:"2018-03-26T16:44:10Z", GoVersion:"go1.9.3", Compiler:"gc", Platform:"linux/amd64"}
```

## kubectlの設定確認

clusterの設定を確認できます
cluster内で共有したい設定値（contexts）も確認できます

```powershell:powershell
> kubectl config view

apiVersion: v1
clusters:
- cluster:
    certificate-authority: ~\.minikube\ca.crt
    server: https://192.168.1.46:8443
  name: minikube
contexts:
- context:
    cluster: minikube
    user: minikube
  name: minikube
current-context: minikube
kind: Config
preferences: {}
users:
- name: minikube
  user:
    client-certificate: ~\.minikube\client.crt
    client-key: ~\.minikube\client.key
```

## クラスタの状態確認

```powershell:powershell
> kubectl cluster-info

Kubernetes master is running at https://192.168.1.46:8443
KubeDNS is running at https://192.168.1.47:8443/api/v1/namespaces/kube-system/services/kube-dns:dns/proxy

To further debug and diagnose cluster problems, use 'kubectl cluster-info dump'.
```

# Node.jsサンプルアプリケーションをデプロイ

さっそく、[公式のNode.jsサンプルアプリケーション](https://github.com/kubernetes/website/tree/master/content/en/examples/minikube)を使ってデプロイしていきます

## 1. サンプルアプリケーションをビルドします

公式サンプルアプリケーションで使用されているDockerfile、server.jsを作成し、以下のようにビルドします

```powershell:powershell
> docker build -t hello-node:v1 ./
```

## 2. 作成したDockerイメージをデプロイ(Deployment)します

- `hello-node`･･･Deploymentの名前になります（デプロイの管理単位）
- `--image`･･･DeploymentでデプロイされるDockerイメージ
- `--port`･･･外部公開用のポート（Minikube内で公開されるポート）
- `--image-pull-policy=Never`･･･今回はローカルレジストリのみからDockerイメージを取得するため、Neverとする

[image pull policy](https://kubernetes.io/docs/concepts/containers/images/#updating-images)

```powershell:powershell
> kubectl run hello-node --image=hello-node:v1 --port=8080 --image-pull-policy=Never
```

## 3. デプロイの確認

Deploymentの確認

`-o wide`を付加するとより多くの情報が表示されるようになります

```powershell:powershell
> kubectl get deployment

NAME         DESIRED   CURRENT   UP-TO-DATE   AVAILABLE   AGE
hello-node   1         1         1            1           3h
```

Podの確認
**Running**になっていればOK

```powershell:powershell
> kubectl get pods

NAME                          READY     STATUS    RESTARTS   AGE
hello-node-57c6b66f9c-prhkq   1/1       Running   0          3h
```

Podの詳細情報を取得します

```powershell:powershell
> kubectl describe pods hello-node
```

## 4. アプリケーションを公開する

k8sでアプリケーションを公開するために、Serviceを作らなければならない

- `--type`･･･公開方法にはNodePort、LoadBalancer、ClusterIP、ExternalNameとあります

[kubectl expose](https://kubernetes.io/docs/tutorials/kubernetes-basics/expose/expose-intro/)

```powershell:powershell
> kubectl expose deployment hello-node --type=LoadBalancer
```

### ポートフォワードによる公開

単一Podに対してポートフォワードによる外部公開ができます
手軽に動作確認するときには有用かも？
なお、Deployment単位でポートフォワードによる公開は

```
# pod名で指定する
> kubectl.exe port-forward hello-node-57c6b66f9c-prhkq 8080:8080
```

ブラウザで`http://localhost:8080`にアクセスすることで動作確認することができます

## 5. 動作確認

Serviceを確認
アプリの`8080`ポートと外部公開用の`32382`ポートがマッピングされます
任意のポートとマッピングするには？

```powershell:powershell
> kubectl get services

NAME         TYPE           CLUSTER-IP       EXTERNAL-IP   PORT(S)          AGE
hello-node   LoadBalancer   10.111.163.219   <pending>     8080:32382/TCP   26m
kubernetes   ClusterIP      10.96.0.1        <none>        443/TCP          1d
```

ブラウザで確認し、「hello world」が表示されればOK!!

```powershell:powershell
> minikube service hello-node
```

ログを確認
`console.log('xxxx')`があれば、その内容が表示されます

```powershell:powershell
> kubectl logs <POD名(kubectl get podsで表示)>
```

## 6. アプリケーションを更新

server.jsで何かコード修正し、Dockerイメージを`v2`としてビルドし直します

```powershell:powershell
> docker build -t hello-node:v2 ./
```

## 7. Deploymentのイメージを更新

最新のイメージをセットすると自動でデプロイされます

`deployment/hello-node`･･･deployment/\<deployment名>

```powershell:powershell
> kubectl set image deployment/hello-node hello-node=hello-node:v2

deployment.apps "hello-node" image updated
```

## 8. 動作確認

Deploymentの確認
v1のpodがTerminatingされていることが確認できます
自動で旧バージョンのPodがTerminateされます

```powershell:powershell
> kubectl get pods

NAME                          READY     STATUS        RESTARTS   AGE
hello-node-57c6b66f9c-prhkq   1/1       Terminating   0          4h
hello-node-57c8dd9df8-tlk9g   1/1       Running       0          10s
```

ブラウザでコード修正した内容が変更されたことを確認

## 9. 一度後片付け

Service、Deploymentを削除

```powershell:powershell
> kubectl delete service hello-node
> kubectl delete deployment hello-node
```

## 10. マニュフェストファイルでDeployment

上記のようにコマンドラインでデプロイするのではなく、Infrastructure as codeできるようにマニュフェストファイルからデプロイします
deployment-hello-node.ymlを作成し、以下のコマンドでデプロイします
マニュフェストって聞くと固い印象ですが、docker-compose.ymlみたいな設定ファイルだと思っていただければOKかと思います

```powershell:powershell
> kubectl apply -f deployment-hello-node.yml

deployment.apps "hello-node" created
```

`labels`･･･関連するキー：値でリソースの指定したりすることができます
例えば、リソースの参照する時に特定のlabelがついたものを抽出するなどに使用されます

```yaml:deployment-hello-node.yml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: hello-node
  labels:
    app: hello-node
spec:
  replicas: 1
  selector:
    matchLabels:
      app: hello-node
  template:
    metadata:
      labels:
        app: hello-node
    spec:
      containers:
      - name: hello-node
        image: hello-node:v1
        ports:
        - containerPort: 8080
```

## 11. デプロイの確認

```powershell:powershell
> kubectl get pods
```

## 12. マニュフェストファイルでService

Deploymentと同様にServiceもマニュフェストファイル化します

```yaml:service-hello-node.yml
kind: Service
apiVersion: v1
metadata:
  name: hello-node
  labels:
    app: hello-node
spec:
  type: LoadBalancer
  selector:
    app: hello-node
  ports:
  - port: 8080
```

```powershell:powershell
> kubectl apply -f service-hello-node.yml

service "hello-node" created
```

## 13. Serviceの確認と動作確認

```powershell:powershell
> kubectl get service

> minikube service hello-node
```

## 14. アプリケーションの更新と適用

アプリケーションのソースコードを修正したら、Dockerイメージのリビルドを実行し、deployment-hello-node.ymlの`image:`のタグ名をv2に更新し、再度同じコマンドを実行するとデプロイされます

```powershell:powershell
> kubectl apply -f deployment-hello-node.yml
```

### アップデート戦略

デフォルトのアップデート動作は`RollingUpdate`ですが、明示的に`Recreate`を選択することも可能
`Recreate`の場合は、旧バージョンのアプリケーションを停止してから、新アプリケーションをデプロイするので、サービスダウンが発生するが、新旧が混在することはない

```yaml:deployment-hello-node.yml
spec:
  replicas: 1
  strategy:
    type: Recreate
```
途中で変えられない？一度Deploymentを削除しなければならない？

```powershell:powershell
> kubectl apply -f deployment-hello-node.yml
```

## 後片付け

マニュフェスト化しているので、リソースの削除も便利になります
マニュフェストで定義されているリソースをすべて削除してくれます

```powershell:powershell
> kubectl delete -f deployment-hello-node.yml
> kubectl delete -f service-hello-node.yml 
```

# マニュフェストをまとめる

上記ではDeploymentマニュフェストとServiceマニュフェストをそれぞれのyamlファイルにしていましたが、1つにまとめて管理することができます（コマンドの入力も1回で済みます）

リソースの定義ごとに`---`で区切ることで1ファイルで管理することができます

```yaml:hello-node.yml
apiVersion: apps/v1
kind: Deployment
~中略~
---
apiVersion: v1
kind: Service
~中略~
```

また、管理するリソースが多くなるとマニュフェストファイルが長くなり、見通しが悪くなるため、管理しやすい単位にファイルを分割し、apply時に`-f`時に保存されているディレクトリを指定することで、まとめて適用してくれます（ファイル名順）

```powershell:powershell
> kubectl apply -f ./
```

指定したディレクトリ内を再帰的に読み込む場合は

```powershell:powershell
> kubectl apply -f ./ -R
```

以上

次回は「[初めてのkubernetes（Minikube） Pod連携とデータの永続化編](https://qiita.com/comefigo/items/b2a044739f8322007c64)」をご参照ください