7.4 利用ConfigMap解耦配置

應(yīng)用配置的關(guān)鍵在于能夠在多個(gè)環(huán)境中區(qū)分配置選項(xiàng)捉邢,將配置從應(yīng)用程序源碼中分離,可頻繁變更配置值吮螺。如果將pod定義描述看作是應(yīng)用程序源代碼饶囚,顯然需要將配置移出pod定義。微服務(wù)架構(gòu)下正是如此鸠补,該架構(gòu)定義了如何將多個(gè)個(gè)體組件組合成功能系統(tǒng)萝风。

7.4.1 ConfigMap介紹

Kubernetes允許將配置選項(xiàng)分離到單獨(dú)的資源對(duì)象ConfigMap中,本質(zhì)上就是一個(gè)鍵/值對(duì)映射紫岩,值可以是短字面量规惰,也可以是完整的配置文件。

應(yīng)用無(wú)須直接讀取ConfigMap泉蝌,甚至根本不需要知道其是否存在卿拴。映射的內(nèi)容通過(guò)環(huán)境變量或者卷文件(如圖7.2所示)的形式傳遞給容器,而并非直接傳遞給容器梨与。命令行參數(shù)的定義中可以通過(guò) $(ENV_VAR)語(yǔ)法引用環(huán)境變量,因而可以達(dá)到將ConfigMap的條目當(dāng)作命令行參數(shù)傳遞給進(jìn)程的效果文狱。

當(dāng)然粥鞋,應(yīng)用程序同樣可以通過(guò)Kubernetes Rest API按需直接讀取ConfigMap的內(nèi)容。不過(guò)除非是需求如此瞄崇,應(yīng)盡可能使你的應(yīng)用保持對(duì)Kubernetes的無(wú)感知呻粹。

不管應(yīng)用具體是如何使用ConfigMap的,將配置存放在獨(dú)立的資源對(duì)象中有助于在不同環(huán)境(開發(fā)苏研、測(cè)試等浊、質(zhì)量保障和生產(chǎn)等)下?lián)碛卸喾萃渲们鍐巍od是通過(guò)名稱引用ConfigMap的摹蘑,因此可以在多環(huán)境下使用相同的pod定義描述筹燕,同時(shí)保持不同的配置值以適應(yīng)不同環(huán)境。

7.4.2 創(chuàng)建ConfigMap

了解一下如何在pod中使用ConfigMap。首先從最簡(jiǎn)單的例子開始撒踪,先創(chuàng)建一個(gè)僅包含單一鍵的映射过咬,并用它填充之前示例中的環(huán)境變量INTERVAL。這里將使用指令kubectl create configmap創(chuàng)建ConfigMap制妄,而非通用指令kubectl create-f掸绞。

使用指令kubectl 創(chuàng)建 ConfigMap

利用kubectl創(chuàng)建ConfigMap的映射條目時(shí)可以指定字面量或者存儲(chǔ)在磁盤上的文件。先創(chuàng)建一個(gè)簡(jiǎn)單的字面量條目:

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

注意 ConfigMap中的鍵名必須是一個(gè)合法的DNS子域耕捞,僅包含數(shù)字字母衔掸、破折號(hào)、下畫線以及圓點(diǎn)俺抽。首位的圓點(diǎn)符號(hào)是可選的敞映。

通過(guò)這條命令創(chuàng)建了一個(gè)叫作fortune-config的ConfigMap,僅包含單映射條目sleep-interval=25.

通過(guò)添加多個(gè)--from-literal參數(shù)可創(chuàng)建包含多條目的ConfigMap:

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

讓我們觀察一下通過(guò)kubectl創(chuàng)建的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  #映射名稱,通過(guò)這個(gè)引用
  namespace: custom
  resourceVersion: "135252"
  uid: 00c4b04d-d885-45ab-9ee0-fb18b73c6648

這沒(méi)有什么特別的瞳抓。編寫這個(gè)YAML文件很容易埃疫,除了metadata中的名稱無(wú)須指定其他字段,然后通過(guò)Kubernetes API創(chuàng)建對(duì)應(yīng)的ConfigMap:

fortune-config.yaml

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

從文件內(nèi)容創(chuàng)建ConfigMap條目

