
這kubernetes中,這類Volume不是為了存放數(shù)據(jù),也不是用來(lái)做數(shù)據(jù)交換,而是為容器提供預(yù)先定義好的數(shù)據(jù)。所以從容器角度來(lái)看,這類Volume就像是被投射進(jìn)容器一樣。
到目前為止,kubernetes支持4種這類Volume:
(1)、Secret
(2)、ConfigMap
(3)、DownloadAP
(4)、ServiceAccountToken
Secret
Secret的作用是把Pod想要訪問(wèn)的加密數(shù)據(jù)存放到Etcd中,然后可以在Pod容器中通過(guò)掛載的方式訪問(wèn)Secret里保存的信息。
一旦Secret被創(chuàng)建,就可以通過(guò)下面三種方式使用它:
(1)在創(chuàng)建Pod時(shí),通過(guò)為Pod指定Service Account來(lái)自動(dòng)使用該Secret。
(2)通過(guò)掛載該Secret到Pod來(lái)使用它。
(3)在Docker鏡像下載時(shí)使用,通過(guò)指定Pod的spc.ImagePullSecrets來(lái)引用它。
例如,我們定義下面這個(gè)Pod:
apiVersion: v1
kind: Pod
metadata:
name: pod-volume-test
labels:
app: pod-volume-test
spec:
containers:
- name: volume-secret-test
image: busybox
command:
- "/bin/sh"
- "-c"
- "sleep 3600"
volumeMounts:
- name: my-secret
mountPath: "/project-volume"
readOnly: true
volumes:
- name: my-secret
projected:
sources:
- secret:
name: my-secret-volume
從上面的yaml文件中可以看到定義了一個(gè)簡(jiǎn)單的容器volume-secret-test,它里面掛載了一個(gè)my-secret的volume,這個(gè)volume是project類型,而這個(gè)數(shù)據(jù)來(lái)源是叫user和password得secret對(duì)象。
這里的secret對(duì)象,可以直接通過(guò)文件生成,也可以通過(guò)定義YAML文件的方式來(lái)創(chuàng)建。
(1)、通過(guò)文件生成
cat ./username.txt
admin
$ cat ./password.txt
c1oudc0w!
$ kubectl create secret generic user --from-file=./username.txt
$ kubectl create secret generic pass --from-file=./password.txt
(2)、通過(guò)YAML方式生成
apiVersion: v1
kind: Secret
metadata:
name: my-secret-volume
type: Opaque
data:
user: cm9vdA==
password: UEBzc1cwcmQ=
其中user和password的值是需要經(jīng)過(guò)base64轉(zhuǎn)碼,如下:
[root@master ~]# echo -n "root" | base64
cm9vdA==
[root@master ~]# echo -n "P@ssW0rd" | base64
UEBzc1cwcmQ=
這個(gè)轉(zhuǎn)碼未經(jīng)過(guò)加密,在實(shí)際生產(chǎn)中是需要添加一個(gè)secret的插件,用來(lái)做加密操作。
然后我們來(lái)創(chuàng)建這個(gè)兩個(gè)pod:
# kubectl apply -f creat-volume-secret.yaml
# kubectl apply -f pod-volume-test.yaml
然后通過(guò)如下命令查看創(chuàng)建的secret:

然后通過(guò)kubectl exec進(jìn)去容器查看是否掛載上:
[root@master volume]# kubectl exec -it pod-volume-test -- /bin/sh
/ # cd project-volume/
/project-volume # ls
password user
/project-volume # cat password
P@ssW0rd
/project-volume # cat user
root
通過(guò)這種方式掛載的Secret,如果某個(gè)數(shù)據(jù)被更新,這些Volume里的內(nèi)容不會(huì)被更新,如果要更新,我們需要重新apply一下或者刪除重建。
比如,我們修改password的值:
apiVersion: v1
kind: Secret
metadata:
name: my-secret-volume
type: Opaque
data:
user: cm9vdA==
password: cGFzc3dvcmQxMjM0NTY=
然后重新執(zhí)行一下:
# kubectl apply -f creat-volume-secret.yaml
然后我們進(jìn)入容器查看password變化(大概等了2分鐘):
[root@master volume]# kubectl exec -it pod-volume-test -- /bin/sh
/ # cd project-volume/
/project-volume # cat password
password123456
說(shuō)明:
每個(gè)單獨(dú)的Secret大小不能超過(guò)1MB,Kubernetes不鼓勵(lì)創(chuàng)建大的Secret,因?yàn)槿绻褂么蟮腟ecret,則將大量占用API Server和kubelet的內(nèi)存。當(dāng)然,創(chuàng)建許多小的Secret也能耗盡APIServer和kubelet的內(nèi)存。
綜上,我們可以通過(guò)Secret保管其他系統(tǒng)的敏感信息(比如數(shù)據(jù)庫(kù)的用戶名和密碼),并以Mount的方式將Secret掛載到Container中,然后通過(guò)訪問(wèn)目錄中文件的方式獲取該敏感信息。當(dāng)Pod被API Server創(chuàng)建時(shí),API Server不會(huì)校驗(yàn)該P(yáng)od引用的Secret是否存在。一旦這個(gè)Pod被調(diào)度,則kubelet將試著獲取Secret的值。如果Secret不存在或暫時(shí)無(wú)法連接到API Server,則kubelet按一定的時(shí)間間隔定期重試獲取該Secret,并發(fā)送一個(gè)Event來(lái)解釋Pod沒(méi)有啟動(dòng)的原因。一旦Secret被Pod獲取,則kubelet將創(chuàng)建并掛載包含Secret的Volume。只有所有Volume都掛載成功,Pod中的Container才會(huì)被啟動(dòng)。在kubelet啟動(dòng)Pod中的Container后,Container中和Secret相關(guān)的Volume將不會(huì)被改變,即使Secret本身被修改。為了使用更新后的Secret,必須刪除舊Pod,并重新創(chuàng)建一個(gè)新Pod。
ConfigMap
ConfigMap和Serect類似,不同之處在于ConfigMap保存的數(shù)據(jù)信息是不需要加密的,比如一些應(yīng)用的配置信息,其他的用法和Secret一樣。同樣,我們可以使用兩種方式來(lái)創(chuàng)建ConfigMap:
- 通過(guò)命令行方式,也就是kubectl create configmap;
- 通過(guò)YAML文件方式;
(1)、通過(guò)命令方式創(chuàng)建
如果我們不知道ConfigMap的命令方式,可以使用kubectl create configmap -h查看使用方法,如下:
Examples:
# Create a new configmap named my-config based on folder bar
kubectl create configmap my-config --from-file=path/to/bar
# Create a new configmap named my-config with specified keys instead of file basenames on disk
kubectl create configmap my-config --from-file=key1=/path/to/bar/file1.txt --from-file=key2=/path/to/bar/file2.txt
# Create a new configmap named my-config with key1=config1 and key2=config2
kubectl create configmap my-config --from-literal=key1=config1 --from-literal=key2=config2
# Create a new configmap named my-config from the key=value pairs in the file
kubectl create configmap my-config --from-file=path/to/bar
# Create a new configmap named my-config from an env file
kubectl create configmap my-config --from-env-file=path/to/bar.env
從上面可以看出,創(chuàng)建ConfigMap可以從給定一個(gè)目錄來(lái)創(chuàng)建。例如,我們定義了如下一些配置文件:
[root@master volume]# cd configmap-daemo/
[root@master configmap-daemo]# ll
total 8
-rw-r--r-- 1 root root 25 Sep 6 17:07 mysqld.conf
-rw-r--r-- 1 root root 25 Sep 6 17:07 redis.conf
[root@master configmap-daemo]# cat mysqld.conf
host=127.0.0.1
port=3306
[root@master configmap-daemo]# cat redis.conf
host=127.0.0.1
port=6379
然后使用一下命令來(lái)進(jìn)行創(chuàng)建:
# kubectl create configmap my-configmap --from-file=configmap-daemo/
然后通過(guò)一下命令查看創(chuàng)建完的configmap:
[root@master volume]# kubectl get configmap
NAME DATA AGE
my-configmap 2 19s
[root@master volume]# kubectl describe configmap my-configmap
Name: my-configmap
Namespace: default
Labels: <none>
Annotations: <none>
Data
====
mysqld.conf:
----
host=127.0.0.1
port=3306
redis.conf:
----
host=127.0.0.1
port=6379
Events: <none>
我們可以看到兩個(gè)key對(duì)應(yīng)的是文件的名字,value對(duì)應(yīng)的是文件的內(nèi)容。如果要看鍵值的話可以通過(guò)如下命令查看:
[root@master volume]# kubectl get configmap my-configmap -o yaml
apiVersion: v1
data:
mysqld.conf: |
host=127.0.0.1
port=3306
redis.conf: |
host=127.0.0.1
port=6379
kind: ConfigMap
metadata:
creationTimestamp: "2019-09-06T09:10:49Z"
name: my-configmap
namespace: default
resourceVersion: "252070"
selfLink: /api/v1/namespaces/default/configmaps/my-configmap
uid: cd8d3752-c1c4-48e0-b523-bafa20f1ba1f
當(dāng)然,我們還可以通過(guò)文件來(lái)創(chuàng)建一個(gè)configmap,比如我們定義一個(gè)如下的配置文件:
[root@master configmap-daemo]# cat nginx.conf
user nobody;
worker_processes 1;
error_log logs/error.log;
error_log logs/error.log notice;
error_log logs/error.log info;
pid logs/nginx.pid;
events {
worker_connections 1024;
}
http {
include mime.types;
default_type application/octet-stream;
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';
access_log logs/access.log main;
sendfile on;
tcp_nopush on;
keepalive_timeout 65;
gzip on;
server {
listen 80;
server_name localhost;
location / {
root html;
index index.html index.htm;
}
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root html;
}
}
}
然后啟動(dòng)如下命令創(chuàng)建一個(gè)nginx的configmap:
# kubectl create configmap nginx-configmap --from-file=nginx.conf
然后查看創(chuàng)建后的信息:
[root@master configmap-daemo]# kubectl get configmap nginx-configmap -o yaml
apiVersion: v1
data:
nginx.conf: |
user nobody;
worker_processes 1;
error_log logs/error.log;
error_log logs/error.log notice;
error_log logs/error.log info;
pid logs/nginx.pid;
events {
worker_connections 1024;
}
http {
include mime.types;
default_type application/octet-stream;
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';
access_log logs/access.log main;
sendfile on;
tcp_nopush on;
keepalive_timeout 65;
gzip on;
server {
listen 80;
server_name localhost;
location / {
root html;
index index.html index.htm;
}
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root html;
}
}
}
kind: ConfigMap
metadata:
creationTimestamp: "2019-09-06T09:25:42Z"
name: nginx-configmap
namespace: default
resourceVersion: "253356"
selfLink: /api/v1/namespaces/default/configmaps/nginx-configmap
uid: c4804bb0-dec3-461e-8fc4-0b0bb516815c
注:在一條命令中--from-file可以指定多次。
另外,通過(guò)幫助文檔我們可以看到我們還可以直接使用字符串進(jìn)行創(chuàng)建,通過(guò)--from-literal參數(shù)傳遞配置信息,同樣的,這個(gè)參數(shù)可以使用多次,格式如下:
[root@master configmap-daemo]# kubectl create configmap my-cm-daemo --from-literal=db.host=localhost --from-literal=db.port=3306
configmap/my-cm-daemo created
[root@master configmap-daemo]# kubectl get configmap my-cm-daemo -o yaml
apiVersion: v1
data:
db.host: localhosy
db.port: "3306"
kind: ConfigMap
metadata:
creationTimestamp: "2019-09-06T09:29:49Z"
name: my-cm-daemo
namespace: default
resourceVersion: "253713"
selfLink: /api/v1/namespaces/default/configmaps/my-cm-daemo
uid: ee8823e7-f2b3-46bf-ba6c-2f32303622c4
(2)、通過(guò)YAML文件創(chuàng)建
通過(guò)YAML文件創(chuàng)建就比較簡(jiǎn)單,我們可以參考上面輸出的yaml信息,比如定義如下一個(gè)YAML文件:
[root@master configmap-daemo]# cat my-cm-daemo2.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: my-cm-daemon2
labels:
app: cm-daemon
data:
redis.conf: |
host=127.0.0.1
port=6379
查看結(jié)果信息:
[root@master configmap-daemo]# kubectl apply -f my-cm-daemo2.yaml
configmap/my-cm-daemon2 created
[root@master configmap-daemo]# kubectl describe configmap my-cm-daemon2
Name: my-cm-daemon2
Namespace: default
Labels: app=cm-daemon
Annotations: kubectl.kubernetes.io/last-applied-configuration:
{"apiVersion":"v1","data":{"redis.conf":"host=127.0.0.1\nport=6379\n"},"kind":"ConfigMap","metadata":{"annotations":{},"labels":{"app":"cm...
Data
====
redis.conf:
----
host=127.0.0.1
port=6379
Events: <none>
(3)、使用ConfigMap
ConfigMap中的配置數(shù)據(jù)可以通過(guò)如下方式進(jìn)行使用:
- 設(shè)置環(huán)境變量值
- 在容器里設(shè)置命令行參數(shù)
- 在數(shù)據(jù)卷中創(chuàng)建config文件
1、通過(guò)設(shè)置環(huán)境變量值來(lái)使用ConfigMap
定義如下YAML文件:
[root@master configmap-daemo]# cat env-configmap.yaml
apiVersion: v1
kind: Pod
metadata:
name: env-configmap
labels:
app: env-configmap-mysql
spec:
containers:
- name: test-configmap
image: busybox
command:
- "/bin/sh"
- "-c"
- "env"
env:
- name: DB_HOST
valueFrom:
configMapKeyRef:
name: my-cm-daemo
key: db.host
- name: DB_PORT
valueFrom:
configMapKeyRef:
name: my-cm-daemo
key: db.port
envFrom:
- configMapRef:
name: my-cm-daemo
查看配置結(jié)果:
[root@master configmap-daemo]# kubectl apply -f env-configmap.yaml
pod/env-configmap created
[root@master configmap-daemo]# kubectl logs env-configmap
KUBERNETES_PORT=tcp://10.68.0.1:443
KUBERNETES_SERVICE_PORT=443
HOSTNAME=env-configmap
DB_PORT=3306
DB_HOST=localhost
......
我們可以看到我們期望得到的變量信息已經(jīng)從ConfigMap中得到了。
2、在容器中設(shè)置命令行參數(shù)來(lái)獲取ConfigMap配置信息
定義YAML問(wèn)價(jià)如下:
[root@master configmap-daemo]# cat command-configmap.yaml
apiVersion: v1
kind: Pod
metadata:
name: command-configmap
spec:
containers:
- name: command-configmap-test
image: busybox
command:
- "/bin/sh"
- "-c"
- "echo $(DB_HOST) $(DB_PORT)"
env:
- name: DB_HOST
valueFrom:
configMapKeyRef:
name: my-cm-daemo
key: db.host
- name: DB_PORT
valueFrom:
configMapKeyRef:
name: my-cm-daemo
key: db.port
查看結(jié)果
[root@master configmap-daemo]# kubectl logs command-configmap
localhosy 3306
3、在數(shù)據(jù)卷中使用ConfigMap
定義YAML文件如下:
[root@master configmap-daemo]# cat volume-configmap.yaml
apiVersion: v1
kind: Pod
metadata:
name: volume-configmap-test
spec:
containers:
- name: volume-configmap-test
image: busybox
command: [ "/bin/sh", "-c", "cat /etc/config/redis.conf" ]
volumeMounts:
- name: config-volume
mountPath: /etc/config
volumes:
- name: config-volume
configMap:
name: my-configmap
運(yùn)行Pod,查看結(jié)果
[root@master configmap-daemo]# kubectl logs volume-configmap-test
host=127.0.0.1
port=6379
我們也可以在ConfigMap值被映射的數(shù)據(jù)卷里去控制路徑,如下Pod定義:
[root@master configmap-daemo]# cat volume-path-configmap.yaml
apiVersion: v1
kind: Pod
metadata:
name: volume-path-configmap
spec:
containers:
- name: volume-path-configmap-test
image: busybox
command: [ "/bin/sh","-c","cat /etc/config/path/to/msyqld.conf" ]
volumeMounts:
- name: config-volume
mountPath: /etc/config
volumes:
- name: config-volume
configMap:
name: my-configmap
items:
- key: mysqld.conf
path: path/to/msyqld.conf
然后運(yùn)行Pod,查看執(zhí)行結(jié)果:
[root@master configmap-daemo]# kubectl logs volume-path-configmap
host=127.0.0.1
port=3306
另外需要注意的是,當(dāng)ConfigMap以數(shù)據(jù)卷的形式掛載進(jìn)Pod的時(shí),這時(shí)更新ConfigMap(或刪掉重建ConfigMap),Pod內(nèi)掛載的配置信息會(huì)熱更新。這時(shí)可以增加一些監(jiān)測(cè)配置文件變更的腳本,然后reload對(duì)應(yīng)服務(wù)。
DownloadAPI
讓這個(gè)Pod里的容器可以直接獲取這個(gè)Pod API對(duì)象本身的信息。
比如:
apiVersion: v1
kind: Pod
metadata:
name: test-downwardapi-volume
labels:
zone: us-est-coast
cluster: test-cluster1
rack: rack-22
spec:
containers:
- name: client-container
image: k8s.gcr.io/busybox
command: ["sh", "-c"]
args:
- while true; do
if [[ -e /etc/podinfo/labels ]]; then
echo -en '\n\n'; cat /etc/podinfo/labels; fi;
sleep 5;
done;
volumeMounts:
- name: podinfo
mountPath: /etc/podinfo
readOnly: false
volumes:
- name: podinfo
projected:
sources:
- downwardAPI:
items:
- path: "labels"
fieldRef:
fieldPath: metadata.labels
通過(guò)這樣的聲明方式,就可以把Pod的labels字段值掛載成kubernetes容器中/etc/podinfo/labels文件。
當(dāng)前DownloadAPI支持的字段如下:
1. 使用 fieldRef 可以聲明使用:
spec.nodeName - 宿主機(jī)名字
status.hostIP - 宿主機(jī) IP
metadata.name - Pod 的名字
metadata.namespace - Pod 的 Namespace
status.podIP - Pod 的 IP
spec.serviceAccountName - Pod 的 Service Account 的名字
metadata.uid - Pod 的 UID
metadata.labels['<KEY>'] - 指定 <KEY> 的 Label 值
metadata.annotations['<KEY>'] - 指定 <KEY> 的 Annotation 值
metadata.labels - Pod 的所有 Label
metadata.annotations - Pod 的所有 Annotation
2. 使用 resourceFieldRef 可以聲明使用:
容器的 CPU limit
容器的 CPU request
容器的 memory limit
容器的 memory request
注意:DownloadAPI能夠獲取到的信息一定是Pod里的容器進(jìn)程啟動(dòng)之前就能夠確定下來(lái)的信息。
ServiceAccountToken
ServiceAccountToken是kubernetes內(nèi)置的一種"服務(wù)賬戶",它是Kubernetes進(jìn)行權(quán)限分配的對(duì)象。ServiceAccount的 授權(quán)信息和文件實(shí)際上是保存在Secret對(duì)象中,它是一個(gè)特殊的Secret對(duì)象。任何運(yùn)行在Kubernetes集群上的應(yīng)用,都必須使用ServiceAccountToken里保存的授權(quán)信息,也就是Token,這樣才能合法的訪問(wèn)API Server。
目前,Kubernetes已經(jīng)提供了一個(gè)默認(rèn)的"服務(wù)賬戶"(Default Service Account),任何一個(gè)運(yùn)行在Kubernetes里的Pod,都可以無(wú)顯示聲明的掛載這個(gè)ServiceAccount并使用它。
我們隨意找一個(gè)Pod查看其詳細(xì)信息,就可以看到一個(gè)default-token-xxxx的Volume自動(dòng)掛載到容器的/var/run/secrets/kubernetes.io/serviceaccount目錄上。如下:
[root@master k8s]# kubectl describe pod nginx-deployment-6f655f5d99-q4fhk
......
Containers:
nginx:
.......
Mounts:
/var/run/secrets/kubernetes.io/serviceaccount from default-token-gmdbb (ro)
Conditions:
......
Volumes:
default-token-gmdbb:
Type: Secret (a volume populated by a Secret)
SecretName: default-token-gmdbb
Optional: false
QoS Class: BestEffort
我們可以看到這個(gè)Volume是Secret類型,正式默認(rèn)Service Account對(duì)應(yīng)的ServiceAccountToken,所以說(shuō),Kubernetes在每個(gè)Pod創(chuàng)建的時(shí)候,自動(dòng)為其添加默認(rèn)的ServiceAccountToken的定義,然后自動(dòng)為每個(gè)容器加上對(duì)應(yīng)的VolumeMounts字段,這個(gè)過(guò)程對(duì)應(yīng)用戶來(lái)說(shuō)是透明的,一旦這個(gè)Pod創(chuàng)建完成,容器里的應(yīng)用可以直接從這個(gè)默認(rèn)的ServiceAccountToken掛載的目錄里訪問(wèn)到授權(quán)信息和文件,這個(gè)路徑在Kubernetes里是固定的。
我們進(jìn)入Pod會(huì)看到掛載的目錄下的文件信息如下:
[root@master k8s]# kubectl exec -it nginx-deployment-6f655f5d99-q4fhk -- /bin/sh
# ls /var/run/secrets/kubernetes.io/serviceaccount
ca.crt namespace token
#
所以,應(yīng)用程序只要加載這些授權(quán)文件,就可以直接訪問(wèn)Kubernetes API了,而且,如果是直接使用Kubernetes官方的包(k8s.io/client-go),是可以直接加載這些授權(quán)文件的。
這種把Kubernetes的配置信息以容器的方式運(yùn)行在集群中,然后使用ServiceAccountToken方式自動(dòng)授權(quán),被稱為"InClusterConfig"。
-----------------------
公眾號(hào):?jiǎn)踢吂适拢↖D:qiaobiangushi)
知乎: 喬邊故事
頭條號(hào):?jiǎn)踢吂适?/strong>
只要臉皮夠厚,整個(gè)世界都將被你踩在腳下。
-----------------------
感謝您的閱讀,歡迎轉(zhuǎn)發(fā)!