Kubenetesを使ってみる

# はじめに
Dockerは開発環境構築によく使っていましたが、業務でコンテナを扱うようになってきたため、今更ながらKubernetesを学習を進めようと思います。
本記事は以下の本の覚書となります。
<a target="_blank"  href="https://www.amazon.co.jp/gp/product/B07QLBS9WL/ref=as_li_tl?ie=UTF8&camp=247&creative=1211&creativeASIN=B07QLBS9WL&linkCode=as2&tag=cocolonifty00-22&linkId=8df9e428b71974761a866e0c21b4d358"><img border="0" src="//ws-fe.amazon-adsystem.com/widgets/q?_encoding=UTF8&MarketPlace=JP&ASIN=B07QLBS9WL&ServiceVersion=20070822&ID=AsinImage&WS=1&Format=_SL160_&tag=cocolonifty00-22" ></a>

# インストール
Docker DesktopはKubernetesをサポートしているためすでにDocker Desktopがインストールされている環境においては、有効化するだけで利用可能となります。

1. タスクバーのDockerから「Pregerences...」を押下
2. Kubernetesを選択し、「Enable Kubernetes」をチェックしたら「Apply & Restart」を押下
![スクリーンショット 2022-02-19 20.05.45.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/380300/a9b29322-02fb-2174-5a66-b655d31e12aa.png)

# kubernetesの基礎
マニフェストファイルを使って、インスタンスを作成してみます。

### 1. マニフェストファイルの作成
コンテナの管理に使用するリソースの定義ファイルをyaml形式で作成します。
必須項目は、`apiVersion`, `kind`, `metadata`, `spec`です。
`kind`のリソースタイプは以下を参照

https://kubernetes.io/docs/reference/kubectl/overview/#resource-types

```yaml:yamlの定義のサンプル
apiVersion: v1
kind: Pod
metadata:
  # Podの名前
  name: nginx
spec:
  # 実行するコンテナの定義
  containers:
  - name: nginx
    image: nginx:1.15.7
    ports:
    - containerPort: 80
```

`kind: Deployment`は複数のPodを管理するためのリソース

```yaml:3つのPodを実行するDeploymentの定義のサンプル
# APIバージョン
apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-deployment
spec:
  selector:
    matchLabels:
      app: nginx
  # 実行するPodの数
  replicas: 3
  # Pod定義のテンプレード
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: nginx:1.15.7
        ports:
        - containerPort: 80
```

APIバージョンは`kind`の種類によって異なります。
調べるには、`kubectl api-resources`を使います。
![スクリーンショット 2022-02-19 20.37.16.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/380300/1da0afb9-cce3-6fed-aa59-6d315ac38088.png)

### 2. マニフェストファイルの登録
作成したマニフェストファイルをKubernetesに登録します。
ここでは、マニフェストファイルは`deployment.yaml`として作成しました。

```bash
$ kubectl apply -f deployment.yaml
deployment.apps/nginx-deployment created
```
Podの起動確認をしてみます。

```bash
$ kubectl get pods
NAME                                READY   STATUS    RESTARTS   AGE
nginx-deployment-7d4d8467d9-kr6m9   1/1     Running   0          117s
nginx-deployment-7d4d8467d9-mwhdf   1/1     Running   0          117s
nginx-deployment-7d4d8467d9-srb8g   1/1     Running   0          117s
```

### 3. Podの負荷分散
先ほど起動した3つのPodへの負荷分散を実現するためのリソースとして`Service`を定義します。
まずは、`service.yaml`を作成します。

```yaml:service.yaml
# APIバージョン
apiVersion: v1
kind: Service
metadata:
  name: nginx-service
spec:
  # 外部からもアクセス可能にする
  type: LoadBalancer
  selector:
    # 先ほど作成したdeploymentのpod(app: nginx)にトラフィックを分散する
    app: nginx
  # Port8080への通信をPodの80ポートに転送する
  ports:
  - protocol: TCP
    port: 8080
    targetPort: 80
```
Kubernetesへ登録

```bash
$ kubectl apply -f service.yaml
service/nginx-service created
```

serviceの状態の確認

```bash
$ kubectl get service
NAME            TYPE           CLUSTER-IP     EXTERNAL-IP   PORT(S)          AGE
kubernetes      ClusterIP      10.96.0.1      <none>        443/TCP          56m
nginx-service   LoadBalancer   10.98.68.206   localhost     8080:32539/TCP   61s
```

Serviceは負荷分散実現のために、仮想IPアドレス（Cluster IP）を作成します。
EXTERNAL-IPが外部からアクセス可能な仮想IPアドレスです。
DockerからインストールしたKubernetesでのEXTERNAL-IPは常に`localhost`となります。

