美文网首页k8s in action实践笔记
7.4 利用ConfigMap解耦配置

7.4 利用ConfigMap解耦配置

作者: 众神开挂 | 来源:发表于2021-07-25 12:34 被阅读0次

    应用配置的关键在于能够在多个环境中区分配置选项,将配置从应用程序源码中分离,可频繁变更配置值。如果将pod定义描述看作是应用程序源代码,显然需要将配置移出pod定义。微服务架构下正是如此,该架构定义了如何将多个个体组件组合成功能系统。

    7.4.1 ConfigMap介绍

    Kubernetes允许将配置选项分离到单独的资源对象ConfigMap中,本质上就是一个键/值对映射,值可以是短字面量,也可以是完整的配置文件。

    应用无须直接读取ConfigMap,甚至根本不需要知道其是否存在。映射的内容通过环境变量或者卷文件(如图7.2所示)的形式传递给容器,而并非直接传递给容器。命令行参数的定义中可以通过 $(ENV_VAR)语法引用环境变量,因而可以达到将ConfigMap的条目当作命令行参数传递给进程的效果。

    当然,应用程序同样可以通过Kubernetes Rest API按需直接读取ConfigMap的内容。不过除非是需求如此,应尽可能使你的应用保持对Kubernetes的无感知。

    不管应用具体是如何使用ConfigMap的,将配置存放在独立的资源对象中有助于在不同环境(开发、测试、质量保障和生产等)下拥有多份同名配置清单。pod是通过名称引用ConfigMap的,因此可以在多环境下使用相同的pod定义描述,同时保持不同的配置值以适应不同环境。

    7.4.2 创建ConfigMap

    了解一下如何在pod中使用ConfigMap。首先从最简单的例子开始,先创建一个仅包含单一键的映射,并用它填充之前示例中的环境变量INTERVAL。这里将使用指令kubectl create configmap创建ConfigMap,而非通用指令kubectl create-f。

    使用指令kubectl 创建 ConfigMap

    利用kubectl创建ConfigMap的映射条目时可以指定字面量或者存储在磁盘上的文件。先创建一个简单的字面量条目:

    $ kubectl create configmap fortune-config --from-literal=sleep-interval=25
    

    注意 ConfigMap中的键名必须是一个合法的DNS子域,仅包含数字字母、破折号、下画线以及圆点。首位的圆点符号是可选的。

    通过这条命令创建了一个叫作fortune-config的ConfigMap,仅包含单映射条目sleep-interval=25.

    通过添加多个--from-literal参数可创建包含多条目的ConfigMap:

    $ kubectl create configmap myconfigmap --from-literal=foo=bar --from-literal=bar=baz --from-literal=one=two
    

    让我们观察一下通过kubectl创建的ConfigMap的YAML格式的定义描述,如下所示。

    代码清单7.8 ConfigMap定义

    $ k get configmap fortune-config -o yaml
    apiVersion: v1
    data:
      sleep-interval: "25"  # 映射中的唯一条目
    kind: ConfigMap
    metadata:
      creationTimestamp: "2021-07-11T10:18:06Z"
      name: fortune-config  #映射名称,通过这个引用
      namespace: custom
      resourceVersion: "135252"
      uid: 00c4b04d-d885-45ab-9ee0-fb18b73c6648
    

    这没有什么特别的。编写这个YAML文件很容易,除了metadata中的名称无须指定其他字段,然后通过Kubernetes API创建对应的ConfigMap:

    fortune-config.yaml

    apiVersion: v1
    kind: ConfigMap
    metadata:
      name: fortune-config
    data:
      sleep-interval: "25"
    
    $ kubectl create -f fortune-config.yaml
    

    从文件内容创建ConfigMap条目

    ConfigMap同样可以存储粗粒度的配置数据,比如完整的配置文件。kubectl create configmap命令支持从磁盘上读取文件,并将文件内容单独存储为ConfigMap中的条目:

    $ kubectl create configmap my-config --from-file=config-file.conf
    

    运行上述命令时,kubectl会在当前目录下查找config-file.conf文件,并将文件内容存储在ConfigMap中以config-file.conf为键名的条目下。当然也可以手动指定键名:

    $ kubectl create configmap my-config --from-file=customkey=config-file.conf
    

    这条命令会将文件内容存在键名为customkey的条目下。与使用字面量时相同,多次使用--from-file参数可增加多个文件条目。

    从文件夹创建ConfigMap

    除单独引入每个文件外,甚至可以引入某一文件夹中的所有文件:

    $ kubectl create configmap my-config --from-file=/path/to/dir
    

    这种情况下,kubectl会为文件夹中的每个文件单独创建条目,仅限于那些文件名可作为合法ConfigMap键名的文件。

    合并不同选项 创建ConfigMap时可以混合使用这里提到的所有选项(注意这里的文件并未包含在本书的代码归档中——如果想要尝试这条命令需自行创建):

    $ kubectl create configmap my-config \
        --from-file=foo.json             #单独的文件
        --from-file=bar=foobar.conf      #自定义键名条目下的文件
        --from-file=config-opts/         #完整的文件夹
        --from-literal=some=thing        #字面量
    
    

    这里的ConfigMap创建自多种选项:完整文件夹、单独文件、自定义键名的条目下的文件(替代文件名作键名)以及字面量。

    $ k get cm
    $ k get configmap
    
    

    7.4.3 给容器传递ConfigMap条目作为环境变量

    如何将映射中的值传递给pod的容器?有三种方法。首先尝试最为简单的一种——设置环境变量,将会使用到7.5.3节中提到的valueFrom字段。pod的定义描述如下面的代码清单所示。

    代码清单7.9 通过配置文件注入环境变量的pod:fortune-pod-env-configmap.yaml

    apiVersion: v1
    kind: Pod
    metadata:
      name: fortune-env-from-configmap
    spec:
      containers:
      - image: luksa/fortune:env
        env:
        - name: INTERVAL #设置环境变量 
          valueFrom: 
            configMapKeyRef:  #使用configmap初始化
              name: fortune-config  #引用的configmap名称
              key: sleep-interval  #对应的键名
        name: html-generator
        volumeMounts:
        - name: html
          mountPath: /var/htdocs
      - image: nginx:alpine
        name: web-server
        volumeMounts:
        - name: html
          mountPath: /usr/share/nginx/html
          readOnly: true
        ports:
        - containerPort: 80
          protocol: TCP
      volumes:
      - name: html
        emptyDir: {}
    
    

    这里定义了一个环境变量INTERVAL,并将其值设置为fortune-config ConfigMap中键名为sleep-interval对应的值。运行在html-generator容器中的进程读取到环境变量INTERVAL的值为25。

    在pod中引用不存在的ConfigMap

    你可能会好奇如果创建pod时引用的ConfigMap不存在会发生什么?Kubernetes会正常调度pod并尝试运行所有的容器。然而引用不存在的ConfigMap的容器会启动失败,其余容器能正常启动。如果之后创建了这个缺失的ConfigMap,失败容器会自动启动,无须重新创建pod。

    注意 可以标记对ConfigMap的引用是可选的(设置configMapKeyRef.optional: true)。这样,即便ConfigMap不存在,容器也能正常启动。

    这个例子展示了如何将配置从pod定义中分离。这样能使所有的配置项较为集中(甚至多个pod也是如此),而不是分散在各处(或者冗余复制于多个pod定义清单)。

    7.4.4 一次性传递ConfigMap的所有条目作为环境变量

    如果ConfigMap包含不少条目,为每个条目单独设置环境变量的过程是单调乏味且容易出错的。

    假设一个ConfigMap包含FOO、BAR和FOO-BAR三个键。可以通过envFrom属性字段将所有条目暴露作为环境变量,而非使用前面例子中的env字段。示例代码如下所示。

    代码清单7.10 pod包含来源于ConfigMap所有条目的环境变量

    spec:
      containers:
      - image: luksa/fortune:env
        envForm:                   #使用的是envForm字段而不是env字段
          - prefix: CONFIG_   #所有环境变量均包含前缀CONFIG_
            configMapKeyRef: 
              name: fortune-config 
              key: sleep-interval 
    
    

    如你所见,可以为所有的环境变量设置前缀,如本例中的 CONFIG_ ,容器中两个环境变量的名称为: CONFIG_FOOCONFIG_BAR

    注意 前缀设置是可选的,若不设置前缀值,环境变量的名称与ConfigMap中的键名相同。

    是否注意到前面说的是两个环境变量,然而ConfigMap拥有三个条目(FOO、BAR和FOO-BAR)?为何没有对应FOO-BAR条目的环境变量呢?

    原因在于CONFIG_FOO-BAR包含破折号,这并不是一个合法的环境变量名称。Kubernetes不会主动转换键名(例如不会将破折号转换为下画线)。如果ConfigMap的某键名格式不正确,创建环境变量时会忽略对应的条目(忽略时不会发出事件通知)。

    7.4.5 传递ConfigMap条目作为命令行参数

    现在让我们来看一下如何将ConfigMap中的值作为参数值传递给运行在容器中的主进程。在字段pod.spec.containers.args中无法直接引用ConfigMap的条目,但是可以利用ConfigMap条目初始化某个环境变量,然后再在参数字段中引用该环境变量。

    代码清单7.11展示了如何在YAML文件中做到这一点。

    代码清单7.11 使用ConfigMap条目作为参数值:fortune-pod-args-configmap.yaml

    apiVersion: v1
    kind: Pod
    metadata:
      name: fortune-args-from-configmap
    spec:
      containers:
      - image: luksa/fortune:args  # 使用参数读取间隔值的镜像
        env:                       # 与之前的环境变量的定义相同
        - name: INTERVAL
          valueFrom: 
            configMapKeyRef:
              name: fortune-config
              key: sleep-interval
        args: ["$(INTERVAL)"]  # 在参数设置中引用环境变量
        name: html-generator
        volumeMounts:
        - name: html
          mountPath: /var/htdocs
      - image: nginx:alpine
        name: web-server  
        volumeMounts:
        - name: html
          mountPath: /usr/share/nginx/html
          readOnly: true
        ports:
        - containerPort: 80
          protocol: TCP
      volumes:
      - name: html
        emptyDir: {}
    
    

    环境变量的定义与之前相同,需通过 $(ENV_VARIABLE_NAME) 将环境变量的值注入参数值。

    7.4.6 使用configMap卷将条目暴露为文件

    环境变量或者命令行参数值作为配置值通常适用于变量值较短的场景。由于ConfigMap中可以包含完整的配置文件内容,当你想要将其暴露给容器时,可以借助前面章节提到过的一种称为configMap卷的特殊卷格式。

    configMap卷会将ConfigMap中的每个条目均暴露成一个文件。运行在容器中的进程可通过读取文件内容获得对应的条目值。

    尽管这种方法主要适用于传递较大的配置文件给容器,同样可以用于传递较短的变量值。

    创建ConfigMap

    这里不再修改脚本fortuneloop.sh,将尝试另一个不同的示例,使用配置文件配置运行在fortune pod的Web服务器容器中的Nginx web服务器。如果想要让Nginx服务器压缩传递给客户端的响应,Nginx的配置文件需开启压缩配置,如下面的代码清单所示。

    代码清单7.12 开启gzip压缩的Nginx配置文件:my-nginx-config.conf

    server {
        listen              80;
        server_name         www.kubia-example.com;
    
        gzip on;
        gzip_types text/plain application/xml; #开启对文本和XML文件的gzip压缩
    
        location / {
            root   /usr/share/nginx/html;
            index  index.html index.htm;
        }
    }
    
    

    现在首先通过 kubectl delete configmap fortune-config 删除现有的ConfigMap fortune-config,然后用存储在本地磁盘上的Nginx配置文件创建一个新的ConfigMap。

    创建一个新文件夹confimap-files并将上面的配置文件存储于 configmap-files/my-nginx-config.conf 中。另外在该文件夹中添加一个名为sleep-interval的文本文件,写入值为25,使ConfigMap同样包含条目sleep-interval。

    sleep-interval文件

    25
    
    

    文件结构如下:

    $ tree configmap-files
    configmap-files
    ├── my-nginx-config.conf
    └── sleep-interval
    
    

    从文件夹创建ConfigMap:

    $ kubectl create configmap fortune-config --from-file=configmap-files
    
    

    下面的代码清单展示了ConfigMap的YAML格式内容。

    代码清单7.13 从文件创建的ConfigMap的YAML格式定义

    $ kubectl get configmap fortune-config -o yaml
    apiVersion: v1
    data:
      my-nginx-config.conf: |
        server {
            listen              80;
            server_name         www.kubia-example.com;
    
            gzip on;
            gzip_types text/plain application/xml;  #条目中包含了nginx配置文件的内容
    
            location / {
                root   /usr/share/nginx/html;
                index  index.html index.htm;
            }
    
        }
      sleep-interval: |  # 条目 sleep-interval
        25
    kind: ConfigMap
    metadata:
      creationTimestamp: "2021-07-11T11:39:04Z"
      name: fortune-config
      namespace: custom
      resourceVersion: "139268"
      uid: a0d46cf3-8d29-44f9-bfac-1e077e75743e
    
    

    注意 所有条目第一行最后的管道符号表示后续的条目值是多行字面量。

    ConfigMap包含两个条目,条目的键名与文件名相同。接下来将在pod的容器中使用该ConfigMap。

    在卷内使用ConfigMap的条目

    创建包含ConfigMap条目内容的卷只需要创建一个引用ConfigMap名称的卷并挂载到容器中。已经学会了如何创建及挂载卷,接下来要学习的仅是如何用ConfigMap的条目初始化卷。

    Nginx需读取配置文件 /etc/nginx/nginx.conf ,而Nginx镜像内的这个文件包含默认配置,并不想完全覆盖这个配置文件。幸运的是,默认配置文件会自动嵌入子文件夹 /etc/nginx/conf.d/ 下的所有.conf文件,因此只需要将你的配置文件置于该子文件夹中即可。

    代码清单7.14 pod挂载ConfigMap条目作为文件:fortune-pod-configmap-volume.yaml

    apiVersion: v1
    kind: Pod
    metadata:
      name: fortune-configmap-volume
    spec:
      containers:
      - image: luksa/fortune:env
        env:
        - name: INTERVAL
          valueFrom:
            configMapKeyRef:
              name: fortune-config
              key: sleep-interval
        name: html-generator
        volumeMounts:
        - name: html
          mountPath: /var/htdocs
      - image: nginx:alpine
        name: web-server
        volumeMounts:
        - name: html
          mountPath: /usr/share/nginx/html
          readOnly: true
        - name: config
          mountPath: /etc/nginx/conf.d  #挂载configMap卷到这个位置
          readOnly: true
        - name: config
          mountPath: /tmp/whole-fortune-config-volume
          readOnly: true
        ports:
          - containerPort: 80
            name: http
            protocol: TCP
      volumes:
      - name: html
        emptyDir: {}
      - name: config
        configMap:
          name: fortune-config #卷定义引用fortune-config ConfigMap
    
    

    pod定义中包含了引用fortune-config ConfigMap的卷,需要被挂载到文件夹 /etc/nginx/conf.d 下让Nginx服务器使用它。

    检查Nginx是否使用被挂载的配置文件

    现在的web服务器应该已经被配置为会压缩响应,可以将localhost:8080转发到pod的80端口,利用curl检查服务器响应来验证配置是否生效,如下面的代码清单所示。

    代码清单7.15 观察nginx响应是否被压缩

    $ kubectl port-forward fortune-configmap-volume 8080:80 &
     Forwarding from 127.0.0.1:8080 -> 80
    
    $ curl -H "Accept-Encoding: gzip" -I localhost:8080
    Handling connection for 8080
    HTTP/1.1 200 OK
    Server: nginx/1.19.6
    Date: Sun, 28 Feb 2021 06:21:44 GMT
    Content-Type: text/html
    Last-Modified: Sun, 28 Feb 2021 06:21:33 GMT
    Connection: keep-alive
    ETag: W/"603b366d-2d"
    Content-Encoding: gzip  #这里说明相应被压缩
    
    

    检查被挂载的configMap卷的内容

    服务器响应说明配置成功生效。现在来看一下文件夹 /etc/nginx/conf.d 下的内容:

    $ kubectl exec fortune-configmap-volume -c web-server -- ls /etc/nginx/conf.d
    
    my-nginx-config.conf
    sleep-interval
    
    

    ConfigMap的两个条目均作为文件置于这一文件夹下。条目sleep-interval对应的文件也被包含在内,然而它只会被fortuneloop容器所使用。可以创建两个不同的ConfigMap,一个用以配置容器fortuneloop,另一个用来配置webserver,然而采用多个ConfigMap去分别配置同一pod中的不同容器的做法是不好的。毕竟同一pod中的容器是紧密联系的,需要被当作整体单元来配置。

    卷内暴露指定的ConfigMap条目

    幸运的是,可以创建仅包含ConfigMap中部分条目的configMap卷——本示例中的条目my-nginx-config.conf。这样容器fortuneloop不会受到影响,条目sleep-interval会作为环境变量传递给容器而不是以卷的方式。

    通过卷的items属性能够指定哪些条目会被暴露作为configMap卷中的文件,如下面的代码清单所示。

    代码清单7.16 ConfigMap的指定条目挂载至pod的文件夹:fortune-pod-configmap-volume-with-itmes.yaml

    apiVersion: v1
    kind: Pod
    metadata:
      name: fortune-configmap-volume-with-items
    spec:
      containers:
      - image: luksa/fortune:env
        name: html-generator
        volumeMounts:
        - name: html
          mountPath: /var/htdocs
      - image: nginx:alpine
        name: web-server
        volumeMounts:
        - name: html
          mountPath: /usr/share/nginx/html
          readOnly: true
        - name: config
          mountPath: /etc/nginx/conf.d/
          readOnly: true
        ports:
        - containerPort: 80
          protocol: TCP
      volumes:
      - name: html
        emptyDir: {}
      - name: config
        configMap:
          name: fortune-config
          items:   #选择包含在卷中的条目
          - key: my-nginx-config.conf #键的条目
            path: gzip.conf # 条目的值被存储在该文件中
    
    

    指定单个条目时需同时设置条目的键名称以及对应的文件名。如果采用上面的配置文件创建pod, /etc/nginx/conf.d 文件夹是比较干净的,仅包含所需的 gzip.conf 文件。

    挂载某一文件夹会隐藏该文件夹中已存在的文件

    这里有一件重要的事情需要讨论。在当前与此前的示例中,将卷挂载至某个文件夹,意味着容器镜像中 /etc/nginx/conf.d 文件夹下原本存在的任何文件都会被隐藏。

    Linux系统挂载文件系统至非空文件夹时通常表现如此。文件夹中只会包含被挂载文件系统中的文件,即便文件夹中原本的文件是不可访问的也是同样如此。

    本示例中,这种现象并不会带来比较糟糕的副作用。不过假设挂载文件夹是/etc,该文件夹通常包含不少重要文件。由于/etc下的所有文件不存在,容器极大可能会损坏。如果你希望添加文件至某个文件夹如/etc,绝不能采用这种方法。

    ConfigMap独立条目作为文件被挂载且不隐藏文件夹中的其他文件

    顺理成章,你会好奇如何能挂载ConfigMap对应文件至现有文件夹的同时不会隐藏现有文件。volumeMount额外的subPath字段可以被用作挂载卷中的某个独立文件或者是文件夹,无须挂载完整卷。图7.10的形象化解释可能更加容易理解。

    假设拥有一个包含文件myconfig.conf的configMap卷,希望能将其添加为/etc文件夹下的文件someconfig.conf。通过属性subPath可以将该文件挂载的同时又不影响文件夹中的其他文件。pod定义中的相关部分如下面的代码清单所示。

    代码清单7.17 pod挂载ConfigMap的指定条目至特定文件

    spec:
      containers:
      - image: luksa/fortune:env
        name: html-generator
        volumeMounts:
        - name: myvolume
          mountPath: /var/myconfig.conf # 挂载某一个文件,而不是文件夹
          subPath: myconfig.config   #仅挂载指定的条目,而不是整个卷
    
    

    挂载任意一种卷时均可以使用subPath属性。可以选择挂载部分卷而不是挂载完整的卷。不过这种独立文件的挂载方式会带来文件更新上的缺陷,你会在接下来的小节中学习到更多的相关知识,在这里还是先要说一些文件权限问题对configMap卷的讨论进行收尾。

    为configMap卷中的文件设置权限

    configMap卷中所有文件的权限默认被设置为 644(-rw-r-r--) 。可以通过卷规格定义中的 defaultMode 属性改变默认权限,如下面的代码清单所示。

    代码清单7.18 设置权限:fortune-pod-configmap-volume-defaultMode.yaml

    apiVersion: v1
    kind: Pod
    metadata:
      name: fortune-configmap-volume
    spec:
      containers:
      - image: luksa/fortune:env
        env:
        - name: INTERVAL
          valueFrom:
            configMapKeyRef:
              name: fortune-config
              key: sleep-interval
        name: html-generator
        volumeMounts:
        - name: html
          mountPath: /var/htdocs
      - image: nginx:alpine
        name: web-server
        volumeMounts:
        - name: html
          mountPath: /usr/share/nginx/html
          readOnly: true
        - name: config
          mountPath: /etc/nginx/conf.d
          readOnly: true
        - name: config
          mountPath: /tmp/whole-fortune-config-volume
          readOnly: true
      volumes:
      - name: html
        emptyDir: {}
      - name: config
        configMap:
          name: fortune-config
          defaultMode: 0660 # 设置所有文件的权限为-rw-rw----
    
    

    ConfigMap通常被用作存储非敏感数据,不过依旧可能希望仅限于文件拥有者的用户和组可读写,正如上面的例子所示。

    7.4.7 更新应用配置且不重启应用程序

    在此之前提到过,使用环境变量或者命令行参数作为配置源的弊端在于无法在进程运行时更新配置。将ConfigMap暴露为卷可以达到配置热更新的效果,无须重新创建pod或者重启容器。

    ConfigMap被更新之后,卷中引用它的所有文件也会相应更新,进程发现文件被改变之后进行重载。Kubernetes同样支持文件更新之后手动通知容器。

    查看配置文件

    $ kubectl exec fortune-configmap-volume -c web-server -- less /etc/nginx/conf.d/my-nginx-config.conf
    
    

    警告 请注意笔者在写这段的时候,更新ConfigMap之后对应文件的更新耗时会出人意料地长(往往需要数分钟)。

    修改ConfigMap

    现在来瞧一瞧如何修改ConfigMap,同时运行在pod中的进程会重载configMap卷中对应的文件。你需要修改前面示例中的Nginx配置文件,使得Nginx能够在不重启pod的前提下应用新配置。尝试用 kubectl edit 命令修改 ConfigMap fortune-config 来关闭gzip压缩:

    $ kubectl edit configmap fortune-config
    
    

    编辑器打开,行 gzip on 改为 gzip off ,保存文件后关闭编辑器。ConfigMap被更新不久之后会自动更新卷中的对应文件。用 kubectl exec 命令打印出该文件内容进行确认:

    若尚未看到文件内容被更新,可稍等一会儿后重试。文件更新过程需要一段时间。最终你会看到配置文件的变化,然而发现这对Nginx并没有什么影响,这是因为Nginx不会去监听文件的变化并自动重载。

    通知Nginx重载配置

    Nginx会持续压缩响应直到你通过以下命令主动通知它:

    $ kubectl exec fortune-configmap-volume -c web-server -- nginx -s reload
    2021/02/28 06:51:49 [notice] 80#80: signal process started
    
    

    现在再次用curl命令访问服务器后会发现响应不再被压缩(响应头中未包含 Content-Encoding: gzip )。在无须重启容器或者重建pod的同时有效修改了应用配置。

    $ curl -H "Accept-Encoding: gzip" -I localhost:8080
    HTTP/1.1 200 OK
    Server: nginx/1.21.1
    Date: Sun, 11 Jul 2021 12:38:22 GMT
    Content-Type: text/html
    Content-Length: 43
    Last-Modified: Sun, 11 Jul 2021 12:38:08 GMT
    Connection: keep-alive
    ETag: "60eae630-2b"
    Accept-Ranges: bytes
    
    

    了解文件被自动更新的过程

    你可能会疑惑在Kubernetes更新完configMap卷中的所有文件之前,应用是否会监听到文件变化并主动进行重载。幸运的是,这不会发生,所有的文件会被自动一次性更新。Kubernetes通过符号链接做到这一点。如果尝试列出configMap卷挂载位置的所有文件,会看到如下内容。

    代码清单7.19 被挂载的configMap卷中的文件

    $ kubectl exec -it fortune-configmap-volume -c web-server -- ls -lA /etc/nginx/conf.d
    total 4
    drwxr-xr-x    2 root     root          4096 Feb 28 06:51 ..2021_02_28_06_51_10.146953250
    lrwxrwxrwx    1 root     root            31 Feb 28 06:51 ..data -> ..2021_02_28_06_51_10.146953250
    lrwxrwxrwx    1 root     root            27 Feb 28 06:19 my-nginx-config.conf -> ..data/my-nginx-config.conf
    lrwxrwxrwx    1 root     root            21 Feb 28 06:19 sleep-interval -> ..data/sleep-interval
    
    

    可以看到,被挂载的configMap卷中的文件是 ..data 文件夹中文件的符号链接,而 ..data 文件夹同样是 ..4984_09_04_something 的符号链接。每当ConfigMap被更新后,Kubernetes会创建一个这样的文件夹,写入所有文件并重新将符号 ..data链接至新文件夹,通过这种方式可以一次性修改所有文件。

    挂载至已存在文件夹的文件不会被更新

    涉及到更新configMap卷需要提出一个警告:如果挂载的是容器中的单个文件而不是完整的卷,ConfigMap更新之后对应的文件不会被更新!至少在写本章节的时候表现如此。

    如果现在你需要挂载单个文件并且在修改源ConfigMap的同时会自动修改这个文件,一种方案是挂载完整卷至不同的文件夹并创建指向所需文件的符号链接。符号链接可以原生创建在容器镜像中,也可以在容器启动时创建。

    了解更新ConfigMap的影响

    容器的一个比较重要的特性是其不变性,从同一镜像启动的多个容器之间不存在任何差异。那么通过修改被运行容器所使用的ConfigMap来打破这种不变性的行为是否是错误的?

    关键点在于应用是否支持重载配置。ConfigMap更新之后创建的pod会使用新配置,而之前的pod依旧使用旧配置,这会导致运行中的不同实例的配置不同。这也不仅限于新pod,如果pod中的容器因为某种原因重启了,新进程同样会使用新配置。因此,如果应用不支持主动重载配置,那么修改某些运行pod所使用的ConfigMap并不是一个好主意。

    如果应用支持主动重载配置,那么修改ConfigMap的行为就算不了什么。不过有一点仍需注意,由于configMap卷中文件的更新行为对于所有运行中示例而言不是同步的,因此不同pod中的文件可能会在长达一分钟的时间内出现不一致的情况。

    相关文章

      网友评论

        本文标题:7.4 利用ConfigMap解耦配置

        本文链接:https://www.haomeiwen.com/subject/trukmltx.html