kubernetes(十一) 存储& statefulset控制器-王辉的博客
source link: https://blog.51cto.com/13812615/2514194
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
数据持久化
Volume
- kubernetes中的volume提供了在容器中挂载外部存储的能力
- pod需要设置卷来源(spec.volume)和挂载点(spec.containers.volumeMounts)两个信息来使用对应的Volume
- 官方文档: https://kubernetes.io/zh/docs/concepts/storage/volumes/
emptyDir
创建一个空卷,挂载到Pod中的容器。POD删除,该卷也会被删除
- 应用场景: pod间数据的共享
$ vim pod_emptydir.yml
apiVersion: v1
kind: Pod
metadata:
name: mypod
spec:
containers:
- name: write
image: centos
command: ["bash","-c","for i in {1..100};do echo $i >> /data/hello;sleep 1;done"]
volumeMounts:
- name: data
mountPath: /data
- name: read
image: centos
command: ["bash","-c","tail -f /data/hello"]
volumeMounts:
- name: data
mountPath: /data
volumes:
- name: data
emptyDir: {}
$ kubectl logs mypod -c read -f #指定容器读
$ kubectl exec -it mypod -c read -- sh #-c 进入指定的容器
$ kubectl get pod -o wide #查看调度节点
$ cd /var/lib/kubelet/pods/ #进入调度节点的kubelet目录
$ docker ps | grep mypod # 查看pod对应的数据目录
$ cd /var/lib/kubelet/pods/1e6cab01-c048-4bb2-ae47-fccf581741d4/volumes/kubernetes.io~empty-dir #就可以看到对应的卷数据
hostPath
挂载node文件系统上的文件或者目录到Pod中的容器
- 应用场景: pod中容器需要访问宿主机文件
$ vim vim pod_hostpath.yml
apiVersion: v1
kind: Pod
metadata:
name: my-pod
spec:
containers:
- name: busybox
image: busybox
args:
- /bin/sh
- -c
- sleep 36000
volumeMounts:
- name: data
mountPath: /data
volumes:
- name: data
hostPath:
path: /tmp
type: Directory
$ kubectl apply -f pod_hostpath.yml
$ kubectl exec my-pod -- ls /data
NFS共享存储的使用
- 环境准备
- 准备一台nfs服务器(192.168.56.18),然后与k8s集群都安装
nfs-utils
- 准备一台nfs服务器(192.168.56.18),然后与k8s集群都安装
$ vim /etc/exports
/ifs/kubernetes *(rw,no_root_squash)
$ systemctl restart nfs
$ showmount -e
- 挂载nfs到k8s集群
$ mount -t nfs 192.168.56.18:/ifs/kubernetes /mnt
$ kubectl create deploy web --image=nginx --dry-run -o yaml > deploy_nfs.yml
$ vim deploy_nfs.yml
apiVersion: apps/v1
kind: Deployment
metadata:
labels:
app: web
name: web
spec:
replicas: 1
selector:
matchLabels:
app: web
strategy: {}
template:
metadata:
labels:
app: web
spec:
containers:
- image: nginx
name: nginx
resources: {}
volumeMounts:
- name: data
mountPath: /usr/share/nginx/html
volumes:
- name: data
nfs:
server: 192.168.56.18
path: /ifs/kubernetes
$ kubectl apply -f deploy_nfs.yml
$ kubectl get pod && kubectl exec -it web-xxxx bash
root@web-587c9fb4bd-9r7r4:/# cd /usr/share/nginx/html
root@web-587c9fb4bd-9r7r4:/usr/share/nginx/html# echo "aa" > index.html
$ kubectl scale deploy web --replicas=3 #扩容
$ kubectl expose deploy web --port=80 --target-port=80 --type=NodePort
- PersistantVolume : 对存储资源创建和使用的抽象,使得存储作为集群资源的管理
pv的静态供给
kubernetes支持持久卷的存储插件: https://kubernetes.io/docs/concepts/storage/persistent-volumes/
- 让用户不在关心具体的Volume实现细节
定义数据卷(准备pv)
$ cd /ifs/kubernetes/ && mkdir pv01 pv02 pv03 # 这是在nfs服务器上执行(192.168.56.18)
##### 接下来在k8s master上操作
$ vim pv01.yml
apiVersion: v1
kind: PersistentVolume
metadata:
name: pv01
spec:
capacity:
storage: 3Gi
accessModes:
- ReadWriteMany
nfs:
path: /ifs/kubernetes/pv01
server: 192.168.56.18
$ kubectl apply -f pv01.yml
$ kubectl get pv #查看pv
####### pod使用pv
$ vim pod_pv.yml
apiVersion: v1
kind: Pod
metadata:
name: my-pod
spec:
containers:
- name: my-pod
image: nginx
ports:
- containerPort: 80
volumeMounts:
- name: www
mountPath: /usr/share/nginx/html/
volumes:
- name: www
persistentVolumeClaim:
claimName: my-pvc
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: my-pvc
spec:
accessModes:
- ReadWriteMany
resources:
requests:
storage: 1Gi
$ kubectl apply -f pod_pv.yml
$ kubectl get pv,pvc #发现pv绑定到my-pv
#### 数据测试
$ kubectl exec -it my-pod -- bash
root@my-pod:/# cd /usr/share/nginx/html/
root@my-pod:/usr/share/nginx/html# echo "hello" > index.html
### nfs服务器查看数据
$ ls /ifs/kubernetes/pv01/
index.html
pv动态供给
- Dynamic Provisioning机制工作的核心在于StorageClass的API对象。
- StorageClass声明存储插件,用于自动创建PV。
- Kubernetes支持动态供给的存储插件:https://kubernetes.io/docs/concepts/storage/storage-classes
pv动态供给NFS
由于K8S不支持NFS动态供给,还需要先安装上图中的nfs-client-provisioner插件:
# cd nfs-client
# vi deployment.yaml # 修改里面NFS地址和共享目录为你的
# kubectl apply -f .
# kubectl get pods
$ vim pod_pvc01.yml
apiVersion: v1
kind: Pod
metadata:
name: my-pod
spec:
containers:
- name: nginx
image: nginx:latest
ports:
- containerPort: 80
volumeMounts:
- name: www
mountPath: /usr/share/nginx/html
volumes:
- name: www
persistentVolumeClaim:
claimName: my-pvc
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: my-pvc
spec:
storageClassName: "managed-nfs-storage"
accessModes:
- ReadWriteMany
resources:
requests:
storage: 1Gi
这次会自动创建5GPV并与PVC绑定。
kubectl get pv,pvc
测试方法同上,进入到容器中/usr/share/nginx/html(PV挂载目录)目录下创建一个文件测试。
再切换到NFS服务器,会发现下面目录,该目录是自动创建的PV挂载点。进入到目录会发现刚在容器创建的文件。
$ ls /ifs/kubernetes/default-my-pvc-pvc-ab48e34d-398d-4f31-b46b-93ff0853b29d
有状态应用部署
StatefulSet控制器概述
StatefulSet:
-
部署有状态应用
- 解决Pod独立生命周期,保持Pod启动顺序和唯一性
-
稳定,唯一的网络标识符,持久存储
-
有序,优雅的部署和扩展、删除和终止
- 有序,滚动更新
应用场景:数据库主从,消息中间件kafka集群,redis集群等
稳定的网络ID
说起StatefulSet稳定的网络标识符,不得不从Headless说起了
标准Service:
apiVersion: v1
kind: Service
metadata:
name: my-service
spec:
selector:
app: nginx
ports:
- protocol: TCP
port: 80
targetPort: 9376
无头Service(Headless Service)
apiVersion: v1
kind: Service
metadata:
name: my-service
spec:
clusterIP: None
selector:
app: nginx
ports:
- protocol: TCP
port: 80
targetPort: 9376
标准Service与无头Service区别是clusterIP: None,
这表示创建Service不要为我(Headless Service)分配Cluster IP,因为我不需要。
为什么标准Service需要?
这就是无状态和有状态的控制器设计理念了,无状态的应用Pod是完全对等的,提供相同的服务,可以在飘移在任意节点,例如Web。而像一些分布式应用程序,例如zookeeper集群、etcd集群、mysql主从,每个实例都会维护着一种状态,每个实例都各自的数据,并且每个实例之间必须有固定的访问地址(组建集群),这就是有状态应用。所以有状态应用是不能像无状态应用那样,创建一个标准Service,然后访问ClusterIP负载均衡到一组Pod上。这也是为什么无头Service不需要ClusterIP的原因,它要的是能为每个Pod固定一个”身份“。
举例说明:
vim stateful_nginx.yml
apiVersion: v1
kind: Service
metadata:
name: headless-svc
spec:
clusterIP: None
selector:
app: nginx
ports:
- protocol: TCP
port: 80
targetPort: 80
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: web
spec:
selector:
matchLabels:
app: nginx
serviceName: "headless-svc"
replicas: 3
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx
ports:
- containerPort: 80
name: web
相比之前讲的yaml,这次多了一个serviceName: “nginx”字段,这就告诉StatefulSet控制器要使用nginx这个headless service来保证Pod的身份。
[root@centos7-node4 statefulset]# kubectl get pods
NAME READY STATUS RESTARTS AGE
nfs-client-provisioner-75f9fd7b57-67jtk 1/1 Running 0 121m
web-0 1/1 Running 0 72s
web-1 1/1 Running 0 55s
web-2 0/1 ContainerCreating 0 45s
每个pod的名称就是statefulSet的序列号获取主机名称的
[root@centos7-node4 ~]# kubectl exec web-0 -- hostname
web-0
[root@centos7-node4 ~]# kubectl exec web-1 -- hostname
web-1
不过,相信你也已经注意到了,尽管 web-0.nginx 这条记录本身不会变,但它解析到的 Pod 的 IP 地址,并不是固定的。这就意味着,对于“有状态应用”实例的访问,你必须使用 DNS 记录或者 hostname 的方式,而绝不应该直接访问这些 Pod 的 IP 地址。
以下是Cluster Domain,Service name,StatefulSet名称以及它们如何影响StatefulSet的Pod的DNS名称的一些选择示例。
Cluster Domain | Service (ns/name) | StatefulSet (ns/name) | StatefulSet Domain | Pod DNS | Pod Hostname |
---|---|---|---|---|---|
cluster.local | default/nginx | default/web | nginx.default.svc.cluster.local | web-{0..N-1}.nginx.default.svc.cluster.local | web-{0..N-1} |
cluster.local | foo/nginx | foo/web | nginx.foo.svc.cluster.local | web-{0..N-1}.nginx.foo.svc.cluster.local | web-{0..N-1} |
kube.local | foo/nginx | foo/web | nginx.foo.svc.kube.local | web-{0..N-1}.nginx.foo.svc.kube.local | web-{0..N-1} |
稳定的存储
StatefulSet的存储卷使用VolumeClaimTemplate创建,称为卷申请模板,当StatefulSet使用VolumeClaimTemplate 创建一个PersistentVolume时,同样也会为每个Pod分配并创建一个编号的PVC。
$ vim stateful_pvc.yml
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: web
spec:
selector:
matchLabels:
app: nginx
serviceName: "headless-svc"
replicas: 3
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx
ports:
- containerPort: 80
name: web
volumeMounts:
- name: www
mountPath: /usr/share/nginx/html
volumeClaimTemplates:
- metadata:
name: www
spec:
accessModes: [ "ReadWriteOnce" ]
storageClassName: "managed-nfs-storage"
resources:
requests:
storage: 1Gi
- 查看占用存储卷的情况
kubectl get pv,pvc
NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE
persistentvolume/pvc-3c5fc93f-e90f-45d1-992f-6589c4901d1b 1Gi RWO Delete Bound default/www-web-1 managed-nfs-storage 100s
persistentvolume/pvc-60c524ff-bae9-4195-bf57-828e20e43ac8 1Gi RWO Delete Bound default/www-web-0 managed-nfs-storage 119s
persistentvolume/pvc-83963bb0-a0fb-481b-9a00-d9dc75f3e60b 1Gi RWO Delete Bound default/www-web-2 managed-nfs-storage 86s
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE
persistentvolumeclaim/www-web-0 Bound pvc-60c524ff-bae9-4195-bf57-828e20e43ac8 1Gi RWO managed-nfs-storage 119s
persistentvolumeclaim/www-web-1 Bound pvc-3c5fc93f-e90f-45d1-992f-6589c4901d1b 1Gi RWO managed-nfs-storage 100s
persistentvolumeclaim/www-web-2 Bound pvc-83963bb0-a0fb-481b-9a00-d9dc75f3e60b 1Gi RWO managed-nfs-storage 86s
结果得知,StatefulSet为每个Pod分配专属的PVC及编号。每个PVC绑定对应的 PV,从而保证每一个 Pod 都拥有一个独立的 Volume。
在这种情况下,删除Pods或StatefulSet时,它所对应的PVC和PV不会被删除。所以,当这个Pod被重新创建出现之后,Kubernetes会为它找到同样编号的PVC,挂载这个PVC对应的Volume,从而获取到以前保存在 Volume 里的数据。
StatefulSet与Deployment区别:有身份的!
有状态应用参考 https://github.com/operator-framework/awesome-operators
身份三要素:
- 存储(PVC)
这里为你准备了一个etcd集群,来感受下有状态部署: https://github.com/lizhenliang/k8s-statefulset/tree/master/etcd
Recommend
About Joyk
Aggregate valuable and interesting links.
Joyk means Joy of geeK