Dapr と React と Spring Boot でマイクロサービスを構成する:Docker 編
こんにちは、@studio_meowtoon です。今回は、WSL Ubuntu 22.04 で Dapr と React と Spring Boot を利用してマイクロサービスを構成する方法を紹介します。
目的
分散アプリケーションの為のランタイム Dapr の理解を深めます。
実現すること
Ubuntu の Docker 環境にて、Dapr、React、Spring Boot で構成する、シンプルなマイクロサービスを実装します。
こちらの記事の続きになります。
開発環境
- Windows 11 Home 22H2 を使用しています。
WSL の Ubuntu を操作していきますので macOS の方も参考にして頂けます。
WSL (Microsoft Store アプリ版) ※ こちらの関連記事からインストール方法をご確認いただけます
> wsl --version
WSL バージョン: 1.0.3.0
カーネル バージョン: 5.15.79.1
WSLg バージョン: 1.0.47
Ubuntu ※ こちらの関連記事からインストール方法をご確認いただけます
$ lsb_release -a
No LSB modules are available.
Distributor ID: Ubuntu
Description: Ubuntu 22.04.1 LTS
Release: 22.04
npm ※ こちらの関連記事からインストール方法をご確認いただけます
$ node -v
v19.8.1
$ npm -v
9.5.1
Java JDK ※ こちらの関連記事からインストール方法をご確認いただけます
$ java -version
openjdk version "11.0.18" 2023-01-17
OpenJDK Runtime Environment (build 11.0.18+10-post-Ubuntu-0ubuntu122.04)
OpenJDK 64-Bit Server VM (build 11.0.18+10-post-Ubuntu-0ubuntu122.04, mixed mode, sharing)
Maven ※ こちらの関連記事からインストール方法をご確認いただけます
$ mvn -version
Apache Maven 3.6.3
Maven home: /usr/share/maven
Java version: 11.0.18, vendor: Ubuntu, runtime: /usr/lib/jvm/java-11-openjdk-amd64
Docker ※ こちらの関連記事からインストール方法をご確認いただけます
$ docker --version
Docker version 23.0.1, build a5ee5b1
Dapr ※ こちらの関連記事からインストール方法をご確認いただけます
$ dapr --version
CLI version: 1.11.0
Runtime version: 1.11.2
※ この記事では基本的に Ubuntu のターミナルで操作を行います。
作成する Web アプリケーションの仕様
No | エンドポイント | HTTPメソッド | MIME タイプ |
---|---|---|---|
1 | /api/data | GET | application/json |
説明を開きます。
/api/data というエンドポイントを実装するバックエンドサービスと、HTTP GET リクエストを送信して、JSON データを取得するフロントエンドのクライアントアプリを実装します。
バックエンドサービスを実装
前回の記事からご確認いただけます。
プロジェクトフォルダに移動します。
※ ~/tmp/hello-spring-dapr をプロジェクトフォルダとします。
$ cd ~/tmp/hello-spring-dapr
CORS への対応のため、アプリケーションクラスを修正します。
$ vim src/main/java/com/example/springdapr/Application.java
ファイルの内容
package com.example.springdapr;
import java.util.Map;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@SpringBootApplication
@RequestMapping("/api")
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
@CrossOrigin
@GetMapping("/data")
public Map<String, String> getData() {
Map<String, String> map = Map.of("message", "Hello World!");
return map;
}
}
Java アプリをビルドします。
$ mvn clean package
ここまでの手順で、target/app.jar Java アプリが作成されました。
コンテナイメージをビルド
Dockerfile を作成します。
$ vim Dockerfile
ファイルの内容
FROM adoptopenjdk/openjdk11:jdk-11.0.11_9-alpine-slim
WORKDIR /app
COPY target/*.jar app.jar
EXPOSE 8080
ENTRYPOINT ["java","-jar","app.jar"]
コンテナイメージをビルドします。
$ docker build -t app-hello-spring-boot .
確認します。
$ docker images | grep app-hello-spring-boot
app-hello-spring-boot latest 2ee39bcf1346 11 seconds ago 278MB
コンテナを起動する
バックエンドサービスのコンテナを実行します。
$ docker run --rm --name app-backend \
-p 8080:8080 app-hello-spring-boot
別ターミナルからリクエストします。
$ curl http://localhost:8080/api/data -w '\n'
出力
{"message":"Hello World!"}
ここまでの手順で、バックエンドのサービスを作成することができました。
フロントエンドアプリを実装
こちらの以前の記事をご参照いただけます。
プロジェクトフォルダの作成
プロジェクトフォルダを作成します。
※ ~/tmp/hello-react をプロジェクトフォルダとします。
$ mkdir -p ~/tmp/hello-react
$ cd ~/tmp/hello-react
JS ファイルの作成
index.js JS ファイルを作成します。
$ mkdir -p src
$ vim src/index.js
ファイルの内容
import React, { useState } from 'react';
import ReactDOM from 'react-dom';
const App = () => {
const [data, setData] = useState(null);
// ボタンがクリックされたときにデータを取得する関数
const fetchData = () => {
fetch('http://localhost:8080/api/data')
.then(response => response.json())
.then(data => {
setData(data); // レスポンスのデータを state にセット
})
.catch(error => {
console.error('Error fetching data:', error);
});
};
return (
<div>
{data ? (
<h1>{data.message}</h1>
) : (
<div>
<h2>Loading...</h2>
<button onClick={fetchData}>データを取得</button>
</div>
)}
</div>
);
};
ReactDOM.render(<App />, document.getElementById("app"));
HTML ファイルの作成
index.html ファイルを作成します。
$ vim src/index.html
ファイルの内容
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>React</title>
</head>
<body>
<div id="app"></div>
</body>
</html>
webpack 設定の作成
webpack.config.js ファイルを作成します。
$ vim webpack.config.js
ファイルの内容
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
mode: 'development',
entry: './src/index.js',
output: {
path: path.resolve(__dirname, 'build'),
filename: 'bundle.js',
},
module: {
rules: [
{
test: /\.(js|jsx)$/,
exclude: /node_modules/,
use: {
loader: 'babel-loader',
options: {
presets: ['@babel/preset-env', '@babel/preset-react'],
},
},
},
],
},
plugins: [
new HtmlWebpackPlugin({
template: './src/index.html',
filename: 'index.html',
})
]
};
プロジェクトの初期化
プロジェクトの初期化を行います。
$ npm init -y
package.json を修正します。
$ vim package.json
ファイルの内容
{
"name": "hello-react",
"version": "1.0.0",
"description": "",
"main": "webpack.config.js",
"scripts": {
"build": "webpack"
},
"keywords": [],
"author": "",
"license": "ISC"
}
ライブラリをインストールします。
$ npm install \
react react-dom \
babel-loader @babel/core @babel/preset-env @babel/preset-react \
webpack webpack-cli html-webpack-plugin \
--save-dev
コンテナイメージをビルド
Dockerfile を作成します。
$ vim Dockerfile
ファイルの内容
FROM node:lts-alpine as build-env
WORKDIR /app
COPY package*.json ./
RUN npm install
COPY . .
RUN npm run build
FROM nginx:alpine
COPY --from=build-env /app/build /usr/share/nginx/html
EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]
コンテナイメージをビルドします。
$ docker build -t app-hello-react .
確認します。
$ docker images | grep app-hello-react
app-hello-react latest e98d06760672 8 seconds ago 42.6MB
コンテナを起動する
フロントエンドアプリのコンテナを実行します。
$ docker run --rm --name app-frontend \
-p 80:80 app-hello-react
WEBアプリにアクセスします。
http://localhost
フロントエンドアプリからバックエンドサービスのデータを取得します。
ここまでの手順で、バックエンドサービス、フロントエンドアプリの各コンテナを直接連携させて動作させることができました。
Dapr を通してコンテナを連携動作させる
フロントエンドアプリの修正
$ mkdir -p src
$ vim src/index.js
ファイルの内容※一部分
// ボタンがクリックされたときにデータを取得する関数
const fetchData = () => {
fetch('http://localhost:3500/v1.0/invoke/app-hello-spring/method/api/data')
.then(response => response.json())
.then(data => {
setData(data); // レスポンスのデータを state にセット
})
.catch(error => {
console.error('Error fetching data:', error);
});
};
コンテナイメージを再ビルドします。
$ docker build -t app-hello-react .
docker-compose.yml の作成
docker-compose.yml ファイルを作成します。
$ vim docker-compose.yml
ファイルの内容
version: '3'
services:
############################
# React app
app-react:
image: app-hello-react
depends_on:
- redis
- placement
ports:
- "80:80"
networks:
- net-hello
############################
# Spring Boot app + Dapr sidecar
app-spring:
image: app-hello-spring-boot
depends_on:
- redis
- placement
ports:
- "3500:3500"
networks:
- net-hello
app-spring-dapr:
image: daprio/daprd:edge
command: [
"./daprd",
"--app-id", "app-hello-spring",
"--app-port", "8080",
"--dapr-http-port", "3500",
"--dapr-grpc-port", "50051",
"--placement-host-address", "placement:50006",
"--components-path", "/components"
]
volumes:
- "./components/:/components"
depends_on:
- app-spring
network_mode: "service:app-spring"
############################
# Dapr placement service
placement:
image: daprio/dapr
command: ["./placement", "-port", "50006"]
ports:
- "50006:50006"
networks:
- net-hello
############################
# Redis state store
redis:
image: redis:alpine
ports:
- "6380:6379"
networks:
- net-hello
networks:
net-hello:
Dapr を React、Spring Boot アプリのサイドカーとして設定しており、Spring Boot アプリのコンテナは 3500 番ポートでリッスンしています。
Docker 環境で起動する
コンテナを起動します。
$ docker-compose up -d
Creating hello-react_placement_1 ... done
Creating hello-react_redis_1 ... done
Creating hello-react_app-react_1 ... done
Creating hello-react_app-spring_1 ... done
Creating hello-react_app-spring-dapr_1 ... done
コンテナを確認します。
$ docker ps | grep hello
2ed6b9cdb78d daprio/daprd:edge "./daprd --app-id ap…" 47 seconds ago Up 47 seconds hell -react_app-spring-dapr_1
c9b60a6a1fe8 app-hello-react "/docker-entrypoint.…" 48 seconds ago Up 47 seconds 0.0.0.0:80->80/tcp, :::80->80/tcp hell -react_app-react_1
7c2c941d6bf5 app-hello-spring-boot "java -jar app.jar" 48 seconds ago Up 47 seconds 0.0.0.0:3500->3500/tcp, :::3500->3500/tcp, 8080/tcp hello-react_app-spring_1
d4a8f2c769ad redis:alpine "docker-entrypoint.s…" 49 seconds ago Up 47 seconds 0.0.0.0:6380->6379/tcp, :::6380->6379/tcp hell -react_redis_1
216a0d2c9efe daprio/dapr "./placement -port 5…" 49 seconds ago Up 47 seconds 0.0.0.0:50006->50006/tcp, :::50006->50006/tcp hell -react_placement_1
フロントエンドアプリからバックエンドサービスのデータを取得します。
ここまでの手順で、フロントエンドアプリからバックエンドサービスの Dapr サイドカーを通して連携動作させることができました。
まとめ
- Dapr、React、Spring Boot で構成する、シンプルなマイクロサービスを実装することが出来ました。
- この記事の例ではまだ Spring Boot 側のアプリに Dapr への依存関係はありません。
- 今後さらに Spring Boot から Dapr クライアントオブジェクトを操作する方法を学ぶ必要があります。
どうでしたか? WSL Ubuntu で Dapr を使用したマイクロサービス開発環境が手軽に構築できます。ぜひお試しください。今後も Ubuntu の開発環境などを紹介していきますので、ぜひお楽しみにしてください。
推奨コンテンツ