Kubernetesでコンテナに設定ファイルを追加したい場合にDockerイメージを作成せずに運用していくtips
大島 雅人
こんにちは、スタディサプリ ENGLISH SREグループの大島です。
本記事では、設定ファイルをConfigMapとして作成して起動時にvolumeとしてmountすることで、わざわざDockerイメージをビルドしてpushする運用をしなくてもよくなるというtipsを紹介します。
スタディサプリ ENGLISHでは、EKS+Argo CD+Kustomizeでのgitopsを採用しており、その場合の設定の仕方について説明していきます。
設定ファイルをinjectしたいモチベーションの背景
今回、説明する題材について簡単に説明します。
スタディサプリ ENGLISHでは、監視システムとして、Datadogを利用しています。
DatadogのagentはDockerコンテナとして起動できるようになっており、設定ファイルを追加すると、追加のメトリクスを取得してくれるという仕組みがあり、その際に設定ファイルをコンテナに含めるためにDocker Imageをビルドしていました。
例えば、MySQLのintegrationはコンテナ内にもともと存在するexampleファイルを変更することで、自動的にMySQLのmetricsを取得してくれるようになります。
設定ファイルはこのようなパスでexampleとしてyamlファイルが用意されています。
$ docker container run -t datadog/agent ls /etc/datadog-agent/conf.d/mysql.d/
conf.yaml.example
ファイルの中身はこのようになっています。よくある設定ファイルの形式だと思います。
init_config:
instances:
## @param server - string - required
## MySQL server to connect to.
## NOTE: Even if the server name is "localhost", the agent connects to MySQL using TCP/IP, unless you also
## provide a value for the sock key (below).
#
- server: localhost
## @param user - string - required
## Datadog Username created to connect to MySQL.
#
user: datadog
~略~
このファイルを変更して、イメージに含めるには以下のようにDockerfileを書いてビルドするしかないと思っていました。
FROM datadog/agent
COPY conf.yaml /etc/datadog-agent/conf.d/mysql.d/
# start.shは自分たちで作る
# 環境変数を利用してconfigを環境ごとに起動時に切り替える置換処理が入っている
CMD ["/start.sh"]
この方法でも特に問題はないのですが、我々の場合、こういった頻繁にデプロイしないようなメインのアプリケーションではない補助的なイメージのためのCI/CDを組んでこなかったというのがあります。
CI/CDが整っていないと、イメージの更新なども放置しがちになってしまうので、configMapをinjectする方法で管理するようにしてみたという背景です。
また、上記のDockerfileのコメントにあるように、自前の起動用のシェルスクリプトを用意しないといけないというのも手間でした。
これらの問題をKustomizeとConfigMapを使って解決することができます。
ConfigMapはファイルから生成できる
まず、ConfigMapはliteralだけでなく、ファイルからも作成することができます。ファイルの中身も特定のフォーマットである必要もなく、そのまま作成できます。
kubectlで言えば次のコマンドでこういうコマンドに相当します。
kubectl create cm mysql-conf --from-file conf.yaml
ただ、Argo CDを導入しているので、kubectlで作成するのではなく、宣言的にyamlで記述しないといけません。
KustomizeでConfigMapを生成する
我々の場合、Argo CDとKustomizeを組み合わせているので、Kustomizeの機能で動的に生成するようにします。
Kustomizeには、ConfigMapを生成するConfigMapGenerator
という仕組みがあります。
以下のように書くことができます。
configMapGenerator:
- name: mysql-conf
files:
- dd-agent/conf.d/mysql.d/conf.yaml
ConfigMapをmountする
ConfigMapとして設定ファイルが作成されるので、このConfigMapをpodから読み込めるようにする必要があります。
これは、volumes
とvolumemMounts
を利用して記述できます。
以下がDeploymentのyamlの設定です。
このように、DockerHubにおいてあるオフィシャルイメージをそのまま使って、必要なconfigだけを追加することができています。
apiVersion: apps/v1
kind: Deployment
metadata:
name: dd-agent
labels:
app: dd-agent
namespace: kube-system
spec:
replicas: 1
selector:
matchLabels:
app: dd-agent
template:
spec:
containers:
- name: dd-agent
image: datadog/agent # officialイメージをそのまま使っている
imagePullPolicy: Always
volumeMounts:
- name: mysql-conf
mountPath: /etc/datadog-agent/conf.d/mysql.d # configMapで追加した設定ファイルをmountする
volumes:
- name: mysql-conf
configMap:
name: mysql-conf
items:
- key: conf.yaml
path: conf.yaml
mode: 0644 # 設定ファイルの権限
configMapGeneratorを使っているので、kustomize build
すると以下のようにhashがsuffixにきます。
なぜsuffixがつくのかということについては、kustomizeの公式documentをご覧ください。
volumes:
- configMap:
items:
- key: conf.yaml
mode: 420
path: conf.yaml
name: mysql-conf-12345abcde # hashがgeneratedされる
name: mysql-conf
最終的な構成
以下のような構成でファイルを置くことで、gitopsで設定をinjectすることができます。
設定ファイル(conf.yaml)内には、環境ごとの設定なども入るので、overlaysで各環境ごとに用意してあげれば、環境ごとにイメージをビルドしないといけないということもないようにすることができます。
manifests
├── dd-agent
│ ├── deployment.yaml
│ └── kustomization.yaml
└── overlays
└── production
├── dd-agent
│ ├── conf.d
│ │ └── mysql.d
│ │ └── conf.yaml # 環境ごとの設定ファイルが入る
│ └── deployment.yaml
└── kustomization.yaml
[おまけ]はまったtips
1. volumeをmountしたときのファイルのownerはroot:root
になる
はまったという程でもないですが、mountする際のファイルの権限はroot:root
になるようです。
今回のdatadog/agent
は設定ファイルのownerが異なっていても、readさえできればよいので問題にはなりませんでしたが、ownerが起動プロセスと同じである必要がある場合は、注意が必要です。
2. volumesで指定するmode
はkustomize buildすると10進数になるけど気にしない
公式referenceにも先頭に0をつけて、8進数で書くようにという指定があります。
Optional: mode bits to use on this file, must be a value between 0 and 0777. If not specified, the volume defaultMode will be used. This might be in conflict with other options that affect the file mode, like fsGroup, and the result can be other mode bits set.
volumes:
- name: mysql-conf
configMap:
name: mysql-conf
items:
- key: conf.yaml
path: conf.yaml
mode: 0644 # 設定ファイルの権限
が、kustomize build
すると、10進数に変換されて420
とoutputされます。えっ、この数字どこからきたの・・と0644
から0
を消してみると、今度は、must be a number between 0 and 0777 (octal)
というエラーになり、ハマってしまいましたが、結論としては、気にせずそのままでOKということです。
こちらのissueのコメントで、8進数を10進数に変換しても数値としては、同じでしょと書いてあり、確かに!!となりました。そしてファイルの権限ってそもそも2進数を8進数とした表現だったんだということに今更気づきました・・w
3. configMapGeneratorはnamespaceを揃えないとhashがつかない
こちらは、kustomizeの仕様かと思いますが、deploymentのvolumesとconfigMapGeneratorでnamespaceが揃っていないと、kustomize build
してもconfigMapへのhashがつかないため、起動時にエラーになるという問題がありました。
これらは、明示的にnamespaceを指定してあげればhashがつくようになりました。
対応としては以下のどちらかのパターンで解決できます。
A. kustomization.yamlでnamespaceを指定する
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
+ namespace: kube-system
B. configMapGeneratorとDeploymentでnamespaceを揃えるようにする
configMapGenerator:
- name: mysql-conf
+ namespace: kube-system
files:
- dd-agent/conf.d/mysql.d/conf.yaml