【4-5】Kubernetes実践編:ConfigMapとSecretで実現する「設定の外部化」
Kubernetes実践編:ConfigMapとSecretで実現する「設定の外部化」"
はじめに
前回の記事では、DeploymentとServiceを組み合わせてKubernetes上にWordPress環境を構築しました。しかし、あのwordpress-deployment.yamlには重大な問題が残っています。
# 前回のYAML(問題あり)
...
env:
- name: WORDPRESS_DB_PASSWORD
value: "wppassword" # <- パスワードがハードコードされている!
...
このように、設定値や機密情報がYAMLファイルにハードコーディングされています。これでは、設定を変更するたびにYAMLを書き換えてk applyし直す必要があり、何より機密情報がGitリポジトリなどで丸見えになってしまいます。
この問題を解決するのが、モダンなアプリ開発の原則「The Twelve-Factor App」の「III. 設定」でうたわれている 「設定の外部化」 です。
Kubernetesでは、この「設定の外部化」を実現するために、2つの専用リソースが用意されています。
- ConfigMap: 機密ではない設定データ(設定ファイル、環境変数など)を管理
- Secret: 機密情報(パスワード、APIキー、TLS証明書など)を管理
この記事では、この2つのリソースを使って、アプリケーションから設定を完全に分離する方法を学びます。
Part 1: ConfigMap - アプリケーション設定の管理
ConfigMapは、機密ではない設定データをキーと値のペアとして保存します。アプリケーションのconfig.iniやsettings.propertiesといった設定ファイルや、環境変数を丸ごと外出しにするイメージです。
# 作業ディレクトリを準備
cd ~/k8s-practice
mkdir config-secret-practice
cd config-secret-practice
1. ConfigMapの作成
作成方法は主に2つあります。
方法A: コマンドライン(リテラル値)から作成
--from-literalでキーと値を直接指定します。環境変数に最適です。
# 「app-config」という名前で3つのキーと値を持つConfigMapを作成
k create configmap app-config \
--from-literal=APP_ENV=production \
--from-literal=LOG_LEVEL=info \
--from-literal=MAX_CONNECTIONS=100
# 作成したConfigMapの詳細を確認
k describe configmap app-config
# (Dataセクションに3つのキーと値が格納されているのを確認)
方法B: ファイルから作成
既存の設定ファイル全体を、ConfigMapの1つのキー(ファイル名)として保存します。
# 元となる設定ファイルを作成
cat > app.properties << 'EOF'
database.host=mysql-service
database.port=3306
EOF
# "app-properties" という名前で、app.propertiesファイルからConfigMapを作成
k create configmap app-properties --from-file=app.properties
# 詳細を確認
k describe configmap app-properties
# (Dataセクションに "app.properties" というキーで内容が丸ごと入っている)
2. ConfigMapをPodで使う(3つの方法)
作成したConfigMapをPodに渡す方法は、主に3つあります。
- valueFrom: キーを指定して、個別の環境変数として注入
- envFrom: ConfigMap全体を、環境変数として一括注入
- volumeMounts: ConfigMap全体を、ファイルとしてコンテナ内にマウント
pod-with-configmap.yamlで、これら3つをすべて試してみます。
cat > pod-with-configmap.yaml << 'EOF'
apiVersion: v1
kind: Pod
metadata:
name: app-with-config
spec:
containers:
- name: app
image: nginx:alpine
env:
# --- 方法1: valueFrom (個別に注入) ---
- name: MY_APP_ENV
valueFrom:
configMapKeyRef:
name: app-config
key: APP_ENV
envFrom:
# --- 方法2: envFrom (一括注入) ---
- configMapRef:
name: app-config
volumeMounts:
# --- 方法3: volumeMounts (ファイルとしてマウント) ---
- name: config-volume
mountPath: /etc/config
volumes:
- name: config-volume
configMap:
name: app-properties
EOF
k apply -f pod-with-configmap.yaml
3. 使用結果の確認
execでPodの内部を覗き、設定が正しく渡されているか確認します。
# 方法1, 2 (環境変数) の確認
k exec app-with-config -- env | grep -E "(APP_ENV|LOG_LEVEL|MAX_CONNECTIONS|MY_APP_ENV)"
# MY_APP_ENV=production <- 方法1 (名前を変えて注入)
# APP_ENV=production <- 方法2 (キー名そのまま)
# LOG_LEVEL=info
# MAX_CONNECTIONS=100
# 方法3 (ファイル) の確認
k exec app-with-config -- cat /etc/config/app.properties
# database.host=mysql-service
# database.port=3306
このように、設定をYAMLから完全に分離できました。
Part 2: Secret - 機密情報の安全な管理
Secretは、パスワードやAPIキーといった機密情報を管理します。使い方はConfigMapとほぼ同じですが、値が自動でBase64エンコードされる点が異なります。
重要:Base64は暗号化ではありません!
Base64は、単なるエンコード(文字の置き換え)です。base64 --decodeコマンドで誰でも簡単に元の値に戻せます。
Secretの目的は、パスワードがYAMLに平文で書かれるのを防ぎ、RBAC(ロールベースアクセス制御)で「誰がSecretに触れるか」をアクセス制御することにあります。
1. Secretの作成と確認
# 「db-credentials」という名前でDBの認証情報を作成
k create secret generic db-credentials \
--from-literal=username=dbuser \
--from-literal=password='SecureP@ssw0rd123'
# SecretのYAML定義を確認
k get secret db-credentials -o yaml
data:セクションを見ると、password: U2VjdXJlUEBzc3cwcmQxMjM=のように、値がBase64エンコードされているのがわかります。
中身を確認したい場合は、jsonpathとbase64コマンドを組み合わせます。
# passwordキーの値を取得し(|)、base64でデコードする
k get secret db-credentials -o jsonpath='{.data.password}' | base64 --decode
# SecureP@ssw0rd123
2. SecretをPodで使う(推奨 vs 非推奨)
Secretの注入方法は2つありますが、セキュリティ上、明確な使い分けがあります。
- 非推奨: 環境変数として注入
- 推奨: ファイルとしてマウント
pod-with-secret.yamlで両方を試してみましょう。
cat > pod-with-secret.yaml << 'EOF'
apiVersion: v1
kind: Pod
metadata:
name: app-with-secret
spec:
containers:
- name: app
image: nginx:alpine
env:
# --- 方法1: 環境変数として注入 (非推奨) ---
- name: DB_USERNAME
valueFrom:
secretKeyRef:
name: db-credentials
key: username
- name: DB_PASSWORD
valueFrom:
secretKeyRef:
name: db-credentials
key: password
volumeMounts:
# --- 方法2: ファイルとしてマウント (推奨) ---
- name: secret-volume
mountPath: "/etc/secrets"
readOnly: true
volumes:
- name: secret-volume
secret:
secretName: db-credentials
EOF
k apply -f pod-with-secret.yaml
3. なぜ環境変数(非推奨)が危険なのか?
execでPodの内部に入り、環境変数を確認してみます。
k exec app-with-secret -- env | grep DB_
# DB_USERNAME=dbuser
# DB_PASSWORD=SecureP@ssw0rd123
パスワードが平文で表示されてしまいました。
環境変数は、k describe podコマンドの出力や、アプリケーションのエラーログ、デバッグコンソールなどに平文で漏洩するリスクが常に伴います。
4. なぜファイルマウント(推奨)が安全なのか?
次に、ファイルとしてマウントされたディレクトリを確認します。
# /etc/secrets にキー名のファイルが作成されている
k exec app-with-secret -- ls -la /etc/secrets
# ... username
# ... password
# ファイルの中身は平文
k exec app-with-secret -- cat /etc/secrets/password
# SecureP@ssw0rd123
ファイルとしてマウント(特にreadOnly: true)し、アプリケーションがこのファイル(例: /etc/secrets/password)を直接読み込むように設計することで、パスワードが環境変数に展開されるのを防ぎ、漏洩リスクを大幅に下げることができます。
これがKubernetesのセキュリティ・ベストプラクティスです。
まとめ
ConfigMapとSecretは、Kubernetesでアプリケーションを運用する上で欠かせないリソースです。
これで、WordPressのDeploymentも、DBのパスワードをYAMLにハードコーディングすることなく、安全に管理できるようになりました。
次のステップでは、PersistentVolumeによる本格的なデータ永続化や、より高度なセキュリティ設定について学んでいきます。
Discussion