【Kubernetes】マニフェストファイルの読み方

## はじめに
Kubernetesのマニフェストファイルの内容がよくわからない、という方いらっしゃるではないでしょうか？
私もその一人です。

今回はマニフェストファイルの読み方について`yaml`コード内にコメントをつけて解説してきたいと思います。
補足説明はコード外にいたします。


## マニフェストファイルの解説

```yaml
apiVersion: apps/v1             # KubernetesにはAPIバージョンがあるためそれを記述します。 ①
kind: Deployment                # リソースの種類を記述します。 (Ingress, Serviceなど) DeploymentはReplicaSetを生成/管理し、ReplicaSetはPod(Podの数 = レプリカ数)を生成/管理します。
metadata:                       # その名の通りメタデータ、要するに「そのデータ(ここではDeployment)に関連する情報を記述したデータ」を記述します。
  name: nginx-deployment        # Deploymentの名前を記述します。
  namespace: default            # 名前空間を記述します。 ②
spec:                           # ReplicaSetのあるべき姿を定義します。
  replicas: 1                   # レプリカ数を定義します = 1の場合1つのPodを要求します。
  selector:                     # Deploymentの管理対象とするPodの情報を指定します。selectorによってラベルの条件が指定され、その条件に一致するPodがDeploymentによって管理されます。
    matchLabels:                # Podをラベルでフィルタリングします。 ③
      app: nginx                # Podが持つラベルを指定します。
  template:                     # Deploymentが生成するPodの使用を定義します。 ④
    metadata:
      labels:
        app: nginx
    spec:                       # コンテナのスペックを定義します。使用するイメージ, cpu, memory, portなど
      containers:
        - name: nginx
          image: nginx:1.27.1 
          resources:
            requests:
              cpu: "2"
              memory: "10G"
          ports:
            - containerPort: 80  # コンテナのポートに関する情報です。 ⑤
          livenessProbe:         # Podに対するヘルスチェックを行う仕組みです。 ⑥
            httpGet:
              port: 80
              path: /liveness/healthcheck
            periodSeconds: 10
            timeoutSeconds: 5
            successThreshold: 1
            failureThreshold: 10

```
## 補足説明

### ① apiVersion
`kubectl`コマンドで`kubectl api-resources`とすると、以下のように各`apiVersion`を表示することができます。
```bash
$ 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
podtemplates                                   v1                                true         PodTemplate
replicationcontrollers            rc           v1                                true         ReplicationController
resourcequotas                    quota        v1                                true         ResourceQuota
secrets                                        v1                                true         Secret
serviceaccounts                   sa           v1                                true         ServiceAccount
services                          svc          v1                                true         Service
mutatingwebhookconfigurations                  admissionregistration.k8s.io/v1   false        MutatingWebhookConfiguration
validatingwebhookconfigurations                admissionregistration.k8s.io/v1   false        ValidatingWebhookConfiguration
customresourcedefinitions         crd,crds     apiextensions.k8s.io/v1           false        CustomResourceDefinition
apiservices                                    apiregistration.k8s.io/v1         false        APIService
controllerrevisions                            apps/v1                           true         ControllerRevision
daemonsets                        ds           apps/v1                           true         DaemonSet
deployments                       deploy       apps/v1                           true         Deployment
replicasets                       rs           apps/v1                           true         ReplicaSet
statefulsets                      sts          apps/v1                           true         StatefulSet
selfsubjectreviews                             authentication.k8s.io/v1          false        SelfSubjectReview
tokenreviews                                   authentication.k8s.io/v1          false        TokenReview
localsubjectaccessreviews                      authorization.k8s.io/v1           true         LocalSubjectAccessReview
selfsubjectaccessreviews                       authorization.k8s.io/v1           false        SelfSubjectAccessReview
selfsubjectrulesreviews                        authorization.k8s.io/v1           false        SelfSubjectRulesReview
subjectaccessreviews                           authorization.k8s.io/v1           false        SubjectAccessReview
horizontalpodautoscalers          hpa          autoscaling/v2                    true         HorizontalPodAutoscaler
cronjobs                          cj           batch/v1                          true         CronJob
jobs                                           batch/v1                          true         Job
certificatesigningrequests        csr          certificates.k8s.io/v1            false        CertificateSigningRequest
leases                                         coordination.k8s.io/v1            true         Lease
endpointslices                                 discovery.k8s.io/v1               true         EndpointSlice
events                            ev           events.k8s.io/v1                  true         Event
flowschemas                                    flowcontrol.apiserver.k8s.io/v1   false        FlowSchema
prioritylevelconfigurations                    flowcontrol.apiserver.k8s.io/v1   false        PriorityLevelConfiguration
ingressclasses                                 networking.k8s.io/v1              false        IngressClass
ingresses                         ing          networking.k8s.io/v1              true         Ingress
networkpolicies                   netpol       networking.k8s.io/v1              true         NetworkPolicy
runtimeclasses                                 node.k8s.io/v1                    false        RuntimeClass
poddisruptionbudgets              pdb          policy/v1                         true         PodDisruptionBudget
clusterrolebindings                            rbac.authorization.k8s.io/v1      false        ClusterRoleBinding
clusterroles                                   rbac.authorization.k8s.io/v1      false        ClusterRole
rolebindings                                   rbac.authorization.k8s.io/v1      true         RoleBinding
roles                                          rbac.authorization.k8s.io/v1      true         Role
priorityclasses                   pc           scheduling.k8s.io/v1              false        PriorityClass
csidrivers                                     storage.k8s.io/v1                 false        CSIDriver
csinodes                                       storage.k8s.io/v1                 false        CSINode
csistoragecapacities                           storage.k8s.io/v1                 true         CSIStorageCapacity
storageclasses                    sc           storage.k8s.io/v1                 false        StorageClass
volumeattachments                              storage.k8s.io/v1                 false        VolumeAttachment

```

