Chuyển đến nội dung chính

レッスン 24: CI/CD、クラウド展開および運用のベスト プラクティス

GitHub アクション CI/CD パイプライン。 Kubernetes を使用して AWS/GCP にデプロイします。 Flyway を使用したデータベース移行。ダウンタイムゼロの導入。制作チェックリストとベストプラクティス。

💻 プログラミング — レッスン 23 レッスン 24: CI/CD、クラウド展開、 本番環境のベストプラクティス

Spring Boot 4: 基本から上級まで

パート 6: マイクロサービスとプロダクション

xdev.asia

はじめに

シリーズの最後の記事では、Spring Boot アプリケーションを運用環境に移行するために必要なすべての内容 (CI/CD パイプライン、データベース移行、Kubernetes デプロイ、ダウンタイムなしの更新、運用チェックリスト) をまとめています。


1. Flyway を使用したデータベースの移行

1.1 セットアップ

// build.gradle.kts
implementation("org.flywaydb:flyway-core")
implementation("org.flywaydb:flyway-database-postgresql")
# application.yml
spring:
  flyway:
    enabled: true
    locations: classpath:db/migration
    baseline-on-migrate: true

1.2 移行ファイル

src/main/resources/db/migration/
├── V1__create_users_table.sql
├── V2__create_products_table.sql
├── V3__create_orders_table.sql
└── V4__add_email_index_to_users.sql
-- V1__create_users_table.sql
CREATE TABLE users (
    id          BIGSERIAL PRIMARY KEY,
    username    VARCHAR(50)  NOT NULL UNIQUE,
    email       VARCHAR(255) NOT NULL UNIQUE,
    password    VARCHAR(255) NOT NULL,
    role        VARCHAR(20)  NOT NULL DEFAULT 'USER',
    created_at  TIMESTAMP    NOT NULL DEFAULT NOW(),
    updated_at  TIMESTAMP    NOT NULL DEFAULT NOW()
);

CREATE INDEX idx_users_email ON users(email);
-- V4__add_email_index_to_users.sql
CREATE INDEX CONCURRENTLY IF NOT EXISTS idx_users_username
    ON users(username);

ルール: すでに実行中の移行ファイルを編集しないでください。新しい移行を作成してスキーマを変更します。


2. GitHub アクションを使用した CI/CD

2.1 パイプラインのビルドとテスト

# .github/workflows/ci.yml
name: CI Pipeline

on:
  push:
    branches: [main, develop]
  pull_request:
    branches: [main]

jobs:
  test:
    runs-on: ubuntu-latest
    services:
      postgres:
        image: postgres:17-alpine
        env:
          POSTGRES_DB: testdb
          POSTGRES_USER: test
          POSTGRES_PASSWORD: test
        ports:
          - 5432:5432
        options: >-
          --health-cmd pg_isready
          --health-interval 10s
          --health-timeout 5s
          --health-retries 5

    steps:
      - uses: actions/checkout@v4

      - uses: actions/setup-java@v4
        with:
          distribution: temurin
          java-version: 21

      - uses: gradle/actions/setup-gradle@v4

      - name: Run tests
        run: ./gradlew test
        env:
          SPRING_DATASOURCE_URL: jdbc:postgresql://localhost:5432/testdb
          SPRING_DATASOURCE_USERNAME: test
          SPRING_DATASOURCE_PASSWORD: test

      - name: Upload test report
        if: always()
        uses: actions/upload-artifact@v4
        with:
          name: test-report
          path: build/reports/tests/

  build:
    needs: test
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - uses: actions/setup-java@v4
        with:
          distribution: temurin
          java-version: 21

      - name: Build Docker image
        run: ./gradlew bootBuildImage --imageName=ghcr.io/${{ github.repository }}:${{ github.sha }}

      - name: Push to Container Registry
        run: |
          echo "${{ secrets.GITHUB_TOKEN }}" | docker login ghcr.io -u ${{ github.actor }} --password-stdin
          docker push ghcr.io/${{ github.repository }}:${{ github.sha }}

3. Kubernetes のデプロイメント

3.1 デプロイメントマニフェスト

# k8s/deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: myapp
  labels:
    app: myapp
spec:
  replicas: 3
  selector:
    matchLabels:
      app: myapp
  strategy:
    type: RollingUpdate
    rollingUpdate:
      maxSurge: 1
      maxUnavailable: 0  # Zero-downtime
  template:
    metadata:
      labels:
        app: myapp
    spec:
      containers:
        - name: myapp
          image: ghcr.io/myorg/myapp:latest
          ports:
            - containerPort: 8080
          env:
            - name: SPRING_PROFILES_ACTIVE
              value: production
            - name: SPRING_DATASOURCE_URL
              valueFrom:
                secretKeyRef:
                  name: db-credentials
                  key: url
          resources:
            requests:
              memory: "256Mi"
              cpu: "250m"
            limits:
              memory: "512Mi"
              cpu: "500m"
          readinessProbe:
            httpGet:
              path: /actuator/health/readiness
              port: 8080
            initialDelaySeconds: 15
            periodSeconds: 5
          livenessProbe:
            httpGet:
              path: /actuator/health/liveness
              port: 8080
            initialDelaySeconds: 30
            periodSeconds: 10
          startupProbe:
            httpGet:
              path: /actuator/health
              port: 8080
            failureThreshold: 30
            periodSeconds: 2

