Contents
マイクロサービス導入判断と Spring Boot を選ぶ理由
Spring Boot マイクロサービス 構築 手順に悩む中級以上の開発者向けの実践ガイドです。
最小構成のサンプルをローカルで立ち上げます。段階的にコンテナ化して Kubernetes へ展開します。
コード・設定・K8s マニフェストを豊富に掲載します。監視や CI/CD まで含めて解説します。
Spring Boot マイクロサービス 構築 手順を通じて Dev から Prod への移行を学べます。
マイクロサービスの特徴と導入判断チェックリスト
マイクロサービスは独立デプロイやチーム単位の所有を可能にします。利点と欠点を把握し、導入基準で判断してください。
- 利点
- サービスごとの独立スケールが可能です。
- チーム単位で技術選択とリリースが行えます。
- 障害を局所化しやすくなります。
- 欠点・コスト
- 運用・監視のオーバーヘッドが増えます。
- データ整合性やトランザクション設計が難しくなります。
- ネットワーク経由の遅延と複雑なデバッグが発生します。
導入判断チェックリスト(分割の基準)
- ビジネスドメイン/バウンデッドコンテキストで独立可能か。
- スケーリングがサービス単位で必要か。
- チーム境界が明確で、独立したデリバリが望めるか。
- データの結合度(強結合なら単一サービスが有利)。
- 運用コスト(監視・CI/CD・運用人員)を受容できるか。
- 可観測性(ログ・メトリクス・トレース)やテスト戦略を整えられるか。
小さく始め、必要に応じて分割する方針が実務的です。最初から多数の小サービスに分けると運用負荷が先行します。
Spring Boot を選ぶ理由(クラウドネイティブ、リアクティブ対応)
Spring Boot はスタータ依存と設定のオピニオンを提供します。運用用のエンドポイントや Micrometer 統合が標準で用意されます。
WebFlux によるリアクティブ対応や Spring Cloud エコシステムとの親和性が高い点も利点です。最新の Spring Boot 3 系は Java 17 以上を前提とします。
結果として、迅速なプロトタイプ作成と本番運用時の観測性確保が容易になるため、マイクロサービスの実務導入に適しています。
事前準備とプロジェクト作成(ローカル → コード)
ここでは開発・検証に必要なツールとプロジェクトの初期作成手順を示します。ローカル検証からコードの基本実装までをカバーします。
推奨環境とツールチェーン
まずは環境を揃えます。ローカルと CI の両方で互換性を保ってください。
推奨例
- Java: 17 または 21(組織の LTS ポリシーに従う)
- ビルド: Maven 3.8+ または Gradle 7+
- Git: 最新安定版
- Docker: 20.10+
- Kubernetes CLI: kubectl(クラスタに合わせたバージョン)
- Helm: 3+
- ローカル K8s: kind / k3d / minikube のいずれか
検証コマンド例
|
1 2 3 4 5 6 |
java -version mvn -v docker -v kubectl version --client helm version --short |
Spring Initializr での依存選定と基本実装
初期テンプレートは start.spring.io を使います。プロジェクトタイプは Maven または Gradle、Java バージョンは 17 以上を選択します。推奨依存は下記です。
推奨依存
- Spring Web(または WebFlux)
- Spring Data JPA
- PostgreSQL / MySQL ドライバ
- Flyway または Liquibase
- Spring Security(OAuth2 Resource Server を含む)
- Spring Boot Actuator
- Micrometer Registry Prometheus
- Resilience4j(回復性)
- Kafka / RabbitMQ(非同期必要時)
- Spring Cloud Gateway(API ゲートウェイが必要な場合)
プロジェクト直後のディレクトリ構成例
- src/main/java/com/example/items/controller
- src/main/java/com/example/items/service
- src/main/java/com/example/items/repository
- src/main/java/com/example/items/model
- src/main/java/com/example/items/dto
- src/main/java/com/example/items/config
- src/main/resources/db/migration(Flyway)
基本的なコントローラ/サービス/リポジトリの雛形(最小例)
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
// src/main/java/com/example/items/controller/ItemController.java package com.example.items.controller; import com.example.items.dto.ItemDto; import com.example.items.service.ItemService; import jakarta.validation.Valid; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; @RestController @RequestMapping("/api/items") public class ItemController { private final ItemService service; public ItemController(ItemService service) { this.service = service; } @PostMapping public ResponseEntity<ItemDto> create(@Valid @RequestBody ItemDto dto) { var created = service.create(dto); return ResponseEntity.status(201).body(created); } @GetMapping("/{id}") public ResponseEntity<ItemDto> get(@PathVariable Long id) { return service.find(id).map(ResponseEntity::ok).orElseGet(() -> ResponseEntity.notFound().build()); } } |
|
1 2 3 4 5 6 7 8 |
// src/main/java/com/example/items/repository/ItemRepository.java package com.example.items.repository; import com.example.items.model.Item; import org.springframework.data.jpa.repository.JpaRepository; public interface ItemRepository extends JpaRepository<Item, Long> {} |
DTO とバリデーション例
|
1 2 3 4 5 6 7 |
// src/main/java/com/example/items/dto/ItemDto.java package com.example.items.dto; import jakarta.validation.constraints.NotBlank; public record ItemDto(Long id, @NotBlank String name) {} |
共通例外処理の例(簡易)
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
// src/main/java/com/example/items/exception/GlobalExceptionHandler.java package com.example.items.exception; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; import org.springframework.web.bind.MethodArgumentNotValidException; @RestControllerAdvice public class GlobalExceptionHandler { @ExceptionHandler(MethodArgumentNotValidException.class) public ResponseEntity<String> onValidation(MethodArgumentNotValidException ex) { return ResponseEntity.badRequest().body("validation error"); } } |
ローカル起動
- mvn spring-boot:run
- または mvn -DskipTests package && java -jar target/your-app.jar
期待されるエンドポイント確認は curl やブラウザで /actuator/health や /api/items を叩いて確認します。
永続化、設定管理、サービス間通信と API 設計
ここではデータ永続化、マイグレーション、外部設定、サービス間通信、API ゲートウェイ設計の実務的な取り扱いを示します。
データ層とスキーママイグレーション(JPA+Flyway/Liquibase)
JPA エンティティは jakarta.persistence パッケージを使います。トランザクションはサービス層で管理します。マッピングは MapStruct などで簡潔にできます。
エンティティ例
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
// src/main/java/com/example/items/model/Item.java package com.example.items.model; import jakarta.persistence.*; import java.time.Instant; @Entity @Table(name = "items") public class Item { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; @Column(nullable = false) private String name; private Instant createdAt = Instant.now(); // getters / setters } |
Flyway の初期マイグレーション例(src/main/resources/db/migration/V1__create_items.sql)
|
1 2 3 4 5 6 |
CREATE TABLE items ( id BIGSERIAL PRIMARY KEY, name VARCHAR(255) NOT NULL, created_at TIMESTAMP WITH TIME ZONE DEFAULT now() ); |
application.yml に Flyway 有効化を追加
|
1 2 3 4 5 6 7 8 |
spring: flyway: enabled: true locations: classpath:db/migration jpa: hibernate: ddl-auto: validate |
テスト時は Testcontainers を使い、実運用に近い DB で検証します。テスト例(JUnit 5 + Testcontainers):
|
1 2 3 4 5 6 7 8 9 10 |
@Container static PostgreSQLContainer<?> postgres = new PostgreSQLContainer<>("postgres:15-alpine"); @DynamicPropertySource static void props(DynamicPropertyRegistry r) { r.add("spring.datasource.url", postgres::getJdbcUrl); r.add("spring.datasource.username", postgres::getUsername); r.add("spring.datasource.password", postgres::getPassword); } |
設定とシークレット管理、サービス間通信とゲートウェイ設計
設定管理は次の階層で使い分けます。
- 環境変数 / System プロパティ: デプロイ環境で取り扱う基本
- application-
.yml: 環境ごとの差分 - Spring Cloud Config: 中央管理が必要な場合
- Vault(HashiCorp): 機密情報管理
- Kubernetes Secret: K8s ネイティブだが base64 のみ。KMS / Vault と併用を推奨
例: application-prod.yml から環境変数を参照するパターン
|
1 2 3 4 5 6 |
spring: datasource: url: ${DB_URL:jdbc:postgresql://localhost:5432/demo} username: ${DB_USER:postgres} password: ${DB_PASS:postgres} |
サービス間通信の比較と推奨
- 同期(HTTP): WebClient(非ブロッキング)または OpenFeign(宣言的)。WebClient を基本推奨。
- 非同期(メッセージ): Kafka(イベントストリーミング向け)/RabbitMQ(メッセージング向け)。イベントスキーマ管理と冪等性設計が重要。
- トランザクション整合: 2フェーズコミットは避け、アウトボックスパターンや補償トランザクションを用いる。
WebClient の簡単な例
|
1 2 3 4 5 6 7 8 9 10 11 12 |
@Bean public WebClient webClient(WebClient.Builder b) { return b.build(); } public Mono<String> callOther(WebClient client) { return client.get().uri("http://other/api") .retrieve() .bodyToMono(String.class) .timeout(Duration.ofSeconds(2)); } |
API ゲートウェイ(Spring Cloud Gateway)簡易ルーティング例(application.yml)
|
1 2 3 4 5 6 7 8 9 |
spring: cloud: gateway: routes: - id: items uri: lb://ITEM-SERVICE predicates: - Path=/api/items/** |
実運用は Envoy / Kong / Istio などのエッジプロキシも検討してください。機能や運用性で選定します。
コンテナ化と Kubernetes デプロイ、観測性・回復性・セキュリティ
ここでは Dockerfile、Kubernetes マニフェスト、観測性、回復性、セキュリティ設計の実用例を示します。
Docker 化と Kubernetes マニフェスト(プローブ、HPA、リリース戦略)
マルチステージ Dockerfile(Maven ビルド)
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
# build stage FROM maven:3.8-eclipse-temurin-17 AS build WORKDIR /workspace COPY pom.xml . RUN mvn -B -DskipTests dependency:go-offline COPY src ./src RUN mvn -B -DskipTests package # runtime stage FROM eclipse-temurin:17-jre RUN addgroup --system app && adduser --system --ingroup app app USER app COPY --from=build /workspace/target/app.jar /app/app.jar EXPOSE 8080 ENTRYPOINT ["java","-Xms256m","-Xmx512m","-jar","/app/app.jar"] |
ビルド・プッシュ例(概念)
|
1 2 3 4 |
mvn -DskipTests package docker build -t my-registry/item-service:0.1.0 . docker push my-registry/item-service:0.1.0 |
Kubernetes Deployment(抜粋)
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 |
apiVersion: apps/v1 kind: Deployment metadata: name: item-service spec: replicas: 2 selector: matchLabels: app: item-service template: metadata: labels: app: item-service spec: containers: - name: item image: my-registry/item-service:0.1.0 ports: - containerPort: 8080 readinessProbe: httpGet: path: /actuator/health/readiness port: 8080 initialDelaySeconds: 10 periodSeconds: 10 livenessProbe: httpGet: path: /actuator/health/liveness port: 8080 initialDelaySeconds: 30 resources: requests: cpu: "200m" memory: "256Mi" limits: cpu: "1" memory: "1Gi" |
HorizontalPodAutoscaler(CPU 利用率に基づく例)
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
apiVersion: autoscaling/v2 kind: HorizontalPodAutoscaler metadata: name: item-service-hpa spec: scaleTargetRef: apiVersion: apps/v1 kind: Deployment name: item-service minReplicas: 2 maxReplicas: 10 metrics: - type: Resource resource: name: cpu target: type: Utilization averageUtilization: 70 |
リリース戦略
- Blue-Green / Canary: Helm とラベル/トラフィック制御で実現可能です。
- より高度な制御が必要な場合は Argo Rollouts や Service Mesh の機能を検討します。
観測性(ログ/メトリクス/トレース)、回復性(Retry/Circuit Breaker)、セキュリティ
ログ
- 構造化ログ(JSON)を推奨します。Logback の JSON エンコーダを利用して、MDC に traceId/spanId を出力します。
メトリクス
- Micrometer と Prometheus を組み合わせます。Actuator の /actuator/prometheus を公開して Prometheus にスクレイプさせます。
application.yml の一部例
|
1 2 3 4 5 6 7 8 9 |
management: endpoints: web: exposure: include: health,info,prometheus endpoint: health: show-details: when-authorized |
分散トレーシング
- OpenTelemetry 自動インストルメンテーション(Java agent)と OTLP/Collector の組み合わせを推奨します。Jaeger をバックエンドにする構成が一般的です。トレースはログ・メトリクスと合わせて障害調査に使います。
回復性
- Resilience4j を用いて Circuit Breaker、Retry、RateLimiter を設定します。WebClient や Feign と組み合わせてタイムアウトやフォールバックを実装します。
Resilience4j 設定例(application.yml 抜粋)
|
1 2 3 4 5 6 7 8 9 10 11 |
resilience4j: circuitbreaker: configs: default: slidingWindowSize: 100 failureRateThreshold: 50 waitDurationInOpenState: 60s instances: remoteService: baseConfig: default |
セキュリティ
- リソースサーバは OAuth2/OIDC を推奨します。JWT の公開鍵検証は JWKS を利用してください。機密情報は環境変数や Vault/K8s Secret で管理し、コードにハードコーディングしないでください。CSRF を本番で無効にしないなど安全なデフォルトを守ります。Pod SecurityContext や NetworkPolicy によるクラスタ側の防御も重要です。
認証サンプル(SecurityFilterChain)
|
1 2 3 4 5 6 7 8 9 10 11 |
@Bean public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { http .authorizeHttpRequests(auth -> auth .requestMatchers("/actuator/health").permitAll() .anyRequest().authenticated() ) .oauth2ResourceServer(oauth2 -> oauth2.jwt()); return http.build(); } |
CI/CD、テスト、運用(SRE)、サンプルリポジトリとトラブルシューティング
最後に、品質保証・継続的デリバリ・運用体制と、よくある障害と対処法を示します。CI から運用までの実務フローを再現してください。
CI/CD とテスト戦略(GitHub Actions の例、Testcontainers、契約テスト)
テスト戦略
- ユニットテスト: 依存モックで高速化
- スライステスト: @WebMvcTest などでコントローラ単位検証
- データ層: @DataJpaTest
- 統合テスト: @SpringBootTest + Testcontainers(DB やメッセージングを実コンポーネントで検証)
- 契約テスト: Pact / Spring Cloud Contract を利用してサービス間インタフェースを保証
Testcontainers の動的プロパティ登録例は前節参照。CI ではコンテナベースの統合テストを実行します。
GitHub Actions のワークフロー(抜粋)
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
name: CI on: [push, pull_request] jobs: build: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: actions/setup-java@v4 with: distribution: temurin java-version: 17 - name: Cache Maven uses: actions/cache@v4 with: path: ~/.m2/repository key: ${{ runner.os }}-m2-${{ hashFiles('**/pom.xml') }} - name: Build and test run: mvn -B verify - name: Build and push image uses: docker/build-push-action@v4 with: push: true tags: my-registry/item-service:${{ github.sha }} |
レジストリ認証は GitHub OIDC を使うとシークレット管理を簡素化できます。CI のシークレットは最小権限で管理してください。
運用・SRE(SLO/SLI、アラート、障害対応)、サンプル構成とリポジトリ構成
SLO / SLI の例
- レイテンシ: P95 < 200ms
- エラー率: 5xx < 0.1%
- 可用性: 月間稼働率 99.9%
アラート設計の方針
- SLI の閾値超過でアラート
- ノイズ抑制のため短期のバーストに対するサプレッションを設定
- Prometheus Alertmanager でルーティングと抑制を行う
障害対応の基本フロー
- 検知(アラート)
- 初動(影響範囲特定、トリアージ)
- 一時対応(スケール、ロールバック、ルーティング切替)
- 根本原因調査(ログ・メトリクス・トレース)
- 永続対策とポストモーテム
ランブック(簡易)
- まずログを確認(kubectl logs / Pod)
- 最新のトレースを確認(Traces)
- DB マイグレーションのステータスを確認(Flyway info)
- 直近のデプロイ履歴を確認(CI/CD の履歴)
- 必要ならスケールやロールバックを実施
サンプルリポジトリに含める推奨ファイル(例)
- src/**: アプリ本体コード
- Dockerfile: マルチステージビルド
- src/main/resources/application.yml, application-prod.yml
- src/main/resources/db/migration/V1__*.sql
- k8s/: Deployment/Service/Ingress/values.yaml
- charts/: Helm チャート(テンプレート化)
- .github/workflows/ci.yml
- scripts/quickstart.sh: ローカル起動やテスト用のスクリプト
- docs/README.md: 主要手順と運用手順
よくあるトラブルと対処法
- 起動失敗: ログで Flyway エラーや環境変数不足を確認。環境変数が読み込まれているか確認する。
- DB 接続タイムアウト: ホスト名/ポート/ネットワークポリシーを確認。Pod から DB へ直接接続テストを行う。
- マイグレーション不整合: Flyway の履歴表を照合し、手動で修正するかダウングレード手順を用意する。
- OOM / GC 問題: コンテナの requests/limits と JVM ヒープ設定を見直す。JVM オプションで GC ログを有効化して分析する。
- スレッド枯渇: ブロッキング呼び出しを非同期に変更するか、スレッドプールを調整する。
参考にする公式リソース(名称のみ)
- Spring Guides(spring.io)
- Spring Framework / Spring Boot リポジトリ(GitHub)
- Micrometer ドキュメント
- OpenTelemetry ドキュメント
- Resilience4j ドキュメント
- Testcontainers ドキュメント
- Docker / Kubernetes 公式ドキュメント
まとめ
- マイクロサービスは利点が大きいが運用コストも増えるため、ビジネスドメインやチーム境界で分割基準を判断する。
- Spring Boot(3 系、Java 17+)は運用性(Actuator、Micrometer)とエコシステムの点で実務向けの選択肢となる。
- ローカル→コンテナ化→Kubernetes の流れを段階的に検証し、観測性・回復性・セキュリティを設計に組み込むことが重要。
- CI/CD、テスト(Testcontainers、契約テスト)を導入して品質を担保し、SLO/SLI ベースで運用する。
- サンプル構成(Dockerfile、application.yml、k8s マニフェスト、Helm、CI ワークフロー)をテンプレート化して再利用できるようにする。
参考資料は公式ドキュメントを優先して参照してください。