ConfigMap同樣可以存儲(chǔ)粗粒度的配置數(shù)據(jù)孩哑,比如完整的配置文件栓霜。kubectl create configmap命令支持從磁盤上讀取文件,并將文件內(nèi)容單獨(dú)存儲(chǔ)為ConfigMap中的條目:

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

運(yùn)行上述命令時(shí)横蜒,kubectl會(huì)在當(dāng)前目錄下查找config-file.conf文件胳蛮,并將文件內(nèi)容存儲(chǔ)在ConfigMap中以config-file.conf為鍵名的條目下。當(dāng)然也可以手動(dòng)指定鍵名:

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

這條命令會(huì)將文件內(nèi)容存在鍵名為customkey的條目下丛晌。與使用字面量時(shí)相同仅炊,多次使用--from-file參數(shù)可增加多個(gè)文件條目。

從文件夾創(chuàng)建ConfigMap

除單獨(dú)引入每個(gè)文件外澎蛛,甚至可以引入某一文件夾中的所有文件:

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

這種情況下抚垄,kubectl會(huì)為文件夾中的每個(gè)文件單獨(dú)創(chuàng)建條目,僅限于那些文件名可作為合法ConfigMap鍵名的文件谋逻。

合并不同選項(xiàng) 創(chuàng)建ConfigMap時(shí)可以混合使用這里提到的所有選項(xiàng)(注意這里的文件并未包含在本書的代碼歸檔中——如果想要嘗試這條命令需自行創(chuàng)建):

$ kubectl create configmap my-config \
    --from-file=foo.json             #單獨(dú)的文件
    --from-file=bar=foobar.conf      #自定義鍵名條目下的文件
    --from-file=config-opts/         #完整的文件夾
    --from-literal=some=thing        #字面量

這里的ConfigMap創(chuàng)建自多種選項(xiàng):完整文件夾呆馁、單獨(dú)文件、自定義鍵名的條目下的文件(替代文件名作鍵名)以及字面量毁兆。

$ k get cm
$ k get configmap

7.4.3 給容器傳遞ConfigMap條目作為環(huán)境變量

如何將映射中的值傳遞給pod的容器浙滤?有三種方法。首先嘗試最為簡(jiǎn)單的一種——設(shè)置環(huán)境變量气堕,將會(huì)使用到7.5.3節(jié)中提到的valueFrom字段纺腊。pod的定義描述如下面的代碼清單所示畔咧。

代碼清單7.9 通過(guò)配置文件注入環(huán)境變量的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 #設(shè)置環(huán)境變量 
      valueFrom: 
        configMapKeyRef:  #使用configmap初始化
          name: fortune-config  #引用的configmap名稱
          key: sleep-interval  #對(duì)應(yīng)的鍵名
    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: {}

這里定義了一個(gè)環(huán)境變量INTERVAL,并將其值設(shè)置為fortune-config ConfigMap中鍵名為sleep-interval對(duì)應(yīng)的值摹菠。運(yùn)行在html-generator容器中的進(jìn)程讀取到環(huán)境變量INTERVAL的值為25盒卸。

在pod中引用不存在的ConfigMap

你可能會(huì)好奇如果創(chuàng)建pod時(shí)引用的ConfigMap不存在會(huì)發(fā)生什么?Kubernetes會(huì)正常調(diào)度pod并嘗試運(yùn)行所有的容器次氨。然而引用不存在的ConfigMap的容器會(huì)啟動(dòng)失敗蔽介,其余容器能正常啟動(dòng)。如果之后創(chuàng)建了這個(gè)缺失的ConfigMap煮寡,失敗容器會(huì)自動(dòng)啟動(dòng)虹蓄,無(wú)須重新創(chuàng)建pod。

注意 可以標(biāo)記對(duì)ConfigMap的引用是可選的(設(shè)置configMapKeyRef.optional: true)幸撕。這樣薇组,即便ConfigMap不存在,容器也能正常啟動(dòng)坐儿。

這個(gè)例子展示了如何將配置從pod定義中分離律胀。這樣能使所有的配置項(xiàng)較為集中(甚至多個(gè)pod也是如此),而不是分散在各處(或者冗余復(fù)制于多個(gè)pod定義清單)貌矿。

7.4.4 一次性傳遞ConfigMap的所有條目作為環(huán)境變量

如果ConfigMap包含不少條目炭菌,為每個(gè)條目單獨(dú)設(shè)置環(huán)境變量的過(guò)程是單調(diào)乏味且容易出錯(cuò)的。

