Helm Chart 中通过 configMap 管理应用程序配置文件

背景

各种成熟软件的官方container image中,大多包含了默认的配置文件,在真正的生产部署中,一定需要灵活的方法管理这些配置文件,以满足下列需求:

  1. 能修改默认配置文件
  2. 对配置文件进行版本管理
  3. 修改配置文件能自动触发 rolling update

helm 作为 container 的编排工具,可以满足以上的需求,虽然有些有些并不那么直接。我们以 helm 官方的 cassandra chart (incubator repo)为基础,看看如何实现以上三个需求。


修改配置文件

配置文件可简单看做一堆配置项(Key: value) 的集合,在安装 chart 时作为参数传入并设置到 deployment 中。这是典型的给 helm chart 传参的过程,因此自然想到可以使用 chart values file -- values.yaml 来存储你自定义的配置文件,通过下面的传递途径把值传入到container 中:

Untitled Diagram.png

cassandra chart 使用的 cassandra image 默认包含了下面一系列必备配置文件。其中,像 cassandra.yaml 这样的核心配置文件是我们在部署过程中常需要修改的,而且,修改后需要重启 cassandra instance 才能生效。

> kubectl exec po/cassandra-01-1 -- ls /etc/cassandra/
cassandra-env.ps1               hotspot_compiler
cassandra-env.sh                jvm.options
cassandra-jaas.config           logback-tools.xml
cassandra-rackdc.properties     logback.xml
cassandra-topology.properties   metrics-reporter-config-sample.yaml
cassandra.yaml                  README.txt
commitlog_archiving.properties  triggers
cqlshrc.sample

默认实现

官方 cassandra chart 中也确实提供了这种实现方式,你可以通过修改 values.yaml 中的这个参数,来覆盖container中的默认配置文件。

parameter Description Default
configOverrides Overrides config files in /etc/cassandra dir {}

如果你想覆盖哪个 config file,你必须在 configOverrides 这个参数下面,以该 config file 的名字为 key 粘贴上整个 config file 的内容,例如,以 cassandra.yaml 为例,你需要在 values.yaml 中添加如下内容:

# values.yaml
...
## Cassandra config files overrides
configOverrides:
  cassandra.yaml: |
    # Cassandra storage config YAML

    # NOTE:
    #   See http://wiki.apache.org/cassandra/StorageConfiguration for
    #   full explanations of configuration directives
    # /NOTE

    # The name of the cluster. This is mainly used to prevent machines in
    # one logical cluster from joining another.
    cluster_name: cassandra
    ...
    (整个config file的内容比如全部出现在这里)
...

只是这种修改的方法比较笨拙,由于 default helm chart 默认覆盖了整个 /etc/cassandra path,因此,你必须把所有 cassandra 启动必须的 config file 的所有内容都填入 values.yaml/configOverride 下,否则的话,该文件将不会出现在 container 中。首先,对于绝大多数我们不需要修改的配置文件,把默认内容贴在这完全是无意义的工作。其次,这会产生一个非常庞大的 values.yaml file,非常不便于维护。

更优雅的实现

针对上面的实现,我希望对现有 chart 做两点改进

  1. 只传入自定义的文件。无需修改的,自动使用 container 自带的默认配置文件。
  2. 自定义的配置文件存放在一个单独目录下,而不是堆放在 values.yaml 中。

实现方法,把你自定义的配置文件存放在 chart 根目录的 ./configs 文件夹下。在 values.yaml/configOverride 下以数组形式列出所有你希望覆盖的配置文件。整个数据传递过程变为:

Untitled Diagram (1).png
values.yaml
## Cassandra config files overrides
configOverrides:
  - cassandra.yaml
  - cassandra-rackdc.properties

values.yaml 中只记录我们要覆盖的config file的索引。

templates/configmap.yaml
{{- if .Values.configOverrides }}
kind: ConfigMap
metadata:
  name: cassandra
...
data:
{{- range .Values.configOverrides }}
  {{ . }}: |
{{ $.Files.Get (printf "%s%s" "configs/" .) | indent 4 }}
{{- end }}
{{- end }}

自动生成一个包含所有自定义 config files 内容的 configMap manifest。每个自定义 config 的内容作为一个 data 的 sub-object 出现,key 是文件名,value 是 config file 的内容。

templates/statefulset.yaml
apiVersion: apps/v1
kind: StatefulSet
...
spec:
  ...
  template:
  ...
    spec:
    ...
{{- if or .Values.configOverrides (not .Values.persistence.enabled) }}
      volumes:
{{- end }}
{{- if .Values.configOverrides }}
      - configMap:
          name: cassandra
        name: cassandra-config-overrides
      - name: cassandra-configs
        emptyDir: {}
{{- end }}
...

首先,显然意见,我们需要把包含自定义配置文件的 configMap resource mount成一个volume -- cassandra-config-overrides。同时,我们还创建了一个 cassandra-configs 的临时 volume,它的作用下面介绍。