`grep`することで、このマニフェストの`apiVersion`ってなんだっけ？を確認する手段として使用できます。
```bash
> kubectl api-resources | grep deployment
deployments                       deploy       apps/v1                           true         Deployment
```

### ② namespace
namespaceとは例えると家の中でそれぞれの名前が付けられた部屋を作って分離することです。
家(クラスター)の中に各部屋(namespace)が存在し、それぞれの部屋は独立して管理されています。
例えば、4人家族で父、母、娘、息子の家族構成の場合、娘の部屋、息子の部屋、寝室などを倫理的に区切り、それらは独立して管理できるようにしています。
娘の部屋を模様替えしても、息子の部屋まで模様替えされることはありえません。

namespaceの運用方法として例えば`運用`と`開発`のチームごとに、namespaceを分離することです。
- 運用のnamespaceには特定のリソースに対してのみ読み取り権限を与える
- 開発のnamespaceにはリソースの作成、変更削除をできる権限を与える
といった具合です。

Kubernetesにはデフォルトで以下のnamespaceが存在し、この名前空間をすぐに使用できるように設定されています。
- default
- kube-system (Kubernetesコンポーネントに使用)
- kube-public (パブリックリソースに使用)

以下コマンドを実行することでnamespaceの情報を取得することができます。
```bash
> kubectl get namespace
NAME                   STATUS   AGE
default                Active   192d
kube-node-lease        Active   192d
kube-public            Active   192d
kube-system            Active   192d
kubernetes-dashboard   Active   192d
```

namespaceを作成する場合は以下コマンドで作成することができます。
```bash
> kubectl create namespace <名前空間名>
```

## ③ matchLabels
説明が長くなりそうなので補足します。
Podをラベルでフィルタリングする方法がmatchLabelsです。
`matchLabels`に`app: nginx`というラベルが指定されています。
つまり、`app: nginx`というラベルを持つPodが`Deployment`によって管理される対象となります。
```yaml
  selector:
    matchLabels:
      app: nginx
```
`Deployment`は`ReplicaSet`を生成/管理し、`ReplicaSet`は`Pod`を生成/管理します。
`ReplicaSet`は指定された`Pod`数に調整、管理を行う仕組みです。
![Deploymentの仕組み.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/379725/159495e1-f025-fc91-48f5-2915140eac08.png)

マニフェストに`Replicaset`を定義することで、障害などで`Pod`がダウンした際にも自動的に指定の数に`Pod`数を調整してくれます。
`Deployment`は例えばコンテナのイメージのバージョンアップをしなければならない場合などに、新しい`ReplicaSet`を作成し、新旧の全体の`Pod`数を調整しながら新しい`Pod`を生成します。
![Deploymentの仕組み_2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/379725/f0217cb9-c8ba-28b7-c190-efbadc9a7c58.png)


## ④ templateとmatchLabelsの違い
###  `template`の役割
Podの仕様を定義する部分で、「どんなコンテナを使用するか」「どんなリソースを要求するか」「どんなラベルを付けるのか」といったことを設定します。


