はじめに
Kubernetesでは設定ファイルや環境変数をConfigMapとして登録し、それをPodから利用できます。
ただ、少しクセがあるので、自分の中の知識をtipsとしてまとめておきます。
記事は、ConfigMapの作成編・ConfigMapのマウント編の2章構成になっています。
また、内容はほとんど各節に収まるように書いているので、逆引きのように見ていただくことが可能です。
実施環境
- Kubernetes v1.20.2
ConfigMapの作成編
この章ではやりたいこと別にConfigMapをどう定義すれば良いかをまとめます。
Podから環境変数として利用したい
この場合は環境変数として定義したい変数を、それぞれdataのサブマッピングとして書きましょう。
apiVersion: v1
kind: ConfigMap
metadata:
name: sample-config-map
data:
ENV1: foo
ENV2: bar
Podにファイルとしてマウントしたい
dataのサブマッピングにファイル内容を直接記載しましょう。
ただし、例に挙げている.env
をPodに環境変数として直接展開することはできません (例えば、docker-composeのenv_file
のような使い方) 。
apiVersion: v1
kind: ConfigMap
metadata:
name: sample-config-map
data:
.env: |
ENV1=foo
ENV2=bar
nginx.conf: |
user nginx;
worker_processes 1;
pid /var/run/nginx.pid;
http {
include /etc/nginx/conf.d/*.conf;
}
Kustomizeで環境変数をPodに渡したい
Kustomizeを使用している場合、configMapGenerator
を用いてConfigMapを作成することが可能です。
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
configMapGenerator:
- name: sample-config-map
literals:
- ENV1=foo
- ENV2=bar
環境変数をPodに埋め込む場合、configMapGeneratorと通常のConfigMapはどっちが良いのか?
ここで実例を紹介すると本筋から逸れるので、参考記事を紹介しておきます。
結論だけ言うと、
- 通常のConfigMapをPodから
envFrom
等で環境変数として読み込む場合は、ConfigMapを変更してもそれを利用するPodは再作成されない - つまり、Podに最新値は自動的に反映されないので、別途Podの再作成を考慮してあげる必要がある
- Deploymentの場合は、
kubectl rollout restart
- Deploymentの場合は、
- configMapGeneratorを使う場合、ConfigMapに毎回ハッシュ値が付与され名前が変わるため、Podが毎回自動的に再作成され最新値が反映される
という違いを抑えて、どちらを使うか選びましょうということです。
参考記事
KustomizeでファイルからConfigMapを作成したい
Kustomizeを使用している場合、configMapGenerator
を用いてファイルからConfigMapを作成することも可能です。
この例の場合、kustomization.yamlが置いてあるディレクトリ直下に.env
とnginx.conf
が置いてある必要があります。
kustomization.yamlが置いてあるディレクトリ以下であれば、さらにディレクトリを掘っても構いません。
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
configMapGenerator:
- name: sample-config-map
files:
- .env
- nginx.conf
Volumeを使ってファイルをマウントする場合、configMapGeneratorと通常のConfigMapはどっちが良いのか?
先の環境変数を埋め込む場合と同様の議論ですが少し違うところがあります。
configMapGeneratorを使う場合は変更の度にPodが再作成され、毎回自動的に値が反映されます。
通常のConfigMapをマウントする場合、次章で紹介するsubPath
を指定するかどうかで挙動が変わります。
- subPathを指定しない場合
- Podの再作成をすることなく値が自動的に反映される
- ただし、多少の遅延あり
- subPathを指定する場合
- Podの再作成を手動でしてあげないと値が反映されない
Volumeを使用するマウント方法は、Podの再作成なしに値を反映することも可能なので、よく考えて適切な方法を選びましょう。
参考記事
ConfigMapのマウント編
この章では、ConfigMapをマウントする方法を紹介します。
ただし簡単のため、例として挙げるConfigMapには通常のConfigMapを使用します。
KustomizeのconfigMapGeneratorを使う場合もやり方は変わりません。
環境変数として読み込みたい
まずは、ConfigMapに記載してあるdataのサブマッピングを全て環境変数として読み込む方法です。
---
apiVersion: v1
kind: ConfigMap
metadata:
name: sample-config-map
data:
ENV1: foo
ENV2: bar
---
apiVersion: v1
kind: Pod
metadata:
name: sample-pod
spec:
containers:
- name: nginx
image: nginx:stable
envFrom:
- configMapRef:
name: sample-config-map
Pod作成後に次のコマンドで確認してみると、環境変数として定義されていることがわかります。
$ kubectl exec -it sample-pod -- sh -c 'echo $ENV1; echo $ENV2'
foo
bar
特定の変数だけ環境変数として読み込みたい
先の例のsample-pod
のspec
を次の内容に書き換えると、ENV2のみ環境変数を定義することが可能です。
フォーマットから分かるように、このやり方はConfigMapに記載してある名前とは別の名前で環境変数を定義することも可能です。
---
apiVersion: v1
kind: ConfigMap
metadata:
name: sample-config-map
data:
ENV1: foo
ENV2: bar
---
spec:
containers:
- name: nginx
image: nginx:stable
env:
- name: ENV2
valueFrom:
configMapKeyRef:
name: sample-config-map
key: ENV2
Pod作成後に次のコマンドを実行すると、ENV2
だけ定義されていることがわかります。
$ kubectl exec -it sample-pod -- sh -c 'echo $ENV1; echo $ENV2'
bar
ファイルをコンテナにマウントしたい
ConfigMapに記載した内容をファイルとしてPodにマウントする方法です。
ConfigMapから作成したVolumeのpathをvolumeMounts[].subPath
に指定することで、volumeMounts[].mountPath
に指定したコンテナ内のファイルに1:1の関係でマウントすることができます。
---
apiVersion: v1
kind: ConfigMap
metadata:
name: sample-config-map
data:
nginx.conf: |
user nginx;
worker_processes 1;
pid /var/run/nginx.pid;
events {
worker_connections 1024;
}
http {
include /etc/nginx/conf.d/*.conf;
}
---
apiVersion: v1
kind: Pod
metadata:
name: sample-pod
spec:
containers:
- name: nginx
image: nginx:stable
volumeMounts:
- mountPath: /etc/nginx/nginx.conf
subPath: nginx.conf
name: nginx-conf
volumes:
- name: nginx-conf
configMap:
name: sample-config-map
items:
- key: nginx.conf
path: nginx.conf
Pod作成後に次のコマンドを実行してみると、マウントできていることが確認できます。
$ kubectl exec -it sample-pod -- ls /etc/nginx
conf.d mime.types nginx.conf uwsgi_params
fastcgi_params modules scgi_params
$ kubectl exec -it sample-pod -- cat /etc/nginx/nginx.conf
user nginx;
worker_processes 1;
pid /var/run/nginx.pid;
events {
worker_connections 1024;
}
http {
include /etc/nginx/conf.d/*.conf;
}
ディレクトリごとコンテナにマウントしたい
ConfigMapから作成したVolumeをディレクトリとしてマウントする方法です。
マウント先に指定したディレクトリの内容は、マウントしたVolumeの内容に置き換わるので、元ファイルが消える(使えない)ことに注意してください。
---
apiVersion: v1
kind: ConfigMap
metadata:
name: sample-config-map
data:
nginx.conf: |
user nginx;
worker_processes 1;
pid /var/run/nginx.pid;
events {
worker_connections 1024;
}
http {
server {
listen 80;
location / {
root /usr/share/nginx/html;
index index.html;
}
}
}
---
apiVersion: v1
kind: Pod
metadata:
name: sample-pod
spec:
containers:
- name: nginx
image: nginx:stable
volumeMounts:
- mountPath: /etc/nginx
name: nginx-conf
volumes:
- name: nginx-conf
configMap:
name: sample-config-map
items:
- key: nginx.conf
path: nginx.conf
Pod作成後に次のコマンドを実行してみると、/etc/nginx
がマウントしたVolumeに置き換わっていることがわかります。
$ kubectl exec -it sample-pod -- ls /etc/nginx
nginx.conf
$ kubectl exec -it sample-pod -- cat /etc/nginx/nginx.conf
user nginx;
worker_processes 1;
pid /var/run/nginx.pid;
events {
worker_connections 1024;
}
http {
server {
listen 80;
location / {
root /usr/share/nginx/html;
index index.html;
}
}
}
実行可能ファイルをマウントしたい
ConfigMapから作成したVolumeのファイルは原則644
のパーミッションとなります。
実行権限を付けたい場合は、mode
を指定してあげる必要があります。
---
apiVersion: v1
kind: ConfigMap
metadata:
name: sample-config-map
data:
entrypoint.sh: |
#!/bin/sh
echo "Hello, ConfigMap!\n"
---
apiVersion: v1
kind: Pod
metadata:
name: sample-pod
spec:
containers:
- name: nginx
image: nginx:stable
volumeMounts:
- mountPath: /run/entrypoint.sh
subPath: entrypoint.sh
name: entrypoint
volumes:
- name: entrypoint
configMap:
name: sample-config-map
items:
- key: entrypoint.sh
path: entrypoint.sh
mode: 0755
Pod作成後に次のコマンドを実行すると、実行可能なパーミッションが付与されていることがわかります。
$ kubectl exec -it sample-pod -- ls -l /run/entrypoint.sh
-rwxr-xr-x 1 root root 37 May 23 08:48 /run/entrypoint.sh
補足
mode: 0755
を指定せずにPodを作成した場合のパーミッションは次のようになります。
-rw-r--r-- 1 root root 37 May 23 08:49 /run/entrypoint.sh
マウントするファイルのグループ権限をroot以外にしたい
これまでの例を見てお気づきの方もいらっしゃると思いますが、ConfigMapのVolumeをPodにマウントするとき、デフォルトではroot権限でマウントされます。
これが意味するところは、アプリケーションの実行ユーザーがroot以外のときに、マウントしたファイルを読み込むことができないということになります(厳密に言うとできますが、、)。
ここでは、マウントするファイルのグループ権限を、実行ユーザーの所属するグループに変更する方法を紹介します。
spec.securityContext.fsGroup
に付与するグループのidを指定します。
---
apiVersion: v1
kind: ConfigMap
metadata:
name: sample-config-map
data:
entrypoint.sh: |
#!/bin/sh
echo "Hello, ConfigMap!\n"
---
apiVersion: v1
kind: Pod
metadata:
name: sample-pod
spec:
securityContext:
fsGroup: 101
containers:
- name: nginx
image: nginx:stable
volumeMounts:
- mountPath: /run/entrypoint.sh
subPath: entrypoint.sh
name: entrypoint
volumes:
- name: entrypoint
configMap:
name: sample-config-map
items:
- key: entrypoint.sh
path: entrypoint.sh
Pod作成後に次のコマンドを実行すると、マウントしたファイルのグループ権限が変更されていることがわかります。
$ kubectl exec -it sample-pod -- ls -l /run/entrypoint.sh
-rw-r--r-- 1 root nginx 37 May 23 09:05 /run/entrypoint.sh
注意
フォーマットから分かる通り、spec.securityContext
は、そのPod内のコンテナ全てに影響します。
次の例は、PHPの公式イメージに同じファイルをマウントしてみた例です。
$ kubectl exec -it sample-pod -c php-fpm -- ls -l /run/entrypoint.sh
-rw-r--r-- 1 root 101 37 May 23 12:53 /run/entrypoint.sh
ここから分かることは、意図しないグループ権限の変更をしてしまう可能性が高いということです。
例えば、php.ini
をマウントしたとしてもグループ権限が101になってしまいます。
言い換えると、nginxコンテナには101、phpコンテナには102、のようなコンテナごとに権限を変えることができず、ひとつしか指定できないことを意味します。
また、実はspec.containers[].securityContext
というのもありますが、fsGroup
が存在しないため、残念ながらコンテナごとに設定することはできません。
マウントするファイルのオーナー権限をroot以外にしたい
これは少しトリッキーな方法を使う必要があります。
Podが起動しているときだけ有効な一時的な保存領域emptyDir
を使用する方法です。
initContainers
の前処理を利用して、ConfigMap
の中身をemptyDir
の方に移し権限を書き換えておき、本体のコンテナではemptyDir
をマウントするというものです。
---
apiVersion: v1
kind: ConfigMap
metadata:
name: sample-config-map
data:
entrypoint.sh: |
#!/bin/sh
echo "Hello, ConfigMap!\n"
---
apiVersion: v1
kind: Pod
metadata:
name: sample-pod
spec:
initContainers:
- name: acquire-ownership
image: nginx:stable
args: ["sh", "-c", "cp /run/entrypoint.sh /ownerships; chown -R 101:101 /ownerships"]
volumeMounts:
- mountPath: /run/entrypoint.sh
subPath: entrypoint.sh
name: entrypoint
- mountPath: /ownerships
name: entrypoint-with-ownership
containers:
- name: nginx
image: nginx:stable
volumeMounts:
- mountPath: /ownerships
name: entrypoint-with-ownership
volumes:
- name: entrypoint
configMap:
name: sample-config-map
items:
- key: entrypoint.sh
path: entrypoint.sh
- name: entrypoint-with-ownership
emptyDir: {}
Pod作成後に次のコマンドを実行すると、権限が書き換わっていることを確認できます。
$ kubectl exec -it sample-pod -c nginx -- ls -l ownerships
total 4
-rw-r--r-- 1 nginx nginx 37 May 23 13:32 entrypoint.sh
おわりに
Kubernetesは進化が非常に早いです。
ここにまとめた方法より良い方法がすぐに登場するかもしれません。
もしくは、書いている時点でそのやり方古いよというのもあるかもしれません。
気づかれた方はコメント頂けるとありがたいです。