假設(shè)一個(gè)ConfigMap包含F(xiàn)OO逛漫、BAR和FOO-BAR三個(gè)鍵黑低。可以通過(guò)envFrom屬性字段將所有條目暴露作為環(huán)境變量酌毡,而非使用前面例子中的env字段克握。示例代碼如下所示。

代碼清單7.10 pod包含來(lái)源于ConfigMap所有條目的環(huán)境變量

spec:
  containers:
  - image: luksa/fortune:env
    envForm:                   #使用的是envForm字段而不是env字段
      - prefix: CONFIG_   #所有環(huán)境變量均包含前綴CONFIG_
        configMapKeyRef: 
          name: fortune-config 
          key: sleep-interval 

如你所見(jiàn)枷踏,可以為所有的環(huán)境變量設(shè)置前綴菩暗,如本例中的 CONFIG_ ,容器中兩個(gè)環(huán)境變量的名稱為: CONFIG_FOOCONFIG_BAR 旭蠕。

注意 前綴設(shè)置是可選的勋眯,若不設(shè)置前綴值,環(huán)境變量的名稱與ConfigMap中的鍵名相同下梢。

是否注意到前面說(shuō)的是兩個(gè)環(huán)境變量,然而ConfigMap擁有三個(gè)條目(FOO塞蹭、BAR和FOO-BAR)孽江?為何沒(méi)有對(duì)應(yīng)FOO-BAR條目的環(huán)境變量呢?

原因在于CONFIG_FOO-BAR包含破折號(hào)番电,這并不是一個(gè)合法的環(huán)境變量名稱岗屏。Kubernetes不會(huì)主動(dòng)轉(zhuǎn)換鍵名(例如不會(huì)將破折號(hào)轉(zhuǎn)換為下畫線)辆琅。如果ConfigMap的某鍵名格式不正確,創(chuàng)建環(huán)境變量時(shí)會(huì)忽略對(duì)應(yīng)的條目(忽略時(shí)不會(huì)發(fā)出事件通知)这刷。

7.4.5 傳遞ConfigMap條目作為命令行參數(shù)

現(xiàn)在讓我們來(lái)看一下如何將ConfigMap中的值作為參數(shù)值傳遞給運(yùn)行在容器中的主進(jìn)程婉烟。在字段pod.spec.containers.args中無(wú)法直接引用ConfigMap的條目,但是可以利用ConfigMap條目初始化某個(gè)環(huán)境變量暇屋,然后再在參數(shù)字段中引用該環(huán)境變量似袁。

代碼清單7.11展示了如何在YAML文件中做到這一點(diǎn)。

代碼清單7.11 使用ConfigMap條目作為參數(shù)值:fortune-pod-args-configmap.yaml

apiVersion: v1
kind: Pod
metadata:
  name: fortune-args-from-configmap
spec:
  containers:
  - image: luksa/fortune:args  # 使用參數(shù)讀取間隔值的鏡像
    env:                       # 與之前的環(huán)境變量的定義相同
    - name: INTERVAL
      valueFrom: 
        configMapKeyRef:
          name: fortune-config
          key: sleep-interval
    args: ["$(INTERVAL)"]  # 在參數(shù)設(shè)置中引用環(huán)境變量
    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: {}

環(huán)境變量的定義與之前相同咐刨,需通過(guò) $(ENV_VARIABLE_NAME) 將環(huán)境變量的值注入?yún)?shù)值昙衅。

7.4.6 使用configMap卷將條目暴露為文件

環(huán)境變量或者命令行參數(shù)值作為配置值通常適用于變量值較短的場(chǎng)景。由于ConfigMap中可以包含完整的配置文件內(nèi)容定鸟,當(dāng)你想要將其暴露給容器時(shí)而涉,可以借助前面章節(jié)提到過(guò)的一種稱為configMap卷的特殊卷格式。

configMap卷會(huì)將ConfigMap中的每個(gè)條目均暴露成一個(gè)文件联予。運(yùn)行在容器中的進(jìn)程可通過(guò)讀取文件內(nèi)容獲得對(duì)應(yīng)的條目值啼县。

盡管這種方法主要適用于傳遞較大的配置文件給容器,同樣可以用于傳遞較短的變量值沸久。

