初めてのKubernetes　KubernetesでHello World！をやってみて基本的な概念について理解してみた

## はじめに
アプリケーションの開発を行うエンジニアであっても、インフラに関する事もある程度は知っておく必要がありそう…。
というわけでコンテナオーケストレーションについて理解を深めておこうと思い、kubernetesの基本概念について理解してみたので、その備忘録を残す。

※本記事内の操作は以下のように、docker/minikube/kubectlのそれぞれが利用可能な状態である事が前提となっている。環境構築については今後記事を執筆予定。

```console
[root@control-plane docker-kubernetes]# docker version
Client: Docker Engine - Community
 Version:           20.10.7
 API version:       1.41
 ...

Server: Docker Engine - Community
 Engine:
  Version:          20.10.7
  API version:      1.41 (minimum version 1.12)
  ...
[root@control-plane docker-kubernetes]# kubectl version
Client Version: version.Info{Major:"1", Minor:"21", GitVersion:"v1.21.2", GitCommit:"092fbfbf53427de67cac1e9fa54aaa09a28371d7", GitTreeState:"clean", BuildDate:"2021-06-16T12:59:11Z", GoVersion:"go1.16.5", Compiler:"gc", Platform:"linux/amd64"}
Server Version: version.Info{Major:"1", Minor:"20", GitVersion:"v1.20.7", GitCommit:"132a687512d7fb058d0f5890f07d4121b3f0a2e2", GitTreeState:"clean", BuildDate:"2021-05-12T12:32:49Z", GoVersion:"go1.15.12", Compiler:"gc", Platform:"linux/amd64"}

[root@control-plane docker-kubernetes]# minikube status
minikube
type: Control Plane
host: Running
kubelet: Running
apiserver: Running
kubeconfig: Configured
```

## kubernetesでHello World!
まずは最初になる事としてはお決まりの"Hello World!"をやってみたいと思う。

やる事としては、[hello-world](https://hub.docker.com/_/hello-world)のDockerイメージをkubernetes（k8s）上で実行し、k8s内のログを確認して削除する、というもの。
実際にk8s上で動かしてみると以下の画像のようになる。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1372684/054b67a9-1599-38ad-d87f-2f3d0db1a3d6.png)

※一応、コマンドで何をしているか？を見ていくと、

