メインコンテンツまでスキップ

Kubernetes 設定管理

ConfigMapとSecretを使用した設定管理と、環境変数・ボリュームマウントによる設定の注入方法を学びます。


目次

  1. 設定管理の概要
  2. ConfigMap
  3. Secret
  4. 設定の注入方法
  5. 設定の更新
  6. External Secrets
  7. IDサービスでの構成例

設定管理の概要

設定の分離

┌─────────────────────────────────────────────────────────────┐
│ Kubernetes 設定管理の原則 │
├─────────────────────────────────────────────────────────────┤
│ │
│ アプリケーションコード 設定 │
│ ┌─────────────────┐ ┌─────────────────┐ │
│ │ │ │ ConfigMap │ │
│ │ コンテナ │ ◄─── │ (非機密情報) │ │
│ │ イメージ │ │ │ │
│ │ │ ├─────────────────┤ │
│ │ │ ◄─── │ Secret │ │
│ │ │ │ (機密情報) │ │
│ └─────────────────┘ └─────────────────┘ │
│ │
│ メリット: │
│ - 環境毎の設定切り替えが容易 │
│ - イメージの再ビルド不要 │
│ - 機密情報の分離管理 │
│ │
└─────────────────────────────────────────────────────────────┘

ConfigMap

作成方法

リテラルから作成

kubectl create configmap app-config \
--from-literal=SPRING_PROFILES_ACTIVE=production \
--from-literal=LOG_LEVEL=INFO

ファイルから作成

# 単一ファイル
kubectl create configmap app-config \
--from-file=application.yml

# ディレクトリ
kubectl create configmap app-config \
--from-file=config/

YAMLから作成

apiVersion: v1
kind: ConfigMap
metadata:
name: idp-config
namespace: production
data:
# キー・バリュー形式
SPRING_PROFILES_ACTIVE: "production"
LOG_LEVEL: "INFO"
SERVER_PORT: "8080"

# ファイル形式
application.yml: |
spring:
application:
name: idp-server
datasource:
hikari:
maximum-pool-size: 20
minimum-idle: 5
server:
port: 8080
shutdown: graceful

logback-spring.xml: |
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} - %msg%n</pattern>
</encoder>
</appender>
<root level="INFO">
<appender-ref ref="CONSOLE" />
</root>
</configuration>

ConfigMapの確認

# 一覧
kubectl get configmap

# 詳細
kubectl describe configmap idp-config

# YAML出力
kubectl get configmap idp-config -o yaml

# 特定キーの値
kubectl get configmap idp-config -o jsonpath='{.data.SPRING_PROFILES_ACTIVE}'

Secret

Secretの種類

┌─────────────────────────────────────────────────────────────┐
│ Secret Types │
├─────────────────────────────────────────────────────────────┤
│ │
│ Opaque (デフォルト) │
│ └── 任意のデータ │
│ │
│ kubernetes.io/basic-auth │
│ └── Basic認証(username, password) │
│ │
│ kubernetes.io/ssh-auth │
│ └── SSH認証(ssh-privatekey) │
│ │
│ kubernetes.io/tls │
│ └── TLS証明書(tls.crt, tls.key) │
│ │
│ kubernetes.io/dockerconfigjson │
│ └── Dockerレジストリ認証 │
│ │
│ kubernetes.io/service-account-token │
│ └── ServiceAccountトークン │
│ │
└─────────────────────────────────────────────────────────────┘

Secretの作成

コマンドで作成

# 基本的なSecret
kubectl create secret generic db-secret \
--from-literal=username=admin \
--from-literal=password=supersecret

# ファイルから
kubectl create secret generic tls-secret \
--from-file=tls.crt=cert.pem \
--from-file=tls.key=key.pem

# TLS Secret
kubectl create secret tls idp-tls \
--cert=cert.pem \
--key=key.pem

# Dockerレジストリ認証
kubectl create secret docker-registry regcred \
--docker-server=ghcr.io \
--docker-username=user \
--docker-password=token

YAMLで作成

apiVersion: v1
kind: Secret
metadata:
name: idp-secrets
namespace: production
type: Opaque
data:
# Base64エンコード必須
database-url: amRiYzpwb3N0Z3Jlc3FsOi8vZGI6NTQzMi9pZHA=
database-username: aWRw
database-password: c3VwZXJzZWNyZXQ=
jwt-secret: bXktand0LXNlY3JldC1rZXktMTIzNDU2Nzg5MA==

