Kubernetes超入門 part3 Reactアプリケーションの作成

前回、rancherを導入しました。

https://qiita.com/Yuumillar/items/e12fbae403b8c2b1f001

今度はkubernetes上にReactアプリケーションをデプロイしてみましょう。

## Reactアプリを作成する

ひとまずviteでサクっとつくりましょう。

```sh
npm create vite@latest
```

react,ts,swcあたりにしておくと良いでしょう。
一応devで動くかどうか確認。
```
npm run dev
```

これで問題なくページが表示されていたらOK。
自分が作成したアプリだとわかりやすいように、App.tsxに「テスト」とか入れておいても良いでしょう。

## Dockerfileを作成する

Reactアプリのひな型ができたら、Dockerfileを作成します。
ReactをbuildしてnginxでserveするDockerfileです。
そのアプリのルートディレクトリに置きます。

```Dockerfile
FROM node:20.12.2-slim as build-stage
WORKDIR /app
COPY package*.json ./
RUN npm install
COPY . .
RUN npm run build

FROM nginx:1.21.5 as production-stage
COPY --from=build-stage /app/dist /usr/share/nginx/html
EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]
```

簡単に説明すると、nodejs v20でビルドします。
その後、ビルドしたファイルをnginxで80番ポートでserveするということです。

ためしにビルドしてみましょう。

```
docker build -t aaa .
```

これで問題なくビルドされたらOK。

さて、ここで作成したイメージをpushしていくのですが、どこに置くのかを考える必要があります。ひとまずdockerhubにしておいても良いのですが、いろいろな制限があるので自分のレジストリを持っておくと良いです。harborというOSSがありますので、それを利用すると無制限のDockerレジストリが手に入ります。harborの導入については、また次の記事で書きます。
kubernetesというかk3sでharborを使用するときの注意点として、registries.yamlというファイルを`/etc/rancher/k3s`に配置しておく必要があります。全ノードに下記のようなファイルを用意して配置しておくと良いでしょう。

```yaml
mirrors:
  docker.io:
    endpoint:
      - "https://reg.example.com"
configs:
  "reg.example.com":
    auth:
      username: admin
      password: superStrongPassword
```


もっと詳しく知りたい人は以下。

https://docs.k3s.io/installation/private-registry


## ついでにgithub actionでbuildするようにしても良い

こんな感じのaction.yamlを設置してpushやmergeしたら自動的にビルドしてpushするようにできたりします。一例。

```yaml:buildAndPush.yaml

name: "BuildAndPushImageOnHarborAndUpdateArgoCDConfig"

on: [push, pull_request]

jobs:
  build:
    runs-on: ubuntu-latest
    container: catthehacker/ubuntu:act-latest
    steps:
      - name: Set up Docker Buildx
        uses: docker/setup-buildx-action@v1

      - uses: docker/login-action@v3
        with:
          registry: reg.example.com
          username: username
          password: superstrongpassword

      - uses: actions/checkout@v3
      - name: BuildAndPushImageOnHarbor
        run: |
          VERSION=$(npm pkg get version --parseable | sed 's/\"//g')
          echo $VERSION
          docker build -t reg.example.com/example/kubernetes-react:$VERSION . && docker push reg.example.com/example/kubernetes-react:$VERSION   
```

このファイルを.git/workflowsに配置することで、npm version patchでパッチバージョンをアップして、それをプッシュしたら自動的にビルドされるようになります。package.jsonからVERSIONを自動的に取得して、タグにつけてくれるおまけつき。

githubでのactionは無料だとCPUの時間制限があったりするので、自分でgitea, act_runnerをセルフホストすると無制限で遊べるようになります。僕はgiteaでやってます。

actionsを使わないなら、手元の環境で`docker build -t reg.example.com/example/kubernetes-react:$VERSION . && docker push reg.example.com/example/kubernetes-react:$VERSION`としてみましょう。このあたりのexample.comとかはよしなに。dockerhubだったらreg.example.comのところは要りません。

## kubernetesにdeployする用のyamlをつくる

