第九章 數(shù)據(jù)管理
Pod是短暫的,Pod在銷毀時(shí)填具,保存在容器內(nèi)部的文件系統(tǒng)各種的數(shù)據(jù)會(huì)被清除。
為了持久化保存容器中的的數(shù)據(jù)匆骗,可以使用K8s Volume劳景。
9.1 Volume
9.1.1 emptyDir
對(duì)于容器來說是持久的,對(duì)于Pod不是碉就。當(dāng)Pod從節(jié)點(diǎn)刪除時(shí)盟广,Volume的內(nèi)容也會(huì)被刪除。但是如果只是容器被銷毀而Pod存在瓮钥,則volume不受影響筋量。也就是說:emptyDir Volume的生命周期與Pod一致烹吵。
Pod中的所有容器都可以共享Volume,它們可以指定各自的mount路徑。
如下Pod有兩個(gè)容器: producer 和 consumer桨武,它們共享一個(gè)Volume. Producer 寫肋拔, consumer 讀。
vim emptyDir.yml
apiVersion: v1
kind: Pod
metadata:
name: producer-consumer
spec:
containers:
- image: busybox
name: producer
volumeMounts: # 將shared-volume mount 到 producer_dir目錄
- mountPath: /producer_dir
name: shared-volume
args: # 將數(shù)據(jù)寫入到文件hello中
- /bin/sh
- -c
- echo "hello world" > /producer_dir/hello ; sleep 30000
- image: busybox
name: consumer
volumeMounts: # 將shared-volume mount 到 /consumer_dir
- mountPath: /consumer_dir
name: shared-volume
args:
- /bin/sh
- -c
- cat /consumer_dir/hello ; sleep 30000 # 通過cat從文件hello讀數(shù)據(jù)
volumes: # 定義了一個(gè)emptyDir類型的Volume,名字是shared-volume.
- name: shared-volume
emptyDir: {}
執(zhí)行
$kubectl apply -f emptyDir.yml
檢查
$kubectl logs producer-consumer consumer
hello world
#
$kubectl get pods producer-consumer -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
producer-consumer 2/2 Running 0 26m 10.244.1.218 k8s-node-122132072 <none> <none>
#在node節(jié)點(diǎn)上
docker ps |grep producer -i
#
docker inspect e2f72565c8bf
"Mounts": [
{
"Type": "bind",
"Source": "/var/lib/kubelet/pods/c5879c3e-8803-4d0d-a0ce-e41304853f3c/volumes/kubernetes.io~empty-dir/shared-volume",
"Destination": "/producer_dir",
"Mode": "",
"RW": true,
"Propagation": "rprivate"
}
emptyDir是host上創(chuàng)建的臨時(shí)目錄呀酸,其優(yōu)點(diǎn)是能夠方便地為Pod中的容器提供共享存儲(chǔ)凉蜂,不需要額外的配置。它不具備持久性性誉,如果Pod沒有了窿吩,emptyDir也就沒有了。所以emptyDir的使用場景是: 適合Pod中的容器需要臨時(shí)共享存儲(chǔ)空間的場景错览。
9.1.2 hostPath
hostPath volume的作用是將Docker Host文件系統(tǒng)中已經(jīng)存在的目錄mount給Pod的容器纫雁。大部分應(yīng)用不會(huì)使用HostPath,因?yàn)樗黾恿薖od與節(jié)點(diǎn)的耦合倾哺。
應(yīng)用場景: 需要訪問K8s或docker內(nèi)部數(shù)據(jù)(配置文件和二進(jìn)制庫)的應(yīng)用需要使用hostPath.
9.1.2 外部Storage Provider
如果K8s部署在公有云上(比如AWS轧邪, Azure等),可以直接使用云硬盤作為Volume.
Ceph: 相對(duì)于emptyDir和hostPath悼粮,這些volume類型的最大特點(diǎn)就是不依賴K8s闲勺。Volume的底層基礎(chǔ)設(shè)施由獨(dú)立的存儲(chǔ)系統(tǒng)管理,與K8S集群分離扣猫。
9.2 PersistentVolume & PersistentVolumeClaim
Volume在可管理上有不足: 要使用Volume菜循,Pod必須事先知道如下信息:
(1)當(dāng)前Volume來自AWS,CEPH
(2)AWS/CEPT Volume 已經(jīng)提前創(chuàng)建申尤, 并且知道確切的Volume-id
Pod是開發(fā)人員維護(hù)癌幕,Volume是存儲(chǔ)系統(tǒng)管理員維護(hù)。二者之間耦合昧穿,不利于大規(guī)模系統(tǒng)的開發(fā)勺远,溝通效率低下。
K8s給出的解決方案是:PersistentVolume(PV) 和 PersistentVolumeClaim(PVC)
PV: 外部存儲(chǔ)系統(tǒng)中的一塊存儲(chǔ)空間时鸵,由管理員維護(hù)和創(chuàng)建胶逢。PV具有持久性,聲明周期獨(dú)立于Pod饰潜。
PVC: 是對(duì)PV的申請(qǐng)初坠。PVC通常由普通用戶創(chuàng)建和維護(hù)。需要為Pod分配資源時(shí)彭雾,用戶可以創(chuàng)建一個(gè)PVC碟刺,指定存儲(chǔ)資源容量的大小和訪問模式,K8s會(huì)查找并提供滿足條件的PV薯酝。
有了PVC半沽,用戶只需要告訴K8s需要什么資源爽柒,而不必關(guān)心真正的空間從哪里來、如何訪問者填。
K8s支持多種類型的PV浩村,比如AWS EBS、Ceph幔托、NFS等穴亏。
9.2.1 NFS PV
1. 建立NFS服務(wù)
#所有節(jié)點(diǎn)安裝nfs
yum install -y nfs-common nfs-utils
#在master節(jié)點(diǎn)創(chuàng)建共享目錄
mkdir /data1/nfsdata
#授權(quán)共享目錄
chmod 666 /data1/nfsdata
#編輯exports文件
cat /etc/exports
/data1/nfsdata *(rw,no_root_squash,no_all_squash,sync)
配置生效
exportfs -r
#啟動(dòng)rpc和nfs(注意順序)
systemctl start rpcbind
systemctl start nfs
#systemctl status rpcbind
systemctl status nfs
exportfs命令:
exportfs [-aruv]
-a :全部mount或者unmount /etc/exports中的內(nèi)容
-r :重新mount /etc/exports中分享出來的目錄
-u :umount 目錄
-v 在export的時(shí)候,將詳細(xì)的信息輸出到屏幕上
客戶端測試NFS
client:
yum install nfs-utils rpcbind
mkdir /data1/nfs_disk
service rpcbind start
service nfs start
$showmount -e 10.122.xx.71 #nfs-server ip
Export list for 10.122.xx.71:
/data1/nfsdata 10.122.xx.0/24
mount -t nfs -o noatime,nodiratime 10.122.xx.71:/data1/nfsdata /data1/nfs_disk
$df -h |grep nfs
10.122.xx.71:/data1/nfsdata 33T 3.1G 33T 1% /data1/nfs_disk
2. 創(chuàng)建PV
創(chuàng)建一個(gè) PV mypv1重挑,配置文件 nfs-pv1.yml
vim nfs-pv1.yml
apiVersion: v1
kind: PersistentVolume
metadata:
name: mypv1
namespace: default
spec:
capacity:
storage: 1Gi
accessModes:
- ReadWriteMany
storageClassName: nfs
persistentVolumeReclaimPolicy: Recycle
nfs:
path: "/data1/nfsdata"
server: 10.122.132.71
創(chuàng)建 mypv1
$kubectl apply -f nfs-pv.yaml
persistentvolume/mypv1 created
$kubectl get pv
NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE
mypv1 1Gi RWX Recycle Available nfs 30s
3. 創(chuàng)建PVC
PVC 就很簡單了嗓化,只需要指定 PV 的容量,訪問模式和 class谬哀。
vim nfs-pvc1.yml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: mypvc1
namespace: default
spec:
accessModes:
- ReadWriteMany
storageClassName: "nfs"
resources:
requests:
storage: 1Gi
#創(chuàng)建pvc
kubectl apply -f nfs-pvc1.yml
persistentvolumeclaim/mypvc1 created
#查看
kubectl get pvc
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE
mypvc1 Bound mypv1 1Gi RWX nfs 25s
4. 創(chuàng)建pod
上面已經(jīng)創(chuàng)建好了pv和pvc刺覆,pod中直接使用這個(gè)pvc即可
vim nfs-pod1.yml
apiVersion: v1
kind: Pod
metadata:
name: mypod1
spec:
containers:
- name: mypod1
image: busybox
args:
- /bin/sh
- -c
- sleep 10000
volumeMounts:
- name: mydata
mountPath: "/mydata"
volumes:
- name: mydata
persistentVolumeClaim:
claimName: mypvc1
##創(chuàng)建pod
kubectl apply -f nfs-pod1.yml
pod/mypod1 created
#查看
kubectl get pod -o wide |egrep "name|mypod" -i
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
mypod1 1/1 Running 0 42s 10.244.3.7 k8s-node-122132073 <none> <none>
與使用普通 Volume 的格式類似,在 volumes 中通過 persistentVolumeClaim 指定使用 mypvc1 申請(qǐng)的 Volume史煎。
5. 驗(yàn)證
kubectl exec -it mypod1 /bin/sh
/ # ls mydata
/ # echo 'hello nfs'>mydata/hello.txt
/ # ls mydata/
hello.txt
可見谦屑,在 Pod 中創(chuàng)建的文件 /mydata/hello 確實(shí)已經(jīng)保存到了 NFS 服務(wù)器目錄 /data1/nfsdata中。
9.2.2 PV的回收
如果不再需要使用 PV篇梭,可用刪除 PVC 回收 PV氢橙。
$kubectl get pv
NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE
mypv1 1Gi RWX Recycle Bound default/mypvc1 nfs 138m
$kubectl get pvc
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE
mypvc1 Terminating mypv1 1Gi RWX nfs 43m
未刪除pvc之前 pv的狀態(tài)是Bound.
$kubectl delete pvc mypvc1
persistentvolumeclaim "mypvc1" deleted
$kubectl get pvc
$kubectl delete pv mypv1
$kubectl get pv
因?yàn)?PV 的回收 恬偷,但這可能不是我們想要的結(jié)果悍手。如果我們希望保留數(shù)據(jù),可以將策略設(shè)置為Retain袍患。
9.2.3 PV的動(dòng)態(tài)供給
前面的例子中坦康,我們提前創(chuàng)建了 PV,然后通過 PVC 申請(qǐng) PV 并在 Pod 中使用诡延,這種方式叫做靜態(tài)供給(Static Provision)滞欠。
與之對(duì)應(yīng)的是動(dòng)態(tài)供給(Dynamical Provision),即如果沒有滿足 PVC 條件的 PV肆良,會(huì)動(dòng)態(tài)創(chuàng)建 PV筛璧。相比靜態(tài)供給,動(dòng)態(tài)供給有明顯的優(yōu)勢:不需要提前創(chuàng)建 PV惹恃,減少了管理員的工作量夭谤,效率高。
動(dòng)態(tài)供給是通過 StorageClass 實(shí)現(xiàn)的座舍,StorageClass 定義了如何創(chuàng)建 PV沮翔,下面是兩個(gè)例子陨帆。
1.StorageClass standard: AWS EBS(略)
#AWS EBS略
Kubernetes 支持其他多種動(dòng)態(tài)供給 PV 的 Provisioner曲秉,完整列表請(qǐng)參考 https://kubernetes.io/docs/concepts/storage/storage-classes/#provisioner
2.glusterfs provisioner
https://kubernetes.io/docs/concepts/storage/storage-classes/#glusterfs
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: slow
provisioner: kubernetes.io/glusterfs
parameters:
resturl: "http://127.0.0.1:8081"
clusterid: "630372ccdc720a92c681fb928f27b53f"
restauthenabled: "true"
restuser: "admin"
secretNamespace: "default"
secretName: "heketi-secret"
gidMin: "40000"
gidMax: "50000"
volumetype: "replicate:3"
9.3 一個(gè)數(shù)據(jù)庫例子
下面演示如何為 MySQL 數(shù)據(jù)庫提供持久化存儲(chǔ)采蚀,步驟為:
創(chuàng)建 PV 和 PVC。
部署 MySQL承二。
向 MySQL 添加數(shù)據(jù)榆鼠。
模擬節(jié)點(diǎn)宕機(jī)故障云稚,Kubernetes 將 MySQL 自動(dòng)遷移到其他節(jié)點(diǎn)侮邀。
驗(yàn)證數(shù)據(jù)一致性县忌。
首先創(chuàng)建 PV 和 PVC骨宠,配置如下:
mysql-pv.yml
vim mysql-pv.yaml
apiVersion: v1
kind: PersistentVolume
metadata:
name: mysql-pv
namespace: default
spec:
capacity:
storage: 1Gi
accessModes:
- ReadWriteOnce
storageClassName: nfs
persistentVolumeReclaimPolicy: Retain
nfs:
path: "/data1/nfsdata/mysql-pv"
server: 10.122.132.71
# 應(yīng)用
kubectl apply -f mysql-pv.yaml
persistentvolume/mysql-pv created
mysql-pvc.yml
vim mysql-pvc.yaml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: mysql-pvc
namespace: default
spec:
accessModes:
- ReadWriteOnce
storageClassName: "nfs"
resources:
requests:
storage: 1Gi
#kubectl apply -f mysql-pvc.yaml
persistentvolumeclaim/mysql-pvc created
檢查
kubectl get pv
NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE
mysql-pv 1Gi RWO Retain Bound default/mysql-pvc nfs 3m30s
[root@k8s-master-122132071 k8s]# kubectl get pvc
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE
mysql-pvc Bound mysql-pv 1Gi RWO nfs 51s
接下來部署 MySQL绎橘,配置文件如下:
vim mysql-service.yaml
apiVersion: v1
kind: Service
metadata:
name: mysql
spec:
selector:
app: mysql
ports:
- protocol: "TCP"
port: 3306
targetPort: 3306
type: LoadBalancer
---
apiVersion: apps/v1 # for versions before 1.9.0 use apps/v1beta2
kind: Deployment
metadata:
name: mysql
spec:
selector:
matchLabels:
app: mysql
template:
metadata:
labels:
app: mysql
spec:
containers:
- image: mysql:5.6
name: mysql
env:
# Use secret in real usage
- name: MYSQL_ROOT_PASSWORD
value: password
ports:
- containerPort: 3306
name: mysql
volumeMounts:
- name: mysql-persistent-storage
mountPath: /var/lib/mysql
- name: tz-config
mountPath: /etc/localtime
volumes:
- name: mysql-persistent-storage
persistentVolumeClaim:
claimName: mysql-pvc
- name: tz-config
hostPath:
path: /usr/share/zoneinfo/Asia/Shanghai
PVC mysql-pvc Bound 的 PV mysql-pv 將被 mount 到 MySQL 的數(shù)據(jù)目錄 var/lib/mysql牡借。
應(yīng)用mysql-service.yaml
#
$kubectl apply -f mysql-service.yaml
service/mysql created
deployment.apps/mysql created
#
$kubectl get pv
$kubectl get pvc
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE
mysql-pvc Bound mysql-pv 1Gi RWO nfs 16m
#
$kubectl get svc -o wide |egrep "NAME|mysql" -i
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE SELECTOR
mysql ClusterIP 10.10.76.104 <none> 3306/TCP 25m app=mysql
#
$kubectl get pods -o wide |egrep "NAME|mysql" -i
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
mysql-fd9db4d59-hjjrb 1/1 Running 0 2m11s 10.244.3.10 k8s-node-122132073 <none> <none>
#查看詳情
$kubectl describe pod mysql-fd9db4d59-hjjrb
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal Scheduled 2m59s default-scheduler Successfully assigned default/mysql-fd9db4d59-hjjrb to k8s-node-122132073
Normal Pulling 2m52s kubelet, k8s-node-122132073 Pulling image "mysql:5.6"
Normal Pulled 2m43s kubelet, k8s-node-122132073 Successfully pulled image "mysql:5.6"
Normal Created 2m43s kubelet, k8s-node-122132073 Created container mysql
Normal Started 2m43s kubelet, k8s-node-122132073 Started container mysql
#查看日志
$kubectl logs mysql-fd9db4d59-hjjrb
2019-10-08 23:11:21 1 [Note] Event Scheduler: Loaded 0 events
2019-10-08 23:11:21 1 [Note] mysqld: ready for connections.
Version: '5.6.45' socket: '/var/run/mysqld/mysqld.sock' port: 3306 MySQL Community Server (GPL)
#
可以看到mysql布署在k8s-node-122132073 上鞋仍。下面通過客戶端訪問 Service mysql:
#測試mysql
mysql -h 10.10.76.104 -P3306 -ppassword -e "select version()"
或者
$kubectl run -it --rm --image=mysql:5.6 --restart=Never mysql-client -- mysql -h mysql -ppassword
If you don't see a command prompt, try pressing enter.
mysql> select version();
+-----------+
| version() |
+-----------+
| 5.6.45 |
+-----------+
$kubectl get pods |egrep 'NAME|mysql' -i
NAME READY STATUS RESTARTS AGE
mysql-client 1/1 Running 0 5m58s
mysql-fd9db4d59-hjjrb 1/1 Running 0 12m
更新數(shù)據(jù)
mysql> show databases;
mysql> create database test;
mysql> use test;
Database changed
mysql> create table my_id(id int(4));
Query OK, 0 rows affected (0.00 sec)
mysql> insert into my_id values(111);
Query OK, 1 row affected (0.00 sec)
#查看文件
ls -l /data1/nfsdata/mysql-pv/
total 110600
-rw-rw---- 1 systemd-bus-proxy ssh_keys 12582912 Oct 8 23:40 ibdata1
-rw-rw---- 1 systemd-bus-proxy ssh_keys 50331648 Oct 8 23:40 ib_logfile0
-rw-rw---- 1 systemd-bus-proxy ssh_keys 50331648 Oct 8 23:40 ib_logfile1
drwx------ 2 systemd-bus-proxy ssh_keys 4096 Oct 8 23:40 mysql
drwx------ 2 systemd-bus-proxy ssh_keys 4096 Oct 8 23:40 performance_schema
drwx------ 2 systemd-bus-proxy ssh_keys 10 Oct 8 23:40 test
測試故障遷移
關(guān)閉 k8s-node-122132073 上的pod,看是否會(huì)進(jìn)行故障切換磕仅。
$ kubectl get pods -o wide |egrep "name|mysql" -i
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
mysql-fd9db4d59-hjjrb 1/1 Running 0 19m 10.244.3.10 k8s-node-122132073 <none> <none>
$ kubectl get svc -o wide |egrep "name|mysql" -i
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE SELECTOR
mysql ClusterIP 10.10.76.104 <none> 3306/TCP 41m app=mysql
#容器中沒有ps要手動(dòng)安裝
apt-get update && apt-get install procps
#k8s-node-122132073 停止pod
[root@k8s-node-122132073 ~]# docker ps |grep mysql
bc771f2776ab mysql "docker-entrypoint.s…" 5 minutes ago Up 5 minutes k8s_mysql_mysql-699d897494-bmfx9_default_11530308-49e9-4f09-ab6c-a5e8e3ba36cb_0
cbc13eb6f7ee registry.aliyuncs.com/google_containers/pause:3.1 "/pause" 5 minutes ago Up 5 minutes k8s_POD_mysql-699d897494-bmfx9_default_11530308-49e9-4f09-ab6c-a5e8e3ba36cb_0
#
$docker stop bc771f2776ab cbc13eb6f7ee
#檢查
mysql -h 10.10.76.104 -P 3306 -ppassword test -e 'select * from my_id'
參考:
- 【目錄】每天5分鐘家妆,玩轉(zhuǎn)kubernetes
- https://kubernetes.io/docs/concepts/storage/persistent-volumes/
- https://jimmysong.io/kubernetes-handbook/concepts/persistent-volume.html
- using-glusterfs-for-persistent-storage
- k8s的持久化存儲(chǔ)PV&&PVC
- https://github.com/rootsongjc/kubernetes-handbook/tree/master/manifests/glusterfs