前言
本篇是Kubernetes第十二篇,大家一定要把環境搭建起來,看是解決不了問題的,必須實戰。
Kubernetes系列文章:
-
Kubernetes介紹 -
Kubernetes環境搭建 -
Kubernetes-kubectl介紹 -
Kubernetes-Pod介紹(-) -
Kubernetes-Pod介紹(二)-生命周期 -
Kubernetes-Pod介紹(三)-Pod調度 -
Kubernetes-Pod介紹(四)-Deployment -
Kubernetes-Service介紹(一)-基本概念 -
Kubernetes-Service介紹(二)-服務發現 -
Kubernetes-Service介紹(三)-Ingress(含最新版安裝踩坑實踐) -
Kubernetes-網絡
為什么需要存儲
對於這個問題其實很簡單,容器中持久化的文件生命周期是短暫的,如果容器中程序崩潰宕機,kubelet 就會重新啟動,容器中的文件將會丟失,所以對於有狀態的應用容器中持久化存儲是至關重要的一個環節;另外很多時候一個 Pod 中可能包含多個 Docker 鏡像,在 Pod 內數據也需要相互共享,Kubernetes 中 Pod 也可以增加副本數量,遇到故障時 Pod 可以轉移到其它節點,為了浮動節點都能夠訪問統一的持久化存儲以及容器間共享數據,Kubernetes 中定義了 Volume 來解決這些問題 ,從本質上講,Volume 只是一個目錄,可能包含一些數據,Pod 中的容器可以訪問它。該目錄是何種形式,是由所使用的 Volume 類型決定的。
Volume介紹
Kubernetes 支持很多 Volume類型,可以分為以下類型:

我們重點介紹下資源對象映射為存儲卷、Node本地存儲卷和持久卷。
資源對象映射為存儲卷
在 Kubernetes中ConfigMap、Serect、DownwardAPI和ServiceAccountToken四種存儲卷,存在的意義不是為了存放容器里的數據,也不是用來進行容器和宿主機之間的數據交換,是為容器提供預先定義好的數據。從容器的角度來看,這些 Volume里的信息就是被映射到容器當中的。
ConfigMap
ConfigMap用於保存應用程序的配置信息,可以通過Volume的形式掛載到容器內部文件系統中,供容器內的應用程序讀取。
使用
-
創建configMap文件;
apiVersion: v1
kind: ConfigMap
metadata:
name: env-config
data:
env-key: test
-
創建Pod文件,在Pod使用configMap配置信息;
apiVersion: v1
kind: Pod
metadata:
name: configmap-pod
spec:
containers:
- name: configmap-pod
image: busybox:1.28.3
command: [ "/bin/sh", "-c", "env" ]
env:
- name: ENV
valueFrom:
configMapKeyRef:
name: env-config
key: env-key
restartPolicy: Never
-
啟動configMap和Pod;
kubectl apply -f env-configMap.yaml
kubectl apply -f configmap-pod.yaml
-
查看啟動日志;
kubectl logs configmap-pod

Secret
Secret功能與ConfigMap類似,它與ConfigMap的區別在於,Secret保存的是需要加密的、應用所需的配置信息。Secret與ConfigMap用法完全相同:你可以使用 kubectl create secret從文件或者目錄創建 Secret,也可以直接編寫Secret對象的YAML文件。
Downward API
Downward API的作用是讓Pod里的容器能夠直接獲取到這個Pod API對象本身的元數據信息。
使用
-
創建Pod,該Pod聲明了一個projected類型的Volume,Volume的數據來源為Downward API,而這個 Downward API Volume,要暴露Pod的metadata.labels 信息給容器,容器啟動以后,則是不斷打印出 /etc/podinfo/labels 里的內容;
apiVersion: v1
kind: Pod
metadata:
name: downwardapi-demo-pod
labels:
cluster: demo-test
spec:
containers:
- name: downwardapi-demo
image: busybox:1.28.3
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
-
啟動Pod和查看日志;
kubectl apply -f downwardapi-demo-pod.yaml
kubectl logs downwardapi-demo-pod