創(chuàng)建ConfigMap

這里不再修改腳本fortuneloop.sh季眷,將嘗試另一個(gè)不同的示例,使用配置文件配置運(yùn)行在fortune pod的Web服務(wù)器容器中的Nginx web服務(wù)器麦向。如果想要讓Nginx服務(wù)器壓縮傳遞給客戶端的響應(yīng)瘟裸,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; #開啟對(duì)文本和XML文件的gzip壓縮

    location / {
        root   /usr/share/nginx/html;
        index  index.html index.htm;
    }
}

現(xiàn)在首先通過(guò) kubectl delete configmap fortune-config 刪除現(xiàn)有的ConfigMap fortune-config话告,然后用存儲(chǔ)在本地磁盤上的Nginx配置文件創(chuàng)建一個(gè)新的ConfigMap。

創(chuàng)建一個(gè)新文件夾confimap-files并將上面的配置文件存儲(chǔ)于 configmap-files/my-nginx-config.conf 中卵慰。另外在該文件夾中添加一個(gè)名為sleep-interval的文本文件沙郭,寫入值為25,使ConfigMap同樣包含條目sleep-interval裳朋。

sleep-interval文件

25

文件結(jié)構(gòu)如下:

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

從文件夾創(chuàng)建ConfigMap:

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

下面的代碼清單展示了ConfigMap的YAML格式內(nèi)容病线。

代碼清單7.13 從文件創(chuàng)建的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配置文件的內(nèi)容

        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

注意 所有條目第一行最后的管道符號(hào)表示后續(xù)的條目值是多行字面量。

ConfigMap包含兩個(gè)條目鲤嫡,條目的鍵名與文件名相同送挑。接下來(lái)將在pod的容器中使用該ConfigMap。

在卷內(nèi)使用ConfigMap的條目

創(chuàng)建包含ConfigMap條目?jī)?nèi)容的卷只需要?jiǎng)?chuàng)建一個(gè)引用ConfigMap名稱的卷并掛載到容器中暖眼。已經(jīng)學(xué)會(huì)了如何創(chuàng)建及掛載卷惕耕,接下來(lái)要學(xué)習(xí)的僅是如何用ConfigMap的條目初始化卷。

Nginx需讀取配置文件 /etc/nginx/nginx.conf 诫肠,而Nginx鏡像內(nèi)的這個(gè)文件包含默認(rèn)配置司澎,并不想完全覆蓋這個(gè)配置文件欺缘。幸運(yùn)的是,默認(rèn)配置文件會(huì)自動(dòng)嵌入子文件夾 /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卷到這個(gè)位置
      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服務(wù)器使用它蛤铜。

檢查Nginx是否使用被掛載的配置文件

現(xiàn)在的web服務(wù)器應(yīng)該已經(jīng)被配置為會(huì)壓縮響應(yīng)嫩絮,可以將localhost:8080轉(zhuǎn)發(fā)到pod的80端口,利用curl檢查服務(wù)器響應(yīng)來(lái)驗(yàn)證配置是否生效昂羡,如下面的代碼清單所示絮记。

代碼清單7.15 觀察nginx響應(yīng)是否被壓縮

$ 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  #這里說(shuō)明相應(yīng)被壓縮

檢查被掛載的configMap卷的內(nèi)容

服務(wù)器響應(yīng)說(shuō)明配置成功生效。現(xiàn)在來(lái)看一下文件夾 /etc/nginx/conf.d 下的內(nèi)容:

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

my-nginx-config.conf
sleep-interval

ConfigMap的兩個(gè)條目均作為文件置于這一文件夾下虐先。條目sleep-interval對(duì)應(yīng)的文件也被包含在內(nèi)怨愤,然而它只會(huì)被fortuneloop容器所使用∮寂可以創(chuàng)建兩個(gè)不同的ConfigMap撰洗,一個(gè)用以配置容器fortuneloop,另一個(gè)用來(lái)配置webserver腐芍,然而采用多個(gè)ConfigMap去分別配置同一pod中的不同容器的做法是不好的差导。畢竟同一pod中的容器是緊密聯(lián)系的设褐,需要被當(dāng)作整體單元來(lái)配置。

卷內(nèi)暴露指定的ConfigMap條目

