一、特殊的 Volume
這種特殊的 Volume,叫作 Projected Volume,你可以把它翻譯為“投射數據卷”
備注:Projected Volume 是 Kubernetes v1.11 之后的新特性
作用

含義

Projected Volume種類

一、Secret
1、作用

2、使用場景

Secret 最典型的使用場景,莫過於存放數據庫的 Credential 信息,比如下面這個例子:
3、方式一:kubectl create secret
apiVersion: v1
kind: Pod
metadata:
name: test-projected-volume
spec:
containers:
- name: test-secret-volume
image: busybox
args:
- sleep
- "86400"
volumeMounts:
- name: mysql-cred
mountPath: "/projected-volume"
readOnly: true
volumes:
- name: mysql-cred
projected:
sources:
- secret:
name: user
- secret:
name: pass
這里用到的數據庫的用戶名、密碼,正是以 Secret 對象的方式交給 Kubernetes 保存的。完成這個操作的指令,如下所示:
$ 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
其中,username.txt 和 password.txt 文件里,存放的就是用戶名和密碼;而 user 和 pass,則是我為 Secret 對象指定的名字。而我想要查看這些 Secret 對象的話,只要執行一條 kubectl get 命令就可以了:
$ kubectl get secrets NAME TYPE DATA AGE user Opaque 1 51s pass Opaque 1 51s
4、方式二:編寫YAML
當然,除了使用 kubectl create secret 指令外,我也可以直接通過編寫 YAML 文件的方式來創建這個 Secret 對象,比如:
apiVersion: v1 kind: Secret metadata: name: mysecret type: Opaque data: user: YWRtaW4= pass: MWYyZDFlMmU2N2Rm

接下來,我們嘗試一下創建這個 Pod:
$ kubectl create -f test-projected-volume.yaml
驗證這些Secret對象是不是已經在容器里了
$ kubectl exec -it test-projected-volume -- /bin/sh $ ls /projected-volume/ user pass $ cat /projected-volume/user root $ cat /projected-volume/pass 1f2d1e2e67df
自動更新

注意事項

5、生產最佳實踐

二、ConfigMap
1、與 Secret 的區別

2、使用方式

比如,一個 Java 應用所需的配置文件(.properties 文件),就可以通過下面這樣的方式保存在 ConfigMap 里:
# .properties 文件的內容
$ cat example/ui.properties
color.good=purple
color.bad=yellow
allow.textmode=true
how.nice.to.look=fairlyNice
# 從.properties 文件創建 ConfigMap
$ kubectl create configmap ui-config --from-file=example/ui.properties
# 查看這個 ConfigMap 里保存的信息 (data)
$ kubectl get configmaps ui-config -o yaml
apiVersion: v1
data:
ui.properties: |
color.good=purple
color.bad=yellow
allow.textmode=true
how.nice.to.look=fairlyNice
kind: ConfigMap
metadata:
name: ui-config
...
備注:kubectl get -o yaml 這樣的參數,會將指定的 Pod API 對象以 YAML 的方式展示出來。
三、Downward API
1、作用

舉個例子:
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
2、Yaml文件注解

$ kubectl create -f dapi-volume.yaml $ kubectl logs test-downwardapi-volume cluster="test-cluster1" rack="rack-22" zone="us-est-coast"
目前,Downward API 支持的字段已經非常豐富了,比如:
1. 使用 fieldRef 可以聲明使用: spec.nodeName - 宿主機名字 status.hostIP - 宿主機 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
3、生產最佳實踐

4、需要注意

5、建議

四、Service Account
1、作用

2、什么是Service Account

3、默認“服務賬戶

4、這是如何做到的呢?
當然還是靠 Projected Volume 機制。
如果你查看一下任意一個運行在 Kubernetes 集群里的 Pod,就會發現,每一個 Pod,都已經自動聲明一個類型是 Secret、名為 default-token-xxxx 的 Volume,然后 自動掛載在每個容器的一個固定目錄上。比如:
$ kubectl describe pod nginx-deployment-5c678cfb6d-lg9lw
Containers:
...
Mounts:
/var/run/secrets/kubernetes.io/serviceaccount from default-token-s8rbq (ro)
Volumes:
default-token-s8rbq:
Type: Secret (a volume populated by a Secret)
SecretName: default-token-s8rbq
Optional: false
這個 Secret 類型的 Volume,正是默認 Service Account 對應的 ServiceAccountToken。所以說,Kubernetes 其實在每個 Pod 創建的時候,自動在它的 spec.volumes 部分添加上了默認 ServiceAccountToken 的定義,然后自動給每個容器加上了對應的 volumeMounts 字段。這個過程對於用戶來說是完全透明的。
這樣,一旦 Pod 創建完成,容器里的應用就可以直接從這個默認 ServiceAccountToken 的掛載目錄里訪問到授權信息和文件。這個容器內的路徑在 Kubernetes 里是固定的,即:/var/run/secrets/kubernetes.io/serviceaccount ,而這個 Secret 類型的 Volume 里面的內容如下所示:
$ ls /var/run/secrets/kubernetes.io/serviceaccount ca.crt namespace token
所以,你的應用程序只要直接加載這些授權文件,就可以訪問並操作 Kubernetes API 了。而且,如果你使用的是 Kubernetes 官方的 Client 包(k8s.io/client-go)的話,
它還可以自動加載這個目錄下的文件,你不需要做任何配置或者編碼操作。InClusterConfig
當然,考慮到自動掛載默認 ServiceAccountToken 的潛在風險,Kubernetes 允許你設置默認不為 Pod 里的容器自動掛載這個 Volume。
除了這個默認的 Service Account 外,我們很多時候還需要創建一些我們自己定義的 Service Account,來對應不同的權限設置。
這樣,我們的 Pod 里的容器就可以通過掛載這些 Service Account 對應的 ServiceAccountToken,來使用這些自定義的授權信息。在后面講解為 Kubernetes 開發插件的時候,我們將會實踐到這個操作。
五、容器健康檢查
在 Kubernetes 中,你可以為 Pod 里的容器定義一個健康檢查“探針”(Probe)。這樣,kubelet 就會根據這個 Probe 的返回值決定這個容器的狀態,而不是直接以容器進行是否運行(來自 Docker 返回的信息)作為依據。這種機制,是生產環境中保證應用健康存活的重要手段。
1、容器健康檢查
我們一起來看一個 Kubernetes 文檔中的例子。
apiVersion: v1
kind: Pod
metadata:
labels:
test: liveness
name: test-liveness-exec
spec:
containers:
- name: liveness
image: busybox
args:
- /bin/sh
- -c
- touch /tmp/healthy; sleep 30; rm -rf /tmp/healthy; sleep 600
livenessProbe:
exec:
command:
- cat
- /tmp/healthy
initialDelaySeconds: 5
periodSeconds: 5
1、有趣的容器

