DockerでコンテナイメージをビルドできたSpring Boot開発者が次に直面するのが、「KubernetesのManifestをどう書けばいいのか」という問題ですよね。特にヘルスチェックや機密設定の扱いはK8s特有のパターンがあります。
この記事ではDeployment・Service・ConfigMap・SecretのManifestを順を追って説明します。DockerイメージのビルドとpushについてはSpring BootアプリをDockerコンテナで動かす方法にまとめています。
前提条件
この記事では以下が完了していることを前提とします。
- Spring Boot 3.x アプリのDockerイメージをレジストリにpush済み
kubectlでK8sクラスターにアクセスできる状態
作成するリソースはDeployment・Service・ConfigMap・Secretの4つです。
Spring Boot側の準備
K8sのProbeに対応するため、spring-boot-starter-actuator が依存関係に入っているか確認してください。
<!-- pom.xml -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
// build.gradle
implementation 'org.springframework.boot:spring-boot-starter-actuator'
Actuatorの基本的な使い方ははじめてのSpring Boot Actuator入門で解説しています。
次に application.yml でKubernetes向けのProbeエンドポイントを有効化します。
management:
endpoint:
health:
probes:
enabled: true
endpoints:
web:
exposure:
include: health
management.endpoint.health.probes.enabled=true を設定すると、/actuator/health/liveness と /actuator/health/readiness が使えるようになります。なお、Spring Boot 3.xでは KUBERNETES_SERVICE_HOST 環境変数が設定されている場合(K8sクラスター上で実行中)はprobesが自動的に有効化されるため、この設定の明示は環境に応じて判断してもかまいません。
Deploymentマニフェストの作成
deployment.yaml を作成します。livenessProbeとreadinessProbeも一緒に設定してしまいましょう。
apiVersion: apps/v1
kind: Deployment
metadata:
name: myapp
spec:
replicas: 2
selector:
matchLabels:
app: myapp
template:
metadata:
labels:
app: myapp
spec:
# imagePullSecrets: [{name: regcred}] # プライベートレジストリの場合は追加
containers:
- name: myapp
image: your-registry/myapp:latest
ports:
- containerPort: 8080
resources:
requests:
cpu: "250m"
memory: "512Mi"
limits:
cpu: "500m"
memory: "1Gi"
envFrom:
- configMapRef:
name: myapp-config
- secretRef:
name: myapp-secret
livenessProbe:
httpGet:
path: /actuator/health/liveness
port: 8080
initialDelaySeconds: 60
periodSeconds: 10
readinessProbe:
httpGet:
path: /actuator/health/readiness
port: 8080
initialDelaySeconds: 30
periodSeconds: 5
livenessProbe はコンテナが「生きているか」を確認し、失敗するとPodが自動再起動されます。readinessProbe はトラフィックを受け付けられる状態かを確認し、失敗するとServiceのルーティング対象から外れます。
initialDelaySeconds はSpring Bootの起動時間に合わせて設定してください。短すぎるとアプリが起動しきる前にProbeが失敗してCrashLoopBackOffになります。起動に30秒かかるなら余裕を見て60秒程度が無難です。プライベートレジストリを使っている場合、imagePullSecrets のコメントアウトを外して認証情報を設定しないとImagePullBackOffになるので注意してください。
Serviceマニフェストの作成
apiVersion: v1
kind: Service
metadata:
name: myapp
spec:
selector:
app: myapp
ports:
- port: 80
targetPort: 8080
type: ClusterIP
type: ClusterIP はクラスター内部からのみアクセスできる設定で、デフォルトの推奨設定です。クラウド環境でインターネットに直接公開したい場合は LoadBalancer に変えると、クラウドプロバイダーのロードバランサーが作成されます。
ConfigMapで環境別プロパティを注入する
非機密の設定値はConfigMapで管理します。
apiVersion: v1
kind: ConfigMap
metadata:
name: myapp-config
data:
SPRING_PROFILES_ACTIVE: "production"
APP_EXTERNAL_API_URL: "https://api.example.com"
SPRING_PROFILES_ACTIVE を環境変数として渡すことで、application-production.yml が自動的に読み込まれます。Profileや外部プロパティの詳細はSpring Bootのプロパティ設定ガイドとSpring BootのProfileを使って環境別の設定を切り替える方法も参考にしてください。
SecretでDB接続情報を渡す
パスワードなど機密情報はSecretで管理します。
apiVersion: v1
kind: Secret
metadata:
name: myapp-secret
type: Opaque
stringData:
SPRING_DATASOURCE_URL: "jdbc:postgresql://db:5432/mydb"
SPRING_DATASOURCE_USERNAME: "appuser"
SPRING_DATASOURCE_PASSWORD: "your-secret-password"
stringData を使うと平文で記述できます。data フィールドを使う場合はBase64エンコードが必要です。
ひとつ重要な注意点があります。Kubernetes SecretはデフォルトではBase64エンコードされるだけで 暗号化はされません 。etcdにそのまま保存されるため、本番環境ではetcdの暗号化設定やExternal Secrets Operatorなどの外部シークレット管理ツールの導入を検討してください。ConfigMapとSecretの使い分けはシンプルで、外部に見せても問題ない設定はConfigMap、パスワードやAPIキーはSecretです。
kubectl applyで動作確認する
# 適用
kubectl apply -f configmap.yaml
kubectl apply -f secret.yaml
kubectl apply -f deployment.yaml
kubectl apply -f service.yaml
# 状態確認
kubectl get pods
kubectl describe pod <pod-name>
kubectl logs <pod-name>
# ローカルからアクセス確認
kubectl port-forward svc/myapp 8080:80
kubectl get pods でSTATUSが Running、READYが 2/2(replicasが2の場合)になれば成功です。
ConfigMapやSecretを更新した後は Podの再起動 が必要です。envFrom で読み込んだ環境変数は起動時にしか取り込まれないため、kubectl rollout restart deployment/myapp で反映させましょう。
まとめ
Spring BootアプリをKubernetesにデプロイするときのポイントをまとめます。
management.endpoint.health.probes.enabled=trueを設定する(K8s環境ではKUBERNETES_SERVICE_HOSTによって自動有効化される場合もある)- livenessProbeには
/actuator/health/liveness、readinessProbeには/actuator/health/readinessを接続する initialDelaySecondsはアプリの起動時間より余裕を持たせて設定する- 非機密設定はConfigMap、パスワード類はSecretで管理する
- SecretはBase64エンコードのみで暗号化ではないため、本番環境では追加の対策が必要
- ConfigMap/Secret更新後はPodの再起動が必要
HelmチャートやCI/CDパイプラインとの連携についてはまた別の機会に紹介します。