Kubernetes 入門1

## Kubernetesとは
Kubernetesはコンテナオーケストレーションプラットフォームです。Kubernetesはコンテナ化されたアプリケーションのデプロイ、スケーリング、管理を自動化するためのオープンソースシステムです。

**Kubernetesの主な機能：**
- コンテナのオーケストレーション（配置、スケジューリング）
- アプリケーションのスケーリング（自動水平/垂直スケーリング）
- サービスディスカバリとロードバランシング
- ストレージオーケストレーション
- 自己修復機能（障害発生時の自動再起動や置き換え）
- シークレットと構成管理
- ローリングアップデートとロールバック

Kubernetesは「K8s」と略されることも多く、現代のクラウドネイティブアプリケーション開発とデプロイにおいて業界標準となっています。
なぜKubernetesを使うのかというと、自前で複数ホストと複数コンテナを管理するのが難しいから。

## Docker RunやDocker Composeと比較したKubernetesの利点について
Docker Runは単一のコンテナを起動するための基本的なコマンドで、Docker Composeは複数コンテナで構成されるアプリケーションを単一ホスト上で管理するツールです。一方、Kubernetesはこれらと比較して以下の理由で使用されます：

**スケーラビリティ**
- Docker Run/Compose: 主に単一ホスト向けで、マルチホスト環境での管理は複雑
- Kubernetes: 複数ノードにわたる大規模クラスタを効率的に管理し、数千のコンテナまでスケール可能

**高可用性**
- Docker Run/Compose: 高可用性機能が限定的
- Kubernetes: ノード障害時の自動復旧、ポッドの再スケジューリング、自己修復機能を提供

**デプロイ管理**
- Docker Run/Compose: 複雑なデプロイ戦略の実装が困難
- Kubernetes: ローリングアップデート、カナリアデプロイ、ブルー/グリーンデプロイなどの高度な戦略をサポート

**ロードバランシング**
- Docker Run/Compose: 基本的なロードバランシングのみ
- Kubernetes: サービスディスカバリと内蔵ロードバランサーで複雑な通信をシームレスに管理

**リソース管理**
- Docker Run/Compose: リソース制限はあるが、動的配分は限定的
- Kubernetes: CPUやメモリの詳細な割り当て、QoS、オートスケーリング機能を提供

**モニタリングと管理**
- Docker Run/Compose: 基本的なログとモニタリング
- Kubernetes: 包括的なモニタリング、ロギング、アラート機能と豊富なエコシステム

Docker Run/Composeは小〜中規模の開発環境やシンプルなアプリケーションに適していますが、Kubernetesは本番環境での大規模デプロイ、高可用性要件、複雑なマイクロサービスアーキテクチャに適しています。

## Minikubeを使用してローカル環境でKubernetesを動かす
Minikubeはローカル環境でKubernetesを簡単に実行するためのツールです。Kubernetesを試したり日々の開発への使用を検討するユーザー向けに、PC上のVM内でシングルノードのKubernetesクラスターを実行することができます。

```
brew install kubectl
brew install minikube
```