こんな感じのyamlになります。
```yaml:kubernetes-react.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: kubenetes-react
spec:
  selector:
    matchLabels:
      app: nginx
  replicas: 3
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: reg.example.com/example/kubernetes-react:1.0.0
        ports:
        - containerPort: 80
---
apiVersion: v1
kind: Service
metadata:
  name: nginx-service
  labels:
    app: nginx
spec:
  type: NodePort
  ports:
  - port: 80
    targetPort: 80
    nodePort: 30080
    protocol: TCP
  selector:
    app: nginx
```

いよいよkubernetesっぽくなってきました。ひとまず、kindという言葉を覚えましょう。kindは種類ですね。kubernetesのクラスタに何かをデプロイするときに、種類を指定する必要があります。`Deployment`はずっとpodが動いているような状態のものです。一般的なWEBアプリケーションはDeploymentになると思います。他にもjobとかcronjobとかいろいろありますが、とりあえずずっと動いてるアプリケーションはDeploymentと覚えましょう。

こういうyamlを最初から書けるかというと難しいので、ひとまずちゃんと動くyamlを幾つか手に入れましょう。そうしたらあとは組み合わせでなんとかなります。copilotとかに頼っても良いでしょう。で、docker-compose.ymlみたいに書けば書くほど書けるようになります。特殊なことをしないならコピペで大丈夫。で、自分なりにどこかにストックしていっていつでも出せる状態にしておくと良いでしょう。yamlを何も見ずにミスなく完璧に……とかやると頭パンクしてしまうでしょう。で、ある程度書けるようになったあとにkubernetesのドキュメントを見ると、いろいろ知識が入ってくるでしょう。最初からドキュメントにいって心が折れたら、とりあえず動くものをつくってから改めて勉強し直すというのもアリかと。

それでは中身。`replicas:3`に注目してみましょう。これがレプリカの数を意味します。レプリカが3なら3個のアプリケーションが生まれます。もしそのうちの1個がダメになっても、残りの2個でいい感じに動いてくれるという仕組みです。そんなにReactアプリというかnginxがおかしくなることって想像しづらいですけれどね。

imageにはさきほど作成したイメージを指定します。portsはdocker-composeとかと一緒で公開するポートですね。

ここまでのものを`kubectl apply -f ./kubernetes-react.yaml`とすることでデプロイできます。ただ、外部に公開するためにはServiceというものを作成しないといけません。Serviceにはいろいろ種類があってややこしいですが、今回はNodePortというものを勉強しましょう。

NodePortについて公式から引用。
> NodePort タイプ
もしtypeフィールドの値をNodePortに設定すると、Kubernetesコントロールプレーンは--service-node-port-rangeフラグによって指定されたレンジのポート(デフォルト: 30000-32767)を割り当てます。 各Nodeはそのポート(各Nodeで同じポート番号)への通信をServiceに転送します。 作成したServiceは、.spec.ports[*].nodePortフィールド内に割り当てられたポートを記述します。

ということで30000-32767の間でポートを公開できます。今回は30080にしたわけですね。前回のpart1で192.168.0.70, 71, 72とサーバーを準備しました。さっきのyamlを`kubectl apply -f ./kubernets-react.yaml`とすることで、どのサーバーの30080にアクセスしても同じアプリケーションを開くことができるようになります。HAProxyを使っていた場合、192.168.0.60がVIPになっていると思います。そこをNodePortとして公開するとわかりやすくて良いと思います。

いくつかのアプリケーションをkubernetes上で実行するとします。社内とか家庭内でクラスタを組むだけ＋ドメインを確保するほどでもないのであれば、VIP+NodePortによって、ポート違いで違うサービスを実行するというのが良いと思います。後々ingressを使用しますが、その場合はローカルアクセスであればhostsなどでドメインをいじる必要が出てきます。

そんな感じで今回はReactアプリケーションの公開でした。当然、nginxにhtmlなどをコピーすれば普通のHTML+CSSみたいなstaticなページも公開できます。Dockerfileをいろいろ調整すればnode.jsアプリも公開できますね。