0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Kubernetes④(全7回)|kind|ConfigMap / Secret / Volume — 設定とデータの分離

0
Last updated at Posted at 2026-02-18

シリーズ記事一覧

📑 目次

  1. この記事について
  2. この記事のゴール
  3. 前提条件
  4. Docker Composeではこうだった(Before)
  5. k8sではこうなる(After)
  6. ハンズオン:設定とデータを分離する
  7. つまずきポイント(体験談)
  8. まとめ
  9. 次回予告

1. この記事について

前回までで、Podを動かし(第2回)、外部からアクセスし(第3回)、アプリケーションが動く状態になりました。

チェーンレストランで言えば「店舗を開店して、お客さんが来れる状態」です。

でも、まだ問題があります。

メニュー表(設定)とレシピ帳(秘密情報)がスタッフの頭の中にしかない。

スタッフが入れ替わったら(Podが再作成されたら)、設定もデータも全部消えてしまいます。

今回は「設定とデータをコンテナから分離する」ことで、Podが使い捨てでも問題ない状態を作ります。


2. この記事のゴール

この記事で書いていること:

  • 設定をコンテナに埋め込まない理由
  • ConfigMap / Secret / Volume の使い分けの整理
  • ConfigMapで環境変数をPodに渡してみた話
  • Secretでパスワードを管理してみた話
  • Volumeの種類と用途

3. 前提条件

項目 要件
前回の完了 第3回でServiceを理解済み
クラスタ状態 kindクラスタが起動中
# WSL2 Ubuntu で実行

kubectl get nodes

4. Docker Composeではこうだった(Before)

4-1. Docker Composeの設定管理

Docker Composeでは、設定は environment.env ファイルで管理します。

# docker-compose.yml

services:
  api:
    image: myapp:latest
    ports:
      - "3000:3000"

    # 環境変数で設定を渡す
    environment:
      - APP_NAME=MyApp
      - RAILS_ENV=production
      - DATABASE_URL=postgresql://user:password@db:5432/myapp
      - SECRET_KEY_BASE=super_secret_key_12345

APP_NAMESECRET_KEY_BASE も同じ environment に並んでいます。

4-2. でもこの壁がある

# Docker Composeの現実
壁1 秘密情報と一般設定が同じ場所 DBパスワードもアプリ名も同じ environment
壁2 設定を変えるたびにイメージ再ビルド 設定がイメージに焼き込まれている場合
壁3 コンテナが消えるとデータも消える volumes を忘れるとDBデータが全消失

🔰 Memo: Docker Composeの environment は手軽で便利です。
でも「DBのパスワードがdocker-compose.ymlに平文で書いてある」のは、本番環境では危険だなと気づきました。
k8sでは設定の種類ごとに管理方法を分けることで、この問題を解決しています。


5. k8sではこうなる(After)

5-1. 3つのリソースで「関心事の分離」

🍽️ 比喩:レストランの情報管理

Docker Composeでは、メニュー表もレシピ帳も同じ引き出しに入れていた。
k8sでは、情報の性質に応じて保管場所を分ける

情報の種類 k8sリソース 比喩
一般的な設定 ConfigMap メニュー表 アプリ名、ログレベル、環境名
秘密情報 Secret レシピ帳(金庫保管) DBパスワード、APIキー
永続データ Volume 食材倉庫 DBデータ、アップロードファイル

メニュー表(ConfigMap)は壁に貼っておけばいい。
レシピ帳(Secret)は金庫に入れて鍵をかける。
食材倉庫(Volume)はスタッフが入れ替わっても中身が残る。

5-2. 全体関係図

3つのリソースがPodとどのように連携するかを図で確認します。

🔰 Note: ポイントは「全部Podの外にある」こと。
Podが削除・再作成されても、ConfigMap・Secret・Volumeは残ります。
これが「使い捨てPod」を実現する仕組みだと理解しました。

5-3. ConfigMapの2つの渡し方

ConfigMapには「環境変数として渡す」と「ファイルとして渡す」の2つの方法があります。
それぞれの違いを図にまとめます。

方法 用途 比喩
環境変数 単純なキー=値 朝礼で口頭伝達「今日の営業時間は10時から」
ファイルマウント 設定ファイルまるごと メニュー表を壁に貼り出す

