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のConfigMapとは何か?

0
Posted at

はじめに

前回は、Ingressを使ってクラスター外部からのHTTP・HTTPSリクエストをServiceへ振り分ける仕組みを学びました。

ブラウザ
    ↓
Ingress・ALB
    ↓
Service
    ↓
Pod
    ↓
Container

これで、リクエストがアプリケーションまで届く流れはつながりました。

しかし、アプリケーションを動かすには、コンテナイメージだけでなく次のような設定値も必要です。

実行環境:production
ログレベル:info
接続先ホスト名:redis-service
機能フラグ:有効

開発環境・ステージング環境・本番環境では、同じアプリケーションでも設定値が異なります。

設定値が変わるたびにDockerイメージを作り直すのでしょうか。

そこで登場するのが ConfigMap(コンフィグマップ) です。

この記事では、次の内容を整理します。

  • ConfigMapとは何か
  • Dockerイメージと設定値を分ける理由
  • ConfigMap・Deployment・Pod・Containerの関係
  • dataに設定値を書く方法
  • 環境変数として渡す方法
  • ファイルとしてマウントする方法
  • ConfigMapを更新したときの反映方法
  • Helmのvalues.yamlとの違い
  • ConfigMapとSecretの使い分け
  • EKSでも同じ仕組みなのか

結論

ConfigMapとは、機密情報ではない設定値を、コンテナイメージから分離して保存するKubernetesリソースです。

ConfigMap
├── RAILS_ENV=production
├── LOG_LEVEL=info
└── REDIS_HOST=redis-service
        ↓ 参照
DeploymentのPodテンプレート
        ↓
Pod
        ↓
Containerの環境変数・設定ファイル

ConfigMapへ値を書いただけでは、コンテナへ自動的に渡されません。

DeploymentやPodのYAMLからConfigMapを参照する必要があります。

ConfigMap
= 設定値の保管場所

Deployment
= どのConfigMapを、どのようにコンテナへ渡すか定義する

Pod・Container
= 渡された設定値を使ってアプリケーションを実行する

なぜDockerイメージへ設定値を入れないのか

たとえば、Railsアプリケーションを次の3環境で動かすとします。

環境 RAILS_ENV LOG_LEVEL
開発 development debug
ステージング staging info
本番 production warn

設定値をDockerイメージへ直接埋め込むと、環境ごとに別のイメージが必要になります。

開発用イメージ
ステージング用イメージ
本番用イメージ

これでは、ステージング環境で確認したイメージと、本番環境へデプロイするイメージが別物になります。

ConfigMapを使うと、イメージと設定値を分けられます。

同じDockerイメージ
├── 開発用ConfigMap
├── ステージング用ConfigMap
└── 本番用ConfigMap

つまり、アプリケーションの中身を変えずに、環境ごとの設定だけを切り替えられます。

ConfigMapは何を保存するのか

ConfigMapは、主に機密性のない設定値をキーと値の組み合わせで保存します。

キー             値
RAILS_ENV        production
LOG_LEVEL        info
REDIS_HOST       redis-service

向いている値の例は次のとおりです。

  • 実行環境名
  • ログレベル
  • Service名やホスト名
  • 公開して問題のないURL
  • タイムゾーン
  • 機能フラグ
  • アプリケーションの設定ファイル

一方、次の値はConfigMapへ入れません。

  • パスワード
  • APIトークン
  • 秘密鍵
  • データベースの認証情報

機密情報にはSecretを使用します。

ConfigMap・Deployment・Podの関係

ConfigMapは、それ自体がコンテナを動かすものではありません。

DeploymentのPodテンプレートからConfigMapを参照します。

ConfigMap
設定値を保持する
        ↓ 名前で参照
Deployment
Podテンプレートへ参照方法を書く
        ↓ Podを作成
Pod
        ↓
Container
設定値を環境変数・ファイルとして利用する

複数のDeploymentやPodから、同じConfigMapを参照することもできます。

                 ┌→ Rails Deployment → Rails Pod
app-config ──────┤
                 └→ Sidekiq Deployment → Sidekiq Pod

RailsとSidekiqを別Podで動かしていても、同じ設定が必要なら同じConfigMapを参照できます。

ConfigMapを定義するYAML

基本的なConfigMapは次のように定義します。

apiVersion: v1
kind: ConfigMap
metadata:
  name: app-config