Service Account Token
Service Account Token是一種針對Pod的賬號,它是Kubernetes進行權限分配的對象。比如Service Account A,可以只被允許對 Kubernetes API 進行 GET 操作,而 Service Account B,則可以有 Kubernetes API 的所有操作權限。Service Account的授權信息和文件,實際上保存在它所綁定的一個特殊的 Secret 對象里的,對於Service Account Token可以理解為一種特殊的Secret對象。
Kubernetes對所有的Pod都提供了一個default Service Account,任何一個運行在 Kubernetes的Pod,都可以直接使用這個默認的 Service Account,而無需顯示地聲明掛載它。
原理探索
實現方式還是靠Projected Volume機制
-
查看該Pod的詳細信息,可以看到該Po的默認掛載一個serviceaccount的路徑;
kubectl describe pod downwardapi-demo-pod

-
我們進入容器內部查看下serviceaccount具體內容,我們可以看到包括3方面內容,分別是token、ca.crt、namespace;
#進入容器
kubectl exec -it downwardapi-demo-pod -- /bin/sh
#查看具體內容
ls /var/run/secrets/kubernetes.io/serviceaccount

-
接下來我們看下系統中的Service Account對象,可以看到有一個default-token-q4qts的Secret對象,該對象是一個Mountable secrets對象,說明此對象需要被掛載的;

-
接下來我們看下default-token-q4qts包含什么東西,我們可以看到包含了token、ca.crt、namespace三類對象;

通過Mountable secrets標識、Pod內容對象以及Secrets包含的對象,我們可以證明每個命名空間下面都會有一個名為default默認的Service Account,在Pod啟動的時候,Secret對象會自動掛載到Pod指定的目錄下,幫助Pod來完成API Server的身份鑒權。
使用
-
創建Service Account資源;
#創建Service Account資源
kubectl create serviceaccount sa-demo
#查看Service Account資源
kubectl get sa

-
查看下是否創建Secret;
#查看是否創建Secret
kubectl describe sa sa-demo、
#查看具體的Secret內容
kubectl describe secret sa-demo-token-k4t96

-
創建使用sa-demo的Pod;
apiVersion: v1
kind: Pod
metadata:
name: sa-demo-pod
spec:
serviceAccountName: sa-demo
containers:
- image: nginx
name: sa-demo-pod
resources:
limits:
cpu: 100m
memory: 200Mi
requests:
cpu: 100m
memory: 200Mi
-
啟動該Pod;
#創建pod資源
kubectl create -f sa-demo-pod.yaml
#查看Pod資源
kubectl get pods
#查看pod是否掛載serviceaccount
kubectl describe pod sa-demo-pod


-
進入容器內部訪問kubernetes服務的API,在Kubernetes集群中,默認為API Server創建了一個名為kubernetes的Service,通過這個Service可以訪問API Server,使用curl命令直接訪問會得到如下返回信息,表示並沒有權限;
#查看服務列表
kubectl get svc
#進入容器內部
kubectl exec -it sa-demo-pod -- /bin/sh
#訪問kubernetes服務
curl https://kubernetes


-
使用ca.crt和Token做認證,先將ca.crt放到CURL_CA_BUNDLE這個環境變量中,curl命令使用CURL_CA_BUNDLE指定證書;再將Token的內容放到TOKEN中,然后帶上TOKEN訪問API Server;
#聲明變量
export CURL_CA_BUNDLE=/var/run/secrets/kubernetes.io/serviceaccount/ca.crt
TOKEN=$(cat /var/run/secrets/kubernetes.io/serviceaccount/token)
#攜帶token訪問服務
curl -H "Authorization: Bearer $TOKEN" https://kubernetes