5-4. Volume の種類

Volumeにはいくつかの種類があり、用途と寿命が異なります。

種類 比喩 寿命 用途
emptyDir 調理台の共有スペース Podと同じ(Pod削除で消える) コンテナ間のデータ共有
hostPath 店舗の倉庫 ノードと同じ テスト用(本番非推奨)
PersistentVolume (PV) 外部の食材倉庫 永続 DB、ファイルストレージ

🔰 Note: 今回ハンズオンしたのは emptyDir だけです。
PersistentVolumeはクラウド環境(EBS等)で真価を発揮するため、ここでは概念のみ整理しました。


6. ハンズオン:設定とデータを分離する

6-1. 作業ディレクトリ

# WSL2 Ubuntu で実行

mkdir -p ~/k8s-handson/config-secret-volume
cd ~/k8s-handson/config-secret-volume

6-2. Step1:ConfigMapで環境変数をPodに渡す

6-2-1. ConfigMapの作成

以下のファイルを作成:

📂 config-secret-volume/
└── app-configmap.yaml             ← Step 1 🆕
# app-configmap.yaml
# ConfigMap = メニュー表(一般的な設定情報)

apiVersion: v1
kind: ConfigMap
metadata:
  name: app-config
data:
  # キー: 値 の形式で設定を記述
  APP_NAME: "MyRestaurant"
  APP_ENV: "development"
  LOG_LEVEL: "info"

6-2-2. ConfigMapを使うPodの作成

📂 config-secret-volume/
├── app-configmap.yaml             ✅ Step 1
└── pod-with-configmap.yaml        ← Step 1 🆕
# pod-with-configmap.yaml
# ConfigMapから環境変数を受け取るPod

apiVersion: v1
kind: Pod
metadata:
  name: configmap-demo
spec:
  containers:
    - name: demo
      image: busybox
      # 起動後、環境変数を表示してスリープ
      command: ["sh", "-c", "echo APP_NAME=$APP_NAME && echo APP_ENV=$APP_ENV && echo LOG_LEVEL=$LOG_LEVEL && sleep 3600"]

      # ConfigMapから環境変数を一括読み込み
      envFrom:
        - configMapRef:
            name: app-config     # ← ConfigMapの名前を指定

6-2-3. マニフェストの構造解説

YAMLの項目 比喩 意味
kind: ConfigMap 「メニュー表です」 一般設定用リソース
data: メニューの内容 キー=値のペア
envFrom 「朝礼で全項目を伝達して」 ConfigMapの全キーを環境変数として注入
configMapRef 「app-configというメニュー表を使って」 参照するConfigMapの指定

6-2-4. 実行・確認

# ~/k8s-handson/config-secret-volume/ で実行

# ConfigMapを作成
kubectl apply -f app-configmap.yaml

# ConfigMapの内容を確認
kubectl describe configmap app-config

# Podを作成
kubectl apply -f pod-with-configmap.yaml

# Podのログを確認(環境変数の値が表示される)
kubectl logs configmap-demo

期待される出力:

APP_NAME=MyRestaurant
APP_ENV=development
LOG_LEVEL=info

ConfigMapからPodに環境変数が渡されました。

💡 Podが起動しない / ログが空の場合 → セクション7-2(ConfigMapを変更してもPodに反映されない)を確認

🍽️ メニュー表(ConfigMap)を朝礼(envFrom)で全スタッフ(Pod)に伝達した、ということです。
メニューを変えたいときは、メニュー表だけ書き換えればOK。
スタッフ(コンテナイメージ)を入れ替える必要はありません。


6-3. Step2:Secretでパスワードを管理する

6-3-1. Secretの作成

📂 config-secret-volume/
├── app-configmap.yaml             ✅ Step 1
├── pod-with-configmap.yaml        ✅ Step 1
└── app-secret.yaml                ← Step 2 🆕
# app-secret.yaml
# Secret = レシピ帳(秘密情報)

apiVersion: v1
kind: Secret
metadata:
  name: app-secret
type: Opaque

# stringData を使えば平文で書ける(k8sが自動でbase64エンコードする)
stringData:
  DB_PASSWORD: "super_secret_password"
  API_KEY: "ak_1234567890abcdef"