2、livenessProbe(健康檢查)

2、現在,讓我們來具體實踐一下這個過程。
1、首先,創建這個 Pod:
$ kubectl create -f test-liveness-exec.yaml
2、然后,查看這個 Pod 的狀態:
$ kubectl get pod NAME READY STATUS RESTARTS AGE test-liveness-exec 1/1 Running 0 10s
可以看到,由於已經通過了健康檢查,這個 Pod 就進入了 Running 狀態。
3、而 30 s 之后,我們再查看一下 Pod 的 Events:
$ kubectl describe pod test-liveness-exec
4、你會發現,這個 Pod 在 Events 報告了一個異常:
FirstSeen LastSeen Count From SubobjectPath Type Reason Message
--------- -------- ----- ---- ------------- -------- ------ -------
2s 2s 1 {kubelet worker0} spec.containers{liveness} Warning Unhealthy Liveness probe failed: cat: can't open '/tmp/healthy': No such file or directory
顯然,這個健康檢查探查到 /tmp/healthy 已經不存在了,所以它報告容器是不健康的。那么接下來會發生什么呢?
3、restartPolicy
我們不妨再次查看一下這個 Pod 的狀態:
$ kubectl get pod test-liveness-exec NAME READY STATUS RESTARTS AGE liveness-exec 1/1 Running 1 1m
這時我們發現,Pod 並沒有進入 Failed 狀態,而是保持了 Running 狀態。這是為什么呢?

需要注意的是

Pod 恢復機制

但一定要強調的是

如何出現在其他可用結點上

六、容器健康檢查和恢復機制
1、Pod 的恢復策略
1、分類

2、合理設計
1、非Always

2、Never

3、設計原理
1、第一條

2、第二條

$ kubectl get pod test-liveness-exec NAME READY STATUS RESTARTS AGE liveness-exec 0/1 Running 1 1m
現在,我們一起回到前面提到的 livenessProbe 上來。
除了在容器中執行命令外,livenessProbe 也可以定義為發起 HTTP 或者 TCP 請求的方式,定義格式如下:
...
livenessProbe:
httpGet:
path: /healthz
port: 8080
httpHeaders:
- name: X-Custom-Header
value: Awesome
initialDelaySeconds: 3
periodSeconds: 3
...
livenessProbe:
tcpSocket:
port: 8080
initialDelaySeconds: 15
periodSeconds: 20
所以,你的 Pod 其實可以暴露一個健康檢查 URL(比如 /healthz),或者直接讓健康檢查去檢測應用的監聽端口。這兩種配置方法,在 Web 服務類的應用中非常常用。
readinessProbe和livenessProbe的區別
1、相同點

2、區別

七、Kubernetes 能不能自動給 Pod 填充某些字段呢?
可是,如果運維人員看到了這個 Pod,他一定會連連搖頭:這種 Pod 在生產環境里根本不能用啊!
所以,這個時候,運維人員就可以定義一個 PodPreset 對象。在這個對象中,凡是他想在開發人員編寫的 Pod 里追加的字段,都可以預先定義好。比如這個 preset.yaml:
apiVersion: settings.k8s.io/v1alpha1
kind: PodPreset
metadata:
name: allow-database
spec:
selector:
matchLabels:
role: frontend
env:
- name: DB_PORT
value: "6379"
volumeMounts:
- mountPath: /cache
name: cache-volume
volumes:
- name: cache-volume
emptyDir: {}
selector

Spec

接下來,我們假定運維人員先創建了這個 PodPreset,然后開發人員才創建 Pod:
$ kubectl create -f preset.yaml $ kubectl create -f pod.yaml
這時,Pod 運行起來之后,我們查看一下這個 Pod 的 API 對象:
$ kubectl get pod website -o yaml
apiVersion: v1
kind: Pod
metadata:
name: website
labels:
app: website
role: frontend
annotations:
podpreset.admission.kubernetes.io/podpreset-allow-database: "resource version"
spec:
containers:
- name: website
image: nginx
volumeMounts:
- mountPath: /cache
name: cache-volume
ports:
- containerPort: 80
env:
- name: DB_PORT
value: "6379"
volumes:
- name: cache-volume
emptyDir: {}
注意事項

如果你定義了同時作用於一個 Pod 對象的多個 PodPreset,會發生什么呢?
實際上,Kubernetes 項目會幫你合並(Merge)這兩個 PodPreset 要做的修改。而如果它們要做的修改有沖突的話,這些沖突字段就不會被修改。
