初心者向けkind(kubernetes in docker)上のサービスへのアクセス方法

# はじめに
kubernetesを勉強したい時にminikubeやkindなどをローカルPCに導入することで、気軽にkubernetesを試せる環境を手軽に構築することができます。
今回はkindを使って学習する際に、kubernetes上にサービス展開した時、そのサービスへの接続方法に困ることがあります。
ですので、kindの初回導入部分からサービスへの接続までをまとめていきます。
ここから導入して、kubernetesとは何かを体験しつつ、kubernetesに興味を持っていただけるきっかけになれば幸いです。

# 準備

## kindとkubectlインストール
kubernetesの素であるkind本体と、kubernetesを操作するクライアントツールkubectlをインストールします。


```
brew install kind kubectl
```

詳しくは[公式サイト](https://kind.sigs.k8s.io/docs/user/quick-start/)を見てみてください。

## kindクラスタを作成するためのマニフェストを用意します
まず、サービスを公開するためにはクラスタを作成する必要があります。
何も指定せずにクラスタを作成するコマンドで作成できますが、マニフェストと呼ばれる構成ファイルを作成することで、「こんなクラスタ」を作りたい！！を指定することができます。

``` kind-config.yaml
kind: Cluster
apiVersion: kind.x-k8s.io/v1alpha4
nodes:
- role: control-plane
- role: worker
- role: worker
```

__解説__

- kind： 作成するオブジェクトの種類です。今回はクラスタを作成するので、その種類を選択しています。
- apiVersion： KubernetesAPIのバージョンを指定しますが、公式に書かれているバージョン「kind.x-k8s.io/v1alpha4」でよいです。
- node：サービスが動作するpodがのる場所です
    - 今回はkubernetesの司令塔部分である、control-plane nodeと、
    - 通常のpodがのるworker nodeを2つ、計3つのnodeを作成します。


## kindクラスタの作成

```
kind create cluster --config kind-config.yaml
```

## nginxのpodを作成します

```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:
      - name: nginx-container
        image: nginx:latest
        resources:
          limits:
            memory: "128Mi"
            cpu: "500m"
```

__解説__

- apiVersion：バージョンを指定します。ほぼ固定です。
- kind: Deploymentを指定しています。これにすると、kubernetesが気を使って、出来るだけサービス断しないようにサービスリリースすることが出来るようになります。
- metadata：オブジェクトを一意に特定するための情報で、このマニフェストの名前のようなものです。
- spec:どんなサービスをどんな感じで作成したいか、希望を書くと、kubernetesが気を使ってその通りにしてくれます。
- replicas：今回はpodを3つ立てたいと願います
- selector:podを3つ立てたいが、どのようなものを立てたいかの条件を指定します。
    - matchLabels：今回はkeyが`app`でvalueが`sample-app`というラベルがついているpodを対象にします。
- template：どんなpodを立てるかの詳細を記載します
    - metadata：どんな情報でも良いですが今回はkeyが`app`でvalueが`sample-app`というラベルにします。
        - ここの設定値とselectorのkeyとvalueが一致していない場合は、作成できないため、注意してください。
    - spec:podの構成を指定します。
        - containers：nginx-containerという名前をつけます。
            - image：nginxの最新版である`nginx:latest`を指定します。こうするとdocker hubから最新のimageを取得できます。
            - resources：podがnodeを食い潰さないようにリミットを決めます。

__コマンド実行__
deployment.yamlを作成してデプロイします。

```bash
kubectl apply -f deployment.yaml
```
これで最新版のnginxが立ち上がりました。

# 接続方法その1 port-forward
一番手軽に接続できる方法だと思います。
kubectlを使って指定した単一podのサービスに接続します。

## Podの状態を確認して表示したいpodを選びます


```bash
❯ kubectl get po   
NAME                                 READY   STATUS        RESTARTS   AGE
sample-deployment-75d79cf8cc-g6xhb   1/1     Running       0          21s
sample-deployment-75d79cf8cc-pp9jj   1/1     Running       0          29s
sample-deployment-75d79cf8cc-xl9tn   1/1     Running       0          37s
```

補足：replicas: 3を指定しているので、ちゃんとpodが3つできていることがわかります。

## 今回はnginxに接続します。

```bash
❯ kubectl port-forward sample-deployment-75d79cf8cc-g6xhb 8080:80
Forwarding from 127.0.0.1:8080 -> 80
Forwarding from [::1]:8080 -> 80
```

## 別のターミナルを立ち上げcurlで接続します

```html

❯ curl -i localhost:8080
HTTP/1.1 200 OK
Server: nginx/1.21.4
Date: Wed, 08 Dec 2021 13:45:30 GMT
Content-Type: text/html
Content-Length: 615
Last-Modified: Tue, 02 Nov 2021 14:49:22 GMT
Connection: keep-alive
ETag: "61814ff2-267"
Accept-Ranges: bytes

<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
html { color-scheme: light dark; }
body { width: 35em; margin: 0 auto;
font-family: Tahoma, Verdana, Arial, sans-serif; }
</style>
</head>
<body>
<h1>Welcome to nginx!</h1>
<p>If you see this page, the nginx web server is successfully installed and
working. Further configuration is required.</p>

<p>For online documentation and support please refer to
<a href="http://nginx.org/">nginx.org</a>.<br/>
Commercial support is available at
<a href="http://nginx.com/">nginx.com</a>.</p>

<p><em>Thank you for using nginx.</em></p>
</body>
</html>
```

kubectl port-forwardのコマンドの説明は、以下になります。

```bash
kubectl port-forward [接続したPod名] [ブラウザで開く転送先ポート]:[nginxポートである転送元ポート]
```

今回はpodに対して接続しました。deploymentやserviceなどに対しても接続できますが、その際は接続先から一つのpodが選ばれて接続状態になります。
詳しくは[公式Docのport-forward](https://kubernetes.io/docs/reference/generated/kubectl/kubectl-commands#port-forward)にも例を見てみてください。

余談ですが、pod自体を操作したい場合は、このコマンドです。

```bash
kubectl exec -it sample-deployment-75d79cf8cc-g6xhb -- bash
```

# 接続方法その2 node port
次にServiceと呼ばれるkindの中の外部公開用node portを使用する方法です。
node portを使用するだけではkind(kubernetes in docker)の仕様上できないため、一つ手順が増えます。


## 準備

記事の最初に作成したkindのクラスタを作り直す必要があります。
まず、クラスタのyamlの最後に3行追加します。

``` kind-config.yaml
kind: Cluster
apiVersion: kind.x-k8s.io/v1alpha4
nodes:
- role: control-plane
- role: worker
- role: worker
  extraPortMappings:
  - containerPort: 30080
    hostPort: 10080
```

__解説__

- containerPortには、nodeportとして設定する予定のポートを指定します。
- hostPortではブラウザで`http://localhost:xxxx`に接続したい際にxxxxの部分を指定します。80でも良いですが、色々被りそうなので、10080としています。

ちなみにnode portは30000-32767の範囲内のいずれかのポートを指定する必要がありますので、containerPortはその範囲内でお願いします。

## クラスタを作り直します
記事通りにしているのであれば、クラスタを作り直す必要があるので、以下のコマンドを実行します。

```bash
# kind-kindクラスタを削除します。
kind delete cluster
# kind-kindクラスタを作成します。
kind create cluster --config kind-config.yaml
# nginxをデプロイします。
kubectl apply -f deployment.yaml
```

## serviceを立ち上げます。
外部公開するにはserviceのリソースを使用します。

```service.yaml
apiVersion: v1
kind: Service
metadata:
  name: sample-service
spec:
  type: NodePort
  ports:
  - name: "http-port"
    protocol: "TCP"
    port: 8080
    targetPort: 80
    nodePort: 30080
  selector:
    app: sample-app
```

__解説__

- type: typeは色々ありますが、今回は外部公開するためにNodePortを指定します。
- ports: nginxを表示するためのポートを指定します。
    - protocol：TCPを選択します。
    - port:　cluster ipと呼ばれる箇所で、クラスタ内のpodたちが通信する際に使うポートを指定します。内部公開なので、今回は直接関係はないです。
    - targetPort：公開元のポートを指定します。nginxのデフォルトポートは80なので、80を指定します。
    - nodePort:クラスタ外からアクセスするためのポートを指定します。公開可能なポートのレンジを気にしつつ、30080を指定します。
- selector：どのpodに対して公開するかの条件を指定します。
    - 今回はDeploymentでデプロイしたnginxの指定したいので、keyが`app`でvalueが`sample-app`というラベルを指定します。

service.yamlを作成して、以下のコマンドを実行します。
```
kubectl apply -f service.yaml
```
これで外部公開できました。本当かどうか確かめてみます。


## curlで確認します。

```bash
curl -i localhost:10080
```

ブラウザで`http://localhost:10080`にアクセスしてもよいです。

__念のために解説__
ポイントとしては、nodeport30080に接続していないことです。
kindの仕様上３００８０では接続できないので、クラスタを作成した際に、extraPortMappingsに設定した、転送元である`containerPort`にnodeportを指定して、転送先である`hostPort`に10080を指定していますので、10080で接続すればよいです。


# おわりに
いかがだったでしょうか。
色々割愛しているので、これは何？というものもあったかと思いますが、
面白そうと思っていただければ、その気になった部分を調べてみてはどうでしょうか。

またkubernetesには資格もあるので、資格取得にも挑戦してみても良いかと思います。
[認定Kubernetes管理者 (CKA-JP)](https://training.linuxfoundation.org/ja/certification/certified-kubernetes-administrator-cka-jp/)

間違いがあれば、コメントいただけると幸いです！

記事を読んでいただき、ありがとうございました！！