apiVersion: apps/v1
kind: StatefulSet
...
spec:
  ...
  template:
  ...
    spec:
{{- if .Values.configOverrides }}
      initContainers:
      - name: config-chown
        image: busybox
        command: [ 'sh', '-c', 'cp /cassandra-config-overrides/* /cassandra-configs/ && chown 999:999 /cassandra-configs/*']
        volumeMounts:
        - name: cassandra-config-overrides
          mountPath: /cassandra-config-overrides
        - name: cassandra-configs
          mountPath: /cassandra-configs/
{{- end }} 

最直接的方法,我们应该把包含 config files 的 cassandra-config-overrides volume 直接 mount 到 container 的 /etc/cassandra 目录中,但是我们并没有这么做,而是创建了一个 initContainer 来把 cassandra-config-overrides 中的 config files 拷贝到刚才创建的 emptyDir cassandra-configs volume 中,并修改文件的 owner 和 group。

之所以多此一举,是因为 kubernetes 的 configMap volume 现在还不支持在 mount 时自定义 user 和 group (参考 https://github.com/kubernetes/kubernetes/issues/81089),直接 mount configMap volume,不满足 cassandra 对配置文件 permission 的要求,container 启动时会遇到如下错误:

chown: changing ownership of '/etc/cassandra/cassandra.yaml': Read-only file system

暂时的解决方案就是增加如上的 initContainer,把配置文件从 Ready-only 的 configMap volume 拷贝到权限更大的 emptyDir volume,并修改文件的 owner:group 为当前 session 的 user:group。然后把 emptyDir volume mount 到 container 中。如下:

apiVersion: apps/v1
kind: StatefulSet
...
spec:
  ...
  template:
  ...
    spec:
      containers:
      - name: {{ template "cassandra.fullname" . }}
        image: "{{ .Values.image.repo }}:{{ .Values.image.tag }}"
        ...
        volumeMounts:
        - name: data
          mountPath: /var/lib/cassandra
{{- if .Values.configOverrides }}
{{- range .Values.configOverrides }}
        - name: cassandra-configs
          mountPath: /etc/cassandra/{{ . }}
          subPath: {{ . }}
{{- end }}
{{- end }}
...

经过以上操作,用户自定义的配置文件的 user:group 被修改为 cassandra instance用户,statefulset 中的 database replica pod 可以正常启动。

> kubectl exec po/cassandra-01-2 -- ls -l /etc/cassandra/cassandra.yaml
-rw-r--r-- 1 cassandra cassandra 58588 May 30 11:49 /etc/cassandra/cassandra.yaml

对配置文件进行版本管理

通常,helm chart 都是存放在 VCS 系统中,例如 github,这种配置管理代码化,可以方便的实现软件部署的版本管理。另外, helm chart 本身也有 release revision的概念,例如,在部署了以上 chart 后,如果运维过程中需要修改软件配置文件,可以通过 helm upgrade 来启用新的配置,或者,如果升级出现问题,可通过 helm rollback 回滚到之前的配置。


修改配置文件能自动触发 rolling update

如果我们用 configMap 导入配置文件,当修改了配置文件后,helm upgrade 会自动更新 kubernetes 中的 configMap resource。但是,即便是 configMap 被 mount 为 volume,它本身的修改并不会触发它所属的 deployment / statefulset 等上层资源的 rolling update。

对于像 cassandra 这样的应用程序来说,配置文件修改后,必须通过 restart 来使新的配置生效。也就是说,在你修改了 ./configs/ 下的某个配置文件后,即便是通过 helm upgrade 发布了修改,它也不会立刻生效,必须手动 rolling update (restart) 所有的 replica pod 之后,新的配置才能真正生效。 如何能自动的触发 rolling update 呢?

参考 charts_tips_and_tricks/#automatically-roll-deployments,我们知道,只有当一个 deployment / statefulset 的 spec 发生修改时,rolling update 才会自动发生。因此,在 helm chart 中,一个巧妙的做法是将 configMap 的 hash 值作为 annotation 写入 deployment / statefulset 的 template.metadata.annotations 中,这样,当 configMap 的内容修改的时候,annotation 会响应发生变化,rolling update 被自动 trigger。

在 cassandra 的例子中,我们希望将范围扩大到:任何 ./configs 下的配置文件的内容发生变化,都会触发 rolling update。实现方法为:

apiVersion: apps/v1
kind: StatefulSet
...
spec:
  ...
  template:
    metadata:
{{- if .Values.configOverrides }}
      annotations:
{{- $SUM := "" }}
{{- range .Values.configOverrides }}
{{- $SUM = $.Files.Get (print "configs/" .) | sha256sum | print $SUM }}
{{- end }}
       checksum/config: {{ $SUM | sha256sum }}
{{- end }}
...

思路非常简单,先对每个配置文件的内容进行 hash,把所有 hash 值拼接成一个字符串,再对字符串进行二次 hash,生成的最终 hash 值作为 annotation 写入 statefulset.spec.template.metadata.annotations。这样,你可以任意修改 config file,在 helm upgrade 后,rolling update 会自动进行,你修改也会随之生效。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 200,302评论 5 470
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 84,232评论 2 377
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 147,337评论 0 332
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 53,977评论 1 272
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,920评论 5 360
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,194评论 1 277
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,638评论 3 390
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,319评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,455评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,379评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,426评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,106评论 3 315
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,696评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,786评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,996评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,467评论 2 346
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,043评论 2 341