- `kubectl run hello-world --image=hello-world --restart=Never`<br>指定したイメージをクラスタ上で実行している<br>`--restart=Never`は通常のpodを作成するための引数<br>https://kubernetes.io/docs/reference/generated/kubectl/kubectl-commands#run
- `kubectl get pod`<br>ps出力形式で全podのリストを表示<br>※pod or podsなのか良く分かっていない…。podでも一覧が表示されるが、https://kubernetes.io/docs/reference/generated/kubectl/kubectl-commands#get を見る限り`kubectl get pods`が正しいのかもしれない
- `kubectl logs pod/hello-world`<br>Podのログをダンプする（標準出力で）<br>これも[公式](https://kubernetes.io/ja/docs/reference/kubectl/_print/#%E5%AE%9F%E8%A1%8C%E4%B8%AD%E3%81%AE%E3%83%9D%E3%83%83%E3%83%89%E3%81%A8%E3%81%AE%E5%AF%BE%E8%A9%B1%E5%87%A6%E7%90%86)を見る限り、`pod/`はなくても`kubectl logs hello-world`でログが見れるのでそれでいいのかもしれない<br>https://kubernetes.io/docs/reference/generated/kubectl/kubectl-commands#logs
- `kubectl delete pod/hello-world`<br>`kubectl delete`はファイル、標準出力、またはラベルセレクター、リソースセレクター、リソースを指定して、リソースを削除するコマンドで、今回はpodを削除している<br>これも[公式](https://kubernetes.io/ja/docs/reference/kubectl/_print/#%E3%83%AA%E3%82%BD%E3%83%BC%E3%82%B9%E3%81%AE%E5%89%8A%E9%99%A4)を見る限り、`pod/`ではなく`kubectl delete pod hello-world`で削除できたのでそれでいいのかもしれない<br>https://kubernetes.io/docs/reference/generated/kubectl/kubectl-commands#delete

という感じ。コマンド実行時に出てきたpodなどのkubernetesの概念は以下の章で見ていく。

- 参考：[status](https://minikube.sigs.k8s.io/docs/commands/status/)  
- 参考：[Operations](https://kubernetes.io/docs/reference/kubectl/#operations)（日本語は[ここ](https://kubernetes.io/ja/docs/reference/kubectl/overview/#%E6%93%8D%E4%BD%9C)）

## 基本概念
### kubernetesとは
一言で言うと「コンテナオーケストレーション（実際の物理リソースに対し、大量のコンテナをデプロイ・管理していく）」仕組み。これだけだと？になると思うのでもう少し概念について詳細を見ていく。

そもそも何でkubernetesというものが登場したのか？という背景としては、今までのシステム運用での課題が分かると分かりやすい気がしたのでその観点でまとめてみると、以下のようになるだろう。

| # | 今までは… | kuberbetesになると… |
|:-:|---|---|
| 1 | :x:<br>システムのリソースの利用率に無駄がある… | :white_check_mark:<br>複数のコンテナを共存させ、リソースを最大限効率的に利用できるように！  |
| 2 | :x:<br>突発的な大量アクセスでシステムが応答しなくなった… | :white_check_mark:<br>システムを構成するサーバーの台数を増やし、システム全体の処理能力を高める事ができるように！（水平スケール） |
| 3 | :x:<br>突然、一部のシステムがダウンしてしまいサービスが利用不可になってしまった… | :white_check_mark:<br>システムの稼働状況を監視・必要であれば自動デプロイができるようになり、継続的なサービス運用が可能に！ |
| 4 | :x:<br>リリースのたびにサービス停止が発生してしまう… | :white_check_mark:<br>複数の稼働中サーバーに対して一定数づつ新しいアプリケーションをデプロイ・リリースができるように！（ローリングデプロイ） |

※詳細については[What is Kubernetes?](https://kubernetes.io/docs/concepts/overview/what-is-kubernetes/)（日本語は[ここ](https://kubernetes.io/ja/docs/concepts/overview/what-is-kubernetes/)）を参照。

### どのような仕組みで動いているか？
kuberbetesで複数のリソースサーバをどのように管理していくか？だが、以下の図のようにnodeという単位が登場し、実際のリソースサーバは`worker node`と呼ばれ、そのworker nodeを束ねて管理するものとして`master node`というものがある、という構成になっている。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1372684/49fcbe47-c02a-5caa-c6ca-b785c6c742e1.png)

そして各リソースサーバであるworker nodeは、master nodeからの命令を受ける形で管理される（ユーザーがリソースサーバを管理する際には、master nodeに対して、[kubectl](https://kubernetes.io/docs/reference/kubectl/)（日本語は[ここ](https://kubernetes.io/ja/docs/reference/kubectl/overview/)）を利用して各リソースサーバ（worker node）に対して命令を行う）。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1372684/e33fd685-d56f-353c-83e5-27cd1cf3286c.png)

※上記の事を見て分かるが、kubernetesの各Node＝実サーバ（サーバーの実態）である

### kubernetesのリソース
上記の[どのような仕組みで動いているか？](#どのような仕組みで動いているか)でリソースサーバという言い方をしていたが、kubernetesにはいったいどんなリソースがあるのか（リソースサーバー上で動くリソースとは）？についてここでは見ていく（[どのような仕組みで動いているか？](#どのような仕組みで動いているか)でkubectlでリソースへの操作・命令できるといったが、以下のようなリソースを管理するためのコマンドがkubectl）。

一覧としては以下のようなものがある（ようである）。
| 分類 | リソースの種別 |
|:-:|---|
| ワークロード | ・Pod：最小単位。コンテナの集合。<br>・ReplicaSet：Podのレプリカ（複製品）の集合<br>・Deployment：Pod（ReplicaSet）の集合（ReplicaSetを管理するもの）<br>・StatefulSet：Podの集合。Podの識別子が順序付けされる。 |
| サービス | ・Service：Podの論理的なセットや、そのPodのセットにアクセスするためのポリシー（IPアドレスなど）を定義する。L4ロードバランサー。<br>・Ingress：L7ロードバランサー。 |
| 設定 | ・ConfigMap：機密性のない設定情報を保存<br>・Secret：機密性のある設定情報を保存 |
| ストレージ | ・PersistentVolume：永続データそのもの（実態）<br>・PersistentVolumeClaim：永続データを要求するもの |

- 参考：[Kubernetesのリソースの一覧と省略形](https://redj.hatenablog.com/entry/2021/02/21/002022)
- 参考：[Kubernetes Components](https://kubernetes.io/docs/concepts/overview/components/)（日本語は[ここ](https://kubernetes.io/ja/docs/concepts/overview/components/)）

#### ワークロード
##### Pod
kubernetesでDockerコンテナを管理するための最小単位で、Dockerコンテナの集合。Dockerコンテナを単独で管理するのではなく、グループとして管理することで、Dockerコンテナの使いにくさを解消することができる。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1372684/0dd01882-4ac5-1a0b-3be5-dd4603ae3324.png)

- 参考：[Pods](https://kubernetes.io/docs/concepts/workloads/pods/)（日本語は[ここ](https://kubernetes.io/ja/docs/concepts/workloads/pods/)）
- [KubernetesのPod機能](https://www.designet.co.jp/ossinfo/kubernetes/pod/#:~:text=Pod%E3%81%A8%E3%81%AF%E3%80%81Kubernetes%E3%81%A7,%E3%81%99%E3%82%8B%E3%81%93%E3%81%A8%E3%81%8C%E3%81%A7%E3%81%8D%E3%81%BE%E3%81%99%E3%80%82)

##### ReplicaSet
複数のPodのレプリカ（複製品）をセットで作成する機能で、Podの集合と言える。例えば、kubernetes起動時のレプリカ数を3にすると、kubernetesは同じ機能のPodを3つ自動的に起動する。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1372684/94681684-cf4a-4700-571b-94a5bd16e485.png)

※ReplicaSetの存在意義だが、それはmaster nodeがkubernetes上で指定した数のレプリカが動作することを保証する＝何らかのトラブル（node障害など）でPodが減少した場合には別のnodeで自動的にPodを起動してレプリカ数を維持する、という事をやってくれるので障害に強くなるという事（だと思っている）。

※自動的にPodのレプリカ数を維持してくれるので、ある意味Podのスケールを行うものと考える事もできる？かも。

- 参考：[ReplicaSet](https://kubernetes.io/docs/concepts/workloads/controllers/replicaset/)（日本語は[ここ](https://kubernetes.io/ja/docs/concepts/workloads/controllers/replicaset/)）
- 参考：[Kubernetesのレプリカセットとデプロイメント](https://www.designet.co.jp/ossinfo/kubernetes/replicaset/)

##### Deployment
Pod（ReplicaSet）をまとめたもの（集合体）（ReplicaSetを管理するもの）。kubernetesでは基本的にPodを単独で起動する事はなく、ReplicaSetなどの管理用のオブジェクトを介してPodを利用するようだが、DeploymentはそのReplicaSetの管理のためのもので、ReplicaSetの世代管理（ロールバック・ロールフォワード）をする役割を持つ。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1372684/44f1de9e-63f7-8d95-1297-bfc3ea5cb99b.png)

※このDeploymentを作成すると、ある理想的な状態（目的の状態）を定義しておくだけでkubernetesのDeploymentコントローラが、現状の状態を目的の状態へ、指定された頻度で変更してくれる。実際にアプリケーションの本番リリースとかで使われる（と思っている）。

- 参考：[Deployments](https://kubernetes.io/docs/concepts/workloads/controllers/deployment/)（日本語は[ここ](https://kubernetes.io/ja/docs/concepts/workloads/controllers/deployment/)）
- 参考：[Deploymentとは](https://www.designet.co.jp/faq/term/?id=RGVwbG95bWVudA)

##### StatefulSet
これもDeploymentと同様に、Podの集合。ただし、Podの順序と一意性を保証するのでスケールする際の名前が一定になる（各Podにおいて管理が大変な同一性を維持する）。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1372684/59e95845-ee0d-894d-7f7a-346a5e22a101.png)

※StatefulSetもDeployment同様にPodの集合であるが、Deploymentとの違いとしては、

- DeploymentでReplicaSetを作成（レプリカ）した場合（Deploymentで管理されるPod）<br>→PodにはランダムのIDが付与される
- StatefulSetで管理されるPod<br>→Podには順序付けされた一意の識別子が付与される

という感じ。

- 参考：[StatefulSets](https://kubernetes.io/docs/concepts/workloads/controllers/statefulset/)（日本語は[ここ](https://kubernetes.io/ja/docs/concepts/workloads/controllers/statefulset/)）
- 参考：[kubernetesのステートフルセットを利用して障害に強いpodを作成する①](https://zenn.dev/suiudou/articles/a8a0dbfdad7507)

#### サービス
##### Service
Podの論理的なセットや、そのPodのセットにアクセスするためのポリシー（IPアドレスなど）を定義するもの。外部公開・名前解決（接続先のIPアドレスの解決）・L4ロードバランサー（送信元IP、送信元ポート、宛先IP、宛先ポート、プロトコルの５つ（5タプルハッシュ）の条件を利用して振り分け先を決めるもの、ロードバランサが送信先のipをapplicationサーバーに書き換える）を担うと思えばいい。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1372684/ba4f8081-67e3-6882-6089-cece4e766230.png)

※このServiceが必要な理由としては、[公式](https://kubernetes.io/docs/concepts/services-networking/service/#motivation)に書かれている通りで、そもそもPodはNodeに散らばっているため、それぞれのPodと通信するにはそのIPIPアドレスを知る必要がある。ただ、kubernetesの特性上、NodeのIPアドレスは一定にならないため、突然Podと通信できなくなる可能性がある（PodのIPアドレスを探し出したり、そのIPアドレスを記録し続けなければならなくなる）。
→そこで、複数のPod・NodeのIPアドレスをクライアントが意識せずに単一のエンドポイントで通信するできるようにするために、Pod,Nodeの存在を抽象化し、Podとの通信に単一のエンドポイントを提供する、という事をやる。このための仕組みがService。

- 参考：[Service](https://kubernetes.io/docs/concepts/services-networking/service/)（日本語は[ここ](https://kubernetes.io/ja/docs/concepts/services-networking/service/)）
- 参考：[Kubernetesの Service についてまとめてみた](https://qiita.com/kouares/items/94a073baed9dffe86ea0)

##### Ingress
ServiceへのHTTPとHTTPSのルートを公開するもの。外部公開・L7ロードバランサー（URLのドメイン名（ホストベース）やパス名（パスベース）により振り分け先を決めるもの、ロードバランサがapplicationサーバーと通信を行う＝applicationサーバーから見た送信元ipはロードバランサになる）を担うと思えばいい
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1372684/79db57b8-9081-a6c3-8f9f-ade7164d17a8.png)

- 参考：[Ingress](https://kubernetes.io/docs/concepts/services-networking/ingress/)（日本語は[ここ](https://kubernetes.io/ja/docs/concepts/services-networking/ingress/)）
- [L4 L7ロードバランサーの違いを30秒で](https://qiita.com/iizukapynyo/items/57bc561d75ab335df863)

#### 設定
##### ConfigMap
機密性のない設定情報を保存するリソース。具体的には環境変数、コマンドライン引数、ボリューム内の設定ファイルとして使われる。

- 参考：[ConfigMaps](https://kubernetes.io/docs/concepts/configuration/configmap/)（日本語は[ここ](https://kubernetes.io/ja/docs/concepts/configuration/configmap/)）

##### Secret
機密情報を保存するためのリソース。具体的には、パスワード、OAuthトークン、SSHキーのようなものを保存する。base64でエンコードされた文字列を保存するのがルール。

- 参考：[Secret](https://kubernetes.io/docs/concepts/configuration/configmap/)（日本語は[ここ](httpストレージs://kubernetes.io/ja/docs/concepts/configuration/secret/)）

#### ストレージ
##### PersistentVolume
永続データの実態（永続化ボリュームそれ自体についてのリソース）。実際の物理ストレージへの接続情報を持ち、ストレージを抽象化する役割がある。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1372684/9921ab81-2dc0-22c4-c6b0-3c7189697ccb.png)

- 参考：[Persistent Volumes](https://kubernetes.io/docs/concepts/storage/persistent-volumes/)（日本語は[ここ](https://kubernetes.io/ja/docs/concepts/storage/persistent-volumes/)）
- 参考：[Kubernetes道場 12日目 - PersistentVolume / PersistentVolumeClaim / StorageClassについて](https://cstoku.dev/posts/2018/k8sdojo-12/)

##### PersistentVolumeClaim
永続データの要求（取得する命令をする）もの（永続化ボリュームの利用請求をするリソース）。抽象化されたストレージを要求する役割がある
。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1372684/93b2e6c7-ca33-bf63-0a8d-925f8394ba91.png)

- 参考：[Persistent Volumes](https://kubernetes.io/docs/concepts/storage/persistent-volumes/)（日本語は[ここ](https://kubernetes.io/ja/docs/concepts/storage/persistent-volumes/)）
- 参考：[Kubernetes道場 12日目 - PersistentVolume / PersistentVolumeClaim / StorageClassについて](https://cstoku.dev/posts/2018/k8sdojo-12/)

## kubernetesのネットワークのイメージ
上記の[どのような仕組みで動いているか？](#どのような仕組みで動いているか)で見たように、リソース（[kubernetesのリソース](#kubernetesのリソース)で取り上げたもの）は各ワーカーノードに分散されて配置される（以下のようなイメージ）。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1372684/dc38f8ae-a4d7-4699-ebda-f7b986bf380d.png)

上記の図において、各ワーカーノード間はネット―ワークでつながっており、そのネットワークはクラスターネットワークと呼ぶ（クラスターネットワークに対して、マスターノードとワーカーノード間のネットワークは外部ネットワークという事になる）。以下の図に書いたように、クラスターネットワークへは外部からは直接アクセスできない。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1372684/3cca8055-de12-462e-0580-dcc465d09fe6.png)

あるコンテナ（Pod内で起動しているコンテナ）の動作確認をするには以下の3つのアプローチがある。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1372684/0776a6a7-2aff-a750-1364-ffe04e4c869b.png)

| # | アプローチ詳細 |  |
|:-:|---|:-:|
| 1 | kubectlを使ってマスターノード経由でPodのコンテナに直接入る<br>※[kubernetesでHello World!](#kubernetesでhello-world)でやったhello-worldコンテナに対して`kubectl logs pod/hello-world`としたのがこれにあたる | ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1372684/f6e43dbb-5409-bb5c-a208-d8f5204aa492.png) |
| 2 | 踏み台に対してkubectlで直接入り、そのコンテナ経由でアクセスする | ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1372684/00fdcf7d-dab0-19b7-7bf4-0c600f0cfd6d.png) |
| 3 | サービス経由でアクセスする | ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1372684/12466c76-2268-c141-e42f-79e00487e44c.png) |

※[kubernetesでHello World!](#kubernetesでhello-world)を実行した環境のネットワークとしては、以下のようにマスターノード/ワーカーノードの区別がないシングルノードのネットワークとなる（以下、[公式](https://kubernetes.io/ja/docs/setup/learning-environment/minikube/)からの引用）。

> PC上のVM内でシングルノードのKubernetesクラスタを実行する

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1372684/0f3a0cc0-fa5c-4208-ffbd-9d857b7269bd.png)
 
## 参考文献
- [Kubernetesとは何かを図でわかりやすく解説！Pod、Namespaceも](https://udemy.benesse.co.jp/development/system/kubernetes.html)
- [Kubernetes上のコンテナをIngressでインターネットに公開するまで](Kubernetes上のコンテナをIngressでインターネットに公開するまで)