data:
  RAILS_ENV: production
  LOG_LEVEL: info
  REDIS_HOST: redis-service
  FEATURE_NEW_EDITOR: "true"

このConfigMapには、4つの設定値が保存されています。

キー 値
RAILS_ENV production
LOG_LEVEL info
REDIS_HOST redis-service
FEATURE_NEW_EDITOR true

YAMLの各項目

apiVersionとkind

apiVersion: v1
kind: ConfigMap
項目 意味
apiVersion: v1 ConfigMapを扱うKubernetes APIのバージョン
kind: ConfigMap 作成するリソースがConfigMapであることを示す

metadata.name

metadata:
  name: app-config

ConfigMapの名前です。

DeploymentやPodは、この名前を使ってConfigMapを参照します。

data

data:
  RAILS_ENV: production
  LOG_LEVEL: info

dataには、文字列のキーと値を書きます。

数値や真偽値に見える値も、文字列として扱いたい場合は引用符で囲むと意図が明確です。

data:
  RETRY_COUNT: "3"
  FEATURE_ENABLED: "true"

ConfigMapにはspecではなく、主にdataやbinaryDataを記述します。

binaryDataには、Base64で表現したバイナリーデータを保存できます。

ただし、ConfigMapは大きなファイルの保管場所ではなく、1つのConfigMapに保存できるデータは1MiBまでです。

ConfigMapを作成する

YAMLをconfigmap.yamlとして保存した場合、次のコマンドで作成・更新できます。

kubectl apply -f configmap.yaml

一覧を確認します。

kubectl get configmap

短縮形も使用できます。

kubectl get cm

内容をYAMLで確認します。

kubectl get configmap app-config -o yaml

詳細やイベントを確認します。

kubectl describe configmap app-config

コマンドからConfigMapを作成する

ConfigMapは、YAMLだけでなくコマンドからも作成できます。

値を直接指定する

kubectl create configmap app-config \
  --from-literal=RAILS_ENV=production \
  --from-literal=LOG_LEVEL=info

ファイルから作成する

kubectl create configmap nginx-config \
  --from-file=nginx.conf

この場合、通常はファイル名がキー、ファイルの中身が値になります。

キー:nginx.conf
値:nginx.confの内容

Gitで設定を管理し、レビューしてから適用する場合は、YAMLとして記述する方法が分かりやすいです。

コンテナへ渡す方法

ConfigMapの代表的な渡し方は次の3つです。

方法 向いている用途
envFrom 複数のキーをまとめて環境変数にする
env.valueFrom.configMapKeyRef 必要なキーだけを環境変数にする
Volume 設定ファイルとしてマウントする

それぞれ確認します。

方法1:すべての値を環境変数として渡す

envFromを使うと、ConfigMapのdataにある値をまとめて環境変数へ渡せます。

apiVersion: apps/v1
kind: Deployment
metadata:
  name: rails
spec:
  replicas: 3
  selector:
    matchLabels:
      app: rails
  template:
    metadata:
      labels:
        app: rails
    spec:
      containers:
        - name: rails
          image: example/rails-app:1.0
          envFrom:
            - configMapRef:
                name: app-config

ここで参照しているのは、次のConfigMapです。

apiVersion: v1
kind: ConfigMap
metadata:
  name: app-config
data:
  RAILS_ENV: production
  LOG_LEVEL: info
  REDIS_HOST: redis-service

コンテナ内では、次の環境変数として利用できます。

RAILS_ENV=production
LOG_LEVEL=info
REDIS_HOST=redis-service

Railsアプリケーションからは、たとえば次のように取得できます。

ENV.fetch("LOG_LEVEL", "info")

方法2:必要な値だけを環境変数として渡す

configMapKeyRefを使うと、ConfigMapの特定のキーだけを参照できます。

apiVersion: apps/v1
kind: Deployment
metadata:
  name: rails
spec:
  replicas: 3
  selector:
    matchLabels:
      app: rails
  template:
    metadata:
      labels:
        app: rails
    spec:
      containers:
        - name: rails
          image: example/rails-app:1.0
          env:
            - name: APP_LOG_LEVEL
              valueFrom:
                configMapKeyRef:
                  name: app-config
                  key: LOG_LEVEL

この設定では、ConfigMapのLOG_LEVELを、コンテナ内のAPP_LOG_LEVELとして渡します。

ConfigMap
LOG_LEVEL=info
        ↓
Container
APP_LOG_LEVEL=info

