🙌

React アプリを Kubernetes で動かしてみた

2021/11/26に公開約7,800字

はじめに

勉強がてら React アプリを Kubernetes 上にデプロイしてみたので備忘録としてまとめておきます。

この記事のゴール

ローカルの Kubernetes 上に React アプリを実行して動作を確認できるところまでを目指します。

前提

環境については macOS を使用していて、Home Brew を導入済みという前提で進めます。使用するツールについては後述します。

% sw_vers
ProductName:    macOS
ProductVersion: 12.0.1
BuildVersion:   21A559

使用するツールとそのセットアップ

  • Docker
  • Minikube
    • ローカルで簡単に Kubernetes を実行できるツールです。試しに動かしてみたい場合に使えるので重宝しています。詳しくは公式ドキュメントをご覧ください。
  • kubectl

それぞれ Home Brew 経由でインストールしていきます。

# Minikube
% brew install minikube

% minikube version
minikube version: v1.23.0
commit: 5931455374810b1bbeb222a9713ae2c756daee10

# kubectl
% brew install kubectl

% kubectl version
Client Version: version.Info{Major:"1", Minor:"22", GitVersion:"v1.22.1", GitCommit:"632ed300f2c34f6d6d15ca4cef3d3c7073412212", GitTreeState:"clean", BuildDate:"2021-08-19T15:38:26Z", GoVersion:"go1.16.6", Compiler:"gc", Platform:"darwin/amd64"}
Server Version: version.Info{Major:"1", Minor:"22", GitVersion:"v1.22.1", GitCommit:"632ed300f2c34f6d6d15ca4cef3d3c7073412212", GitTreeState:"clean", BuildDate:"2021-08-19T15:39:34Z", GoVersion:"go1.16.7", Compiler:"gc", Platform:"linux/amd64"}

サクッと試してみたい方

これから手順に沿って解説していくのですが、そんなものはいらないからとりあえず動かしてみたいという方は下記にソースコードを公開しているのでそちらをご覧ください。

https://github.com/ega4432/react-k8s-sample

React プロジェクトの作成

まずは、シンプルな React アプリケーションを作成します。

# 適当なディレクトリに移動
% cd path/you/like

# プロジェクトを作成(テンプレートとして TS を指定)
% npx create-react-app --template tyepscript react-k8s-sample

これで React プロジェクトの雛形が作成されました。試しに立ち上げてみましょう。

% cd react-k8s-sample

# 依存パッケージをインストール
% yarn

% yarn start

start スクリプトでブラウザが立ち上がり localhost:3000 を開き下記のように表示されたら OK です。

確認できたら ^C で一旦停止しておきましょう。

コンテナ化

続いて、プロジェクト全体をコンテナ化していきます。

Dockerfile の作成

まずは、コンテナ化していくにあたりコンテナのベースとなるイメージを作成していきます。プロジェクト直下に Dockerfile というファイルを作成し、下記のように記述します。

Dockerfile
FROM node:16.13.0-alpine AS builder

WORKDIR /usr/local/app

COPY . .

RUN yarn --frozen-lockfile && \
    yarn build

FROM nginx:1.20-alpine

COPY --from=builder /usr/local/app/build /usr/share/nginx/html

CMD [ "nginx", "-g", "daemon off;" ]

そこまで凝ったことはしていませんが、簡単に解説します。

1 つ目の FROM 命令でビルド用の node をベースイメージとして指定しています。そして React をビルドし、2 つ目の FROM 命令以下でビルドによって生成されたファイル ./build 以下を nginx イメージにコピーしています。

このようにマルチステージビルドを使用することで、Dockerfile の保守性を向上させ、最終的な成果物としてのイメージサイズを抑えることができます。可搬性が高いというコンテナの性質を活かし、デプロイを速くするためにもイメージサイズには常に気にすることですよね。

イメージのビルド、コンテナの起動

記述した Dockerfile を元にコンテナで正常にプロジェクトが起動できるか簡単にチェックしておきましょう。

# イメージをビルド
% docker build --tag <YOUR_NAME>/react-k8s-sample:latest \
  --no-cache .

# 生成されたイメージを確認
% docker images --filter "dangling=false" \
    --format "table {{ .Repository }}:{{ .Tag }} {{ .Size }}" | \
    head -n 2
REPOSITORY:TAG                             SIZE
<YOUR_NAME>/react-k8s-sample:latest        23.7MB

# イメージからコンテナを起動
% docker run --name tmp-react-k8s-sample \
  --rm --publish 3000:80 \
  <YOUR_NAME>/react-k8s-sample:latest

ターミナルでログが出力されて、コンテナが起動できたら再度 localhost:3000 を開いてみましょう。先ほどと同様に React のデフォルト画面が表示されることを確認できたら OK です。また ^C でターミナルのプロセスを終了させておきます。

イメージのプッシュ

作成した Docker イメージをレジストリにプッシュします。今回は、イメージレジストリには Docker Hub を使用します。

% docker push <YOUR_NAME>/react-k8s-sample:latest

マニフェストファイルの作成

