🖥️

【4-5】Kubernetes実践編:ConfigMapとSecretで実現する「設定の外部化」

に公開

Kubernetes実践編:ConfigMapとSecretで実現する「設定の外部化」"

はじめに

前回の記事では、DeploymentServiceを組み合わせてKubernetes上にWordPress環境を構築しました。しかし、あのwordpress-deployment.yamlには重大な問題が残っています。

# 前回のYAML(問題あり)
...
env:
- name: WORDPRESS_DB_PASSWORD
  value: "wppassword" # <- パスワードがハードコードされている!
...

https://zenn.dev/koikoi_infra/articles/95ce1174da02ef

このように、設定値や機密情報がYAMLファイルにハードコーディングされています。これでは、設定を変更するたびにYAMLを書き換えてk applyし直す必要があり、何より機密情報がGitリポジトリなどで丸見えになってしまいます。

この問題を解決するのが、モダンなアプリ開発の原則「The Twelve-Factor App」の「III. 設定」でうたわれている 「設定の外部化」 です。

Kubernetesでは、この「設定の外部化」を実現するために、2つの専用リソースが用意されています。

  • ConfigMap: 機密ではない設定データ(設定ファイル、環境変数など)を管理
  • Secret: 機密情報(パスワード、APIキー、TLS証明書など)を管理

この記事では、この2つのリソースを使って、アプリケーションから設定を完全に分離する方法を学びます。

Part 1: ConfigMap - アプリケーション設定の管理

ConfigMapは、機密ではない設定データをキーと値のペアとして保存します。アプリケーションのconfig.inisettings.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エンコードされているのがわかります。

中身を確認したい場合は、jsonpathbase64コマンドを組み合わせます。

# 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のセキュリティ・ベストプラクティスです。

まとめ

ConfigMapSecretは、Kubernetesでアプリケーションを運用する上で欠かせないリソースです。

これで、WordPressのDeploymentも、DBのパスワードをYAMLにハードコーディングすることなく、安全に管理できるようになりました。

次のステップでは、PersistentVolumeによる本格的なデータ永続化や、より高度なセキュリティ設定について学んでいきます。

Discussion