はじめに
シリーズの最後の記事では、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 プローブを使用したローリング アップデート、ダウンタイムのない展開のための正常なシャットダウン
- 運用チェックリスト: セキュリティ ヘッダー、接続プーリング、キャッシュ、モニタリング - アプリケーションが運用準備ができていることを確認します。
演習
- Flyway のセットアップ: プロジェクト内のすべてのエンティティの移行ファイルを作成し、テストします。
./gradlew flywayMigrate - GitHub アクション CI/CD を作成する: PostgreSQL サービスでテストを実行し、Docker イメージを構築し、GHCR にプッシュします
- Kubernetes マニフェストを作成します: デプロイ (3 つのレプリカ)、サービス、イングレス。プローブと正常なシャットダウンを構成します。ローリングアップデートをテストする