幸運(yùn)的是助析,可以創(chuàng)建僅包含ConfigMap中部分條目的configMap卷——本示例中的條目my-nginx-config.conf椅您。這樣容器fortuneloop不會(huì)受到影響,條目sleep-interval會(huì)作為環(huán)境變量傳遞給容器而不是以卷的方式掀泳。

通過(guò)卷的items屬性能夠指定哪些條目會(huì)被暴露作為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 # 條目的值被存儲(chǔ)在該文件中

指定單個(gè)條目時(shí)需同時(shí)設(shè)置條目的鍵名稱以及對(duì)應(yīng)的文件名。如果采用上面的配置文件創(chuàng)建pod捅伤, /etc/nginx/conf.d 文件夾是比較干凈的巫玻,僅包含所需的 gzip.conf 文件。

掛載某一文件夾會(huì)隱藏該文件夾中已存在的文件

這里有一件重要的事情需要討論仍秤。在當(dāng)前與此前的示例中,將卷掛載至某個(gè)文件夾诗力,意味著容器鏡像中 /etc/nginx/conf.d 文件夾下原本存在的任何文件都會(huì)被隱藏凰浮。

Linux系統(tǒng)掛載文件系統(tǒng)至非空文件夾時(shí)通常表現(xiàn)如此。文件夾中只會(huì)包含被掛載文件系統(tǒng)中的文件苇本,即便文件夾中原本的文件是不可訪問(wèn)的也是同樣如此袜茧。

本示例中,這種現(xiàn)象并不會(huì)帶來(lái)比較糟糕的副作用瓣窄。不過(guò)假設(shè)掛載文件夾是/etc笛厦,該文件夾通常包含不少重要文件。由于/etc下的所有文件不存在俺夕,容器極大可能會(huì)損壞裳凸。如果你希望添加文件至某個(gè)文件夾如/etc,絕不能采用這種方法劝贸。

ConfigMap獨(dú)立條目作為文件被掛載且不隱藏文件夾中的其他文件

順理成章姨谷,你會(huì)好奇如何能掛載ConfigMap對(duì)應(yīng)文件至現(xiàn)有文件夾的同時(shí)不會(huì)隱藏現(xiàn)有文件。volumeMount額外的subPath字段可以被用作掛載卷中的某個(gè)獨(dú)立文件或者是文件夾映九,無(wú)須掛載完整卷梦湘。圖7.10的形象化解釋可能更加容易理解。

假設(shè)擁有一個(gè)包含文件myconfig.conf的configMap卷件甥,希望能將其添加為/etc文件夾下的文件someconfig.conf捌议。通過(guò)屬性subPath可以將該文件掛載的同時(shí)又不影響文件夾中的其他文件。pod定義中的相關(guān)部分如下面的代碼清單所示嚼蚀。

代碼清單7.17 pod掛載ConfigMap的指定條目至特定文件

spec:
  containers:
  - image: luksa/fortune:env
    name: html-generator
    volumeMounts:
    - name: myvolume
      mountPath: /var/myconfig.conf # 掛載某一個(gè)文件禁灼,而不是文件夾
      subPath: myconfig.config   #僅掛載指定的條目,而不是整個(gè)卷

掛載任意一種卷時(shí)均可以使用subPath屬性轿曙∨叮可以選擇掛載部分卷而不是掛載完整的卷。不過(guò)這種獨(dú)立文件的掛載方式會(huì)帶來(lái)文件更新上的缺陷导帝,你會(huì)在接下來(lái)的小節(jié)中學(xué)習(xí)到更多的相關(guān)知識(shí)守谓,在這里還是先要說(shuō)一些文件權(quán)限問(wèn)題對(duì)configMap卷的討論進(jìn)行收尾。

為configMap卷中的文件設(shè)置權(quán)限

configMap卷中所有文件的權(quán)限默認(rèn)被設(shè)置為 644(-rw-r-r--) 您单≌瘢可以通過(guò)卷規(guī)格定義中的 defaultMode 屬性改變默認(rèn)權(quán)限,如下面的代碼清單所示虐秦。

代碼清單7.18 設(shè)置權(quán)限: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 # 設(shè)置所有文件的權(quán)限為-rw-rw----

ConfigMap通常被用作存儲(chǔ)非敏感數(shù)據(jù)凤优,不過(guò)依舊可能希望僅限于文件擁有者的用戶和組可讀寫筑辨,正如上面的例子所示棍辕。