ConfigMap側のキー名と、コンテナ内の環境変数名を変えることもできます。

必要な値が明確になるため、参照する設定を限定したい場合に向いています。

方法3:設定ファイルとしてマウントする

ConfigMapは、環境変数だけでなくファイルとしてPodへマウントできます。

まず、ファイルの内容をConfigMapへ定義します。

apiVersion: v1
kind: ConfigMap
metadata:
  name: app-file-config
data:
  application.yml: |
    app_name: freee-sign
    log_level: info
    timeout_seconds: 30

|を使うと、複数行の文字列を記述できます。

Deployment側では、VolumeとしてConfigMapを参照します。

apiVersion: apps/v1
kind: Deployment
metadata:
  name: rails
spec:
  replicas: 3
  selector:
    matchLabels:
      app: rails
  template:
    metadata:
      labels:
        app: rails
    spec:
      containers:
        - name: rails
          image: example/rails-app:1.0
          volumeMounts:
            - name: app-config-volume
              mountPath: /app/config/runtime
              readOnly: true
      volumes:
        - name: app-config-volume
          configMap:
            name: app-file-config

コンテナ内には、次のファイルが作られます。

/app/config/runtime/application.yml

関係は次のとおりです。

ConfigMapのキー
application.yml
        ↓ ファイル名になる
/app/config/runtime/application.yml
        ↑
ConfigMapの値がファイル内容になる

1つのConfigMapに複数のキーがある場合、通常はキーごとにファイルが作られます。

環境変数とファイルのどちらを使うのか

観点 環境変数 Volumeによるファイル
向いている値 短い単一の値 複数行の設定ファイル
アプリの読み方 ENVなど ファイルを開く
ConfigMap更新 実行中のコンテナへ自動反映されない 最終的にマウントへ反映される
アプリ側の対応 Podの再作成が必要 ファイルの再読み込みが必要な場合がある

たとえば、次の値は環境変数に向いています。

LOG_LEVEL=info
REDIS_HOST=redis-service

次のような設定は、ファイルに向いています。

logging:
  level: info
  format: json
  output: stdout

ConfigMapを更新するとどうなるのか

ここは重要です。

ConfigMapを更新しても、利用方法によって反映のされ方が異なります。

環境変数として利用している場合

ConfigMapを更新しても、すでに起動しているコンテナの環境変数は変わりません。

ConfigMapを更新
        ↓
起動済みContainerの環境変数
        × そのまま

新しい値を使うには、Podを再作成します。

Deploymentを再起動する例です。

kubectl rollout restart deployment/rails

状態を確認します。

kubectl rollout status deployment/rails

Deploymentが新しいPodを作成すると、そのPodは更新後のConfigMapを読み込みます。

Volumeとして利用している場合

VolumeとしてマウントしたConfigMapは、ConfigMapの更新後、時間を置いてPod内のファイルにも反映されます。

ただし、即時反映ではありません。

ConfigMapを更新
        ↓
kubeletが変更を検知
        ↓
マウントされたファイルを更新

また、ファイルが変わっても、アプリケーションが自動で読み直すとは限りません。

アプリケーション側に設定ファイルを再読み込みする仕組みがなければ、Podの再作成が必要です。

なお、subPathを使ってConfigMapの一部だけをマウントしたコンテナには、ConfigMapの更新が反映されません。

ConfigMapの変更でDeploymentは自動再起動しない

ConfigMapとDeploymentは別のKubernetesリソースです。

ConfigMapだけを更新しても、DeploymentのPodテンプレートは変更されていません。

そのため、Deploymentは自動的にローリングアップデートを始めません。

ConfigMapだけ変更
        ↓
DeploymentのPodテンプレートは同じ
        ↓
新しいPodを作るきっかけがない

環境変数として利用する場合は、次のような運用が必要です。

  • kubectl rollout restartでDeploymentを再起動する
  • ConfigMap名へバージョンを付ける
  • HelmでConfigMap内容のチェックサムをPodテンプレートのannotationへ入れる

ConfigMap名へバージョンを付ける例です。

app-config-v1
        ↓ 更新時
app-config-v2

Deploymentが参照する名前をapp-config-v2へ変えると、Podテンプレートが変わるためローリングアップデートが始まります。

ConfigMapとNamespace

ConfigMapはNamespaceに所属するリソースです。

通常、Podは同じNamespaceにあるConfigMapを参照します。

