【備忘録】Kubernetesの仕組みを整理する

#本記事の目的
社内でのDevOps検証に合わせて、Kubernetesの基礎知識を学び、アウトプットとして備忘録を記す。
本記事ではKubernetesのアーキテクチャ、操作方法、及び基本リソースの概念について説明する。

#クラスタのアーキテクチャ
※Control Planeのコンポーネントは<font color="OrangeRed">**赤**</font>、(Worker) Nodeのコンポーネントは<font color="DodgerBlue">**青**</font>で記載する。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/559588/b1036edd-e398-f102-6209-e50b761e64b3.png)


###<font color="OrangeRed">1. etcd</font>
- KVS(KeyとValueを組み合わせる構造を持つデータストア)の一種。
- etcdctlコマンドで操作可能。
- Kubernetesでは、etcdがクラスターに関連するオブジェクト(ノード、ポッド等)の情報を保持する。

###<font color="OrangeRed">2. kube-apiserver</font> 
- KubernetesのAPIを提供するコンポーネント。
- ユーザーの認証/リクエストの検証/etcdからのデータ取り込み/etcdのアップデート/kube-schedulerとの通信/kubeletとの通信を実施する。
- Kubernetesのコンポーネントの中で、唯一etcdと直接通信が可能である。

###<font color="OrangeRed">3. kube-controller-manager</font>
- Kubernetesのオブジェクトを処理するコントローラを実行するコンポーネント群。
- ステータスの監視、(必要に応じて)オブジェクトの修復を実施する。
- オブジェクトを処理するために様々なコントローラーが用意されている。ex.)Node-Controller,Replicaset-Controller

###<font color="OrangeRed">4. kube-scheduler</font>
- Podを適切なNodeに割り当てるコンポーネント。
- spec.nodeName(起動するノード情報)が割り当てられていないPodを検知し、kube-apiserverにリクエストを送る事でspec.Nodenameを更新する。

###<font color="DodgerBlue">5. kubelet</font>
- container runtimeに命令する事で、Podの起動/監視を実施するエージェント。
- kubeletによるPodの起動方法は、以下の2種類。
    - kube-schedulerによるPodの割り当てに従い、自身のNode上でPodを起動させる。(デフォルト)
    - Nodeの特定のパスにManifestファイルを配置する事で、自身のNode上でPodを起動させる。(Static Pod)

###<font color="DodgerBlue">6. kube-proxy</font>
- Service宛のトラフィックをPodに転送する事で、Podのルーティングを実施するコンポーネント。

###<font color="DodgerBlue">7. container runtime</font>
- kubeletからの命令を受け、コンテナを実際に操作するコンポーネント。
- Docker, containerd, cri-o等様々な種類が存在する。

#クラスタの操作方法
###1. kubectlとmanifest
- クラスタを手動で操作する場合は、KubernetesのCLIツールであるkubectlコマンドと、オブジェクトが定義されたmanifestファイルを用いる事が一般的である。
- クラスタでの一部の操作は、kubectlコマンドのみでも可能である。
- manifestファイルはYAML形式及びJSON形式での記載が必須である。一般的にYAML形式が用いられる。

###2. 命令型と宣言型
- kubectlコマンドによる操作は、命令形と宣言型に分かれる。

- 命令型は、コマンド自身が具体的なクラスタ操作内容を指定する。コマンドが実施されると、コマンドで指定されたクラスタ操作内容に従って、特定のクラスタ操作が実施される。`kubectl apply`以外のコマンドが該当する。

- 宣言型は、コマンド自身は具体的なクラスタ操作内容を指定しない。コマンドが実施されると、指定されたmanifestファイルが履歴として保存される。また前回保存されたmanifestファイルの履歴と、コマンドで指定されたmanifestファイルの内容の差分を基に、適切なクラスタ操作が実施される。`kubectl apply`コマンドのみが該当する。

```
$ kubectl apply -f [manifestファイル名]
```

#Kubernetesの基本リソース
###1. Pod
- Kubernetesにデプロイ出来る最小単位のリソース。
- 1つ以上のコンテナで構成される。
- Pod内のコンテナは、同一のNICとストレージを共有する。
- manifestファイルを用いずに、以下のコマンドでPodを作成できる。

```
$ kubectl run [任意のPod名] --image=[コンテナイメージ名]
```

- manifestファイルを用いてPodを作成する場合、主に以下のフィールドを指定する必要がある。

|  フィールド名 |  設定内容  |
| ---- | ---- |
| apiVersion |  Kubernetes APIにおける、オブジェクトが属するグループ及びバージョン。 ex.)v1, apps/v1 |
| kind |  オブジェクトの種類。 ex.) Pod, Replicaset, Deployment |
| metadata　|  オブジェクトの基本情報。`name`フィールドには任意のオブジェクト名を指定する。labelsフィールドには任意のラベル名を指定する。 |
| spec　|  オブジェクトの状態。`containers > name`フィールドには任意のコンテナ名を指定する。`containers > image`フィールドには任意のコンテナイメージ名を指定する。 |

- manifestファイル例

```yaml:pod.yaml
apiVersion: v1
kind: Pod
metadata:
  name: sample-pod
  labels:
    app: sample-app
spec:
  containers:
  - name: sample-container
    image: nginx
```

###2. ReplicaSet
- 指定した数のPodを複製し、維持し続けるリソース。
- 1つ以上のPodで構成される。
- manifestファイルを用いずに、既存のオブジェクトを対象として、Podを複製できる。