これからようやく Kubernetes です。
Kubernetes ではリソースやそれらの設定をマニフェストファイルと呼ばれる YAML に定義します。

今回は Deployment と Service を作成していきます。

deployment-service.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: react-k8s-sample
spec:
  replicas: 1
  selector:
    matchLabels:
      app: react-k8s-sample
  template:
    metadata:
      labels:
        app: react-k8s-sample
    spec:
      containers:
      - name: react
        image: <YOUR_NAME>/react-k8s-sample   # Edit
        imagePullPolicy: IfNotPresent
        ports:
        - containerPort: 80
---
apiVersion: v1
kind: Service
metadata:
  name: react-k8s-sample
spec:
  type: NodePort
  selector:
    app: react-k8s-sample
  ports:
    - port: 3000
      targetPort: 80
      protocol: TCP
      name: react-k8s-sample

Deployment では、先ほど作ったコンテナを起動する Pod と、レプリカセットを(ここでは単に 1 つ)定義しています。spec.template.spec.containers[0].image の部分だけご自身の Docker Hub のユーザ名に書き換えてください。

Service では、Deployment で作成した Pod に対してクラスタ外部から通信するための設定を書いていきます。Service の種類の中でも静的なポートで公開する NodePort を使用します。

また、余談にはなりますが、マニフェストファイルは --- で分けることによって複数のリソースを定義できます。別ファイルとしての管理も可能ですが、今回はこの量なので 1 ファイルにまとめました。

Kubernetes クラスタ上で動かす

Minikube を起動

# Minikube を起動
% minikube start

..

🏄  完了しました! kubectl が「"minikube"」クラスタと「"default"」ネームスペースを使用するよう構成されました

# 確認
% minikube status
minikube
type: Control Plane
host: Running
kubelet: Running
apiserver: Running
kubeconfig: Configured

上記のように Running になっていたら OK です。

デプロイ

次に、Minikube 上の Kubernetes クラスタにマニフェストファイルに書いたリソースをデプロイしていきます。

# デプロイ
% kubectl apply --filename ./deployment-service.yaml
deployment.apps/react-k8s-sample created
service/react-k8s-sample created

# 実行結果を確認
% kubectl get all | grep -v kubernetes
NAME                                    READY   STATUS    RESTARTS   AGE
pod/react-k8s-sample-6c5dc7dd67-wjvcj   1/1     Running   0          10m

NAME                       TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)          AGE
service/react-k8s-sample   NodePort    10.111.180.101   <none>        3000:30669/TCP   40m

NAME                               READY   UP-TO-DATE   AVAILABLE   AGE
deployment.apps/react-k8s-sample   1/1     1            1           40m

NAME                                          DESIRED   CURRENT   READY   AGE
replicaset.apps/react-k8s-sample-6544bfd9d6   1         1         1       4m19s

Pod や Deployment などの READY が 0/1 と表示されている場合は、数秒待って再度実行すると上記のように 1/1 となり、ステータスも Running となります。

Service の公開

% kubectl get service | grep -v kubernetes
NAME               TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)          AGE
react-k8s-sample   NodePort    10.111.180.101   <none>        3000:30669/TCP    4m

Service の情報を見てみると、EXTERNAL-IP が none になっています。このままだとクラスタ外からはアクセスできないので、NodePort 経由で Service を公開します。

% minikube service react-k8s-sample

上記のコマンドを実行すると、自動的にブラウザが開かれて下記のように表示が確認できます。

これで Minikube を使ってローカルの Kubernetes 上に React アプリケーションをデプロイできました。

クリーンナップ

作業が終わったらリソースを停止、削除します。

# Deployment, Service を削除
% kubectl delete --filename ./deployment-service.yaml
deployment.apps "react-k8s-sample" deleted
service "react-k8s-sample" deleted

# クラスターの停止
% minikube stop  
✋  ノード "minikube" を停止しています...
🛑  SSH 経由で「minikube」の電源をオフにしています...
🛑  1台のノードが停止しました

# クラスターの削除
% minikube delete
🔥  docker の「minikube」を削除しています...
🔥  コンテナ "minikube" を削除しています...
🔥  /Users/<YOUR_NAME>/.minikube/machines/minikube を削除しています...
💀  クラスタ "minikube" の全てのトレースを削除しました。

まとめ

今回は Minikube を使ってローカルの Kubernetes 上に React プロジェクトを構築してみました。かなり長々となってしまいましたが、kubectl コマンドの使い方やマニフェストファイルの YAML の書き方など非常に勉強になることが多ったです。

Kubernetes は、ローカルでの学習が大変ですが、このように少しずつユースケースベースで触って試すのがなんだかんだで近道な気がしています。

ソースコードも公開しているので、読んでくださっている方の参考になれたら幸いです。

https://github.com/ega4432/react-k8s-sample

参考にさせていただいたサイト

https://kubernetes.io/ja/docs/concepts/workloads/controllers/deployment/
https://matsuand.github.io/docs.docker.jp.onthefly/develop/develop-images/multistage-build/
https://kubernetes.io/ja/docs/setup/learning-environment/minikube/
GitHubで編集を提案

Discussion

ログインするとコメントできます