Namespace: production
├── ConfigMap: app-config
└── Pod: rails-xxxxx
        ○ 参照できる

Namespace: staging
└── Pod: rails-yyyyy
        × productionのConfigMapは直接参照できない

環境ごとにNamespaceを分ける場合は、それぞれのNamespaceへConfigMapを作成します。

kubectl apply -f configmap.yaml -n production

確認するときもNamespaceを指定します。

kubectl get configmap app-config -n production

存在しないConfigMapを参照するとどうなるのか

Podが必須のConfigMapを参照しているのに、そのConfigMapが存在しない場合、Podは正常に起動できません。

Deployment
└── app-configを参照

app-configが存在しない
        ↓
Podの起動に失敗・待機

まずConfigMapを作成し、その後にDeploymentを作成するのが基本です。

意図的にConfigMapがなくても起動させたい場合は、参照へoptional: trueを指定できます。

envFrom:
  - configMapRef:
      name: optional-config
      optional: true

ただし、アプリケーションに必須の設定までoptionalにすると、起動後に別のエラーが発生する可能性があります。

immutableなConfigMap

変更させたくないConfigMapには、immutable: trueを指定できます。

apiVersion: v1
kind: ConfigMap
metadata:
  name: app-config-v1
immutable: true
data:
  RAILS_ENV: production
  LOG_LEVEL: info

immutableなConfigMapは、作成後にdataやbinaryDataの内容を変更できません。

変更が必要な場合は、新しい名前のConfigMapを作成し、参照先を切り替えます。

app-config-v1
        ↓ 新しい設定を作成
app-config-v2

誤った変更を防ぎたい場合や、設定をバージョン管理したい場合に利用できます。

ConfigMapとSecretの違い

ConfigMapとSecretは、どちらも設定値をPodへ渡すために利用できます。

違いは、保存するデータの性質です。

リソース 保存するもの 例
ConfigMap 機密ではない設定値 ログレベル、ホスト名、機能フラグ
Secret 機密情報 パスワード、トークン、秘密鍵
LOG_LEVEL=info
→ ConfigMap

DATABASE_PASSWORD=xxxxx
→ Secret

ConfigMapには秘密を守る暗号化機能はありません。

そのため、パスワードをConfigMapへ保存してはいけません。

Secretの仕組みと注意点は、次の記事で詳しく扱います。

ConfigMapとHelmのvalues.yamlの違い

Helmを利用していると、values.yamlとConfigMapの違いが分かりにくくなることがあります。

values.yamlは、Helmテンプレートへ値を渡すためのファイルです。

ConfigMapは、Kubernetesクラスター上に作られるリソースです。

values.yaml
Helmへ渡す値
        ↓
templates/configmap.yaml
テンプレートへ値を埋め込む
        ↓ helm template・helm install・helm upgrade
ConfigMap
Kubernetes上に作成される
        ↓
Deployment・Podから参照される

たとえば、values.yamlへ次の値を書きます。

appConfig:
  railsEnv: production
  logLevel: info

ConfigMapのテンプレートでは、次のように参照できます。

apiVersion: v1
kind: ConfigMap
metadata:
  name: {{ .Release.Name }}-app-config
data:
  RAILS_ENV: {{ .Values.appConfig.railsEnv | quote }}
  LOG_LEVEL: {{ .Values.appConfig.logLevel | quote }}

Helmがテンプレートを展開した結果、通常のConfigMap YAMLが生成されます。

つまり、values.yaml自体をPodが直接読むわけではありません。

EKSでも同じ仕組みなのか

はい。同じです。

ConfigMapはAWS独自の機能ではなく、Kubernetes標準のリソースです。

EKSクラスター
└── Kubernetes API
    ├── Deployment
    ├── Service
    ├── Ingress
    ├── ConfigMap
    └── Secret

EKSでも、ConfigMapを作成し、DeploymentのPodテンプレートから参照します。

ConfigMapのデータをコンテナへ渡す処理は、各Nodeで動くkubeletが担当します。

RailsとSidekiqで同じConfigMapを使う例

RailsとSidekiqを別Deploymentで動かす場合を考えます。

app-config
├── RAILS_ENV=production
├── LOG_LEVEL=info
└── REDIS_HOST=redis-service
        ↓
        ├── Rails Deployment
        │       └── Rails Pod
        │
        └── Sidekiq Deployment
                └── Sidekiq Pod

Rails Deploymentでは次のように参照します。

