マニフェストファイルからkubernetesのオブジェクトを作成してみる

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

## kubernetesについて
kubernetesとは？というのは[公式](https://kubernetes.io/ja/docs/concepts/overview/what-is-kubernetes/)や、以下を参照。

https://qiita.com/yuta-katayama-23/items/8d5528005c9ce2b7614c

以降では実際にマニフェストファイルを作成して、kubernetesオブジェクトを作成してみようと思う。

## 初めてのマニフェストファイル　Hello Worldコンテナを作成する
マニフェストファイルとは？という話をするより、実物を見る方がいい気がするので実物を見てからマニフェストファイルとは？そのファイルの中身の構成は？などについてみていく。

以下はPodを作成するマニフェストファイル。

```yaml
# pod.yaml
apiVersion: v1
kind: Pod
metadata:
  name: test
  namespace: default
  labels:
    env: study
spec:
  containers:
  - name: hello-world
    image: hello-world
```

ソースコード全体は以下。

https://github.com/yuta-katayama-23/docker-kubernetes/commit/0a667c8bc47acdf433d16cde5414726ff0ab2e95

上記のマニフェストファイルを適用して、Pod（以下の「kubernetesのリソース（作成できるオブジェクトの種類）」の章で詳細を見ていく）を作成・状態確認・削除をしてみると以下のような感じになる。
以降の章で何をやったのか？などの詳細については見ていく。

```console
[root@control-plane docker-kubernetes]# kubectl apply -f pod.yaml
pod/test created


[root@control-plane docker-kubernetes]# kubectl get -f pod.yaml
NAME   READY   STATUS             RESTARTS   AGE
test   0/1     CrashLoopBackOff   1          11s
[root@control-plane docker-kubernetes]# kubectl get all
NAME       READY   STATUS      RESTARTS   AGE
pod/test   0/1     Completed   3          63s

NAME                 TYPE        CLUSTER-IP   EXTERNAL-IP   PORT(S)   AGE
service/kubernetes   ClusterIP   10.96.0.1    <none>        443/TCP   3d13h


[root@control-plane docker-kubernetes]# kubectl delete -f pod.yaml
pod "test" deleted
[root@control-plane docker-kubernetes]# kubectl get pods
No resources found in default namespace.
[root@control-plane docker-kubernetes]# kubectl get all
NAME                 TYPE        CLUSTER-IP   EXTERNAL-IP   PORT(S)   AGE
service/kubernetes   ClusterIP   10.96.0.1    <none>        443/TCP   3d13h
```

## マニフェストファイルとは？
そもそもkubernetesには[kubectl](https://kubernetes.io/ja/docs/reference/kubectl/overview/)という、[Kubernetes API](https://kubernetes.io/ja/docs/concepts/overview/kubernetes-api/)を使用したkubernetesを操作するためのコマンドラインツールがある（kubernetes APIの[リファレンス](https://kubernetes.io/ja/docs/reference/)に書かれている内容はkubectlでできるという事）。

そのkubectlを使ってkubernetesのオブジェクトを作成する事もできるが、コマンドから作成するのではなく、以下のようにオブジェクトの定義を作成してそれを適用（kubernetesに反映）という手順でオブジェクトを作成する事もできる。この定義を記載したものがマニフェストファイルでありyaml形式で記述する。これによりIaC（インフラのコード化）もできる（なので基本的にはこのマニフェストファイルを用いたオブジェクトの作成（kubernetesリソースの構成定義）が一般的）。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1372684/b525ff39-06e8-0108-01f4-8123a50fa5b4.png)

・参考：[Kubernetes クラスタとは](https://www.redhat.com/ja/topics/containers/what-is-a-kubernetes-cluster)

## マニフェストファイルの構成
大きく分けて以下の3つのブロックから構成されている（コメントとなっているのはyamlのコメントなのでマニフェストファイルとは関係ない）。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1372684/e9077200-43ac-3f48-5f64-8d6ac421e1a9.png)

※この3ブロックで構成されるが、yamlのフィールドとして必須である項目は以下の4つフィールドになる（[公式](https://kubernetes.io/ja/docs/concepts/overview/working-with-objects/kubernetes-objects/#%E5%BF%85%E9%A0%88%E3%83%95%E3%82%A3%E3%83%BC%E3%83%AB%E3%83%89)を参照）。

・apiVersion  
・kind  
・metadata  
・spec  

・参考：[必須フィールド](https://kubernetes.io/ja/docs/concepts/overview/working-with-objects/kubernetes-objects/#%E5%BF%85%E9%A0%88%E3%83%95%E3%82%A3%E3%83%BC%E3%83%AB%E3%83%89)

### オブジェクトの種別
そのままだが、作成したいオブジェクトの種類を設定するためのフィールド。具体的には、"apiVersion"、"kind"で指定をする。

#### apiVersion  
どのバージョンのKubernetes APIを利用してオブジェクトを作成するか？を設定するもの。これは[API Reference](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.23/)を参照する事で判断ができ、例えば今回のようにPodというリソースのオブジェクトを作成したい場合であれば、以下のように[リファレンス](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.23/#pod-v1-core)に書かれているので"v1"となる。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1372684/3590402e-7790-e220-e641-80b97282d387.png)

このapiVersionは次に出てくるkindに応じて異なっており、基本的には"Group/Version"という規則で記載する必要がある。ただしGroupがcoreであるものは省略可能。なのでPodの場合には"v1"と表記できる。逆に、例えば以下のように[kindがIngress](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.23/#ingress-v1-networking-k8s-io)である場合（Ingressというリソースのオブジェクトを作成する際）には、Groupが"networking.k8s.io"なのでapiVersionは以下のように書く必要がある。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1372684/aa2d6669-e129-f91e-8399-93e23b3f1edd.png)

```yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
```

#### kind
どの種類のオブジェクトを作成するか？を設定するもの。指定できるものは[リソースタイプ一覧](https://kubernetes.io/ja/docs/reference/kubectl/overview/#resource-types)に書かれている（kubectlで"kubectl api-resources"としてもリソース一覧を確認できる）。

今回はPodのオブジェクトを作成したかったのでPodを指定している。

### メタデータ
オブジェクトを一意に特定するための情報で、"name"、"namespace"、"labels"というフィールドで設定する。

#### namespace、namse
[API Conventions（GitHubのページ）](https://github.com/kubernetes/community/blob/master/contributors/devel/sig-architecture/api-conventions.md#metadata)を読むと、namespace, name, uidのいずれも必須と書かれているが、以下の理由からnamespaceとuidは省略可能。

・namsespace：未指定の場合"default"というnamespaceが割り当てられるため（以下、公式からの引用）
> The default namespace is 'default'.

・uid：kubernetesで自動生成するため（以下、[公式](https://kubernetes.io/ja/docs/concepts/overview/working-with-objects/names/#uids)からの引用）
> オブジェクトを一意に識別するためのKubernetesが生成する文字列

今回は特に意味のある名前を付けなくてもいいのでnameは"test"として、namespaceは"default"とした（namsespaceは何も指定しなければ"default"になるので書かなくてもいいが）。

※ちなみに、上記の事からnameは省略不可という事になるが、実際にnameをコメントアウトして"kubectl apply -f pod.yaml"を実行すると以下のようにエラーになる（labelsは以下で見るようにオプションの項目）。

```console
[root@control-plane docker-kubernetes]# kubectl apply -f pod.yaml
error: error when retrieving current configuration of:
Resource: "/v1, Resource=pods", GroupVersionKind: "/v1, Kind=Pod"
Name: "", Namespace: "default"
from server for: "pod.yaml": resource name may not be empty
```
```yaml
apiVersion: v1
kind: Pod
metadata:
  # name: test
  # namespace: default ←　省略可なのでコメントアウト
  # labels:　　　　　　 ←　省略可なのでコメントアウト
  #   env: study
spec:
  containers:
  - name: hello-world
    image: hello-world
```

※上記のGitHubのページは[オブジェクトのspec(仕様)とstatus(状態)](https://kubernetes.io/ja/docs/concepts/overview/working-with-objects/kubernetes-objects/)の章にある以下の記述の「Kubernetes API Conventions」から飛べる。

> spec、status、metadataに関するさらなる情報は、[Kubernetes API Conventions](https://github.com/kubernetes/community/blob/master/contributors/devel/sig-architecture/api-conventions.md)をご確認ください。

#### labels
ユーザーによる整理を目的としたもので、してしなくも問題ないもの。今回はあえて勉強用のものだよという意味を持たせるために、env（環境）というキーにstudyを設定してみた。

> Labels are intended for organizational purposes by end users

・参考：[ラベル(Labels)とセレクター(Selectors)](https://kubernetes.io/ja/docs/concepts/overview/working-with-objects/labels/)

### 作成するオブジェクトの理想状態の定義
これは少しわかりにくいが、そもそもkubernetesの思想としては、ある状態を維持するように自動で色々調整する事で、人が何かをしなくても済む＝運用を楽にする、というものがある。具体的には[公式](https://kubernetes.io/ja/docs/concepts/#%E6%A6%82%E8%A6%81)に以下のように書かれている通り、色々なリソースに対してこうなっていてほしいを設定していく。

> 実行したいアプリケーションやその他のワークロード、使用するコンテナイメージ、レプリカ(複製)の数、どんなネットワークやディスクリソースを利用可能にするかなど

#### spec
上記で見てきた、理想の状態を実際に定義するフィールド。どんな設定をするのか？は作成するオブジェクトによって様々だが、例えばPodであれば[PodSpec v1 core](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.23/#podspec-v1-core)にどんな設定項目があるか？が書かれている。

今回は初めてのマニフェストファイルという事で、特に複雑な設定はしないのでcontainersだけ設定した（containersの中でもさらに設定ができるが、それは[Container v1 core](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.23/#container-v1-core)に書かれている）。具体的には[hello-world](https://hub.docker.com/_/hello-world)のDockerイメージを使って"hello-world"という名前のコンテナを構築するという設定をしている。

・参考：[概要](https://kubernetes.io/ja/docs/concepts/#%E6%A6%82%E8%A6%81)

## kubernetesのリソース（作成できるオブジェクトの種類）
続いて、上記の中で出てきたPodについて何か？についてみていく。Podとはkubernetesのリソースの1つで、コンテナを管理するための最小単位。Podは以下の図のようにPod：コンテナ＝1：1というわけではなく、Pod：コンテナ＝1：xという関係で、Podには複数のコンテナが存在する事もある。複数のコンテナをまとめて1つのユニットとして管理することで、（Docker）コンテナの使いにくさを解消する事ができる（複数のコンテナを1つのPodに作成できるのか？は、おまけの「Podに複数のコンテナを作成できるってホント？」を参照）。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1372684/99ab4bcb-9e15-4edc-65a1-bca38b5248dd.png)

Pod以外のkubernetesのオブジェクトとして作成できるもの一覧（リソースタイプ一覧）は[公式](https://kubernetes.io/ja/docs/reference/kubectl/overview/#resource-types)に書かれている。また、kubectlで"kubectl api-resources"としてもリソース一覧を確認できる。

```console
[root@control-plane docker-kubernetes]# kubectl api-resources
NAME                              SHORTNAMES   APIVERSION                             NAMESPACED   KIND
bindings                                       v1                                     true         Binding
componentstatuses                 cs           v1                                     false        ComponentStatus
configmaps                        cm           v1                                     true         ConfigMap
endpoints                         ep           v1                                     true         Endpoints
events                            ev           v1                                     true         Event
limitranges                       limits       v1                                     true         LimitRange
namespaces                        ns           v1                                     false        Namespace
nodes                             no           v1                                     false        Node
persistentvolumeclaims            pvc          v1                                     true         PersistentVolumeClaim
persistentvolumes                 pv           v1                                     false        PersistentVolume
pods                              po           v1                                     true         Pod
...
```

※ちなみに、今回「初めてのマニフェストファイル　Hello Worldコンテナを作成する」で作成したPodのオブジェクトについてみると、以下のように図示できる（ノードというのが出てきたが、これはおまけの「kubernetesの構成の補足」を参照）。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1372684/ac73b7f9-1500-0ae0-b715-9bef5710d042.png)

kubectlから確認するなら以下のようにして確認できる（今回はPod名"test"でPodを作成した（マニフェストファイルのname）ので"kubectl describe pod test"とすればPodの詳細を見る事ができ、実際にhello-worldコンテナがある事が存在することが分かる）。

```console
[root@control-plane docker-kubernetes]# kubectl describe pod test
Name:         test
Namespace:    default
Priority:     0
Node:         control-plane.minikube.internal/192.168.92.128
Start Time:   xxx, xx Mar 2022 13:41:35 +0900
Labels:       env=study
Annotations:  <none>
Status:       Running
IP:           172.17.0.3
IPs:
  IP:  172.17.0.3
Containers:
  hello-world:
    Container ID:   docker://71582a78517436640864d6061b7cbf29dd34b95b9d491b2000dd0a209dc034b6
    Image:          hello-world
    Image ID:       docker-pullable://hello-world@sha256:4c5f3db4f8a54eb1e017c385f683a2de6e06f75be442dc32698c9bbe6c861edd
    Port:           <none>
    Host Port:      <none>
    State:          Waiting
      Reason:       CrashLoopBackOff
    Last State:     Terminated
      Reason:       Completed
      Exit Code:    0
      Started:      xxx, xx Mar 2022 13:42:29 +0900
      Finished:     xxx, xx Mar 2022 13:42:29 +0900
    Ready:          False
    Restart Count:  3
    Environment:    <none>
    Mounts:
      /var/run/secrets/kubernetes.io/serviceaccount from default-token-jb9bf (ro)
  ...
```

※公式にも以下のように書かれているが、コンテナはDockerコンテナとは限らないが、はじめのうちはkubernetesのコンテナ＝Dockerコンテナというイメージだと理解がしやすいみたいなのでコンテナ＝Dockerコンテナでよさそう。

> 備考: KubernetesはDockerだけでなく複数のコンテナランタイムをサポートしていますが、Dockerが最も一般的に知られたランタイムであるため、Docker由来の用語を使ってPodを説明するのが理解の助けとなります。

・参考：[Pod](https://kubernetes.io/ja/docs/concepts/workloads/pods/)

## kubectlのコマンドについて
最後に、マニフェストファイルから実際にオブジェクトを作成するためにkubectlコマンドを使ったが、そのコマンドで何をしていたか？を見ていく。

・"kubectl apply -f pod.yaml"  
　pod.yamlの定義を使用してPodを作成  
・"kubectl get pods"  
　すべてのPodの一覧をプレーンテキスト形式で表示  
・"kubectl delete -f pod.yaml"  
　pod.yamlファイルで指定された名前を用いてPodを削除

・参考：[kubectlの概要](https://kubernetes.io/ja/docs/reference/kubectl/overview/)  
・参考：[kubectlリファレンス](https://kubernetes.io/docs/reference/generated/kubectl/kubectl-commands)  
・参考：[例: 一般的な操作](https://kubernetes.io/ja/docs/reference/kubectl/overview/#%E4%BE%8B-%E4%B8%80%E8%88%AC%E7%9A%84%E3%81%AA%E6%93%8D%E4%BD%9C)

## まとめとして
今回はkubernetesのHello World！に続いて、マニフェストファイルから実際にkubernetesのオブジェクトを作成するのをやってみた。今後さらにkubernetesに関して色々理解を深めて良ければと思っている。

## おまけ
### Podに複数のコンテナを作成できるってホント？
以下のようなマニフェストファイルを作成して、kubernetesに適用して"kubectl describe pod test"で確認してみると、確かにコンテナが2つ作成されている事が確認できる。

```yaml
apiVersion: v1
kind: Pod
metadata:
  name: test
  namespace: default
  labels:
    env: study
spec:
  containers:
  - name: hello-world
    image: hello-world:linux
  - name: centos
    image: centos:7
```
```console
[root@control-plane docker-kubernetes]# kubectl describe pod test
Name:         test
Namespace:    default
Priority:     0
Node:         control-plane.minikube.internal/192.168.92.128
Start Time:   xxx, xx Mar 2022 13:49:13 +0900
Labels:       env=study
Annotations:  <none>
Status:       Running
IP:           172.17.0.3
IPs:
  IP:  172.17.0.3
Containers:
  hello-world:
    Container ID:   docker://d39b38bed1c62fe56b138ead0a3efb392bba497f722123bd6ea3f2f93cad3c02
    Image:          hello-world:linux
    Image ID:       docker-pullable://hello-world@sha256:19c35675aac535e0f5803f12000ed7ffae510a43f1e3a839e7f4a9942a03dace
    Port:           <none>
    Host Port:      <none>
    State:          Waiting
      Reason:       CrashLoopBackOff
    Last State:     Terminated
      Reason:       Completed
      Exit Code:    0
      Started:      xxx, xx Mar 2022 13:49:20 +0900
      Finished:     xxx, xx Mar 2022 13:49:20 +0900
    Ready:          False
    Restart Count:  1
    Environment:    <none>
    Mounts:
      /var/run/secrets/kubernetes.io/serviceaccount from default-token-jb9bf (ro)
  centos:
    Container ID:   docker://9aa4c62ddfc46e346627d301deca3bc6cf68c719ef8061ebfb28bb2a47412f07
    Image:          centos:7
    Image ID:       docker-pullable://centos@sha256:c73f515d06b0fa07bb18d8202035e739a494ce760aa73129f60f4bf2bd22b407
    Port:           <none>
    Host Port:      <none>
    State:          Waiting
      Reason:       CrashLoopBackOff
    Last State:     Terminated
      Reason:       Completed
      Exit Code:    0
      Started:      xxx, xx Mar 2022 13:49:20 +0900
      Finished:     xxx, xx Mar 2022 13:49:20 +0900
    Ready:          False
    Restart Count:  1
    Environment:    <none>
    Mounts:
      /var/run/secrets/kubernetes.io/serviceaccount from default-token-jb9bf (ro)
...
```

### kubernetesの構成の補足
「kubernetesのリソース（作成できるオブジェクトの種類）」で「ノード」というものが出てきたが、kubernetesにはマスターノードとワーカーノードという概念があり、マスターノードは各ワーカーノードを管理する役割を持つ。イメージとしては以下の図のような感じ。そして、この図の中のワーカーノードにPodなどの各種kubernetesのオブジェクト（リソース）が配置される。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1372684/ed40bfb4-d1e0-e720-4a86-1f315d75b4a0.png)

※ただし、minikubeの場合にはマスターノード・ワーカーノードの区別がないシングルノードの環境（ネットワーク）になる（以下、[公式](https://kubernetes.io/ja/docs/setup/learning-environment/minikube/)からの引用）。

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

実際にkubectlでノードの中身を見てみると、確かに"control-plane.minikube.internal"というノードが見つかり、そのノードには"test"というPodがある事が分かる（Namespaceが"kube-system"であるものはkubernetesによって作成されたオブジェクト）。

```console
[root@control-plane docker-kubernetes]# kubectl get nodes
NAME                              STATUS   ROLES                  AGE   VERSION
control-plane.minikube.internal   Ready    control-plane,master   48m   v1.20.7

[root@control-plane docker-kubernetes]# kubectl describe nodes control-plane.minikube.internal
Name:               control-plane.minikube.internal
Roles:              control-plane,master
...
Non-terminated Pods:          (8 in total)
  Namespace                   Name                                                       CPU Requests  CPU Limits  Memory Requests  Memory Limits  Age
  ---------                   ----                                                       ------------  ----------  ---------------  -------------  ---
  default                     test                                                       0 (0%)        0 (0%)      0 (0%)           0 (0%)         39m
  kube-system                 coredns-74ff55c5b-6jz4p                                    100m (5%)     0 (0%)      70Mi (1%)        170Mi (4%)     48m
  kube-system                 etcd-control-plane.minikube.internal                       100m (5%)     0 (0%)      100Mi (2%)       0 (0%)         48m
  kube-system                 kube-apiserver-control-plane.minikube.internal             250m (12%)    0 (0%)      0 (0%)           0 (0%)         48m
  kube-system                 kube-controller-manager-control-plane.minikube.internal    200m (10%)    0 (0%)      0 (0%)           0 (0%)         48m
  kube-system                 kube-proxy-mmczz                                           0 (0%)        0 (0%)      0 (0%)           0 (0%)         48m
  kube-system                 kube-scheduler-control-plane.minikube.internal             100m (5%)     0 (0%)      0 (0%)           0 (0%)         48m
  kube-system                 storage-provisioner                                        0 (0%)        0 (0%)      0 (0%)           0 (0%)         48m
...
```

※以下の記事にkubernetesの全体的な概念についてはまとめている。

https://qiita.com/yuta-katayama-23/items/8d5528005c9ce2b7614c

・参考：[Kubernetesのコンポーネント](https://kubernetes.io/ja/docs/concepts/overview/components/)