Contents
1. プロジェクトの作成と依存関係
1‑1. Spring Initializr の利用
Spring Boot 3 系は Jakarta EE 9 に対応し、標準 JDK は Java 17 LTS(以降「最新安定版」)です。
以下の手順でプロジェクトを生成します。
|
1 2 3 4 5 6 7 |
curl https://start.spring.io/starter.tgz \ -d dependencies=web,data-jpa,actuator,lombok \ -d javaVersion=17 \ -d bootVersion=$(curl -s https://api.spring.io/projects/spring-boot/versions | jq -r '.[0]') \ -d packaging=jar \ -d name=order-service | tar -xzvf - |
bootVersion は Spring の公式 API (https://api.spring.io) から取得することで、常に最新の安定版を使用できます。
1‑2. ビルド設定(Gradle Kotlin DSL)
生成された build.gradle.kts に自動で以下が追加されます。
|
1 2 3 4 5 6 7 8 9 10 11 12 |
plugins { id("org.springframework.boot") version "3.+" kotlin("jvm") version "1.9+" } dependencies { implementation("org.springframework.boot:spring-boot-starter-web") implementation("org.springframework.boot:spring-boot-starter-data-jpa") implementation("org.springframework.boot:spring-boot-starter-actuator") compileOnly("org.projectlombok:lombok") annotationProcessor("org.projectlombok:lombok") } |
ポイント
- バージョンは3.+、1.9+のように「キャレット」表記で記載し、将来のリリースでも自動的に解決させます。
- 公式ドキュメント: https://spring.io/projects/spring-boot#overview
2. REST API 実装のベストプラクティス
2‑1. レイヤードアーキテクチャと DDD の基本方針
| 層 | 主な責務 |
|---|---|
| Domain | エンティティ / 集約 (ビジネスロジック) |
| Application | ユースケース・サービス (トランザクション境界) |
| Infrastructure | リポジトリ実装、外部 API クライアント等 |
この構造により「層間の依存が一方向」になるため、テスト容易性と置き換え可能性が向上します。
2‑2. サンプルコード(Order マイクロサービス)
|
1 2 3 4 5 6 7 8 9 10 11 12 |
// src/main/java/com/example/order/domain/Order.java @Entity @Table(name = "orders") public class Order { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; private String productCode; private Integer quantity; // Lombok を使う場合は @Getter/@Setter/@NoArgsConstructor でも可 } |
|
1 2 3 |
// src/main/java/com/example/order/repository/OrderRepository.java public interface OrderRepository extends JpaRepository<Order, Long> {} |
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
// src/main/java/com/example/order/service/OrderService.java @Service @RequiredArgsConstructor public class OrderService { private final OrderRepository repo; @Transactional public Order create(OrderDto dto) { var order = new Order(); order.setProductCode(dto.getProductCode()); order.setQuantity(dto.getQuantity()); return repo.save(order); } @Transactional(readOnly = true) public List<Order> list() { return repo.findAll(); } } |
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
// src/main/java/com/example/order/controller/OrderController.java @RestController @RequestMapping("/api/orders") @RequiredArgsConstructor public class OrderController { private final OrderService service; @PostMapping public ResponseEntity<Order> create(@RequestBody @Valid OrderDto dto) { return ResponseEntity.status(HttpStatus.CREATED).body(service.create(dto)); } @GetMapping public List<Order> list() { return service.list(); } } |
まとめ
-Orderは集約のルートとして、外部からは必ずOrderServiceを経由して操作します。
-@Transactionalによりビジネスロジックがデータ層に依存しない形で実装できます。
3. Docker イメージの最適化
3‑1. マルチステージビルド(公式例)
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
# ---------- Build stage ---------- FROM eclipse-temurin:17-jdk-alpine AS build WORKDIR /src COPY gradlew . COPY gradle ./gradle COPY build.gradle.kts settings.gradle.kts ./ COPY src ./src RUN ./gradlew bootJar --no-daemon # ---------- Runtime stage ---------- # Distroless Java 17 (non‑root) の公式イメージ FROM gcr.io/distroless/java17:nonroot@sha256:8e5b3c... WORKDIR /app COPY --from=build /src/build/libs/*.jar app.jar EXPOSE 8080 ENTRYPOINT ["java","-jar","/app/app.jar"] |
- Distroless の公式リファレンス: https://github.com/GoogleContainerTools/distroless/tree/main/java
nonrootタグはデフォルトで UID 1000(非 root)として実行されます。
3‑2. イメージサイズとセキュリティのチップス
| 項目 | 推奨設定 |
|---|---|
| 不要ファイル削除 | RUN rm -rf /src/src/test && apk del ...(Alpine の場合) |
| ユーザー権限 | 既に non‑root イメージを使用しているので追加設定は不要。ただし独自ベースイメージの場合は USER 1000:1000 を明示。 |
| 脆弱性スキャン | GitHub Actions → aquasecurity/trivy-action(公式ドキュメント: https://github.com/aquasecurity/trivy-action) |
4. Kubernetes マニフェストと共通設定
4‑1. 基本リソース(Deployment・Service・Ingress)
|
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 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 |
apiVersion: apps/v1 kind: Deployment metadata: name: order-service spec: replicas: 3 selector: matchLabels: app: order-service template: metadata: labels: app: order-service spec: containers: - name: order-service image: <registry>/order-service:{{VERSION}} ports: - containerPort: 8080 envFrom: - configMapRef: name: order-config - secretRef: name: order-secret resources: limits: cpu: "500m" memory: "512Mi" requests: cpu: "250m" memory: "256Mi" livenessProbe: httpGet: path: /actuator/health port: 8080 initialDelaySeconds: 30 periodSeconds: 15 --- apiVersion: v1 kind: Service metadata: name: order-service spec: selector: app: order-service ports: - protocol: TCP port: 80 targetPort: 8080 type: ClusterIP --- apiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: order-ingress annotations: # AWS (ALB) 用アノテーション例 alb.ingress.kubernetes.io/scheme: internet-facing # Azure Application Gateway 用アノテーション例(同一マニフェストで切り替え可能) azure.application-gateway.ingress.kubernetes.io/backend-path-prefix: "/" spec: rules: - http: paths: - path: /orders pathType: Prefix backend: service: name: order-service port: number: 80 |
ポイント
-Deployment・Service・Ingressのみで外部公開が完結し、クラウド固有の差分は アノテーション に集約。
- 公式ドキュメント:
* EKS Ingress (ALB) – https://docs.aws.amazon.com/eks/latest/userguide/alb-ingress.html
* Azure Application Gateway Ingress – https://learn.microsoft.com/azure/application-gateway/ingress-controller-overview
4‑2. ConfigMap と Secret の管理
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
apiVersion: v1 kind: ConfigMap metadata: name: order-config data: LOG_LEVEL: INFO SPRING_PROFILES_ACTIVE: prod --- apiVersion: v1 kind: Secret metadata: name: order-secret type: Opaque stringData: DB_URL: jdbc:postgresql://db.example.com:5432/orderdb DB_USERNAME: order_user DB_PASSWORD: <BASE64エンコード済みパスワード> |
- ベストプラクティス
- 機密情報は必ず
Secretに格納し、Pod の環境変数として注入 (envFrom)。 - ネットワークポリシーで名前空間外からのアクセスを制限(例:
NetworkPolicy)
5. AWS EKS と Azure Container Apps へのデプロイ手順
5‑1. 共通前提条件
| 項目 | 推奨設定 |
|---|---|
| Kubernetes バージョン | 各クラウドの「最新安定版」(2026‑04 時点で EKS は v1.30、Azure は AKS の同等バージョン) – 公式ページで随時確認 |
| コンテナレジストリ | Amazon ECR / Azure Container Registry (ACR)。イメージは同一タグでプッシュし、マニフェストは image: <registry>/order-service:${VERSION} と変数化 |
AWS EKS デプロイフロー
|
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 |
# 1. クラスター作成(eksctl が推奨) eksctl create cluster \ --name order-cluster \ --version $(aws eks describe-addon-versions --kubernetes-version 1.30 | jq -r '.addons[0].kubernetesVersion') \ --region ap-northeast-1 \ --nodegroup-name standard-workers \ --node-type t3.medium \ --nodes 2 \ --managed # 2. AWS Load Balancer Controller のインストール(公式手順) eksctl utils associate-iam-policy \ --cluster order-cluster \ --arn arn:aws:iam::aws:policy/ELBReadOnlyPolicy \ --region ap-northeast-1 \ --approve helm repo add eks https://aws.github.io/eks-charts helm install aws-load-balancer-controller eks/aws-load-balancer-controller \ -n kube-system \ --set clusterName=order-cluster \ --set serviceAccount.create=false \ --set serviceAccount.name=aws-load-balancer-controller # 3. マニフェスト適用 kubectl apply -f k8s/ |
公式ガイド: https://docs.aws.amazon.com/eks/latest/userguide/deploying-services.html
Azure Container Apps デプロイフロー
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
# 1. リソースグループ・環境作成 az group create -n rg-order -l japaneast az containerapp env create -g rg-order -n order-env --location japaneast # 2. ACR にイメージプッシュ(GitHub Actions 等で自動化可) az acr login -n myacr docker tag order-service:latest myacr.azurecr.io/order-service:${VERSION} docker push myacr.azurecr.io/order-service:${VERSION} # 3. Container App 作成(CLI 1 行で外部公開設定完了) az containerapp create \ -g rg-order \ -n order-app \ --environment order-env \ --image myacr.azurecr.io/order-service:${VERSION} \ --ingress external \ --target-port 8080 \ --cpu 0.5 --memory 1Gi \ --registry-server myacr.azurecr.io \ --env-vars SPRING_PROFILES_ACTIVE=prod |
公式ドキュメント: https://learn.microsoft.com/azure/container-apps/
中立的な比較
- 両者とも「Kubernetes の上に構築されたマネージドサービス」ですが、EKS は自前のクラスター管理が必要になる一方で、より高度なカスタマイズが可能です。Azure Container Apps はサーバーレス的に インフラ運用負荷が低く、短時間で外部公開できます。
6. CI/CD パイプラインと Observability の実装例
6‑1. 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 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 |
name: CI/CD for Order Service on: push: branches: [ main ] env: IMAGE_NAME: order-service VERSION: ${{ github.sha }} jobs: build-test: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - name: Setup JDK 17 uses: actions/setup-java@v3 with: java-version: '17' distribution: temurin - name: Build & Test run: | ./gradlew clean bootJar test integrationTest --no-daemon docker-build-push: needs: build-test runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - name: Docker Build run: docker build -t ${{ env.IMAGE_NAME }}:${{ env.VERSION }} . # ---------- ECR ---------- - name: Login to Amazon ECR if: github.ref == 'refs/heads/main' uses: aws-actions/amazon-ecr-login@v2 with: region: ap-northeast-1 - name: Push to ECR if: github.ref == 'refs/heads/main' run: | docker tag ${{ env.IMAGE_NAME }}:${{ env.VERSION }} ${{ secrets.AWS_ECR_REPO }}:${{ env.VERSION }} docker push ${{ secrets.AWS_ECR_REPO }}:${{ env.VERSION }} # ---------- ACR ---------- - name: Login to Azure Container Registry if: github.ref == 'refs/heads/main' run: | echo "${{ secrets.AZURE_ACR_PASSWORD }}" | docker login myacr.azurecr.io -u ${{ secrets.AZURE_ACR_USERNAME }} --password-stdin - name: Push to ACR if: github.ref == 'refs/head/main' run: | docker tag ${{ env.IMAGE_NAME }}:${{ env.VERSION }} myacr.azurecr.io/${{ env.IMAGE_NAME }}:${{ env.VERSION }} docker push myacr.azurecr.io/${{ env.IMAGE_NAME }}:${{ env.VERSION }} # ---------- Trivy スキャン ---------- - name: Scan image with Trivy uses: aquasecurity/trivy-action@0.24.0 with: image-ref: ${{ secrets.AWS_ECR_REPO }}:${{ env.VERSION }} format: table exit-code: '0' deploy: needs: docker-build-push runs-on: ubuntu-latest steps: # ---- EKS デプロイ ---- - name: Deploy to EKS if: github.ref == 'refs/heads/main' run: | aws eks update-kubeconfig --region ap-northeast-1 --name order-cluster kubectl set image deployment/order-service order-service=${{ secrets.AWS_ECR_REPO }}:${{ env.VERSION }} # ---- Azure Container Apps デプロイ ---- - name: Deploy to Azure Container Apps if: github.ref == 'refs/heads/main' run: | az login --service-principal -u ${{ secrets.AZURE_SP_ID }} -p ${{ secrets.AZURE_SP_PASSWORD }} --tenant ${{ secrets.AZURE_TENANT }} az containerapp update -g rg-order -n order-app --image myacr.azurecr.io/${{ env.IMAGE_NAME }}:${{ env.VERSION }} |
secretsに格納すべき情報は公式ドキュメントに従い、最小権限の IAM ロール/Service Principal を使用してください。
6‑2. Observability(Actuator + Prometheus/Grafana)
6‑2‑1. application.yml の設定
|
1 2 3 4 5 6 7 8 9 10 |
management: endpoints: web: exposure: include: health,info,prometheus metrics: export: prometheus: enabled: true |
6‑2‑2. Prometheus の ServiceMonitor(EKS)
|
1 2 3 4 5 6 7 8 9 10 11 12 13 |
apiVersion: monitoring.coreos.com/v1 kind: ServiceMonitor metadata: name: order-service-sm spec: selector: matchLabels: app: order-service endpoints: - port: http path: /actuator/prometheus interval: 30s |
Azure では Azure Monitor の scrapeConfiguration に同様のエンドポイントを追加すれば、同一メトリクスが取得できます。
6‑2‑3. Grafana ダッシュボードインポート
Grafana Hub にある「Spring Boot 3 Dashboard」 (ID: 11074) をインポートすると、CPU・GC・HTTP レイテンシなどが即座に可視化されます。
6‑3. テスト戦略(JUnit 5 + Testcontainers + Pact)
| 種類 | ツール | 主な利点 |
|---|---|---|
| ユニットテスト | JUnit 5 + MockMvc | コントローラ単体を高速に検証 |
| 統合テスト | Testcontainers (PostgreSQL) | 本番と同一 DB コンテナで実行、環境差異排除 |
| 契約テスト | Pact JVM | Consumer と Provider の API 互換性をコードベースで管理 |
結論
上記ツールチェーンにより、ローカル・CI 両方で 90 % 以上のカバレッジ を維持しつつ、リグレッションリスクを最小化できます。
7. 次に取るべきステップ
- ローカル検証
bash
docker compose up --build
curl http://localhost:8080/api/orders - マルチクラウドデプロイの確認
- EKS クラスターで
kubectl get ingress→ ALB の DNS が取得できるか。 -
Azure Container Apps でポータルまたは CLI から外部 URL が生成されているか。
-
CI/CD と Observability を本番環境に適用
- 本番向けに
resources.requests/limitsの調整、PodDisruptionBudgetの追加。 -
アラートポリシー(CPU > 80 % 連続5分)やログ集約(CloudWatch / Azure Monitor)を設定。
-
継続的なバージョン管理
- Spring Boot、Java、Kubernetes の各コンポーネントは「公式 API」または「リリースノート」から最新安定版を取得するスクリプト(例:
curl https://api.spring.io/...)で自動化し、ドキュメントのバージョン固定を避ける。
最終的なまとめ
- Spring Boot 3.x + Java 17 が現在の標準スタック。
- マルチステージ Docker と Distroless により サイズ 120 MB 前後、非 root 実行 を実現。
- Kubernetes の 共通マニフェスト とクラウド固有アノテーションで、AWS EKS と Azure Container Apps の両方に同一コードベースをデプロイ可能。
- GitHub Actions で ビルド・テスト・スキャン・マルチレジストリプッシュ・自動デプロイ を一括管理し、Trivy による脆弱性検知と Prometheus/Grafana による可観測性を確保。
- テストは JUnit 5 + Testcontainers + Pact の組み合わせで、品質保証を自動化。
これらの手順を踏めば、最新のクラウド基盤上で安全かつスケーラブルな Java マイクロサービスを短期間に構築・運用できるようになります。