Kubernetes: NodePortを利用したアプリの公開、デプロイ

# 0.アプリを公開したい
久しぶりに投稿します。今回はKubernetesでNodePort Serviceを利用してアプリを公開する方法を紹介していきます。
Kubernetesを使って試しに、気軽にアプリをデプロイしてみたい(お金はかけたくない。。。)！という方向けです。

今回使用した環境はIBM Cloudですが、どのCloudでも同様にデプロイできると思います。Kubernetes環境の無料枠が一ヶ月で、期限が切れても再び構築することはできます。
無料枠ではIngressなどを使うことはできませんが、Serviceリソースの一つであるNodePortを利用してアプリを公開することはできます。

Dockerコンテナの作成からKubernetes上へのデプロイまでを行なっているので、これらの一連の流れが掴めるようにはなってると思います。

#### 前提

- IBM Cloud アカウントの取得
- IBM CloudのKubernetes上にクラスターを構築してあること
- DockerがインストールされていてDocker Hubのアカウントを持っている
- 公開してみたいアプリがあること(なんでもよし。今回作成するアプリに関しては以下の環境が必要)
    - Node.js(node: v12.18.2, npm: 6.14.5)環境があること
    - [create-react-app](https://github.com/facebook/create-react-app)コマンドを使えること

# 1.サンプルアプリのDockerコンテナの作成

今回は[create-react-app](https://github.com/facebook/create-react-app)で簡単に作成できるReactアプリをデプロイしてみようと思います。まずはコンテナ化して動作確認します。

1-1. Reactアプリ作成([create-react-app](https://github.com/facebook/create-react-app)はインストールされている前提)

```
$ create-react-app my-react-app
$ cd my-react-app
```
1-2. Dockerfile作成

Dockerfileを以下のように作成し、my-react-appディレクトリ直下に配置します。

```Dockerfile:Dockerfile
FROM node:12
COPY . /app
WORKDIR /app
RUN npm run build && \
npm install -g serve
CMD [ "serve" , "-s", "build" ]
```
今回のコンテナ作成の流れとしてはproduction用の静的ファイルを生成し,それを静的ファイルサーバーを用いて実行するものになっています。
静的ファイルサーバー実行時、デフォルトでは5000番ポートを受け付けます。

1-3. ビルドし、ローカルで動くことを確認する

```
$ docker build -t my-react-app:0.0.1 .

$ docker run -p 5000:5000 my-react-app:0.0.1
INFO: Accepting connections at http://localhost:5000

#localhost:5000でreactアプリが動いていることができたらexit
```

# 2.DockerHubにpushする

2-1. 'docker tag'で1で作成したイメージに自分のDocker Hubのアカウント名(レポジトリ名)を付与する

```
$ docker login
Login with your Docker ID to push and pull images from Docker Hub. If you don't have a Docker ID, head over to https://hub.docker.com to create one.
Username: <DockerHubアカウント名>
Password:
Login Succeeded

$ docker tag my-react-app:0.0.1 <DockerHubアカウント名>/my-react-app:0.0.1
```

2-2. イメージをDocker Hubのレポジトリににpushする

```
my-react-app $ docker push <DockerHubアカウント名>/my-react-app:0.0.1
The push refers to repository [docker.io/bangdr2018/my-react-app]
24531b8429e8: Pushed
ea8d003551da: Pushed
9cee2cb2fb8e: Mounted from library/node
eb3f44bfde1e: Mounted from library/node
3b93aab45097: Mounted from library/node
174e334f3f46: Mounted from library/node
cbe6bbd0c86f: Mounted from library/node
ef5de533cb53: Mounted from library/node
a4c504f73441: Mounted from library/node
e8847c2734e1: Mounted from library/node
b323b70996e4: Mounted from library/node
0.0.1: digest: sha256:f032be9c6a2530de810d1a29b75777a01655e7a23b64a7aa39e81f5154a82640 size: 2638
```

2-3. イメージがpushできたことDocker Hubのコンソール上で確認(アカウント名は念のため切ってます)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/347450/e2d94d67-1c5c-6d87-06f4-c390ca606f06.png)

# 3. Kubernetesへのデプロイ、アプリの公開

3-1. kubectlコマンドのコンテキストを確認する。

コンテキストが存在しなかったり、ローカルなど異なるコンテキストとなっていた場合は、kubectlコマンドをダウンロードする

```
// すでにIBM Cloudに構築したKubernetesからkubectlコマンドをダウンロードしていた場合コンテキストは次のようになる
$ kubectl config current-context
<クラスター名>/<クラスターID>

```
ダウンロードしていなかった場合はIBM Cloudコンソール上でクラスターのプロファイルを開き、「アクセス」の箇所に以下のようにkubectlコマンドのダウンロード方法が載っている(ibmcloudコマンドをすでに使用できる場合「CLIツールの一回かぎりのセットアップ」は不要)。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/347450/8f643d2c-7a63-bb9b-5dd6-891cc84ffe79.png)


3-2. deployment.ymlの作成

以下のようにdeployment.ymlを作成。containerのimageに2でpushしたイメージを指定する

```yaml:deployment.yml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: my-react-app-deployment
spec:
  selector:
    matchLabels:
      app: my-react-app
  template:
    metadata:
      labels:
        app: my-react-app
    spec:
      containers:
      - name: my-react-app
        image: <DockerHubアカウント名>/my-react-app:0.0.1
        resources:
          limits:
            memory: "128Mi"
            cpu: "500m"
        ports:
        - containerPort: 5000
```
Podが生成、Runnningとなっていることを確認(Podの名前はデプロイメント名に付随する)

```
$ my-react-app $ kubectl get pods
NAME                                      READY   STATUS    RESTARTS   AGE
my-react-app-deployment-c4f95c7cd-rx8xt   1/1     Running   0          8s
```

3-3. NodePortを利用したserviceを作成する

以下のようにservice.ymlを作成。selectorのラベル(app: my-react-app)とPodのラベル(app: my-react-app)を一致させる。
ポートの番号は30000-32767の間に指定する。

```yaml:service.yml
apiVersion: v1
kind: Service
metadata:
  name: my-react-app-service
spec:
  type: NodePort
  selector:
    app: my-react-app
  ports:
  - port: 5000
    targetPort: 5000
    nodePort: 30080
    protocol: "TCP"
    name: "http-port"
```

serviceが生成されていることを確認の確認(Podの名前はデプロイメント名に付随する)

```
my-react-app $ kubectl get service
NAME                   TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)          AGE
my-react-app-service   NodePort    172.21.94.246    <none>        5000:30080/TCP   10s
```

3-4. Nodeに割り振られているExternal IPを確認する

NodePort Serviceはその名の通り、<NodeのIPアドレス>:<ポート>で受信したトラフィックをコンテナに送信するものなので、nodeのEXTERNAL-IPを確認する。


```
// -o wideオプションで表示可能
$ kubectl get node -o wide
NAME             STATUS   ROLES    AGE   VERSION        INTERNAL-IP      EXTERNAL-IP(これ！)      OS-IMAGE             KERNEL-VERSION      CONTAINER-RUNTIME
10.144.194.234   Ready    <none>   13d   v1.17.11+IKS   10.144.194.234   159.122.xxx.xxx   Ubuntu 16.04.7 LTS   4.4.0-187-generic   containerd://1.3.4
```

3-5. 公開されたアプリをみてみる

さあ緊張の瞬間ですが<NodeのIPアドレス>:<Nodeポート>で公開されたアプリを見てみましょう。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/347450/93347a4a-bf70-f1f6-f68a-ad5e86560888.png)

公開できました！やりましたね！



# おわりに

今回はアプリのコンテナ化からKubernetes上へのデプロイまでの一連の流れを紹介してみました。
結構簡単に、かつ柔軟にデプロイできると思いました。

ただKubernetesのメリットとしてはこんなものではないので自分自身も勉強しつつ有益だと思ったらどんどん投稿していこうと思います。

次はKubernetesで構築したアプリの環境変数周りのリソースについて説明してみようかなと思ってます。