クラスターを起動
```
minikube start
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/4077353/c036a193-0a21-4f9e-8cae-d6c7c8be8fc6.png)

ステータスを確認
```
minikube
type: Control Plane
host: Running
kubelet: Running
apiserver: Running
kubeconfig: Configured
```

クラスタのバージョンを確認
```
kubectl version
```
kubectlコマンドは、Kubernetesクラスターと対話するための公式コマンドラインツールです。
```
Client Version: v1.30.5
Kustomize Version: v5.0.4-0.20230601165947-6ce0bf390ce3
Server Version: v1.32.0
WARNING: version difference between client (1.30) and server (1.32) exceeds the supported minor version skew of +/-1
```

クラスタの正常性確認
```
kubectl cluster-info
kubectl get componentstatuses
```

クラスタのノード（物理/仮想マシン）一覧の表示
ノードとは、Kubernetesクラスター内の作業マシン（ワーカーマシン）のことです。
```
kubectl get nodes
```

クラスタのノード（物理/仮想マシン）詳細情報を表示
```
kubectl describe nodes
```

## 役立つツールとコマンド
- `watch`''で囲んだコマンドを2秒ごとに実行してくれる
    ```
    brew install watch
    ```
    ```
    watch -t 'kubectl get pod'
    # 詳細な方
    watch 'kubectl get pod,svc -o wide'
    ```

- `batコマンド`
    ```
    brew install bat
    ```
    ```
    bat pod.yaml
    bat Dockerfile
    ```
    ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/4077353/c2f4fe7e-1de4-495c-9d75-0bb996e02a9f.png)

## Podとは?
Podとは、Kubernetesにおける最小のデプロイ単位であり、1つ以上のコンテナをグループ化して管理するための概念です。
1. コンテナのグループ

    通常は単一のアプリケーションを表す1つのメインコンテナと、オプションの補助的なコンテナ（サイドカー）で構成
    同一Podの複数コンテナは常に同一ノード上でスケジュールされる

2. 共有リソース

    Pod内のコンテナは同じネットワーク名前空間を共有（localhost経由で通信可能）
    同じIPアドレスとポート空間を共有
    共有ストレージボリュームにアクセス可能

Podは「論理的なホスト」と考えることができ、密接に関連するコンテナをまとめ、従来のVMに似た環境を提供します。ただし、Podは永続的な存在ではなく、再作成される可能性があるため、ステートレスな設計が推奨されます。

### Podを使ってHello WorldをK8sにDeployしよう
1. Hello WorldのPodを起動
    ```
    kubectl run --image gcr.io/google-samples/hello-app:1.0 --restart Never helloworld
    ```

2. Podの表示
    ```
    kubectl get pods
    ```
    ```
    NAME         READY   STATUS    RESTARTS   AGE
    helloworld   1/1     Running   0          28s
    ```

3. Podないのコンテナのログを表示
    ```
    kubectl logs helloworld
    ```
    
4. Podのメタデータを表示
    ```
    kubectl describe pod helloworld
    ```

    ```
    # Pod の基本識別情報
    Name:             helloworld                    # Pod の名前（この Pod を識別するための名前）
    Namespace:        default                       # Pod が属するネームスペース（リソースをグループ分けする仕組み）
    Priority:         0                            # Pod の優先度（0が最低、数値が高いほど優先される）
    Service Account:  default                      # Pod が使用するサービスアカウント（権限管理用）
    
    # Pod が動作している物理的な場所
    Node:             minikube/192.168.49.2        # Pod が配置されているノード（サーバー）の名前とIPアドレス
    Start Time:       Sun, 25 May 2025 17:31:07 +0900  # Pod が開始された日時
    
    # Pod の分類・管理用ラベル
    Labels:           run=helloworld               # Pod に付けられたラベル（検索や管理に使用）
    Annotations:      <none>                       # Pod に付けられた注釈情報（現在は未設定）
    
    # Pod の現在の状態
    Status:           Running                      # Pod の実行状態（Running = 正常に動作中）
    
    # ネットワーク関連の設定
    IP:               10.244.0.6                  # Pod に割り当てられた内部IPアドレス
    IPs:                                          # Pod が持つIPアドレスの一覧
     IP:  10.244.0.6                           # 同上（複数IP対応のため配列形式）
    
    # Pod 内のコンテナ情報
    Containers:                                   # Pod に含まれるコンテナの一覧
     helloworld:                                # コンテナの名前
       Container ID:   docker://bceef2d5e09710e621489bba7fdcda458ca29d66da27474bedefbabe09b14c06  # コンテナの一意識別子
       Image:          gcr.io/google-samples/hello-app:1.0  # 使用しているDockerイメージ
       Image ID:       docker-pullable://gcr.io/google-samples/hello-app@sha256:173d532964a8cfd250b01878a46c18ccb303bcf21855b0fb9ac445bc486d1dbc  # イメージの詳細識別子
       Port:           <none>                    # コンテナが公開するポート（未設定）
       Host Port:      <none>                   # ホスト側のポート（未設定）
       State:          Running                  # コンテナの実行状態
         Started:      Sun, 25 May 2025 17:31:07 +0900  # コンテナが開始された日時
       Ready:          True                     # コンテナがリクエストを受け付ける準備ができているか
       Restart Count:  0                        # コンテナが再起動された回数（0 = 安定している）
       Environment:    <none>                   # 環境変数の設定（現在は未設定）
       Mounts:                                  # コンテナにマウントされたボリューム
         /var/run/secrets/kubernetes.io/serviceaccount from kube-api-access-zxqt6 (ro)  # Kubernetes API へのアクセス用トークン（読み取り専用）
    
    # Pod の健全性チェック結果
    Conditions:                                  # Pod の各種状態チェック結果
     Type                        Status         # チェック項目とその結果
     PodReadyToStartContainers   True          # コンテナ開始準備完了
     Initialized                 True          # 初期化完了
     Ready                       True          # サービス提供準備完了
     ContainersReady             True          # 全コンテナが準備完了
     PodScheduled                True          # ノードへの配置完了
    
    # ストレージ関連の設定
    Volumes:                                     # Pod が使用するボリューム
     kube-api-access-zxqt6:                    # ボリューム名
       Type:                    Projected (a volume that contains injected data from multiple sources)  # 複数のデータソースを統合したボリューム
       TokenExpirationSeconds:  3607           # APIアクセス用トークンの有効期限（秒）
       ConfigMapName:           kube-root-ca.crt  # 使用するConfigMap名
       ConfigMapOptional:       <nil>          # ConfigMapが必須かどうか
       DownwardAPI:             true           # Pod情報をファイルとして公開するか
    
    # リソース使用に関する設定
    QoS Class:                   BestEffort     # サービス品質クラス（BestEffort = 最低限の保証）
    
    # Pod配置に関する設定
    Node-Selectors:              <none>         # 特定ノードを選択する条件（未設定）
    Tolerations:                               # ノードの問題を許容する設定
     node.kubernetes.io/not-ready:NoExecute op=Exists for 300s      # ノードが準備未完了でも300秒間は実行継続
     node.kubernetes.io/unreachable:NoExecute op=Exists for 300s    # ノードが到達不能でも300秒間は実行継続
    
    # Pod に関するイベント履歴
    Events:                                    # Pod で発生したイベントの履歴
     Type    Reason     Age   From               Message
     ----    ------     ----  ----               -------
     Normal  Scheduled  85s   default-scheduler  Successfully assigned default/helloworld to minikube  # Pod がノードに正常に配置された
     Normal  Pulled     85s   kubelet            Container image "gcr.io/google-samples/hello-app:1.0" already present on machine  # イメージが既にローカルに存在
     Normal  Created    85s   kubelet            Created container: helloworld  # コンテナが正常に作成された
     Normal  Started    85s   kubelet            Started container helloworld  # コンテナが正常に開始された 
    ```

5. Pod内のコンテナの環境変数を定義
    ```
    kubectl run --env TEST_ENV=hellow_world \
        --image gcr.io/google-samples/hello-app:1.0 \
        --restart Never helloworld
    ```

    以下のコマンドで設定したENVを確認できる。
    ```
    kubectl describe pod helloworld
    ```
    
    ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/4077353/77b5a243-1854-4849-8706-77be67973fac.png)



6. Podを削除
    ```
    kubectl delete pod helloworld
    ```

7. ubuntuのイメージで作られたubuntuという名前のPodを作成し、起動し続ける。そしてコンテナに入る。
    ```
    kubectl run ubuntu --image=ubuntu --restart Never -- sleep infinity
    
    # コンテナの中に入る
    kubectl exec -it ubuntu -- bash
    ```

    各パラメータの回詳細説明
    ```
    kubectl run
    # 意味：新しいPodを作成して実行する基本コマンド
    # 類似：docker run（Dockerでコンテナを実行するのと似ている）
    
    ubuntu
    # 意味：作成するPodの名前
    # 結果：Pod名が「ubuntu」になる
    # 確認：kubectl get pods で「ubuntu」という名前で表示される
    
    --image=ubuntu
    # 意味：使用するDockerイメージを指定
    # ubuntu：Ubuntu Linux の公式イメージ
    # 取得元：Docker Hub から自動でダウンロード
    # 類似：docker run ubuntu と同じイメージを使用
    
    --restart Never
    # 意味：Podが終了しても再起動しない設定
    # Never：一度だけ実行（ジョブのような動作）
    # 対比：Always（常に再起動）、OnFailure（失敗時のみ再起動）
    
    -- sleep infinity
    # コンテナが終了せずに起動し続ける
    ```

---

## Serviceとは?
ServiceはPodをクラスター内外に公開する**静的IP**を持った**L4ロードバランサー**です。
クラスター内外からPodへの安定的なアクセスを提供できる仮想のIPアドレスをServiceに割り当てます。
いつ消えるかわからないPodのIPを覚えておく必要がなくなる。


### ClusterIP Service
ClusterIPのServiceを使う利点は、いつ消えるかわからないPodIPを抽象化し、StaticIPを持ったProxyを前に置くことで：
- Podにアクセスする際に、Pod IPを知る必要がなくなる
- Podにアクセスする際に、ロードバランスしてくれる
ことです。

 **コマンド**

1. 'Helloworld'PodをClusterIPのServiceとしてクラスタ内部で公開

    ```
    kubectl expose pod helloworld --type ClusterIP --port 8080 --name hellowworld-clusterip
    ```

2. Serviceをリストアップ
    ```
    kubectl get service
    ```

3. クラスターないにCurlコンテナのPodを作成しシェル接続
    ```
    kubectl run --restart Never --image curlimages/curl:7.68.0 -it --rm curl sh
    ```

4. `helloworld-clusterip` ClusterIP serviceへCurlでアクセステスト
    ```
    curl helloworld-clusterip:8080
    ```
    レスポンス
    ```
    Hello, world!
    Version: 1.0.0
    Hostname: helloworld
    ```

### NodePort Serviceとは
NodePort Serviceを使う利点は、ClusterIPでは不可能だったクラスター街へのPodの公開を、NodeIPとNodePort経由で可能にすること。

1. ` helloworld-nodeport`という名前で NodePortのServiceを作成。
    ```
    kubectl expose pod helloworld --type NodePort --port 8080 --name helloworld-nodeport
    ```

2. 外部からの接続確認
    ```
    minikube service helloworld-nodeport
    ```
    ```
        |-----------|---------------------|-------------|---------------------------|
    | NAMESPACE |        NAME         | TARGET PORT |            URL            |
    |-----------|---------------------|-------------|---------------------------|
    | default   | helloworld-nodeport |        8080 | http://192.168.49.2:30724 |
    |-----------|---------------------|-------------|---------------------------|
    🏃  helloworld-nodeport サービス用のトンネルを起動しています。
    |-----------|---------------------|-------------|------------------------|
    | NAMESPACE |        NAME         | TARGET PORT |          URL           |
    |-----------|---------------------|-------------|------------------------|
    | default   | helloworld-nodeport |             | http://127.0.0.1:52965 |
    |-----------|---------------------|-------------|------------------------|
    🎉  デフォルトブラウザーで default/helloworld-nodeport サービスを開いています...
    ❗  Docker ドライバーを darwin 上で使用しているため、実行するにはターミナルを開く必要があります。
    ```
    以下で動作確認できた。
    http://127.0.0.1:52965

NodePortのServiceでクラスター外にPodを公開できるが、NodeIPやNode Portを知らないといけないという問題がある。

### LoadBalancer Serviceとは
クラウドプロバイダのL4ロードバランサーのDNSから、各ノードの特定のポートにRoutingしてPodにアクセスする。

1. ロードバランサーのserviceを作成。
    ```
    kubectl expose pod helloworld --type LoadBalancer --port 8080 --name helloworld-lb
    ```

LoadBalanserサービスにも問題があるので実際はL7のIngressを使う。

**LoadBalancer:**
- L4（TCP/UDP）レベル
- シンプルだが高コスト
- 1サービス = 1ロードバランサー

**Ingress:**
- L7（HTTP/HTTPS）レベル
- 複雑だが低コスト
- 1つのロードバランサーで複数サービス

## L4 vs L7 の違い
### L4（トランスポート層）
- TCP/UDP レベルの負荷分散
- IPアドレス + ポート番号で判断
- 中身を見ない（HTTPの内容は無視）

### L7（アプリケーション層）
- HTTP/HTTPS レベルの負荷分散
- URL、ヘッダー、Cookieなどで判断
- 中身を理解（リクエストの詳細を解析）

### 具体例
**L4の判断基準**
```
192.168.1.100:80 → サーバーA
192.168.1.100:443 → サーバーB
# IPとポートのみで振り分け
```

**L7の判断基準**
```
example.com/api → APIサーバー
example.com/web → Webサーバー  
example.com/admin → 管理サーバー
# URLパスで振り分け
```