7.4.7 更新應(yīng)用配置且不重啟應(yīng)用程序

在此之前提到過(guò)楚昭,使用環(huán)境變量或者命令行參數(shù)作為配置源的弊端在于無(wú)法在進(jìn)程運(yùn)行時(shí)更新配置拍顷。將ConfigMap暴露為卷可以達(dá)到配置熱更新的效果凭舶,無(wú)須重新創(chuàng)建pod或者重啟容器爱沟。

ConfigMap被更新之后呼伸,卷中引用它的所有文件也會(huì)相應(yīng)更新括享,進(jìn)程發(fā)現(xiàn)文件被改變之后進(jìn)行重載铃辖。Kubernetes同樣支持文件更新之后手動(dòng)通知容器娇斩。

查看配置文件

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

警告 請(qǐng)注意筆者在寫這段的時(shí)候锦积,更新ConfigMap之后對(duì)應(yīng)文件的更新耗時(shí)會(huì)出人意料地長(zhǎng)(往往需要數(shù)分鐘)丰介。

修改ConfigMap

現(xiàn)在來(lái)瞧一瞧如何修改ConfigMap,同時(shí)運(yùn)行在pod中的進(jìn)程會(huì)重載configMap卷中對(duì)應(yīng)的文件带膀。你需要修改前面示例中的Nginx配置文件本砰,使得Nginx能夠在不重啟pod的前提下應(yīng)用新配置。嘗試用 kubectl edit 命令修改 ConfigMap fortune-config 來(lái)關(guān)閉gzip壓縮:

$ kubectl edit configmap fortune-config

編輯器打開舔株,行 gzip on 改為 gzip off 载慈,保存文件后關(guān)閉編輯器办铡。ConfigMap被更新不久之后會(huì)自動(dòng)更新卷中的對(duì)應(yīng)文件。用 kubectl exec 命令打印出該文件內(nèi)容進(jìn)行確認(rèn):

若尚未看到文件內(nèi)容被更新秤茅,可稍等一會(huì)兒后重試框喳。文件更新過(guò)程需要一段時(shí)間五垮。最終你會(huì)看到配置文件的變化放仗,然而發(fā)現(xiàn)這對(duì)Nginx并沒(méi)有什么影響诞挨,這是因?yàn)镹ginx不會(huì)去監(jiān)聽(tīng)文件的變化并自動(dòng)重載小作。

通知Nginx重載配置

Nginx會(huì)持續(xù)壓縮響應(yīng)直到你通過(guò)以下命令主動(dòng)通知它:

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

現(xiàn)在再次用curl命令訪問(wèn)服務(wù)器后會(huì)發(fā)現(xiàn)響應(yīng)不再被壓縮(響應(yīng)頭中未包含 Content-Encoding: gzip )达罗。在無(wú)須重啟容器或者重建pod的同時(shí)有效修改了應(yīng)用配置粮揉。

$ 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

了解文件被自動(dòng)更新的過(guò)程

你可能會(huì)疑惑在Kubernetes更新完configMap卷中的所有文件之前扶认,應(yīng)用是否會(huì)監(jiān)聽(tīng)到文件變化并主動(dòng)進(jìn)行重載辐宾。幸運(yùn)的是叠纹,這不會(huì)發(fā)生,所有的文件會(huì)被自動(dòng)一次性更新与涡。Kubernetes通過(guò)符號(hào)鏈接做到這一點(diǎn)驼卖。如果嘗試列出configMap卷掛載位置的所有文件,會(huì)看到如下內(nèi)容谬莹。

代碼清單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 文件夾中文件的符號(hào)鏈接蕉扮,而 ..data 文件夾同樣是 ..4984_09_04_something 的符號(hào)鏈接喳钟。每當(dāng)ConfigMap被更新后奔则,Kubernetes會(huì)創(chuàng)建一個(gè)這樣的文件夾,寫入所有文件并重新將符號(hào) ..data鏈接至新文件夾酬蹋,通過(guò)這種方式可以一次性修改所有文件。

掛載至已存在文件夾的文件不會(huì)被更新

涉及到更新configMap卷需要提出一個(gè)警告:如果掛載的是容器中的單個(gè)文件而不是完整的卷骄恶,ConfigMap更新之后對(duì)應(yīng)的文件不會(huì)被更新僧鲁!至少在寫本章節(jié)的時(shí)候表現(xiàn)如此寞秃。