---
# stringDataを使用(エンコード不要)
apiVersion: v1
kind: Secret
metadata:
name: idp-secrets-plain
type: Opaque
stringData:
database-url: "jdbc:postgresql://db:5432/idp"
database-username: "idp"
database-password: "supersecret"

Base64エンコード

# エンコード
echo -n "supersecret" | base64
# 出力: c3VwZXJzZWNyZXQ=

# デコード
echo "c3VwZXJzZWNyZXQ=" | base64 -d
# 出力: supersecret

設定の注入方法

環境変数として注入

apiVersion: apps/v1
kind: Deployment
metadata:
name: idp-server
spec:
template:
spec:
containers:
- name: idp-server
image: my-idp-server:latest
env:
# 直接指定
- name: SERVER_PORT
value: "8080"

# ConfigMapから
- name: SPRING_PROFILES_ACTIVE
valueFrom:
configMapKeyRef:
name: idp-config
key: SPRING_PROFILES_ACTIVE

# Secretから
- name: DATABASE_PASSWORD
valueFrom:
secretKeyRef:
name: idp-secrets
key: database-password

# Podの情報
- name: POD_NAME
valueFrom:
fieldRef:
fieldPath: metadata.name
- name: POD_IP
valueFrom:
fieldRef:
fieldPath: status.podIP

# ConfigMap全体を環境変数に
envFrom:
- configMapRef:
name: idp-config
- secretRef:
name: idp-secrets
# optional: true # Secretが存在しなくてもOK

ボリュームとしてマウント

apiVersion: apps/v1
kind: Deployment
metadata:
name: idp-server
spec:
template:
spec:
containers:
- name: idp-server
image: my-idp-server:latest
volumeMounts:
# ConfigMapをファイルとしてマウント
- name: config-volume
mountPath: /app/config
readOnly: true

# 特定のキーのみマウント
- name: app-settings
mountPath: /app/application.yml
subPath: application.yml
readOnly: true

# Secretをマウント
- name: secrets-volume
mountPath: /app/secrets
readOnly: true

volumes:
- name: config-volume
configMap:
name: idp-config

- name: app-settings
configMap:
name: idp-config
items:
- key: application.yml
path: application.yml

- name: secrets-volume
secret:
secretName: idp-secrets
# パーミッション設定
defaultMode: 0400

マウント方法の比較

方法メリットデメリット
環境変数シンプル、アクセスが容易起動時のみ読み込み
ボリュームマウント動的更新可能、ファイル形式ファイル操作が必要

設定の更新

ConfigMap/Secretの更新

# 編集
kubectl edit configmap idp-config

# 置き換え
kubectl create configmap idp-config \
--from-file=config/ \
--dry-run=client -o yaml | kubectl apply -f -

# パッチ
kubectl patch configmap idp-config \
--type merge \
-p '{"data":{"LOG_LEVEL":"DEBUG"}}'

更新時の動作

┌─────────────────────────────────────────────────────────────┐
│ 設定更新時の動作 │
├─────────────────────────────────────────────────────────────┤
│ │
│ 環境変数として注入した場合: │
│ └── Podの再起動が必要 │
│ └── 自動では反映されない │
│ │
│ ボリュームとしてマウントした場合: │
│ └── 自動で更新される(kubeletが定期的に同期) │
│ └── 更新間隔: デフォルト約1分 │
│ └── subPathマウントは更新されない │
│ │
│ アプリケーションが設定変更を検知する方法: │
│ └── ファイル監視(inotify等) │
│ └── Spring Cloud Config Reload │
│ └── Sidecar + SIGHUP │
│ │
└─────────────────────────────────────────────────────────────┘

ローリング更新のトリガー

apiVersion: apps/v1
kind: Deployment
metadata:
name: idp-server
spec:
template:
metadata:
annotations:
# ConfigMapのハッシュをアノテーションに追加
# ConfigMap更新時にPodが再作成される
checksum/config: "{{ sha256sum .Values.configmap | trunc 63 }}"
# 手動でローリング更新をトリガー
kubectl rollout restart deployment/idp-server

External Secrets

AWS Secrets Manager連携

# External Secretsオペレーター使用
apiVersion: external-secrets.io/v1beta1
kind: SecretStore
metadata:
name: aws-secrets-manager
spec:
provider:
aws:
service: SecretsManager
region: ap-northeast-1
auth:
jwt:
serviceAccountRef:
name: external-secrets-sa

---
apiVersion: external-secrets.io/v1beta1
kind: ExternalSecret
metadata:
name: idp-external-secrets
spec:
refreshInterval: 1h
secretStoreRef:
name: aws-secrets-manager
kind: SecretStore