```
$ kubectl scale [対象のオブジェクトの種類] [対象のオブジェクト名] --replicas=[Podの複製数]
```
- manifestファイルを用いてReplicaSetを作成する場合、Podで必要なフィールドに加えて、主にspecフィールド配下に以下のフィールドを指定する必要がある。

|  フィールド名 |  設定内容  |
| ---- | ---- |
| replicas |  Podの複製数。 |
| selector |  Labelを選択するための設定。`matchLabels`フィールドを設定すると等価ベースで比較し、指定したLabelと全て一致したものが対象となるように選択する。基本的に`selector > matchLabels`フィールドの定義と、`template > metadata > labels`フィールドの定義を一致させる必要がある。 |
| template　|  複製するPodの雛形。(内容はPodのmetadata及びspecと同一) |

    
- manifestファイル例

```yaml:replicaset.yaml
apiVersion: apps/v1
kind: ReplicaSet
metadata:
  name: sample-replicaset
spec:
  replicas: 3
  selector:
    matchLabels:
      app: sample-app
  template:
    metadata:
      labels:
        app: sample-app
    spec:
      containers:
      - image: nginx
        name: sample-container
```

###3. Deployment
- 複数のReplicaSetを管理し、ローリングアップデートやロールバックを実現するリソース。
- 1つ以上のReplicaSetで構成される。
- manifestファイルを用いてDeploymentを作成する場合、ReplicaSetで必要なフィールドがそのまま必要となる。
- manifestファイルを用いずに、以下のコマンドでDeploymentを作成できる。

```
$ kubectl create deployment [任意のDeployment名] --image=[コンテナイメージ名]
```

- manifestファイル例

```yaml:deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: sample-deployment
spec:
  replicas: 3
  selector:
    matchLabels:
      app: sample-app
  template:
    metadata:
      labels:
        app: sample-app
    spec:
      containers:
      - image: nginx
        name: sample-container
```

###4. Namespace
- クラスタを分離するリソース。
- 初期状態では、以下3種類のNamespaceが存在する。

|  Namespace名 |  使用用途  |
| ---- | ---- |
| kube-system |  Clusterに必須のコンポーネントが配置される。 |
| kube-public |  全ユーザが利用できる認証情報等が配置される。 |
| default     |  デフォルトで使用される。 |

- ResourceQuotaリソースを用いる事で、Namespaceで使用できるCPU/メモリ/ストレージを制限できる。
- manifestファイルを用いずに、以下のコマンドでNamespaceを作成できる。

```
$ kubectl create namespace [任意のNamespace名]
```

- manifestファイル例

```yaml:namespace.yaml
apiVersion: apps/v1
kind: Namespace
metadata:
  name: sample-namespace
```

###5. Service
- 受信したトラフィックを複数のPodに負荷分散するリソース。 
- Serviceには様々な種類が存在するが、以下3種類が特に使用される事が多い。

|  Service名 |  機能  |
| ---- | ---- |
| ClusterIP   |  クラスタ内部でIPアドレスが払い出され、各々のPod間での通信を可能にする。 |
| NodePort    |  ClusterIPの機能に加え、クラスタ内部に存在する全てのNodeの特定のポートが受信したトラフィックを、各々のPodに転送する。 |
| LoadBalancer|  NodePortの機能に加え、クラスタ外部に存在するロードバランサーが受信したトラフィックを、各々のNodeに分散させる。 |

- manifestファイルを用いずに、既存のオブジェクトを対象として、以下のコマンドでServiceを作成できる。

```
$ kubectl expose [対象のオブジェクトの種類] [対象のオブジェクト名] --port=[使用するポート番号] --type=[Serviceの種類] --name=[任意のService名]
```

- manifestファイルを用いてServiceを作成する場合、Podで必要なフィールドに加えて、specフィールド内に以下のフィールドを指定する必要がある。

|  フィールド名 |  設定内容  |
| ---- | ---- |
| type |  Serviceの種類。デフォルトは`ClusterIP`。 ex.) ClusterIP, NodePort, LoadBalancer |
| ports |  使用するポート番号。`port`フィールドにはServiceで使用するポート番号を設定する。`targetPort`フィールドにはPodで使用するポート番号を設定する(デフォルトはportフィールドと同一のポート番号)。 |
| selector　|  トラフィックをルーティングするPodのラベル。Podのmanifestファイルの`metadata > labels`フィールドと同一の内容を記載する。 |


- manifestファイル例

```yaml:service.yaml
apiVersion: v1
kind: Service
metadata:
  name: sample-nodeport
spec:
  type: NodePort
  ports:
    - port: 80
      targetPort: 8080 
  selector:
    type: front-end
```



#感想
個人的に、Kubernetesでの検証を行う際は、操作方法や基本リソースはもちろんだが、加えてアーキテクチャを理解する事が重要であると考える。
去年もKubernetesの構築やトラブルシューティングを実施したが、もしアーキテクチャの知識があればより作業のスピードを上げられたように感じている。

また今後機会があれば、Container Runtimeのアーキテクチャについてもまとめてみたい。

#参考文献
- 公式ページ：[Kubernetesドキュメント](https://kubernetes.io/ja/docs/home/)
- Udemy講座：[Certified Kubernetes Administrator (CKA) with Practice Tests](https://www.udemy.com/course/certified-kubernetes-administrator-with-practice-tests/)
- 書籍：[Kubernetes完全ガイド](https://www.amazon.co.jp/gp/product/B08FZX8PYW/ref=dbs_a_def_rwt_bibl_vppi_i0)
- ブログ：[Toku's Blog - Kubernetes道場](https://cstoku.dev/)