如果現(xiàn)在你需要掛載單個(gè)文件并且在修改源ConfigMap的同時(shí)會(huì)自動(dòng)修改這個(gè)文件蜕该,一種方案是掛載完整卷至不同的文件夾并創(chuàng)建指向所需文件的符號(hào)鏈接。符號(hào)鏈接可以原生創(chuàng)建在容器鏡像中扒腕,也可以在容器啟動(dòng)時(shí)創(chuàng)建瘾腰。

了解更新ConfigMap的影響

容器的一個(gè)比較重要的特性是其不變性蹋盆,從同一鏡像啟動(dòng)的多個(gè)容器之間不存在任何差異栖雾。那么通過(guò)修改被運(yùn)行容器所使用的ConfigMap來(lái)打破這種不變性的行為是否是錯(cuò)誤的析藕?

關(guān)鍵點(diǎn)在于應(yīng)用是否支持重載配置。ConfigMap更新之后創(chuàng)建的pod會(huì)使用新配置治泥,而之前的pod依舊使用舊配置遮精,這會(huì)導(dǎo)致運(yùn)行中的不同實(shí)例的配置不同吮播。這也不僅限于新pod意狠,如果pod中的容器因?yàn)槟撤N原因重啟了环戈,新進(jìn)程同樣會(huì)使用新配置。因此遮晚,如果應(yīng)用不支持主動(dòng)重載配置,那么修改某些運(yùn)行pod所使用的ConfigMap并不是一個(gè)好主意汹族。

如果應(yīng)用支持主動(dòng)重載配置顶瞒,那么修改ConfigMap的行為就算不了什么守问。不過(guò)有一點(diǎn)仍需注意耗帕,由于configMap卷中文件的更新行為對(duì)于所有運(yùn)行中示例而言不是同步的,因此不同pod中的文件可能會(huì)在長(zhǎng)達(dá)一分鐘的時(shí)間內(nèi)出現(xiàn)不一致的情況探越。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市鲤氢,隨后出現(xiàn)的幾起案子卷玉,更是在濱河造成了極大的恐慌相种,老刑警劉巖寝并,帶你破解...
    沈念sama閱讀 219,270評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異衬潦,居然都是意外死亡斤蔓,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,489評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門镀岛,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)弦牡,“玉大人,你說(shuō)我怎么就攤上這事漂羊±” “怎么了?”我有些...
    開封第一講書人閱讀 165,630評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵拨与,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我买喧,道長(zhǎng)低淡,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,906評(píng)論 1 295
  • 正文 為了忘掉前任皂吮,我火速辦了婚禮需纳,結(jié)果婚禮上慌盯,老公的妹妹穿的比我還像新娘国瓮。我一直安慰自己孵睬,他們只是感情好烁试,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,928評(píng)論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般绞愚。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上佛致,一...
    開封第一講書人閱讀 51,718評(píng)論 1 305
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼藕施。 笑死,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 40,442評(píng)論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了株扛?” 一聲冷哼從身側(cè)響起油昂,我...
    開封第一講書人閱讀 39,345評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤挑庶,失蹤者是張志新(化名)和其女友劉穎岖瑰,沒(méi)想到半個(gè)月后玫锋,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,802評(píng)論 1 317
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,984評(píng)論 3 337
  • 正文 我和宋清朗相戀三年疾党,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了雪位。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片队伟。...
    茶點(diǎn)故事閱讀 40,117評(píng)論 1 351
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡咪惠,死狀恐怖落午,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情详炬,我是刑警寧澤盐类,帶...
    沈念sama閱讀 35,810評(píng)論 5 346
  • 正文 年R本政府宣布,位于F島的核電站呛谜,受9級(jí)特大地震影響在跳,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜隐岛,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,462評(píng)論 3 331
  • 文/蒙蒙 一猫妙、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧聚凹,春花似錦割坠、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,011評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至湘今,卻和暖如春敢朱,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,139評(píng)論 1 272
  • 我被黑心中介騙來(lái)泰國(guó)打工拴签, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留孝常,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,377評(píng)論 3 373
  • 正文 我出身青樓蚓哩,卻偏偏與公主長(zhǎng)得像构灸,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子岸梨,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,060評(píng)論 2 355