containers:
  - name: rails
    image: example/rails-app:1.0
    envFrom:
      - configMapRef:
          name: app-config

Sidekiq Deploymentでも同じConfigMapを参照します。

containers:
  - name: sidekiq
    image: example/rails-app:1.0
    command: ["bundle", "exec", "sidekiq"]
    envFrom:
      - configMapRef:
          name: app-config

同じDockerイメージと同じConfigMapを利用しながら、コンテナの起動コマンドを変えられます。

ただし、ConfigMapを環境変数として渡しているため、設定変更時にはRailsとSidekiqの両方のPodを再作成する必要があります。

kubectl rollout restart deployment/rails
kubectl rollout restart deployment/sidekiq

実際に値が渡されたか確認する

Pod名を確認します。

kubectl get pods

コンテナ内の環境変数を確認します。

kubectl exec rails-xxxxx -- printenv LOG_LEVEL

出力例です。

info

ファイルとしてマウントした場合は、ファイルを確認します。

kubectl exec rails-xxxxx -- cat /app/config/runtime/application.yml

Podが起動しない場合は、詳細とイベントを確認します。

kubectl describe pod rails-xxxxx

次の点を確認します。

  • ConfigMapが存在するか
  • ConfigMapとPodが同じNamespaceにあるか
  • ConfigMap名が一致しているか
  • 参照しているキーが存在するか
  • YAMLのインデントが正しいか

よくある勘違い

ConfigMapを作れば自動的に全Podへ渡される

自動では渡されません。

DeploymentやPodのYAMLから、ConfigMapを明示的に参照します。

ConfigMapを更新すれば環境変数もすぐ変わる

起動済みコンテナの環境変数は変わりません。

新しい値を反映するには、Podの再作成が必要です。

ConfigMapを更新すればDeploymentが自動再起動する

ConfigMapの更新だけでは、Deploymentは自動再起動しません。

再起動する運用や、変更をPodテンプレートへ反映する仕組みが必要です。

ConfigMapならパスワードも保存できる

保存自体はできてしまいますが、ConfigMapは機密情報を守るためのリソースではありません。

パスワードやトークンにはSecretを使用します。

ConfigMapは環境変数専用である

環境変数だけでなく、コマンド引数やVolume内の設定ファイルとしても利用できます。

ConfigMapには何でも保存できる

ConfigMapは大きなデータの保存には向いておらず、サイズ上限は1MiBです。

画像や大きなファイルを保管するストレージとしては使いません。

これまでの流れへConfigMapを加える

ここまで学んだリソースを並べると、次のようになります。

Node
└── Podを動かすサーバー
        ↑
Deployment
└── Podの数と更新を管理する
        ↓
Pod
└── Containerを動かす単位
        ↑
Service
└── 変化するPod群へ安定した接続先を提供する
        ↑
Ingress
└── 外部のHTTP・HTTPS通信をServiceへ振り分ける

ConfigMap
└── Pod内のContainerへ機密ではない設定値を渡す

通信経路と設定値の経路を分けて考えると、理解しやすくなります。

通信の流れ
Ingress → Service → Pod → Container

設定の流れ
ConfigMap → Pod → Container

まとめ

ConfigMapは、機密ではない設定値をコンテナイメージから分離して管理するKubernetesリソースです。

ConfigMap
= 設定値の保管場所

Deployment・Pod
= ConfigMapの参照方法を定義する

Container
= 環境変数やファイルとして設定値を使う

重要な点は次のとおりです。

  • ConfigMapには機密ではない設定値を保存する
  • 同じDockerイメージへ環境ごとの設定を渡せる
  • envFromで複数の値をまとめて環境変数にできる
  • configMapKeyRefで特定の値だけを参照できる
  • Volumeを使うと設定ファイルとしてマウントできる
  • 環境変数はConfigMap更新だけでは変わらない
  • ConfigMapの更新だけではDeploymentは再起動しない
  • PodとConfigMapは通常、同じNamespaceに置く
  • パスワードやトークンにはConfigMapではなくSecretを使う
  • values.yamlはHelmの入力であり、ConfigMapはKubernetesリソースである

一言でまとめると、次のようになります。

ConfigMapは、同じコンテナイメージを使いながら、環境ごとの設定値を外から渡す仕組みです。

次は、パスワード・APIトークン・秘密鍵などの機密情報をPodへ渡す Secret について整理します。

参考資料

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?