ブラウザで、`http://localhost:8080`へアクセスしてみます。
![スクリーンショット 2022-02-19 21.01.24.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/380300/bcd434f1-6bbf-d84a-43f0-824b00cf2c1e.png)

nginxの画面が表示されました。


# kubenetesオブジェクトの管理方法
以下の3つがあります。

- [命令型コマンド](#命令型コマンド)
- [命令型オブジェクト設定](#命令型オブジェクト設定)
- [宣言型オブジェクト設定](#宣言型オブジェクト設定)

### 命令型とは
①MySQL5.7.21をDBにインストール②nginxバージョン1.14.2をWEBにインストール、という手順を実際にスクリプトや設定ファイルに書き出し、その手順を管理ツールが手順を実行します。

### 宣言型とは
①MySQL5.7.21がDBに、nginx1.14.2がWEBにインストールされている、というシステムの望ましい形を定義します。

## 命令型コマンド
nginxコンテナの単一インスタンスを起動

```bash:runコマンド
$ kubectl run nginx --image nginx
```
同じことを異なる構文で行います

```bash:createコマンド
$ kubectl create deployment nginx --image nginx
```
nginxコンテナのバージョンを更新

```bash:setコマンド
$ kubectl create deployment nginx --image nginx:1.14.2
$ kubectl set image deployment/nginx nginx=nginx:1.15.8
```
nginxコンテナのレプリカを3に変更

```bash:scaleコマンド
$ kubectl create deployment nginx --image nginx
$ kubectl scale deployment/nginx --replicas 3
```
オブジェクトの削除

```bash:deleteコマンド
$ kubectl delete deployment nginx
```
## 命令型オブジェクト設定
設定ファイルに定義されたオブジェクトを作成

```bash:createコマンド
$ kubectl create -f nginx.v1.14.2.yaml
```

設定ファイルに定義された情報で、現行の設定を上書き更新

```bash:replaceコマンド
$ kubectl replace -f nginx.v1.15.8.yaml
```
設定ファイルに定義されたオブジェクトを削除

```bash:deleteコマンド
$ kubectl delete -f nginx.yaml -f redis.yaml
```

### 命令型の問題

```bash:
$ kubectl create -f nginx.v1.14.2.yaml
$ kubectl scale deployment/nginx --replicas 3
$ kubectl replace -f nginx.v1.15.8.yaml //2つ目のコマンドの変更が消失
```
このような問題を防止するために、第三者が変更を行わないようにする運用をする必要があります。

## 宣言型オブジェクト設定
宣言型は、`apply`を使います。
また、ディレクトリ指定が可能です。

```bash:
// 更新前にdiffで確認
$ kubectl diff -R -f configs/
$ kubectl apply -R -f configs/
```
### 宣言型の差分計算

| 差分 | 比較元のソース | 説明 |
| ---- | ---- | ---- |
| 追加分 | 実際のオブジェクト | 実際のオブジェクトにないが、適用するマニフェストファイルに存在するフィールドを追加 |
| 更新分 | 実際のオブジェクト | 実際のオブジェクトと適用するマニフェストファイルの差分を更新 |
| 削除分 | 前回適用したマニフェストファイル | 適用するマニフェストファイルに存在しないが前回適用したマニフェストファイルには存在するフィールドを削除 |
注目すべきは、削除分が実際のオブジェクトではなく前回のマニフェストファイルとの比較になる事です。
これは、実際のオブジェクトへ行われた変更を保持できることを意味します。
以下は、実際のオブジェクトに`replicas:3`と`image:nginx:1.16`の変更が加えられていた場合です。

|適用する<br>マニフェストファイル|前回適用した<br>マニフェストファイル|　実際のオブジェクト　|apply後オブジェクト|
| ---- | ---- | ---- | ---- |
| kind:Deployment<br>spec:<br>　containers:<br> 　- name:nginx<br>　　image:nginx:1.15 | kind:Deployment<br>spec:<br>　containers:<br> 　- name:nginx<br>　　image:nginx:1.14 | kind:Deployment<br>spec:<br>　replicas:3<br>　containers:<br> 　- name:nginx<br>　　image:nginx:1.16 | kind:Deployment<br>spec:<br>　replicas:3<br>　containers:<br> 　- name:nginx<br>　　image:nginx:1.15 |

`replicas:3`は、適用するマニフェストファイルに存在しないフィールドですが、前回適用のマニフェストファイルにも存在しないため削除されずに残ります。
`image:nginx:1.16`は、更新分として今回適用するマニフェストファイルの内容に更新されます。