應(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_FOO
與 CONFIG_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)不一致的情況探越。