Minikubeの環境を構築してKubernetesのイメージを掴む(step0)


Kubernetesをいきなり始める場合、訳のわからない単語がいっぱい出てきて（単語自体の意味が分かってもその単語が何を意味しているかはわからないことが多い）、なかなか全体のイメージしにくい。まずシンプルな環境で理解しようとする。

# 単語
そうは言っても、幾つか基本単語の意味を少し把握しないと。

Kubernetes（k8s）は、ギリシャ語で航海長または水先案内人の意味である。

航海長または水先案内人として、仕事は何でしょう？
海運・港湾で大量のコンテナが行き来するから、それをさばけるよう水路を開けたり航路を指示したりして、
コンテナがうまく機能するよう、オーケストレーションする「システム」である。

詳細は[Wikipeidia](https://ja.wikipedia.org/wiki/Kubernetes)にあるの「Kubernetesのアーキテクチャ図」はわかりやすい。

Kubernetesのパッケージ管理ツールはhelm「舵」というツールである。

パッケージはchart「海図」という倉庫に保管される（githubと類似）。

Kubernetesの管理は、コマンドラインツールであるkubectlまたはweb画面のコンソールで行う。

**幾つかわかりにくい点についての説明：**

* Kubernetesは基本的にクラスタ内で閉ざされている世界となる。
* ポッドにクラスタ内のIPアドレスをランダムに割り当てて、ポッド再起動するとIPアドレスが変わる（かもしれない）。
* コンテナはIPアドレスを持たない。外部との通信にIPアドレスが必要とする場合、ポッドのIPアドレスを共用する（恐らく）。
* コンテナは提供する機能をサービスとしてポッド経由で公開できる。それでも、クラスタ内からのみ利用できる。
* サービスへのアクセスは、環境変数かクラスタDNSを用いてアクセスする。
  → 具体的などれかのポッド（IPアドレス）を指名してアクセスできない。
* サービスをIngressという仕組みでクラスタ外部へ公開できる。
***
Google Kubernetes Engine（GKT）を90日間300ドルクレジット付きで無料体験可能。
ただ、Kubernetesは複雑なため、その前に、ローカルで体験可能なminikubeで概念把握をしたほうがよい。

以下はUbuntu上でminikubeをセットアップしてみる方法を説明する。

# Dockerのインストール
  minikubeはDockerを利用してローカル上にminikubeという仮想マシン（コンテナ）を作成し、その中でkubernetesのクラスタを一つ構築する。
  なので、先にDockerをインストールしておく。
  ちなみに、仮想マシン作成できるのであれば、Dockerではなく、KVMやVirtualBoxなどのハイパーバイザーでもよい。

```shell:
~$ sudo apt install docker.io
```

 ※ インストール後dockerコマンドを実行するたびにsudoが必要だが、これを省略する方法として、下記コマンドで現在ユーザxxxをdockerグループに追加してubuntu再起動。

```shell:
~$ sudo gpasswd -a xxx docker
```

# kubectlのインストール
  minikube含め、kubernetesとのやり取りに使うコマンドツールである。先にインストールしておく。

```shell:
~$ curl -LO "https://storage.googleapis.com/kubernetes-release/release/$(curl -s https://storage.googleapis.com/kubernetes-release/release/stable.txt)/bin/linux/amd64/kubectl"
~$ sudo chmod +x kubectl
~$ sudo mv kubectl /usr/local/bin/
```

# minikubeのインストール

```shell:
~$ curl -Lo minikube https://storage.googleapis.com/minikube/releases/latest/minikube-linux-amd64 
~$ sudo chmod +x minikube
~$ sudo mv minikube /usr/local/bin/
```

# minikube起動

```shell:
~$ minikube start
```

 初回起動時は、k8s（minikube）のイメージ（preloaded-images-k8s-v6-v1.19.4-docker-overlay2-amd64.tar.lz4）をインターネットからpullするので、多少時間がかかる（500MiB弱）。
 参考のため、以下その出力を示す：

```shell:
~$ minikube start
😄  Ubuntu 20.04 上の minikube v1.15.1
✨  dockerドライバーが自動的に選択されました
👍  コントロールプレーンのノード minikube を minikube 上で起動しています
🚜  Pulling base image ...
💾  Kubernetes v1.19.4 のダウンロードの準備をしています
    > preloaded-images-k8s-v6-v1.19.4-docker-overlay2-amd64.tar.lz4: 486.35 MiB
🔥  docker container (CPUs=2, Memory=2200MB) を作成しています...
🐳  Docker 19.03.13 で Kubernetes v1.19.4 を準備しています...
🔎  Kubernetes コンポーネントを検証しています...
🌟  有効なアドオン: storage-provisioner, default-storageclass
🏄  Done! kubectl is now configured to use "minikube" cluster and "default" namespace by default
```

  デフォルトでは2cpuと2Gのメモリのクラスタを一つ作成する。これではactiviti7のデプロイメントに足りないので、後で再作成が必要。
  *そう、最終目標は`activiti7`をローカルで構築したいので、これは別話。*

* 状態確認

```shell:
~$ minikube stats
minikube
type: Control Plane
host: Running
kubelet: Running
apiserver: Running
kubeconfig: Configured
```

* kubectlで確認

```shell:
~$ kubectl version
Client Version: version.Info{Major:"1", Minor:"19", GitVersion:"v1.19.4", GitCommit:"d360454c9bcd1634cf4cc52d1867af5491dc9c5f", GitTreeState:"clean", BuildDate:"2020-11-11T13:17:17Z", GoVersion:"go1.15.2", Compiler:"gc", Platform:"linux/amd64"}
Server Version: version.Info{Major:"1", Minor:"19", GitVersion:"v1.19.4", GitCommit:"d360454c9bcd1634cf4cc52d1867af5491dc9c5f", GitTreeState:"clean", BuildDate:"2020-11-11T13:09:17Z", GoVersion:"go1.15.2", Compiler:"gc", Platform:"linux/amd64"}
```

ここでの`Server Version`というのが、kubernetesクラスタのこと。

`minikube start`は、実際にminikubeというコンテナを作成して、その中にControl Plane一式（マスター）が実行している。

* dockerでminikubeの実体を確認してみる：

```shell:
~$ docker ps 
CONTAINER ID        IMAGE                                 COMMAND                  CREATED             STATUS              PORTS                                                                                                      NAMES
18ac3ceb3c43        gcr.io/k8s-minikube/kicbase:v0.0.14   "/usr/local/bin/entr…"   12 minutes ago      Up 12 minutes       127.0.0.1:32771->22/tcp, 127.0.0.1:32770->2376/tcp, 127.0.0.1:32769->5000/tcp, 127.0.0.1:32768->8443/tcp   minikube
```

* dockerのネットワークを確認してみる：

```shell:
~$ docker network ls
NETWORK ID          NAME                DRIVER              SCOPE
fa92b9cbeb63        bridge              bridge              local
ef4235778ea4        host                host                local
94102e388ff6        minikube            bridge              local
78575dd74c91        none                null                local
```

docker標準以外、`minikube`というI/Fも作成されていることがわかる。

* 更に、ホスト（ubuntu）上のネットワークを確認すると

```shell:
~$ ip addr
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
    inet6 ::1/128 scope host 
       valid_lft forever preferred_lft forever
2: ens33: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP group default qlen 1000
    link/ether 00:0c:29:6b:7e:b6 brd ff:ff:ff:ff:ff:ff
    inet 192.168.241.130/24 brd 192.168.241.255 scope global dynamic noprefixroute ens33
       valid_lft 1489sec preferred_lft 1489sec
    inet6 fe80::867c:e538:c0a6:4e9e/64 scope link noprefixroute 
       valid_lft forever preferred_lft forever
3: docker0: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc noqueue state DOWN group default 
    link/ether 02:42:aa:49:53:39 brd ff:ff:ff:ff:ff:ff
    inet 172.17.0.1/16 brd 172.17.255.255 scope global docker0
       valid_lft forever preferred_lft forever
    inet6 fe80::42:aaff:fe49:5339/64 scope link 
       valid_lft forever preferred_lft forever
5: br-94102e388ff6: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default 
    link/ether 02:42:d9:15:92:83 brd ff:ff:ff:ff:ff:ff
    inet 192.168.49.1/24 brd 192.168.49.255 scope global br-94102e388ff6
       valid_lft forever preferred_lft forever
    inet6 fe80::42:d9ff:fe15:9283/64 scope link 
       valid_lft forever preferred_lft forever
11: veth1696d8c@if10: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master br-94102e388ff6 state UP group default 
    link/ether 4e:bc:ef:46:b9:3a brd ff:ff:ff:ff:ff:ff link-netnsid 1
    inet6 fe80::4cbc:efff:fe46:b93a/64 scope link 
       valid_lft forever preferred_lft forever
```

`192.168.49.1/24`のI/Fはminikubeが作ったもので、minikubeコンテナ（クラスタ）自身は`192.168.49.2`となる。
`docker ps`で表示したとおり、minikube内ポート22、2376、5000、8443が開いている。ローカルでsshも接続できるが、minikubeコマンドでも入ることは可能。

```shell:
~$ minikube ssh
```
 *プロンプトは`docker@minikube:~$`になり、dockerというユーザでminikube内に入っていることがわかる。*

```shell:
docker@minikube:~$ ip addr  
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
2: docker0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default 
    link/ether 02:42:35:84:79:d0 brd ff:ff:ff:ff:ff:ff
    inet 172.17.0.1/16 brd 172.17.255.255 scope global docker0
       valid_lft forever preferred_lft forever
4: veth031a7c0@if3: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master docker0 state UP group default 
    link/ether a6:0b:0a:46:1e:59 brd ff:ff:ff:ff:ff:ff link-netnsid 1
10: eth0@if11: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default 
    link/ether 02:42:c0:a8:31:02 brd ff:ff:ff:ff:ff:ff link-netnsid 0
    inet 192.168.49.2/24 brd 192.168.49.255 scope global eth0
       valid_lft forever preferred_lft forever
```

ついでに、minikube内実行しているものを確認している：

```shell:
docker@minikube:~$ docker ps
CONTAINER ID        IMAGE                  COMMAND                  CREATED             STATUS              PORTS               NAMES
2913795687ac        86262685d9ab           "/metrics-sidecar"       9 minutes ago       Up 9 minutes                            k8s_dashboard-metrics-scraper_dashboard-metrics-scraper-c95fcf479-tcrxh_kubernetes-dashboard_abc990db-b76d-4b49-a132-e9075f2b5284_0
dcff4352dfe1        503bc4b7440b           "/dashboard --insecu…"   9 minutes ago       Up 9 minutes                            k8s_kubernetes-dashboard_kubernetes-dashboard-584f46694c-bln68_kubernetes-dashboard_17c76048-19ad-4b31-a265-65e6eaf11930_0
3b38a2626b9c        k8s.gcr.io/pause:3.2   "/pause"                 9 minutes ago       Up 9 minutes                            k8s_POD_dashboard-metrics-scraper-c95fcf479-tcrxh_kubernetes-dashboard_abc990db-b76d-4b49-a132-e9075f2b5284_0
5fc085cffc88        k8s.gcr.io/pause:3.2   "/pause"                 9 minutes ago       Up 9 minutes                            k8s_POD_kubernetes-dashboard-584f46694c-bln68_kubernetes-dashboard_17c76048-19ad-4b31-a265-65e6eaf11930_0
c5c25ae72949        bad58561c4be           "/storage-provisioner"   32 minutes ago      Up 32 minutes                           k8s_storage-provisioner_storage-provisioner_kube-system_e293829b-88c0-4945-96d3-da9b4cf315cd_1
01331ea01bff        bfe3a36ebd25           "/coredns -conf /etc…"   33 minutes ago      Up 33 minutes                           k8s_coredns_coredns-f9fd979d6-d8dxz_kube-system_a8874625-18e0-4519-afcb-8de29785de20_0
b8bbe6dda1b6        635b36f4d89f           "/usr/local/bin/kube…"   33 minutes ago      Up 33 minutes                           k8s_kube-proxy_kube-proxy-bqbxx_kube-system_236b6d80-b0d6-4bec-85d9-e4743a1e02c7_0
2ca9dba74db4        k8s.gcr.io/pause:3.2   "/pause"                 33 minutes ago      Up 33 minutes                           k8s_POD_storage-provisioner_kube-system_e293829b-88c0-4945-96d3-da9b4cf315cd_0
d6250bd16083        k8s.gcr.io/pause:3.2   "/pause"                 33 minutes ago      Up 33 minutes                           k8s_POD_kube-proxy-bqbxx_kube-system_236b6d80-b0d6-4bec-85d9-e4743a1e02c7_0
4da275c3e1ca        k8s.gcr.io/pause:3.2   "/pause"                 33 minutes ago      Up 33 minutes                           k8s_POD_coredns-f9fd979d6-d8dxz_kube-system_a8874625-18e0-4519-afcb-8de29785de20_0
dce47e24c1b0        14cd22f7abe7           "kube-scheduler --au…"   34 minutes ago      Up 34 minutes                           k8s_kube-scheduler_kube-scheduler-minikube_kube-system_38744c90661b22e9ae232b0452c54538_0
c935d124f5ec        4830ab618586           "kube-controller-man…"   34 minutes ago      Up 34 minutes                           k8s_kube-controller-manager_kube-controller-manager-minikube_kube-system_627d9013c9c4b1cbfb72b4c0ef6cd100_0
3313416b54e3        0369cf4303ff           "etcd --advertise-cl…"   34 minutes ago      Up 34 minutes                           k8s_etcd_etcd-minikube_kube-system_d186e6390814d4dd7e770f47c08e98a2_0
f62f32307740        b15c6247777d           "kube-apiserver --ad…"   34 minutes ago      Up 34 minutes                           k8s_kube-apiserver_kube-apiserver-minikube_kube-system_e30eb1a2f7c2dbcda239c972918b3eb4_0
e5fb4bcabab3        k8s.gcr.io/pause:3.2   "/pause"                 34 minutes ago      Up 34 minutes                           k8s_POD_kube-scheduler-minikube_kube-system_38744c90661b22e9ae232b0452c54538_0
2cd92b6687d0        k8s.gcr.io/pause:3.2   "/pause"                 34 minutes ago      Up 34 minutes                           k8s_POD_kube-controller-manager-minikube_kube-system_627d9013c9c4b1cbfb72b4c0ef6cd100_0
2a8784961b87        k8s.gcr.io/pause:3.2   "/pause"                 34 minutes ago      Up 34 minutes                           k8s_POD_kube-apiserver-minikube_kube-system_e30eb1a2f7c2dbcda239c972918b3eb4_0
e20d541cffa9        k8s.gcr.io/pause:3.2   "/pause"                 34 minutes ago      Up 34 minutes                           k8s_POD_etcd-minikube_kube-system_d186e6390814d4dd7e770f47c08e98a2_0
```

kubernetesのコンポーネント一式（`api-server`、`scheduler`、`controller-manager`など）もコンテナ方式で実行されていることがわかる。

# Dashboardでクラスタを覗いてみる
  Dashboardはkubernetesの機能であり、addonで実装している。addonをenableでもよいし、以下のコマンドで自動的にdashboard有効化とブラウザ表示ができる。

```shell:
~$ minikube dashboard &
🔌  ダッシュボードを有効化しています...
🤔  ダッシュボードの状態を確認しています...
🚀  プロキシを起動しています...
🤔  プロキシの状態を確認しています...
🎉  Opening http://127.0.0.1:37457/api/v1/namespaces/kubernetes-dashboard/services/http:kubernetes-dashboard:/proxy/ in your default browser...
```

# webアプリデプロイ
簡単なサンプルwebアプリをデプロイして、クラスタ外部（ホスト）からアクセスできるようにやってみる。

* echoserverをデプロイする

```shell:
~$ kubectl create deployment hello-minikube --image=k8s.gcr.io/echoserver:1.10
deployment.apps/hello-minikube created
```

   echoserverはhello-minikubeというIDでクラスタにデプロイメントされる。
   → クラスタは適切なノードに適切な数のポッドを作成し、その中にコンテナを作成してくれる。

* 確認してみる：

```shell:
$ kubectl get pod
NAME                              READY   STATUS    RESTARTS   AGE
hello-minikube-5d9b964bfb-hlcbp   1/1     Running   0          14m
```

`hello-minikube-5d9b964bfb-hlcbp`というポッドが実行してくれてる。

* hello-minikubeにアクセス可能にする

```shell:
~$ kubectl expose deployment hello-minikube --type=NodePort --port=8080
service/hello-minikube exposed
```
   → type=NodePortという方法でポッドの8080ポートに公開した。

* サービスの公開状況を確認

```shell:
~$ kubectl get service
NAME             TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)          AGE
hello-minikube   NodePort    10.101.171.101   <none>        8080:30985/TCP   10m
kubernetes       ClusterIP   10.96.0.1        <none>        443/TCP          59m
```

  `10.101.171.101`は`hello-minikube-5d9b964bfb-hlcbp`ポッドのIPのようだが、どうやってアクセス？

```shell:  
~$ minikube service hello-minikube --url
http://192.168.49.2:30985
```

つまり、上記URLにマッピングしていること。192.168.49.2の30985にアクセスすれば、クラスタがhello-minikube-5d9b964bfb-hlcbpポッドの8080番へ転送してくれる。
ブラウザで確認してよいし、curlで確認してみる：

```shell:
~$ curl http://192.168.49.2:30985

Hostname: hello-minikube-5d9b964bfb-hlcbp

Pod Information:
	-no pod information available-

Server values:
	server_version=nginx: 1.13.3 - lua: 10008

Request Information:
	client_address=172.17.0.1
	method=GET
	real path=/
	query=
	request_version=1.1
	request_scheme=http
	request_uri=http://192.168.49.2:8080/

Request Headers:
	accept=*/*
	host=192.168.49.2:30985
	user-agent=curl/7.68.0

Request Body:
	-no body in request-
```
上記client_address=172.17.0.1に注目したい。このIPはminikube内のdocker0というI/Fに割り当てている。
つまり、通信は192.168.49.1(ubuntu)->192.168.49.2(minikube)->172.17.0.1(minikube内のdocker)->10.101.171.101のような流れ。

「kubectl expose」でサービスを公開するときに作成する外部ポートはランダムなため、これをクラスタ外部から利用しにくい。
これの解決方法は`ingress controler`（入り口制御）という仕組み。適切にルーティングしてくれる。

* ingress controler有効化

```shell:
~$ minikube addons enable ingress
🔎  Verifying ingress addon...
🌟  The 'ingress' addon is enabled
```

* 以下のingressリソース用yamlファイル(例：example-ingress.yaml)を作成する

```shell:example-ingress.yaml
apiVersion: networking.k8s.io/v1beta1
kind: Ingress
metadata:
  name: example-ingress
  annotations:
    nginx.ingress.kubernetes.io/rewrite-target: /$1
spec:
  rules:
  - host: 192.168.49.2.nip.io
    http:
      paths:
      - path: /
        backend:
          serviceName: hello-minikube
          servicePort: 8080
```

hostの192.168.49.2.nip.ioはhostsファイル編集しなくても使えるdnsサービスnip.ioを活用したい。
このファイルはminikubeの外部IP192.168.49.2に対してhttpつまり80番ポートに対してリクエストが来ると、そのURLは`http://192.168.49.2.nip.io`のルートであれば、「hello-minikube」というサービスの8080番へルーティングする、という意味である。

* このファイルを適用

```shell:
~$ kubectl apply -f example-ingress.yaml
Warning: networking.k8s.io/v1beta1 Ingress is deprecated in v1.19+, unavailable in v1.22+; use networking.k8s.io/v1 Ingress
ingress.networking.k8s.io/example-ingress created
```

警告はとりあえず無視。

* ingressリソース作成したか確認

```shell:
~$ kubectl get ingress
Warning: extensions/v1beta1 Ingress is deprecated in v1.14+, unavailable in v1.22+; use networking.k8s.io/v1 Ingress
NAME              CLASS    HOSTS                 ADDRESS        PORTS   AGE
example-ingress   <none>   192.168.49.2.nip.ip   192.168.49.2   80      49s
```

良さそう。確認してみる。

```shell:
~$ curl http://192.168.49.2.nip.io

Hostname: hello-minikube-5d9b964bfb-hlcbp

Pod Information:
	-no pod information available-

Server values:
	server_version=nginx: 1.13.3 - lua: 10008

Request Information:
	client_address=172.17.0.6
	method=GET
	real path=/
	query=
	request_version=1.1
	request_scheme=http
	request_uri=http://192.168.49.2.nip.io:8080/

Request Headers:
	accept=*/*
	host=192.168.49.2.nip.io
	user-agent=curl/7.68.0
	x-forwarded-for=192.168.49.1
	x-forwarded-host=192.168.49.2.nip.io
	x-forwarded-port=80
	x-forwarded-proto=http
	x-real-ip=192.168.49.1
	x-request-id=7cb265038c1206b47b32d0b613c41638
	x-scheme=http

Request Body:
	-no body in request-
```

いい感じでござる。

# minikubeの停止

```shell:
~$ minikube stop
✋  ノード "minikube" を停止しています...
🛑  SSH 経由で「minikube」の電源をオフにしています...
🛑  1台のノードが停止しました。
```

クラスタが停止しただけなので、次回startすれば、上記addonやデプロイ、ingressなど、全て復元できる。

# minikubeの削除

```shell:
~$ minikube delete
```

これは、minikubeというコンテナを削除するため、次回startは初期状態の新しいコンテナが作成されることになる。
minikubeのイメージはローカルに残されているため、インターネットから再ダウンロード不要。再作成は速い。

**以上、とりあえずminikubeを構築し、簡単なwebアプリもminikubeの外からアクセスできるところまで来た。
めでたしめでたし。**