やること
今回は簡単にKubernetes環境が作れる「Minikube」を用いてローカル環境にReactアプリケーションをデプロイし、疎通確認をしていきます。
また、今回のソースコードはGitHubに乗せています。
使用する環境
- OS : Apple M1 macOS BigSur 11.4
- docker : v20.10.7
Minikubeのインストール
こちらのサイトからインストールを行ってください。
(Intel版、Apple M1版ありますので、自分の環境にあったものをインストールしてください。)
M1のMacの場合は以下で入手できます。
$ curl -LO https://storage.googleapis.com/minikube/releases/latest/minikube-darwin-arm64
$ sudo install minikube-darwin-arm64 /usr/local/bin/minikube
また、上記のURLページ内にも記載がありますが、Minikubeを使うにはDocker等のコンテナ、仮想マシン環境が必要になるので別途インストールをお願いします。
kubectlのインストール
Kubernetesを使用する際には「kubectl」コマンドを使うため使えるようにします。
こちらのサイトからインストールを行ってください。
M1のMacの方は以下で入手できます。
$ curl -LO "https://dl.k8s.io/release/$(curl -L -s https://dl.k8s.io/release/stable.txt)/bin/darwin/arm64/kubectl"
$ curl -LO "https://dl.k8s.io/release/$(curl -L -s https://dl.k8s.io/release/stable.txt)/bin/darwin/arm64/kubectl.sha256"
$ echo "$(<kubectl.sha256) kubectl" | shasum -a 256 --check
$ chmod +x ./kubectl
$ sudo mv ./kubectl /usr/local/bin/kubectl
$ sudo chown root: /usr/local/bin/kubectl
Minikubeの動作確認
インストールしたminikubeとkubectlの確認をしてみましょう。
以下コマンドで起動できます。
$ minikube start
😄 Darwin 11.4 (arm64) 上の minikube v1.21.0
✨ dockerドライバーが自動的に選択されました
👍 コントロールプレーンのノード minikube を minikube 上で起動しています
🚜 イメージを Pull しています...
🔥 docker container (CPUs=2, Memory=1988MB) を作成しています...
🧯 Docker is nearly out of disk space, which may cause deployments to fail! (87% of capacity)
💡 提案:
Try one or more of the following to free up space on the device:
1. Run "docker system prune" to remove unused Docker data (optionally with "-a")
2. Increase the storage allocated to Docker for Desktop by clicking on:
Docker icon > Preferences > Resources > Disk Image Size
3. Run "minikube ssh -- docker system prune" if using the Docker container runtime
🍿 Related issue: https://github.com/kubernetes/minikube/issues/9024
🐳 Docker 20.10.7 で Kubernetes v1.20.7 を準備しています...
▪ 証明書と鍵を作成しています...
▪ Control Plane を起動しています...
▪ RBAC のルールを設定中です...
🔎 Kubernetes コンポーネントを検証しています...
▪ イメージ gcr.io/k8s-minikube/storage-provisioner:v5 を使用しています
🌟 有効なアドオン: storage-provisioner, default-storageclass
🏄 完了しました! kubectl が「"minikube"」クラスタと「"default"」ネームスペースを使用するよう構成されました
kubectlコマンドも確認しましょう。
$ kubectl version
Client Version: version.Info{Major:"1", Minor:"21", GitVersion:"v1.21.2", GitCommit:"092fbfbf53427de67cac1e9fa54aaa09a28371d7", GitTreeState:"clean", BuildDate:"2021-06-16T12:59:11Z", GoVersion:"go1.16.5", Compiler:"gc", Platform:"darwin/arm64"}
Server Version: version.Info{Major:"1", Minor:"20", GitVersion:"v1.20.7", GitCommit:"132a687512d7fb058d0f5890f07d4121b3f0a2e2", GitTreeState:"clean", BuildDate:"2021-05-12T12:32:49Z", GoVersion:"go1.15.12", Compiler:"gc", Platform:"linux/arm64"}
また、minikubeではダッシュボードを用いてデプロイ状況をGUIで確認することができるので、合わせてみておくと良いと思います。
$ minikube dashboard
ローカルのDockerイメージを使えるように設定
後ほどminikubeでデプロイする際に、ローカルのイメージを使うことができるように環境変数を設定します。
$ eval $(minikube docker-env)
※この後の作業で、Dockerイメージのビルド、Kubernetesのymlファイルの実行は上記コマンドを実行したターミナルで行ってください。
Reactアプリケーションの準備
今回はアプリの中までは作成しないため、簡単にcreate-react-appで作成します。
$ npx create-react-app react-k8s
$ cd react-k8s
せっかくなので文言を少し変えておきましょう。
10行目の文言を変えておきます。
import logo from './logo.svg';
import './App.css';
function App() {
return (
<div className="App">
<header className="App-header">
<img src={logo} className="App-logo" alt="logo" />
<p>
{/* Edit <code>src/App.js</code> and save to reload. */}
{/* ↑ここを変える */}
Hello React Application!!
</p>
<a
className="App-link"
href="https://reactjs.org"
target="_blank"
rel="noopener noreferrer"
>
Learn React
</a>
</header>
</div>
);
}
export default App;
変更できたら、以下コマンドで起動させてlocalhost:3000へアクセス。
$ yarn start
Dockerfileの作成
デプロイする際にはdockerイメージが必要なので、そこに使うためにDockerfileを作成します。
# nodeの軽量版を使用
FROM node:14.17.0-alpine
# ワークディレクトリを設定
WORKDIR /app
# ホストの.(カレントディレクトリ)に作成したreactアプリをコンテナの.(=/app)にコピー
COPY . .
# 依存関係のインストール
RUN yarn install
# 起動コマンド
CMD ["yarn", "start"]
イメージを作成して動かしてみましょう。
$ docker build -t mobilitysdg/react-k8s:1.0 .
$ docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
mobilitysdg/react-k8s 1.0 9c30bb0dd575 3 minutes ago 589MB
できていますね!イメージを起動して動くか確認しましょう。
$ docker run -p 3000:3000 mobilitysdg/react-k8s:1.0
yarn run v1.22.5
$ react-scripts start
ℹ 「wds」: Project is running at http://172.17.0.3/
ℹ 「wds」: webpack output is served from
ℹ 「wds」: Content not from webpack is served from /app/public
ℹ 「wds」: 404s will fallback to /
Starting the development server...
Compiled successfully!
You can now view reacr-k8s in the browser.
Local: http://localhost:3000
On Your Network: http://172.17.0.3:3000
Note that the development build is not optimized.
To create a production build, use yarn build.
良さそうですね!
Kubenetesのymlファイルを書く
1.deployment.yml
apiVersion: apps/v1
kind: Deployment
metadata:
name: react-k8s
spec:
replicas: 1
selector:
matchLabels:
app: react-k8s
template:
metadata:
labels:
app: react-k8s
spec:
containers:
- name: react-k8s
image: mobilitysdg/react-k8s:1.0
imagePullPolicy: IfNotPresent
ports:
- containerPort: 3000
command: ["yarn", "start"]
ローカルのイメージを使用する場合は、imagePullPolicy
をIfNotPresent
かNever
に設定してください。
2.service.yml
上記deployment.ymlで設定した内容をクラスター外へ公開するためにserviceを作成するための設定を書きます。
apiVersion: v1
kind: Service
metadata:
name: react-k8s
spec:
type: NodePort
selector:
app: react-k8s
ports:
- port: 3000
targetPort: 3000
protocol: TCP
name: react-k8s
クラスターの外部からアクセスする際には type
にNodePort
やLoadBalancer
を使用しますが、今回はNodePort
を使用します。
またこれらにはL4ロードバランサーの役割しかないため、ipアドレスのみでの接続となります。
本番運用する際にはIngressという別のサービスを使用してHTTPでのアクセスができるようにします。
デプロイする
上で作成したdeployment.yml
とservice.yml
を用いて実際にデプロイをしていきます。
まずはdeployment.yml
から。
$ kubectl apply -f deployment.yml
deployment.apps/react-k8s created
$ kubectl get all
NAME READY STATUS RESTARTS AGE
pod/react-k8s-75cf4d8ff6-4ksf2 1/1 Running 0 17s
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service/kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 3h51m
NAME READY UP-TO-DATE AVAILABLE AGE
deployment.apps/react-k8s 1/1 1 1 17s
NAME DESIRED CURRENT READY AGE
replicaset.apps/react-k8s-75cf4d8ff6 1 1 1 17s
pod
がRunning状態になっており、replicaset
とdeployment
が作成されているのがわかります。
ちなみにダッシュボードを見てみると以下のようにデプロイされていることが確認できます。
次にservice.yml
です。
$ kubectl apply -f service.yml
service/react-k8s created
$ kubectl get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 3h59m
react-k8s NodePort 10.110.66.69 <none> 3000:31609/TCP 23s
react-k8sというserviceが作成されていますね。
これでデプロイ作業は終了です!
ちなみに、今回は役割ごとにdelpoyment.ymlとservie.ymlを分けて書きましたが、一つのファイルにすることもできます。その場合は---
でつなげて以下のように書くことができます。
apiVersion: apps/v1
kind: Deployment
metadata:
name: react-k8s
spec:
replicas: 1
selector:
matchLabels:
app: react-k8s
template:
metadata:
labels:
app: react-k8s
spec:
containers:
- name: react-k8s
image: mobilitysdg/react-k8s:1.0
imagePullPolicy: IfNotPresent
ports:
- containerPort: 3000
command: ["yarn", "start"]
---
apiVersion: v1
kind: Service
metadata:
name: react-k8s
spec:
type: NodePort
selector:
app: react-k8s
ports:
- port: 3000
targetPort: 3000
protocol: TCP
name: react-k8s
このファイルをkubectl applyすることでまとめてデプロイすることができます。
接続確認
本来であればminikube ip
コマンドで取得できるipアドレスと、service作成時に決められたポート(今回は31609)を用いてブラウザからアクセス可能(例えば192.168.49.2:31609)ですが、m1のMacは接続ができないようです、、
なのでminikube serviceを使用します。サービス名は今回react-k8s
としたので以下コマンドでサービスへの接続先を確認します。
$ minikube service react-k8s --url
🏃 Starting tunnel for service react-k8s.
|-----------|-----------|-------------|------------------------|
| NAMESPACE | NAME | TARGET PORT | URL |
|-----------|-----------|-------------|------------------------|
| default | react-k8s | | http://127.0.0.1:60778 |
|-----------|-----------|-------------|------------------------|
http://127.0.0.1:60778
アクセスが確認できました!
確認ができたらデプロイしたリソースは削除しておきます。
$ kubectl delete -f service.yml
service "react-k8s" deleted
$ kubectl delete -f deployment.yml
deployment.apps "react-k8s" deleted
まとめ
今回はアプリ作成〜デプロイまでを広く浅くの形で一通り実施してみました。
Kubernetesを用いたデプロイの参考になればと思います!
宣伝
パーソルプロセス&テクノロジー株式会社(以下パーソルP&T)、システムソリューション(SSOL)事業部所属の戸田です。
私はモビリティソリューションデザインチームに所属しており、モビリティ(ここでは移動手段全般)に関するサービスを考えたり、アプリを構築したりしております。
いわゆる「MaaS」に取り組んでおります。
私たちが「MaaS」に取り組む中で、現在活用している、もしくは活用する予定の技術やサービスやとりあえず発信したいことなどなど、幅広くチームメンバーと共に執筆していきたいと思います。
メンバーごとに違った内容を発信していきますので、お楽しみに!
また、「MaaS」について詳しく知りたい方は、チームメンバーの吉田が記事を掲載しておりますので、
ぜひそちらをご覧ください。
「MaaSとは」でたどり着いて欲しい記事 (1/3 前編)
「MaaSとは」でたどり着いて欲しい記事 (2/3 中編)
「MaaSとは」でたどり着いて欲しい記事 (3/3 後編)
最後まで読んでいただき、ありがとうございました!