概要
コンテナのポータビリティのため、設定値を環境変数で管理することは一般的ですが、
といった際にYAMLで管理すると便利です。
KubernetesではConfigMapがあるためそれを使ってYAMLファイルをマウントします。
環境
- Go v1.22.2
- Kubernetes v1.26.14
- Kustomize v5.1.1
対応方法
以下のような設定値をYAMLで管理するとします。
--- params: endpoint: example.com timeout: 10s retry: 3 key: |- -----BEGIN PUBLIC KEY----- MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA7v6LsXjyw... -----END PUBLIC KEY-----
コード
と同じようなコードを用意します。
type ServerParams struct { Endpoint string `yaml:"endpoint"` Timeout time.Duration `yaml:"timeout" default:"10s"` Retry int `yaml:"retry" default:"3"` } type Config struct { Params ServerParams `yaml:"params"` Key string `yaml:"key"` } type EnvVars struct { ConfigPath string `split_words:"true" default:"./config.yaml"` } func main() { var s EnvVars err := envconfig.Process("", &s) if err != nil { log.Fatal(err.Error()) } b, err := os.ReadFile(s.ConfigPath) if err != nil { log.Fatal(err) } cfg := Config{} if err := yaml.Unmarshal(b, &cfg); err != nil { log.Fatal(err) } fmt.Printf("%#v\n", cfg) }
きちんと読み込めることをローカルで確認しておきます。
$ go run main.go Config{Params:main.ServerParams{Endpoint:"https://example.com", Timeout:0, Retry:0}, Key:"-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA7v6LsXjyw...\n-----END PUBLIC KEY-----"}
Kubernetes
次にKubernetesの設定です。大きく2通りあります。
- ConfigMapを直接扱う
YAMLファイルを直接扱う場合は後者のconfigMapGenerator
が向いています。
kustomize.yaml
configMapGenerator
の場合、次のようにkustomize.yamlが参照できる場所にYAMLファイル(今回は例としてmyconfig.yaml
)を置き、
$ tree . ├── config │ └── myconfig.yaml ├── deployment.yaml ├── kustomization.yaml └── service.yaml
次のようにconfigMapGenerator
フィールドを書けばOKです。ConfigMapを定義するよりも直感的でシンプルですね。
もちろんルートディレクトリでもOKです。
apiVersion: kustomize.config.k8s.io/v1beta1 kind: Kustomization configMapGenerator: - name: myapp-config files: - config/myconfig.yaml
ただconfigMapGenerator
と通常のConfigMap
とでは挙動の違いがあります。
通常、ボリュームで使用されているConfigMapが更新されている場合、中のキーも同じく更新されます。これはkubeletが定期的な同期ごとにマウントされているConfigMapが更新されているかチェックしてくれるためです。
しかしながらconfigMapGenerator
はfileが更新されるとPod自体が再作成されることで、ConfigMapの中身を更新します。
subPathも交えながらまとめるとこの様になります。
ケース | Podの再作成 | 自動更新 |
---|---|---|
ConfigMapでsubPathなし | されない | される |
ConfigMapでsubPathあり | されない | されない |
configMapGenerator | される | 再作成で自動更新 |
ref: Podを構成してConfigMapを使用する | Kubernetes
ref: kustomize/examples/configGeneration.md at master · kubernetes-sigs/kustomize · GitHub
Deployment
Deploymentは通常のConfigMapと同じようにマウントして扱います。
apiVersion: apps/v1 kind: Deployment metadata: name: myapp-deploy spec: selector: matchLabels: name: myapp template: metadata: labels: name: myapp spec: containers: - name: myapp image: xxxx env: - name: CONFIG_PATH value: /data/myconfig.yaml volumeMounts: - mountPath: /data name: config-volume # spec.volumes[].nameと合わせる volumes: - name: config-volume configMap: name: myapp-config # configMapGenerator[].nameと合わせる
以上でConfigMapでYAMLファイルをマウントし、アプリケーションで使えるようになります。
その他
subPathを使うとどうなるか
ConfigMapでググるとよく出てくるsubPathですが、具体的なユースケースは何か説明します。
例えば、以下のように複数のファイルを持つConfigMapがあるとします。
apiVersion: v1 kind: ConfigMap metadata: name: example-configmap data: database.conf: | # Database configuration host: db.example.com port: 5432 app.conf: | # Application configuration logging: info
このConfigMapから、特定のファイルをPodの異なるパスにマウントするには、次のようなマニフェストにします。
apiVersion: v1 kind: Pod metadata: name: example-pod spec: containers: - name: example-container image: nginx volumeMounts: - name: config-volume mountPath: /etc/app.conf subPath: app.conf readOnly: true - name: config-volume mountPath: /etc/db/database.conf subPath: database.conf readOnly: true volumes: - name: config-volume configMap: name: example-configmap
このようにsubPath
を使用すると、ConfigMap内の特定のファイルだけをPodの特定のパスにマウントすることができます。
YAMLの|
と|-
の違い
どちらもリテラルブロック(改行などもベタ書きで書きたい)スタイルですが、
- |
- 最後の改行も含む
- |-
- 最後の改行を消す
という違いがあります。
リテラルブロックのインデント
プログラミング言語だとヒアドキュメントを表現したい時は
const hoge = `xxxx yyyy zzzz `
のようにインデントせずに書くことが多いですが、YAMLの場合は
--- hoge: | xxxx yyyy zzzz
のようにインデントしないといけないので忘れないようにしましょう。
まとめ
ConfigMapを使ってYAMLファイルを管理することで設定の複雑性や運用負荷を下げる方法を説明しました。