target:
name: idp-secrets
creationPolicy: Owner

data:
- secretKey: database-password
remoteRef:
key: idp/production/database
property: password

- secretKey: jwt-secret
remoteRef:
key: idp/production/jwt
property: secret-key

HashiCorp Vault連携

apiVersion: external-secrets.io/v1beta1
kind: SecretStore
metadata:
name: vault-backend
spec:
provider:
vault:
server: "https://vault.example.com"
path: "secret"
version: "v2"
auth:
kubernetes:
mountPath: "kubernetes"
role: "idp-app"
serviceAccountRef:
name: idp-sa

---
apiVersion: external-secrets.io/v1beta1
kind: ExternalSecret
metadata:
name: idp-vault-secrets
spec:
refreshInterval: 5m
secretStoreRef:
name: vault-backend
kind: SecretStore

target:
name: idp-secrets

data:
- secretKey: database-password
remoteRef:
key: idp/database
property: password

IDサービスでの構成例

完全な設定構成

# configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: idp-config
namespace: idp-system
data:
SPRING_PROFILES_ACTIVE: "kubernetes,production"
SERVER_PORT: "8080"
MANAGEMENT_SERVER_PORT: "9090"

application.yml: |
spring:
application:
name: idp-server
datasource:
hikari:
maximum-pool-size: 20
minimum-idle: 5
connection-timeout: 30000
idle-timeout: 600000
max-lifetime: 1800000
redis:
timeout: 5000ms
server:
port: 8080
shutdown: graceful
tomcat:
max-threads: 200
accept-count: 100
management:
server:
port: 9090
endpoints:
web:
exposure:
include: health,info,metrics,prometheus
endpoint:
health:
show-details: always
probes:
enabled: true

---
# secret.yaml
apiVersion: v1
kind: Secret
metadata:
name: idp-secrets
namespace: idp-system
type: Opaque
stringData:
DATABASE_URL: "jdbc:postgresql://postgres:5432/idp"
DATABASE_USERNAME: "idp"
DATABASE_PASSWORD: "your-secure-password"
REDIS_HOST: "redis"
REDIS_PASSWORD: "redis-password"
JWT_SECRET_KEY: "your-jwt-secret-key-at-least-32-chars"

---
# deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: idp-server
namespace: idp-system
spec:
replicas: 3
selector:
matchLabels:
app: idp-server
template:
metadata:
labels:
app: idp-server
spec:
serviceAccountName: idp-server

containers:
- name: idp-server
image: ghcr.io/myorg/idp-server:v1.0.0
ports:
- name: http
containerPort: 8080
- name: management
containerPort: 9090

# 環境変数(ConfigMapから)
envFrom:
- configMapRef:
name: idp-config

# 環境変数(Secretから)
env:
- name: SPRING_DATASOURCE_URL
valueFrom:
secretKeyRef:
name: idp-secrets
key: DATABASE_URL
- name: SPRING_DATASOURCE_USERNAME
valueFrom:
secretKeyRef:
name: idp-secrets
key: DATABASE_USERNAME
- name: SPRING_DATASOURCE_PASSWORD
valueFrom:
secretKeyRef:
name: idp-secrets
key: DATABASE_PASSWORD
- name: SPRING_REDIS_HOST
valueFrom:
secretKeyRef:
name: idp-secrets
key: REDIS_HOST
- name: JWT_SECRET_KEY
valueFrom:
secretKeyRef:
name: idp-secrets
key: JWT_SECRET_KEY

# 設定ファイルをマウント
volumeMounts:
- name: config
mountPath: /app/config/application.yml
subPath: application.yml
readOnly: true

resources:
requests:
memory: "512Mi"
cpu: "250m"
limits:
memory: "1Gi"
cpu: "500m"

livenessProbe:
httpGet:
path: /actuator/health/liveness
port: management
initialDelaySeconds: 60
periodSeconds: 10

readinessProbe:
httpGet:
path: /actuator/health/readiness
port: management
initialDelaySeconds: 30
periodSeconds: 5

volumes:
- name: config
configMap:
name: idp-config

# イメージプルシークレット
imagePullSecrets:
- name: ghcr-secret

まとめ

チェックリスト

  • 機密情報はSecretに格納
  • 非機密設定はConfigMapに格納
  • Base64エンコードを忘れずに(Secret)
  • 適切な注入方法を選択(環境変数 vs ボリューム)
  • 設定更新時の反映方法を検討
  • 本番環境ではExternal Secretsを検討

次のステップ


参考リソース