寫在前面
上一篇文章中kubernetes系列教程(八)Pod健康檢查機制介紹了kubernetes中Pod健康檢查機制,通過實戰介紹了kubernetes中兩種健康檢查探針:livenessProbe存活檢查,readinessProbe就緒檢查,存活檢查用於檢查應用的可用性,就緒檢查用於檢查容器是否準備接受流量,健康檢查包含三種探測的方法:exec命令行探測,tcpSocket端口檢測,httpGet請求檢測,分別適用於不同場景下的健康檢查。接下來介紹kubernetes系列教程pod的存儲管理。
kubernetes存儲管理按照發展的歷程,涉及到有Volume,PV(Persistent Volume)和PVC(PersistentVolumeClaims),和StorageClass,Volume是最早提出的存儲卷,主要解決容器和數據存儲的依賴關係,抽象底層驅動以支持不同的存儲類型;使用Volume需要了解底層存儲細節,因此提出了PV,Persistent Volume是由k8s管理員定義的存儲單元,應用端使用PersistentVolumeClaims聲明去調用PV存儲,進一步抽象了底層存儲;隨着PV數量的增加,管理員需要不停的定義PV的數量,衍生了通過StorageClass動態生成PV,StorageClass通過PVC中聲明存儲的容量,會調用底層的提供商生成PV。本文介紹Volume的使用,下篇文章介紹PV,PVC和StorageClass。
- Volume 存儲卷,獨立於容器,後端和不同的存儲驅動對接
- PV Persistent Volume持久化存儲卷,和node類似,是一種集羣資源,由管理員定義,對接不同的存儲
- PVC PersistentVolumeClaims持久化存儲聲明,和pod類似,作爲PV的使用者
- StorageClass 動態存儲類型,分爲靜態和動態兩種類型,通過在PVC中定義存儲類型,自動創建所需PV
1. kubernetes存儲管理
1.1 存儲概述
kubernetes容器中的數據是臨時的,即當重啓重啓或crash後容器的數據將會丟失,此外容器之間有共享存儲的需求,所以kubernetes中提供了volume存儲的抽象,volume後端能夠支持多種不同的plugin驅動,通過.spec.volumes中定義一個存儲,然後在容器中.spec.containers.volumeMounts調用,最終在容器內部以目錄的形式呈現。
kubernetes內置能支持多種不同的驅動類型,大體上可以分爲四種類型:1. 公/私有云驅動接口,如awsElasticBlockStore實現與aws EBS集成,2. 開源存儲驅動接口,如ceph rbd,實現與ceph rb塊存儲對接,3. 本地臨時存儲,如hostPath,4. kubernetes對象API驅動接口,實現其他對象調用,如configmap,每種存儲支持不同的驅動,如下介紹:
- 公/私有云驅動接口
- awsElasticBlockStore AWS的EBS雲盤
- azureDisk 微軟azure雲盤
- azureFile 微軟NAS存儲
- gcePersistentDisk google雲盤
- cinder openstack cinder雲盤
- vsphereVolume VMware的VMFS存儲
- scaleIO EMC分佈式存儲
- 開源存儲驅動接口
- ceph rbd ceph塊存儲
- cephfs ceph文件存儲
- glusterfs glusterfs存儲
- nfs nfs文件
- iscsi
- flexvolume
- csi 社區標準化驅動
- flocker
- 本地臨時存儲
- hostpath 宿主機文件
- emptyDir 臨時目錄
- kubernetes對象API驅動接口
- configMap 調用configmap對象,注入配置文件
- secrets 調用secrets對象,注入祕文配置文件
- persistentVolumeClaim 通過pvc調用存儲
- downloadAPI 下載URL
- projected
1.2 emptyDir臨時存儲
emptyDir是一種臨時存儲,pod創建的時候會在node節點上爲容器申請一個臨時的目錄,跟隨容器的生命週期,如容器刪除,emptyDir定義的臨時存儲空間也會隨之刪除,容器發生意外crash則不受影響,同時如果容器發生了遷移,其上的數據也會丟失,emptyDir一般用於測試,或者緩存場景。
- 定義一個emptyDir存儲大小爲1G,將其掛載到redis的/data目錄中
[root@node-1 happylau]# cat emptydir-redis.yaml
apiVersion: v1
kind: Pod
metadata:
name: emptydir-redis
labels:
volume: emptydir
annotations:
kubernetes.io/storage: emptyDir
spec:
containers:
- name: emptydir-redis
image: redis:latest
imagePullPolicy: IfNotPresent
ports:
- name: redis-6379-port
protocol: TCP
containerPort: 6379
volumeMounts: #將定義的驅動emptydir-redis掛載到容器的/data目錄,通過名字方式關聯
- name: emptydir-redis
mountPath: /data
volumes: #定義一個存儲,驅動類型爲emptyDir,大小1G
- name: emptydir-redis
emptyDir:
sizeLimit: 1Gi
- 生成redis pod,並查看describe pod的詳情信息
[root@node-1 happylau]# kubectl apply -f emptydir-redis.yaml
pod/emptydir-redis created
執行kubectl describe pods emptydir-redis查看容器的存儲掛載信息
Containers:
emptydir-redis:
Container ID: docker://dddd9f3d0e395d784c08b712631d2b0c259bfdb30b0c655a0fc8021492f1ecf9
Image: redis:latest
Image ID: docker-pullable://redis@sha256:cb379e1a076fcd3d3f09e10d7b47ca631fb98fb33149ab559fa02c1b11436345
Port: 6379/TCP
Host Port: 0/TCP
State: Running
Started: Tue, 01 Oct 2019 11:04:30 +0800
Ready: True
Restart Count: 0
Environment: <none>
Mounts: #掛載信息,將emptydir-redis掛載到/data目錄,且是rw讀寫狀態
/data from emptydir-redis (rw)
/var/run/secrets/kubernetes.io/serviceaccount from default-token-5qwmc (ro)
Conditions:
Type Status
Initialized True
Ready True
ContainersReady True
PodScheduled True
Volumes: #定義了一個EmptyDir類型的存儲,大小爲1Gi
emptydir-redis:
Type: EmptyDir (a temporary directory that shares a pod's lifetime)
Medium:
SizeLimit: 1Gi
default-token-5qwmc:
Type: Secret (a volume populated by a Secret)
SecretName: default-token-5qwmc
Optional: false
- 向redis中寫入數據
獲取pod的ip地址
[root@node-1 happylau]# kubectl get pods emptydir-redis -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
emptydir-redis 1/1 Running 1 17m 10.244.1.27 node-2 <none> <none>
安裝客戶端redis-cli
[root@node-1 ~]# yum install redis
向redis中寫入兩個key
10.244.1.27:6379> set volume emptydir
OK
10.244.1.27:6379> set username happylauliu
OK
10.244.1.27:6379> get volume
"emptydir"
10.244.1.27:6379> get username
"happylauliu"
- 登陸到pod中安裝一個查看進程的工具procps,進程一般爲1,如下圖redis-server進程,可以直接kill,進程被kill後kubelet會自動將進程重啓
登陸容器
[root@node-1 ~]# kubectl exec -it emptydir-redis /bin/bash
安裝軟件
root@emptydir-redis:/data# apt-get update ; apt-get install procps
可以通過top查看進程,進程號一般爲1
root@emptydir-redis:/data# kill 1
- pod異常重啓後,再次登錄redis並查看redis中的數據內容,發現數據沒有丟失。
[root@node-1 ~]# redis-cli -h 10.244.1.27
10.244.1.27:6379> get volume
"emptydir"
10.244.1.27:6379> get username
"happylauliu"
- emptyDir實際是宿主機上創建的一個目錄,將目錄以bind mount的形勢掛載到容器中,跟隨容器的生命週期
[root@node-2 ~]# docker container list |grep redis
e0e9a6b0ed77 01a52b3b5cd1 "docker-entrypoint.s…" 20 minutes ago Up 20 minutes k8s_emptydir-redis_emptydir-redis_default_4baadb25-1e62-4cf5-9724-821d04dcdd44_2
dfef32905fe5 k8s.gcr.io/pause:3.1 "/pause" 45 minutes ago Up 45 minutes k8s_POD_emptydir-redis_default_4baadb25-1e62-4cf5-9724-821d04dcdd44_0
docker container inspect e0e9a6b0ed77查看存儲內容如下圖:
查看目錄的信息:
[root@node-2 ~]# ls -l /var/lib/kubelet/pods/4baadb25-1e62-4cf5-9724-821d04dcdd44/volumes/kubernetes.io~empty-dir/emptydir-redis
總用量 4
-rw-r--r-- 1 polkitd input 156 10月 8 14:55 dump.rdb
- Pod刪除後,volume的信息也隨之刪除
[root@node-1 ~]# kubectl delete pods emptydir-redis
pod "emptydir-redis" deleted
[root@node-1 ~]# ssh node-2
Last login: Tue Oct 8 15:15:41 2019 from 10.254.100.101
[root@node-2 ~]# ls -l /var/lib/kubelet/pods/4baadb25-1e62-4cf5-9724-821d04dcdd44/volumes/kubernetes.io~empty-dir/emptydir-redis
ls: 無法訪問/var/lib/kubelet/pods/4baadb25-1e62-4cf5-9724-821d04dcdd44/volumes/kubernetes.io~empty-dir/emptydir-redis: 沒有那個文件或目錄
小結:emptyDir是host上定義的一塊臨時存儲,通過bind mount的形式掛載到容器中使用,容器重啓數據會保留,容器刪除則volume會隨之刪除。
1.3 hostPath主機存儲
與emptyDir類似,hostpath支持將node節點的目錄或文件掛載到容器中使用,用於單機測試場景,此外適用於一些容器業務需要訪問宿主機目錄,如監控系統訪問/proc和/sys目錄,日誌系統訪問/var/lib/docker目錄的一些場景。支持設置不同的type類型
- Directory 本地存在的目錄
- DirectoryOrCreate 目錄,如果不存在則創建,權限設置爲755,屬主和組設置和kubelet一致
- File 本地存在文件
- FileOrCreate 文件,如果不存在則創建,權限設置爲644,屬主和組設置和kubelet一致
- Socket 本地已存在Socket文件
- CharDevice 本地已存在的Char字符設備
- BlockDevice 本地已存在的Block塊設備
- 掛載本地/mnt目錄到容器中
[root@node-1 happylau]# cat hostpath-demo.yaml
apiVersion: v1
kind: Pod
metadata:
name: hostpath-demo
labels:
storage: hostpath
annotations:
kubernetes.io/storage: hostpath
spec:
containers:
- name: nginx
image: nginx:latest
imagePullPolicy: IfNotPresent
ports:
- name: nginx-http-port
protocol: TCP
containerPort: 80
volumeMounts: #掛載到nginx的web站點目錄下
- name: hostpath-demo
mountPath: /usr/share/nginx/html
volumes: #定一個hostPath本地的存儲
- name: hostpath-demo
hostPath:
type: DirectoryOrCreate
path: /mnt/data
- 生成nginx容器和web站點數據
[root@node-1 happylau]# kubectl apply -f hostpath-demo.yaml
pod/hostpath-demo created
獲取pod所在的node節點
[root@node-1 happylau]# kubectl get pods hostpath-demo -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
hostpath-demo 1/1 Running 0 31s 10.244.2.24 node-3 <none> <none>
生成web站點的數據
[root@node-1 happylau]# ssh node-3
Last login: Tue Oct 8 22:49:14 2019 from 10.254.100.101
[root@node-3 ~]# echo "hostPath test page" >/mnt/data/index.html
[root@node-3 ~]# curl http://10.244.2.24
hostPath test page
- 查看容器掛載存儲的情況,以bind mount的形式掛載到容器中
- 模擬容器重啓的的故障,容器重啓後volume中的數據依保留
#docker層面kill掉進程
[root@node-3 ~]# docker container list |grep hostpath
39a7e21afebb f949e7d76d63 "nginx -g 'daemon of…" 11 minutes ago Up 11 minutes k8s_nginx_hostpath-demo_default_6da41e3d-8585-4997-bf90-255ca0948030_0
490f50108e41 k8s.gcr.io/pause:3.1 "/pause" 11 minutes ago Up 11 minutes k8s_POD_hostpath-demo_default_6da41e3d-8585-4997-bf90-255ca0948030_0
[root@node-3 ~]# docker container kill 39a7e21afebb
39a7e21afebb
[root@node-3 ~]# exit
登出
#獲取pod的地址,通過RESTART可知,容器重啓過一次,測試數據依舊保留
[root@node-1 happylau]# kubectl get pods -o wide hostpath-demo
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
hostpath-demo 1/1 Running 1 12m 10.244.2.24 node-3 <none> <none>
[root@node-1 happylau]# curl http://10.244.2.24
hostPath test page
小結:hostPath與emptyDir類似提供臨時的存儲,hostPath適用於一些容器需要訪問宿主機目錄或文件的場景,對於數據持久化而言都不是很好的實現方案。
1.4 NFS存儲對接
NFS是實現Network File System網絡文件共享的NAS存儲,kubernetes與NFS對接實現存儲的共享,當容器刪除不影響存儲且可以實現跨機存儲共享,本文以搭建一個NFS存儲實現kubernetes對接。
- 準備一個nfs server共享,將node-1的/mnt/data目錄共享
安裝nfs服務
[root@node-1 ~]# yum install nfs-utils -y
配置nfs共享,提前創建好目錄
[root@node-1 ~]# cat /etc/exports
/mnt/data 10.254.100.0/24(rw)
重啓並驗證
[root@node-1 ~]# systemctl restart nfs
[root@node-1 ~]# showmount -e node-1
Export list for node-1:
/mnt/data 10.254.100.0/24
- kubernets使用nfs的驅動對接
[root@node-1 happylau]# cat nfs-demo.yaml
apiVersion: v1
kind: Pod
metadata:
name: nfs-demo
labels:
storage: nfs
annotations:
kubernetes.io/storage: nfs
spec:
containers:
- name: nginx
image: nginx:latest
imagePullPolicy: IfNotPresent
ports:
- name: nginx-http-port
protocol: TCP
containerPort: 80
volumeMounts: #掛載到nfs的目錄下
- name: nfs-demo
mountPath: /usr/share/nginx/html
volumes: #定義一個nfs驅動的存儲
- name: nfs-demo
nfs:
server: 10.254.100.101
path: /mnt/data
- 生成pod,使用kubectl get pods的時候提示events中報錯信息,掛載失敗
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal Scheduled 40s default-scheduler Successfully assigned default/nfs-demo to node-2
Warning FailedMount 39s kubelet, node-2 MountVolume.SetUp failed for volume "nfs-demo" : mount failed: exit status 32
Mounting command: systemd-run
Mounting arguments: --description=Kubernetes transient mount for /var/lib/kubelet/pods/78bf6a81-082d-4d6c-a163-75241bf21cde/volumes/kubernetes.io~nfs/nfs-demo --scope -- mount -t nfs 10.254.100.101:/mnt/data /var/lib/kubelet/pods/78bf6a81-082d-4d6c-a163-75241bf21cde/volumes/kubernetes.io~nfs/nfs-demo
Output: Running scope as unit run-29843.scope.
mount: wrong fs type, bad option, bad superblock on 10.254.100.101:/mnt/data,
missing codepage or helper program, or other error
(for several filesystems (e.g. nfs, cifs) you might
need a /sbin/mount.<type> helper program)
- 從上面的步驟中得知,宿主機掛載nfs的時候提示沒有mount.nfs的命令,因此需要在所有的node節點上安裝上nfs的客戶端軟件nfs-utils,以node-2爲例,其他節點類似
[root@node-1 happylau]# ssh node-2
Last login: Tue Oct 8 15:22:04 2019 from 10.254.100.101
[root@node-2 ~]# yum install nfs-utils -y
- 測試站點數據
[root@node-1 happylau]# kubectl get pods nfs-demo -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
nfs-demo 1/1 Running 0 4m41s 10.244.1.28 node-2 <none> <none>
[root@node-1 happylau]# echo "nfs test age" >/mnt/data/index.html
[root@node-1 happylau]# curl http://10.244.1.28
nfs test age
- 刪除pod後查看nfs共享的數據情況,原有數據依舊保留
[root@node-1 happylau]# kubectl delete pods nfs-demo
pod "nfs-demo" deleted
[root@node-1 happylau]# mount.nfs node-1:/mnt/data/ /media/
[root@node-1 happylau]# ls -l /media/
總用量 4
-rw-r--r-- 1 root root 13 10月 8 23:26 index.html
1.5 TKE使用volume存儲
TKE支持在創建Workload時如Deployments,DaemonSets,StatefulSets等指定存儲卷,支持臨時目錄emptyDir,主機路徑hostPath,nfs盤,pvc,雲硬盤,configmap,secrets,此處以騰訊雲CFS爲例(提前在CFS中創建好存儲,確保CFS和容器宿主機在同一個VPC網絡內)。
- 創建存儲卷,使用NFS掛載騰訊雲CFS存儲
- Pod中使用存儲,通過volume-nfs-demo名字調用存儲卷
- 對應生成的yaml文件內容如下
apiVersion: apps/v1beta2
kind: Deployment
metadata:
annotations:
deployment.kubernetes.io/revision: "1"
description: demo
creationTimestamp: "2019-10-08T15:45:18Z"
generation: 1
labels:
k8s-app: the-volume-demo
qcloud-app: the-volume-demo
name: the-volume-demo
namespace: default
resourceVersion: "618380753"
selfLink: /apis/apps/v1beta2/namespaces/default/deployments/the-volume-demo
uid: a0fc4600-e9e2-11e9-b3f4-decf0ef369cf
spec:
minReadySeconds: 10
progressDeadlineSeconds: 600
replicas: 1
revisionHistoryLimit: 10
selector:
matchLabels:
k8s-app: the-volume-demo
qcloud-app: the-volume-demo
strategy:
rollingUpdate:
maxSurge: 1
maxUnavailable: 0
type: RollingUpdate
template:
metadata:
creationTimestamp: null
labels:
k8s-app: the-volume-demo
qcloud-app: the-volume-demo
spec:
containers:
- image: nginx:latest
imagePullPolicy: Always
name: nginx-demo
resources:
limits:
cpu: 500m
memory: 1Gi
requests:
cpu: 250m
memory: 256Mi
securityContext:
privileged: false
terminationMessagePath: /dev/termination-log
terminationMessagePolicy: File
volumeMounts: #掛載到pod中
- mountPath: /usr/share/nginx/html
name: volume-nfs-demo
dnsPolicy: ClusterFirst
imagePullSecrets:
- name: qcloudregistrykey
- name: tencenthubkey
restartPolicy: Always
schedulerName: default-scheduler
securityContext: {}
terminationGracePeriodSeconds: 30
volumes: #CFS存儲
- name: volume-nfs-demo
nfs:
path: /
server: 10.66.200.7
寫在最後
本文介紹了kubernetes存儲中最基本volume的使用,介紹了volume支持多種不同驅動,以實際案例介紹emptyDir,hostPath,nfs驅動的對接,並介紹了TKE下volume功能的使用。由於volume需要知道底層存儲的細節,不便於廣泛使用,後來衍生爲PV,管理員定義PV實現和底層存儲對接,用戶通過PVC使用PV,下節我們將介紹PV/PVC和StorageClass的使用。
參考文獻
volume管理:https://kubernetes.io/docs/concepts/storage/volumes/
pod中使用volume:https://kubernetes.io/docs/tasks/configure-pod-container/configure-volume-storage/
當你的才華撐不起你的野心時,你就應該靜下心來學習
**如果覺得文章對您有幫助,請訂閱專欄,分享給有需要的朋友吧