`template`以下を見てもらうとわかりやすいかと思いますが、ぱっと見でも「コンテナの名前」「コンテナのイメージ」「コンテナのスペック」などが定義されているのがわかります。
```yaml
spec:
  replicas: 1
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
        - name: nginx
          image: nginx:1.27.1 
          resources:
            requests:
              cpu: "2"
              memory: "10G"
          ports:
            - containerPort: 80
```
以下に記述されている`template.metadata.labels`に記述されているフィールドは生成されるPodにどんなラベルを付けるを指定する箇所です。
生成されるPodには`app: nginx`というラベルが付与されます。
```yaml
  template:
    metadata:
      labels:
        app: nginx
```

### `matchLabels`の役割
`selector.matchLabels`については「`app: nginx`というラベルを持つPodを管理します」ということを指定しています。
```yaml
  selector:
    matchLabels:
      app: nginx
```

Deploymentによる管理の流れは以下のようになります。
1. `Deployment`はPodを作成するときに`template`に基づいてPodを生成します。この時、生成される全てのPodに`app: nginx`というラベルが付与されます。
1. `selector.matchLabels`は「`app: nginx`というラベルを持つPod」を管理対象として指定します。
```yaml
Deployment
  └─ spec:
      └─ template:   <-- Podがこのテンプレートに従って作成される
            └─ metadata:
                  └─ labels: 
                        └─ app: nginx  <-- 作成されたPodに「app=nginx」ラベルが付与される

  └─ spec:
      └─ selector:   <-- このラベルに一致するPodを管理する
            └─ matchLabels:
                  └─ app: nginx

```

## ⑤ containerport
この`containerport`ですが、あくまでコンテナのポートは`80`を指定していますよ、ということにすぎず、実際にマニフェストファイルにポートを記述したからと言って記述したポートが実際のコンテナに設定されるわけではありません。
つまり`ただの飾り`であることがわかりました。

以下の公式ドキュメントに内にも
https://kubernetes.io/ja/docs/concepts/services-networking/connect-applications-service/
> コンテナポートの仕様を指定していることに注意してください。

と記載があるように、`仕様を指定`しているにすぎません。

ほんとですか？という信じられない気持ちでしたので検証してみました。

:::note warn
前提としてKubernetesクラスターはkindを使用してKubernetesリソースの作成ができている状態で検証しております。
:::

以下のDockerfileを準備します。
```Dockerfile
   # Dockerfile
   FROM nginx:latest

   # Install bash net-tools
   RUN apt-get update && apt-get install -y bash　bash net-tools && rm -rf /var/lib/apt/lists/*
```
`docker image` を作成します。
```bash
docker image build -t my-nginx .
```

`containerport`を`8080`にしてPodを生成します。
```yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-liveness-check
  namespace: default
spec:
  replicas: 1
  selector:
    matchLabels:
      app: nginx-liveness
  template:
    metadata:
      labels:
        app: nginx-liveness
    spec:
      containers:
        - name: nginx
          image: my-nginx:latest
          ports:
            - containerPort: 8080
```

`apply`してPodを作成 -> 起動します。
`kubectl get pods`で`Running`になっていることを確認します。
```bash
kubectl apply -f <yamlファイル名>

kubectl get pods -w
NAME                                    READY   STATUS    RESTARTS   AGE
<Pod名>   1/1     Running   0          4s
```

以下コマンドでコンテナに接続します。
```bash
kubectl exec -it <Pod名> -- /bin/bash
```

`netstat`でリッスンしているポートを確認すると`0.0.0.0:80`となっているので`8080`でリッスンしていないことが確認できます。
```bash
netstat -tnlp

Proto Recv-Q Send-Q Local Address           Foreign Address         State       PID/Program name
tcp        0      0 0.0.0.0:80              0.0.0.0:*               LISTEN      1/nginx: master pro
tcp6       0      0 :::80                   :::*                    LISTEN      1/nginx: master pro
```
まんまと騙されましたが、マニフェストに記載する`containerport`はただの飾りであることがわかりました。

## ⑥ Probeについて
Probeについては以前の記事で解説しておりますので、こちらを参照ください。

https://qiita.com/triple321jhango/items/d8f4a5bc6133da4698fa


## 最後に
Kubernetesマニフェストファイルの中身についてどんな意味なのかについて深掘りしていきました。
あいまいだった理解がアウトプットすることで少しづつ深まっていきました。
また、実際に動作確認することで、見た目に騙されないマニフェストファイルの奥深さを理解できて非常に勉強になりました。
本記事がマニフェストファイルの読み方がわからない、という方の助けになれば幸いです。

## 参考
https://cloud.google.com/blog/products/containers-kubernetes/kubernetes-best-practices-organizing-with-namespaces?hl=en

https://sysdig.jp/blog/multi-tenant-isolation-boundaries-kubernetes/