Kubernetes Kubernetes

設定:ConfigMap と Secret

設定と認証情報をイメージの外に置き、Pod に注入し、よくある落とし穴を避けます。

14 min learn.level.中級

データベースの URL やログレベルがコンテナイメージに埋め込まれていると、それを変更するには、イメージの再ビルド、プッシュ、再デプロイが必要になります。Kubernetes は、設定とイメージを 2 つのオブジェクトで分離します。通常の設定用の ConfigMap と、機密性のある値用の Secret です。このガイドでは、両方の作成方法、Pod に取り込む方法、そして Secret が何を守り、何を守らないかを説明します。

ConfigMap を作成する

ConfigMap は、キーと値のペアの集合です。値は短い文字列でも、ファイル全体でも構いません。

apiVersion: v1
kind: ConfigMap
metadata:
  name: web-config
data:
  LOG_LEVEL: info
  API_URL: http://api.internal:8080
  motd.txt: |
    Welcome to the demo environment

コマンドラインからも作成でき、ファイルには便利です。

kubectl create configmap web-config \
  --from-literal=LOG_LEVEL=info \
  --from-file=motd.txt
kubectl describe configmap web-config

ConfigMap は、小さく機密性のないデータ向けで(上限は 1 MiB)、namespace に属します。Pod は、自分の namespace の ConfigMap しか使えません。

環境変数またはファイルとして使う

同じ Pod が、ConfigMap を両方の方法で利用できます。

apiVersion: v1
kind: Pod
metadata:
  name: web
spec:
  containers:
    - name: web
      image: nginx:1.25
      env:
        - name: LOG_LEVEL
          valueFrom:
            configMapKeyRef:
              name: web-config
              key: LOG_LEVEL
      volumeMounts:
        - name: config
          mountPath: /usr/share/nginx/html/motd.txt
          subPath: motd.txt
  volumes:
    - name: config
      configMap:
        name: web-config

ここでは 2 つのことが起きています。env のエントリは、1 つのキーを環境変数にコピーします。(envFrom は、すべてのキーを一度にインポートできますが、有効な変数名ではないキーはスキップされます。)volumes と volumeMounts のエントリは、ConfigMap をファイルとしてマウントし、キーごとに 1 つのファイルになります。

ConfigMap 全体を、このようなディレクトリにマウントすると、そのディレクトリはマウントされたファイルに置き換えられます。

      volumeMounts:
        - name: config
          mountPath: /etc/web

自分が所有するディレクトリなら問題ありませんが、そのパスにすでにあったものが隠されます。/etc/nginx のような既存の設定ディレクトリにマウントすると、アプリケーションが壊れる可能性があり、コンテナが「file not found」エラーでクラッシュループに入ることがあります。そのために subPath があります。最初の例のように、単一のキーを単一のファイルとしてマウントし、ディレクトリの残りには手を付けません。トレードオフは、subPath でマウントされたファイルには、その後の ConfigMap の更新が反映されないことです。

コンテナに何が入ったかを確認します。

kubectl exec web -- ls /usr/share/nginx/html
kubectl exec web -- env | grep LOG_LEVEL

認証情報を Secret に保存する

Secret は ConfigMap に似ていますが、パスワード、トークン、鍵のためのものです。

kubectl create secret generic db-credentials \
  --from-literal=username=app \
  --from-literal=password='S3cr3t-value'

YAML では、stringData を使ってプレーンテキストで書き、エンコードを Kubernetes に任せます。

apiVersion: v1
kind: Secret
metadata:
  name: db-credentials
type: Opaque
stringData:
  username: app
  password: S3cr3t-value

エンコードされた data フィールドを自分で書く場合は、echo -n 'value' | base64 でエンコードします。-n がないと、末尾の改行がパスワードの一部になり、認証が分かりにくい形で失敗します。Secret は ConfigMap と同じように使います。

      env:
        - name: DB_PASSWORD
          valueFrom:
            secretKeyRef:
              name: db-credentials
              key: password

実際の値が入った Secret のマニフェストを Git にコミットするのは避けてください。Sealed Secrets、External Secrets Operator、SOPS などのツールは、暗号化された、あるいは参照のみのシークレットをリポジトリに保管できるようにするために存在します。

Secret が守るものと守らないものを知る

Base64 はエンコードであって、暗号化ではありません。API 経由で Secret を読める人は誰でも、デコードできます。

kubectl get secret db-credentials -o jsonpath='{.data.password}' | base64 -d

デフォルトでは、Secret は etcd にも暗号化されずに保存されます。実際に保護するには、次のようにします。

  • RBAC で Secret を読める人を制限します。また、namespace に Pod を作成できる人は誰でも、その Secret をマウントできることを忘れないでください。
  • etcd の保存時の暗号化を有効にするか、クラウドプロバイダーのキー管理を使います。
  • 要件が厳しい場合は、外部のシークレットマネージャーを検討します。

環境変数は便利ですが漏洩しやすく、kubectl exec ... env、クラッシュダンプ、ときにはログに現れます。機密性のある値は、Secret をファイルとしてマウントするほうが、より良い選択であることがよくあります。

設定の更新を意図的に行う

環境変数はコンテナの起動時に一度だけ読み込まれるため、変更された ConfigMap や Secret は、実行中の Pod には届きません。変更を反映するには、再起動してください。

kubectl rollout restart deployment/web

ディレクトリ全体をマウントした場合のファイルは、いずれ更新されますが、多くのアプリケーションは、どのみち起動時にしか設定を読み込みません。自動化された構成では、設定が変わるたびに Pod テンプレートが変わるようにします。Argo CD の Helm と Kustomize のガイドの、設定ハッシュの手法は、そのためのものです。

人気のガイド