Node本地存儲卷
Kubernetes管理Node本地存儲卷有是那種類型:
-
EmptyDir: 與Pod同生命周期,Node臨時存儲;
-
HostPath: Node目錄;
-
Local: 基於持久卷PV管理的Node目錄;
EmptyDir
當Pod被調度到某個節點上時,在Node上創建的是一個emptyDir卷,此卷剛開始是一個空目錄,並且只要 Pod在該節點上運行,卷就一直存在,與Pod生命周期相同, 當Pod被銷毀的時候,Node上的目錄也會被銷毀。同一個Pod的多個容器都可以掛載到改卷上。
emptyDir可以在以下幾種場景下使用:
-
臨時空間,例如基於磁盤的合並排序;
-
設置檢查點以從崩潰事件中恢復未執行完畢的長計算;
-
保存內容管理器容器從Web服務器容器提供數據時所獲取的文件;
使用
-
創建一個使用emptyDir volume的Pod;
apiVersion: v1
kind: Pod
metadata:
name: emptydir-pod
spec:
containers:
- image: busybox:1.28.3
name: emptydir-pod
command: [ "sleep", "3600" ]
volumeMounts:
- mountPath: /data
name: data-volume
volumes:
- name: data-volume
emptyDir: {}
-
創建完成之后我們通過describe命令查看該Pod的詳情,我們可以看到上面創建的Pod Volumes 類型是:EmptyDir;
kubectl describe pod emptydir-pod

-
進入容器中查看掛載情況;
kubectl exec -it emptydir-pod /bin/sh

HostPath
hostPath類型則是映射Node節點文件系統中的文件或者目錄到Pod里。在使用hostPath類型的存儲卷時,也可以設置type字段,支持的類型有文件、目錄、File、Socket、CharDevice和BlockDevice。
hostPath可以在以下幾種場景使用:
-
容器應用中關鍵的數據需要持久化到宿主機上;
-
需要使用運行一個訪問 Docker內部數據Pod, 可以將主機的/var/lib/docker目錄掛載到容器內;
-
監控系統,例如在容器中運行cAdvisor需要以hostPath方式掛載/sys;
-
Pod啟動需要依賴宿主機某個目錄或者文件信息;
使用
-
創建一個使用hostPath volume的Pod;
apiVersion: v1
kind: Pod
metadata:
name: hostpath-pod
spec:
containers:
- image: busybox:1.28.3
name: hostpath-pod
command: [ "sleep", "3600" ]
volumeMounts:
- mountPath: /test-data
name: test-volume
volumes:
- name: test-volume
hostPath:
# directory location on host
path: /data
# this field is optional
type: Directory
-
創建完成之后我們通過describe命令查看該Pod的詳情,我們可以看到上面創建的Pod Volumes 類型是:HostPath,如果發現有個hostPath type check failed: /data is not a directory的告警,你需要在每個節點創建/data目錄;
kubectl describe pod hostpath-pod


-
接下來我們進入容器,在容器中創建文件;
#進入容器
kubectl exec -it hostpath-pod /bin/sh
#編輯文件
vi 123.txt
#輸出文件
cat 123.txt

-
查看Pod被調度到那個節點;
kubectl get pods -o wide

-
進入demo-slave-2查看/data目錄下是否增加文件;

-
銷毀hostpath-pod,檢查文件是否存在,我們會發現Pod已經被刪除了,volume卷中的數據仍然還在;
kubectl delete -f hostpath-pod.yaml

emptyDir和hostPath都可以實現本地存儲卷的功能、但是二者在功能上還是有異同的:
-
二者都是node節點的本地存儲卷方式;
-
emptyDir可以選擇把數據存到tmpfs類型的本地文件系統中去,hostPath並不支持這一點;
-
hostPath除了支持掛載目錄外,還支持File、Socket、CharDevice和BlockDevice,既支持把已有的文件和目錄掛載到容器中,也提供了“如果文件或目錄不存在,就創建一個”的功能;
-
emptyDir是臨時存儲空間,完全不提供持久化支持;
-
hostPath的卷數據是持久化在Node節點的文件系統中的,即便pod已經被刪除了,volume卷中的數據還會留存在Node節點上;
結束
歡迎大家點點關注,點點贊!