🔰 Memo: Secretには data(base64エンコード済み)と stringData(平文OK)の2種類があります。
学習では stringData が圧倒的に楽でした。
本番では外部のシークレット管理ツール(AWS Secrets Manager等)と連携するのが一般的なようです。

6-3-2. Secretを使うPodの作成

📂 config-secret-volume/
├── app-configmap.yaml             ✅ Step 1
├── pod-with-configmap.yaml        ✅ Step 1
├── app-secret.yaml                ✅ Step 2
└── pod-with-secret.yaml           ← Step 2 🆕
# pod-with-secret.yaml
# ConfigMap と Secret の両方を使うPod

apiVersion: v1
kind: Pod
metadata:
  name: secret-demo
spec:
  containers:
    - name: demo
      image: busybox
      command: ["sh", "-c", "echo APP_NAME=$APP_NAME && echo DB_PASSWORD=$DB_PASSWORD && echo API_KEY=$API_KEY && sleep 3600"]

      # ConfigMapから一般設定を読み込み
      envFrom:
        - configMapRef:
            name: app-config

      # Secretから個別に環境変数を読み込み
      env:
        - name: DB_PASSWORD
          valueFrom:
            secretKeyRef:
              name: app-secret       # Secret名
              key: DB_PASSWORD       # Secretのキー

        - name: API_KEY
          valueFrom:
            secretKeyRef:
              name: app-secret
              key: API_KEY

6-3-3. ConfigMap と Secret の渡し方の違い

方法 対象 YAMLの書き方 用途
envFrom ConfigMap全体 configMapRef 一般設定をまとめて渡す
env[].valueFrom Secret個別 secretKeyRef 秘密情報を1つずつ渡す

🍽️ メニュー表(ConfigMap)は壁に貼る=全員が見られる。
レシピ帳(Secret)は必要な人にだけ金庫から取り出して渡す。

6-3-4. 実行・確認

# ~/k8s-handson/config-secret-volume/ で実行

# Secretを作成
kubectl apply -f app-secret.yaml

# Secretの内容を確認(値はbase64で表示される)
kubectl get secret app-secret -o yaml

# Podを作成
kubectl apply -f pod-with-secret.yaml

# ログで確認
kubectl logs secret-demo

期待される出力:

APP_NAME=MyRestaurant
DB_PASSWORD=super_secret_password
API_KEY=ak_1234567890abcdef

ConfigMapとSecretの両方がPodに渡されました。

6-3-5. Secretの確認

# Secretの値はbase64エンコードされて保存されている
kubectl get secret app-secret -o jsonpath='{.data.DB_PASSWORD}'

期待される出力:

c3VwZXJfc2VjcmV0X3Bhc3N3b3Jk
# デコードすると元の値が見える
kubectl get secret app-secret -o jsonpath='{.data.DB_PASSWORD}' | base64 --decode

期待される出力:

super_secret_password

🔰 Note: base64は「暗号化」ではありません。
簡単にデコードできます。
Secretの安全性は、base64ではなく「RBACによるアクセス制御」で担保するそうです(第5回で扱います)。


6-4. Step3:ConfigMapをファイルとしてマウント

環境変数ではなく、設定ファイルそのものをPodに渡す方法です。

6-4-1. nginx設定用のConfigMap

📂 config-secret-volume/
├── app-configmap.yaml             ✅ Step 1
├── pod-with-configmap.yaml        ✅ Step 1
├── app-secret.yaml                ✅ Step 2
├── pod-with-secret.yaml           ✅ Step 2
└── nginx-config.yaml              ← Step 3 🆕
# nginx-config.yaml
# ConfigMap に nginx の設定ファイルを格納

apiVersion: v1
kind: ConfigMap
metadata:
  name: nginx-config
data:
  # キー = ファイル名、値 = ファイルの中身
  default.conf: |
    server {
        listen       80;
        server_name  localhost;

        location / {
            root   /usr/share/nginx/html;
            index  index.html;
        }

        # カスタム:ヘルスチェック用エンドポイント
        location /health {
            return 200 'OK';
            add_header Content-Type text/plain;
        }
    }

6-4-2. ConfigMapをマウントするPod

