前回、rancherを導入しました。
今度はkubernetes上にReactアプリケーションをデプロイしてみましょう。
Reactアプリを作成する
ひとまずviteでサクっとつくりましょう。
npm create vite@latest
react,ts,swcあたりにしておくと良いでしょう。
一応devで動くかどうか確認。
npm run dev
これで問題なくページが表示されていたらOK。
自分が作成したアプリだとわかりやすいように、App.tsxに「テスト」とか入れておいても良いでしょう。
Dockerfileを作成する
Reactアプリのひな型ができたら、Dockerfileを作成します。
Reactをbuildしてnginxでserveする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
に配置しておく必要があります。全ノードに下記のようなファイルを用意して配置しておくと良いでしょう。
mirrors:
docker.io:
endpoint:
- "https://reg.example.com"
configs:
"reg.example.com":
auth:
username: admin
password: superStrongPassword
もっと詳しく知りたい人は以下。
ついでにgithub actionでbuildするようにしても良い
こんな感じのaction.yamlを設置してpushやmergeしたら自動的にビルドしてpushするようにできたりします。一例。
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になります。
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アプリも公開できますね。