3.2 サービスとイングレス

# k8s/service.yaml
apiVersion: v1
kind: Service
metadata:
  name: myapp
spec:
  selector:
    app: myapp
  ports:
    - port: 80
      targetPort: 8080
  type: ClusterIP

---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: myapp
  annotations:
    cert-manager.io/cluster-issuer: letsencrypt
spec:
  tls:
    - hosts:
        - api.example.com
      secretName: myapp-tls
  rules:
    - host: api.example.com
      http:
        paths:
          - path: /
            pathType: Prefix
            backend:
              service:
                name: myapp
                port:
                  number: 80

3.3 Spring Boot での Kubernetes プローブ

# application-production.yml
management:
  endpoint:
    health:
      probes:
        enabled: true
  health:
    readinessstate:
      enabled: true
    livenessstate:
      enabled: true

4. ダウンタイムゼロの導入

4.1 正常なシャットダウン

# application.yml
server:
  shutdown: graceful

spring:
  lifecycle:
    timeout-per-shutdown-phase: 30s

4.2 ローリングアップデート戦略

Replica 1 (v1) ─── Running ───────────────────►
Replica 2 (v1) ─── Running ───────────────────►
Replica 3 (v1) ─── Running ─── Terminating ──►

Replica 4 (v2) ──── Starting ─── Ready ──────►
Replica 5 (v2) ─────── Starting ─── Ready ──►
Replica 6 (v2) ──────────── Starting ─ Ready►

5. 製造チェックリスト

5.1 構成

# application-production.yml
spring:
  jpa:
    open-in-view: false        # Tắt OSIV
    show-sql: false             # Không log SQL
    hibernate:
      ddl-auto: validate        # Chỉ validate, không auto-create

  jackson:
    default-property-inclusion: non_null

server:
  error:
    include-stacktrace: never   # Không expose stacktrace
    include-message: never

logging:
  level:
    root: WARN
    com.example: INFO

5.2 セキュリティチェックリスト

アイテムステータス
HTTPS のみ (TLS)☐
セキュリティヘッダー (HSTS、CSP)☐
有効期限が短い JWT (15 ~ 30 分)☐
レート制限☐
すべてのエンドポイントの入力検証☐
SQL インジェクション保護 (JPA)☐
env vars/vault のシークレット☐
アクチュエータのエンドポイントが保護されています☐

5.3 パフォーマンスチェックリスト

アイテムステータス
コネクションプーリング(HikariCP)☐
クエリ用のデータベース インデックス☐
キャッシュ (カフェイン/Redis)☐
読み取りクエリの場合は @Transactional(readOnly=true)☐
リストエンドポイントのページネーション☐
重いタスクの非同期処理☐
GZip圧縮☐

5.4 モニタリング

# Grafana Dashboard essentials
Metrics to monitor:
  - JVM Memory (Heap/Non-Heap)
  - GC Pause time
  - HTTP request rate & latency (p50, p95, p99)
  - Error rate (4xx, 5xx)
  - Database connection pool usage
  - Thread pool utilization
  - Custom business metrics

6. シリーズの概要

Bạn đã học:

Phần 1: Nền tảng Spring Boot
  └── Spring IoC, Auto-Configuration, DI, Bean Lifecycle

Phần 2: Xây dựng REST API
  └── Controller, DTO, Validation, JPA, Specification

Phần 3: Bảo mật ứng dụng
  └── Spring Security, JWT, OAuth2, Method Security

Phần 4: Tính năng nâng cao
  └── Transaction, Cache, Async, Events, WebSocket, OpenAPI

Phần 5: Testing & Chất lượng
  └── JUnit 5, Mockito, Testcontainers, Actuator, Observability

Phần 6: Microservices & Production
  └── Docker, Kubernetes, Kafka/RabbitMQ, CI/CD, Production Best Practices

概要

  • Flyway がデータベース移行を管理: スキーマのバージョン管理。すでに実行された移行は決して編集しない
  • GitHub Actions CI/CD: テスト → Docker イメージのビルド → レジストリへのプッシュ → Kubernetes へのデプロイ
  • Kubernetes: readiness/liveness プローブを使用したローリング アップデート、ダウンタイムのない展開のための正常なシャットダウン
  • 運用チェックリスト: セキュリティ ヘッダー、接続プーリング、キャッシュ、モニタリング - アプリケーションが運用準備ができていることを確認します。

演習

  1. Flyway のセットアップ: プロジェクト内のすべてのエンティティの移行ファイルを作成し、テストします。 ./gradlew flywayMigrate
  2. GitHub アクション CI/CD を作成する: PostgreSQL サービスでテストを実行し、Docker イメージを構築し、GHCR にプッシュします
  3. Kubernetes マニフェストを作成します: デプロイ (3 つのレプリカ)、サービス、イングレス。プローブと正常なシャットダウンを構成します。ローリングアップデートをテストする