📂 config-secret-volume/
├── app-configmap.yaml             ✅ Step 1
├── pod-with-configmap.yaml        ✅ Step 1
├── app-secret.yaml                ✅ Step 2
├── pod-with-secret.yaml           ✅ Step 2
├── nginx-config.yaml              ✅ Step 3
└── pod-with-nginx-config.yaml     ← Step 3 🆕
# pod-with-nginx-config.yaml
# ConfigMapをファイルとしてマウントするPod

apiVersion: v1
kind: Pod
metadata:
  name: nginx-config-demo
spec:
  containers:
    - name: nginx
      image: nginx:1.25
      ports:
        - containerPort: 80

      # ConfigMapをファイルとしてマウント
      volumeMounts:
        - name: nginx-conf-volume          # ボリューム名
          mountPath: /etc/nginx/conf.d     # マウント先のパス
          readOnly: true

  # ボリュームの定義
  volumes:
    - name: nginx-conf-volume              # volumeMountsのnameと一致させる
      configMap:
        name: nginx-config                 # ConfigMap名

6-4-3. マニフェストの構造解説

YAMLの項目 比喩 意味
volumeMounts 「メニュー表を壁のここに貼って」 コンテナ内のどこにマウントするか
mountPath 壁の位置 コンテナ内のディレクトリパス
volumes 「メニュー表はこれです」 マウントする元データの定義
configMap.name どのメニュー表か 参照するConfigMap

6-4-4. 実行・確認

# ~/k8s-handson/config-secret-volume/ で実行

# ConfigMapを作成
kubectl apply -f nginx-config.yaml

# Podを作成
kubectl apply -f pod-with-nginx-config.yaml

# Podが起動するまで待つ
kubectl wait \
  --for=condition=Ready \
  pod/nginx-config-demo \
  --timeout=60s

# マウントされたファイルを確認
kubectl exec nginx-config-demo -- cat /etc/nginx/conf.d/default.conf

# ヘルスチェックエンドポイントにアクセス
kubectl exec nginx-config-demo -- curl -s http://localhost/health

期待される出力(/health):

OK

ConfigMapの設定ファイルがnginxに適用されました。

🍽️ メニュー表(ConfigMap)をお店の壁(/etc/nginx/conf.d/)に貼った結果、新メニュー(/healthエンドポイント)が使えるようになりました。


6-5. Step4:Volume(emptyDir)でPod内データ共有

emptyDir は、同じPod内の複数コンテナがデータを共有するためのVolumeです。

📂 config-secret-volume/
├── app-configmap.yaml             ✅ Step 1
├── pod-with-configmap.yaml        ✅ Step 1
├── app-secret.yaml                ✅ Step 2
├── pod-with-secret.yaml           ✅ Step 2
├── nginx-config.yaml              ✅ Step 3
├── pod-with-nginx-config.yaml     ✅ Step 3
└── pod-with-emptydir.yaml         ← Step 4 🆕
# pod-with-emptydir.yaml
# emptyDir で2つのコンテナがデータを共有するPod

apiVersion: v1
kind: Pod
metadata:
  name: emptydir-demo
spec:
  containers:
    # コンテナ1:データを書き込む(料理人)
    - name: writer
      image: busybox
      command: ["sh", "-c", "while true; do date >> /shared-data/log.txt; sleep 5; done"]
      volumeMounts:
        - name: shared-volume
          mountPath: /shared-data       # 書き込み先

    # コンテナ2:データを読み取る(配膳係)
    - name: reader
      image: busybox
      command: ["sh", "-c", "tail -f /shared-data/log.txt"]
      volumeMounts:
        - name: shared-volume
          mountPath: /shared-data       # 読み取り元(同じボリューム)

  # emptyDir ボリューム(Pod起動時に空のディレクトリが作られる)
  volumes:
    - name: shared-volume
      emptyDir: {}

🍽️ 1つのPod(厨房)に2人のスタッフがいる状態です。

  • writer(料理人):調理台(/shared-data)に料理を置く
  • reader(配膳係):調理台から料理を取ってお客さんに運ぶ

2人が同じ調理台を共有している = emptyDir

6-5-1. 実行・確認

# ~/k8s-handson/config-secret-volume/ で実行

# Podを作成
kubectl apply -f pod-with-emptydir.yaml

# 10秒ほど待ってから、readerコンテナのログを確認
kubectl logs emptydir-demo -c reader

期待される出力:

Wed Jan  1 00:00:00 UTC 2026
Wed Jan  1 00:00:05 UTC 2026
Wed Jan  1 00:00:10 UTC 2026
...

writerが書いたデータを、readerが読めています。

🔰 Note: emptyDirは Podが削除されると消えます
永続化が必要なデータ(DB等)にはPersistentVolumeを使うそうです。
PersistentVolumeはクラウド環境(AWS EBS等)で本領を発揮するため、今回は概念のみ整理しています。


6-6. 後片付け

# 全てのリソースを削除
kubectl delete pod configmap-demo secret-demo nginx-config-demo emptydir-demo
kubectl delete configmap app-config nginx-config
kubectl delete secret app-secret

# 確認
kubectl get all
kubectl get configmap
kubectl get secret

7. つまずきポイント(体験談)

7-1. Secretの値をbase64エンコードし忘れる

症状: data フィールドにbase64でない文字列を書くとエラー。

# ❌ NG:data フィールドには base64 エンコード済みの値が必要
data:
  DB_PASSWORD: "super_secret_password"

# ✅ OK(方法1):base64 エンコード済みの値
data:
  DB_PASSWORD: "c3VwZXJfc2VjcmV0X3Bhc3N3b3Jk"

# ✅ OK(方法2):stringData を使う(推奨)
stringData:
  DB_PASSWORD: "super_secret_password"

対策: 学習中は stringData を使いましょう。
自動でbase64エンコードしてくれます。

7-2. ConfigMapを変更してもPodに反映されない

症状: ConfigMapを kubectl apply で更新したが、Pod内の環境変数が古いまま。

原因: 環境変数として渡した場合、Pod起動時に読み込まれるため、Podの再起動が必要です。

# Podを再起動する方法
kubectl delete pod <Pod名>
# Deploymentの場合は自動で新しいPodが作られる

# Deploymentの場合はrollout restartも使える
kubectl rollout restart deployment <Deployment名>

🔰 Note: ファイルマウント(Step3の方法)の場合は、ConfigMap変更後に自動で更新されるそうです(少し遅延あり)。
環境変数方式とファイルマウント方式で動作が違う点は要注意だと思いました。

7-3. volumeMountsの名前がvolumesと一致しない

症状:

Error: failed to create containerd task: ... mount source not found

対策: volumeMounts.namevolumes.name を必ず一致させてください。

# ✅ OK:name が一致
volumeMounts:
  - name: shared-volume       # ← ここと
    mountPath: /data
volumes:
  - name: shared-volume       # ← ここが一致
    emptyDir: {}

# ❌ NG:name が不一致
volumeMounts:
  - name: shared-vol          # ← shared-vol
    mountPath: /data
volumes:
  - name: shared-volume       # ← shared-volume(違う!)
    emptyDir: {}

8. まとめ

8-1. この記事でやったこと

# 内容 状態
1 ConfigMapで環境変数をPodに渡した
2 Secretでパスワードを安全に管理した
3 ConfigMapをファイルとしてマウントした
4 emptyDirでコンテナ間データ共有を体験した

8-2. 3つのリソースの使い分け(まとめ表)

観点 ConfigMap Secret Volume
用途 一般設定 秘密情報 データ保存
比喩 メニュー表 レシピ帳(金庫) 食材倉庫
渡し方 環境変数 or ファイル 環境変数 or ファイル ディレクトリマウント
暗号化 なし base64(+ RBAC制御) -
Pod削除時 残る 残る emptyDir: 消える / PV: 残る

8-3. Docker Compose → k8s 対応表

Docker Compose Kubernetes 備考
environment: ConfigMap + Secret k8sは性質で分離する
.env ファイル ConfigMap 環境変数の外部化
volumes: Volume(emptyDir / PV) k8sは種類が豊富
(該当なし) Secret Docker Composeには秘密管理の仕組みがない

9. 次回予告

第5回:Namespace / RBAC — マルチテナントと権限設計

設定もデータも分離できました。
でもまだ問題があります。

🍽️ チェーンレストランの全店舗が同じ厨房を共有している状態。
A店のスタッフがB店のレシピ帳を勝手に見れてしまう!

次回は:

  • Namespaceで「店舗ごとの区画」を作る
  • RBACで「誰が何をしていいか」を制御する
  • ServiceAccountの役割

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?